/**
* Executes like-related actions.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\Like
- * @deprecated since 5.2, use \wcf\data\reaction\ReactionAction instead
- *
- * @method Like create()
- * @method LikeEditor[] getObjects()
- * @method LikeEditor getSingleObject()
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Like
+ * @deprecated since 5.2, use \wcf\data\reaction\ReactionAction instead
+ *
+ * @method Like create()
+ * @method LikeEditor[] getObjects()
+ * @method LikeEditor getSingleObject()
*/
-class LikeAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction {
- /**
- * @inheritDoc
- */
- protected $allowGuestAccess = ['getGroupedUserList', 'getLikeDetails', 'load'];
-
- /**
- * @inheritDoc
- */
- protected $className = LikeEditor::class;
-
- /**
- * likeable object
- * @var \wcf\data\like\object\ILikeObject
- */
- public $likeableObject = null;
-
- /**
- * object type object
- * @var \wcf\data\object\type\ObjectType
- */
- public $objectType = null;
-
- /**
- * like object type provider object
- * @var ILikeObjectTypeProvider
- */
- public $objectTypeProvider = null;
-
- /**
- * Validates parameters to fetch like details.
- */
- public function validateGetLikeDetails() {
- $this->validateObjectParameters();
- }
-
- /**
- * Returns like details.
- *
- * @return string[]
- */
- public function getLikeDetails() {
- $sql = "SELECT userID, likeValue
- FROM wcf".WCF_N."_like
- WHERE objectID = ?
- AND objectTypeID = ?
- ORDER BY time DESC";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([
- $this->parameters['data']['objectID'],
- $this->objectType->objectTypeID
- ]);
- $data = [
- Like::LIKE => [],
- Like::DISLIKE => []
- ];
- while ($row = $statement->fetchArray()) {
- $data[$row['likeValue']][] = $row['userID'];
- }
-
- $values = [];
- if (!empty($data[Like::LIKE])) {
- $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
- /** @noinspection PhpUndefinedMethodInspection */
- $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
- }
- if (!empty($data[Like::DISLIKE])) {
- $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
- /** @noinspection PhpUndefinedMethodInspection */
- $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
- }
-
- // load user profiles
- GroupedUserList::loadUsers();
-
- WCF::getTPL()->assign([
- 'groupedUsers' => $values
- ]);
-
- return [
- 'containerID' => $this->parameters['data']['containerID'],
- 'template' => WCF::getTPL()->fetch('groupedUserList')
- ];
- }
-
- /**
- * Validates parameters for like-related actions.
- */
- public function validateLike() {
- $this->validateObjectParameters();
-
- // check permissions
- if (!WCF::getUser()->userID || !WCF::getSession()->getPermission('user.like.canLike')) {
- throw new PermissionDeniedException();
- }
-
- // check if liking own content but forbidden by configuration
- $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
- $this->likeableObject->setObjectType($this->objectType);
- if ($this->likeableObject->getUserID() == WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
- }
-
- /**
- * @inheritDoc
- */
- public function like() {
- return $this->updateLike(Like::LIKE);
- }
-
- /**
- * @inheritDoc
- */
- public function validateDislike() {
- // No longer supported since 5.2.
- throw new PermissionDeniedException();
- }
-
- /**
- * @inheritDoc
- */
- public function dislike() {
- return $this->updateLike(Like::DISLIKE);
- }
-
- /**
- * Sets like/dislike for an object, executing this method again with the same parameters
- * will revert the status (removing like/dislike).
- *
- * @param integer $likeValue
- * @return array
- */
- protected function updateLike($likeValue) {
- $likeData = LikeHandler::getInstance()->like($this->likeableObject, WCF::getUser(), $likeValue);
-
- // handle activity event
- if (UserActivityEventHandler::getInstance()->getObjectTypeID($this->objectType->objectType.'.recentActivityEvent')) {
- if ($likeData['data']['liked'] == 1) {
- UserActivityEventHandler::getInstance()->fireEvent($this->objectType->objectType.'.recentActivityEvent', $this->parameters['data']['objectID'], $this->likeableObject->getLanguageID());
- }
- else {
- UserActivityEventHandler::getInstance()->removeEvent($this->objectType->objectType.'.recentActivityEvent', $this->parameters['data']['objectID']);
- }
- }
-
- // get stats
- return [
- 'likes' => ($likeData['data']['likes'] === null) ? 0 : $likeData['data']['likes'],
- 'dislikes' => ($likeData['data']['dislikes'] === null) ? 0 : $likeData['data']['dislikes'],
- 'cumulativeLikes' => ($likeData['data']['cumulativeLikes'] === null) ? 0 : $likeData['data']['cumulativeLikes'],
- 'isLiked' => ($likeData['data']['liked'] == 1) ? 1 : 0,
- 'isDisliked' => ($likeData['data']['liked'] == -1) ? 1 : 0,
- 'containerID' => $this->parameters['data']['containerID'],
- 'newValue' => $likeData['newValue'],
- 'oldValue' => $likeData['oldValue'],
- 'users' => $likeData['users']
- ];
- }
-
- /**
- * Validates permissions for given object.
- */
- protected function validateObjectParameters() {
- if (!MODULE_LIKE) {
- throw new PermissionDeniedException();
- }
-
- $this->readString('containerID', false, 'data');
- $this->readInteger('objectID', false, 'data');
- $this->readString('objectType', false, 'data');
-
- $this->objectType = LikeHandler::getInstance()->getObjectType($this->parameters['data']['objectType']);
- if ($this->objectType === null) {
- throw new UserInputException('objectType');
- }
-
- $this->objectTypeProvider = $this->objectType->getProcessor();
- $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
- $this->likeableObject->setObjectType($this->objectType);
- if ($this->objectTypeProvider instanceof IRestrictedLikeObjectTypeProvider) {
- if (!$this->objectTypeProvider->canViewLikes($this->likeableObject)) {
- throw new PermissionDeniedException();
- }
- }
- else if (!$this->objectTypeProvider->checkPermissions($this->likeableObject)) {
- throw new PermissionDeniedException();
- }
- }
-
- /**
- * @inheritDoc
- */
- public function validateGetGroupedUserList() {
- $this->validateObjectParameters();
-
- $this->readInteger('pageNo');
-
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * @inheritDoc
- */
- public function getGroupedUserList() {
- // fetch number of pages
- $sql = "SELECT COUNT(*)
- FROM wcf".WCF_N."_like
- WHERE objectID = ?
- AND objectTypeID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([
- $this->parameters['data']['objectID'],
- $this->objectType->objectTypeID
- ]);
- $pageCount = ceil($statement->fetchSingleColumn() / 20);
-
- $sql = "SELECT userID, likeValue
- FROM wcf".WCF_N."_like
- WHERE objectID = ?
- AND objectTypeID = ?
- ORDER BY likeValue DESC, time DESC";
- $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
- $statement->execute([
- $this->parameters['data']['objectID'],
- $this->objectType->objectTypeID
- ]);
- $data = [
- Like::LIKE => [],
- Like::DISLIKE => []
- ];
- while ($row = $statement->fetchArray()) {
- $data[$row['likeValue']][] = $row['userID'];
- }
-
- $values = [];
- if (!empty($data[Like::LIKE])) {
- $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
- /** @noinspection PhpUndefinedMethodInspection */
- $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
- }
- if (!empty($data[Like::DISLIKE])) {
- $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
- /** @noinspection PhpUndefinedMethodInspection */
- $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
- }
-
- // load user profiles
- GroupedUserList::loadUsers();
-
- WCF::getTPL()->assign([
- 'groupedUsers' => $values
- ]);
-
- return [
- 'containerID' => $this->parameters['data']['containerID'],
- 'pageCount' => $pageCount,
- 'template' => WCF::getTPL()->fetch('groupedUserList')
- ];
- }
-
- /**
- * Validates parameters to load likes.
- */
- public function validateLoad() {
- if (!MODULE_LIKE) {
- throw new IllegalLinkException();
- }
-
- $this->readInteger('lastLikeTime', true);
- $this->readInteger('userID');
- $this->readInteger('likeValue');
- $this->readString('likeType');
-
- $user = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
-
- if ($user === null) {
- throw new IllegalLinkException();
- }
-
- if ($user->isProtected()) {
- throw new PermissionDeniedException();
- }
- }
-
- /**
- * Loads a list of likes.
- *
- * @return array
- */
- public function load() {
- $likeList = new ViewableLikeList();
- if ($this->parameters['lastLikeTime']) {
- $likeList->getConditionBuilder()->add("like_table.time < ?", [$this->parameters['lastLikeTime']]);
- }
- if ($this->parameters['likeType'] == 'received') {
- $likeList->getConditionBuilder()->add("like_table.objectUserID = ?", [$this->parameters['userID']]);
- }
- else {
- $likeList->getConditionBuilder()->add("like_table.userID = ?", [$this->parameters['userID']]);
- }
- $likeList->getConditionBuilder()->add("like_table.likeValue = ?", [$this->parameters['likeValue']]);
- $likeList->readObjects();
- if (!count($likeList)) {
- return [];
- }
-
- // parse template
- WCF::getTPL()->assign([
- 'likeList' => $likeList
- ]);
-
- return [
- 'lastLikeTime' => $likeList->getLastLikeTime(),
- 'template' => WCF::getTPL()->fetch('userProfileLikeItem')
- ];
- }
-
- /**
- * Copies likes from one object id to another.
- */
- public function copy() {
- $reactionAction = new ReactionAction([], 'copy', $this->getParameters());
- $reactionAction->executeAction();
- }
+class LikeAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction
+{
+ /**
+ * @inheritDoc
+ */
+ protected $allowGuestAccess = ['getGroupedUserList', 'getLikeDetails', 'load'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $className = LikeEditor::class;
+
+ /**
+ * likeable object
+ * @var \wcf\data\like\object\ILikeObject
+ */
+ public $likeableObject;
+
+ /**
+ * object type object
+ * @var \wcf\data\object\type\ObjectType
+ */
+ public $objectType;
+
+ /**
+ * like object type provider object
+ * @var ILikeObjectTypeProvider
+ */
+ public $objectTypeProvider;
+
+ /**
+ * Validates parameters to fetch like details.
+ */
+ public function validateGetLikeDetails()
+ {
+ $this->validateObjectParameters();
+ }
+
+ /**
+ * Returns like details.
+ *
+ * @return string[]
+ */
+ public function getLikeDetails()
+ {
+ $sql = "SELECT userID, likeValue
+ FROM wcf" . WCF_N . "_like
+ WHERE objectID = ?
+ AND objectTypeID = ?
+ ORDER BY time DESC";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ $this->parameters['data']['objectID'],
+ $this->objectType->objectTypeID,
+ ]);
+ $data = [
+ Like::LIKE => [],
+ Like::DISLIKE => [],
+ ];
+ while ($row = $statement->fetchArray()) {
+ $data[$row['likeValue']][] = $row['userID'];
+ }
+
+ $values = [];
+ if (!empty($data[Like::LIKE])) {
+ $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
+ /** @noinspection PhpUndefinedMethodInspection */
+ $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
+ }
+ if (!empty($data[Like::DISLIKE])) {
+ $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
+ /** @noinspection PhpUndefinedMethodInspection */
+ $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
+ }
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign([
+ 'groupedUsers' => $values,
+ ]);
+
+ return [
+ 'containerID' => $this->parameters['data']['containerID'],
+ 'template' => WCF::getTPL()->fetch('groupedUserList'),
+ ];
+ }
+
+ /**
+ * Validates parameters for like-related actions.
+ */
+ public function validateLike()
+ {
+ $this->validateObjectParameters();
+
+ // check permissions
+ if (!WCF::getUser()->userID || !WCF::getSession()->getPermission('user.like.canLike')) {
+ throw new PermissionDeniedException();
+ }
+
+ // check if liking own content but forbidden by configuration
+ $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
+ $this->likeableObject->setObjectType($this->objectType);
+ if ($this->likeableObject->getUserID() == WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function like()
+ {
+ return $this->updateLike(Like::LIKE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateDislike()
+ {
+ // No longer supported since 5.2.
+ throw new PermissionDeniedException();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dislike()
+ {
+ return $this->updateLike(Like::DISLIKE);
+ }
+
+ /**
+ * Sets like/dislike for an object, executing this method again with the same parameters
+ * will revert the status (removing like/dislike).
+ *
+ * @param int $likeValue
+ * @return array
+ */
+ protected function updateLike($likeValue)
+ {
+ $likeData = LikeHandler::getInstance()->like($this->likeableObject, WCF::getUser(), $likeValue);
+
+ // handle activity event
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($this->objectType->objectType . '.recentActivityEvent')) {
+ if ($likeData['data']['liked'] == 1) {
+ UserActivityEventHandler::getInstance()->fireEvent(
+ $this->objectType->objectType . '.recentActivityEvent',
+ $this->parameters['data']['objectID'],
+ $this->likeableObject->getLanguageID()
+ );
+ } else {
+ UserActivityEventHandler::getInstance()->removeEvent(
+ $this->objectType->objectType . '.recentActivityEvent',
+ $this->parameters['data']['objectID']
+ );
+ }
+ }
+
+ // get stats
+ return [
+ 'likes' => ($likeData['data']['likes'] === null) ? 0 : $likeData['data']['likes'],
+ 'dislikes' => ($likeData['data']['dislikes'] === null) ? 0 : $likeData['data']['dislikes'],
+ 'cumulativeLikes' => ($likeData['data']['cumulativeLikes'] === null) ? 0 : $likeData['data']['cumulativeLikes'],
+ 'isLiked' => ($likeData['data']['liked'] == 1) ? 1 : 0,
+ 'isDisliked' => ($likeData['data']['liked'] == -1) ? 1 : 0,
+ 'containerID' => $this->parameters['data']['containerID'],
+ 'newValue' => $likeData['newValue'],
+ 'oldValue' => $likeData['oldValue'],
+ 'users' => $likeData['users'],
+ ];
+ }
+
+ /**
+ * Validates permissions for given object.
+ */
+ protected function validateObjectParameters()
+ {
+ if (!MODULE_LIKE) {
+ throw new PermissionDeniedException();
+ }
+
+ $this->readString('containerID', false, 'data');
+ $this->readInteger('objectID', false, 'data');
+ $this->readString('objectType', false, 'data');
+
+ $this->objectType = ReactionHandler::getInstance()->getObjectType($this->parameters['data']['objectType']);
+ if ($this->objectType === null) {
+ throw new UserInputException('objectType');
+ }
+
+ $this->objectTypeProvider = $this->objectType->getProcessor();
+ $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
+ $this->likeableObject->setObjectType($this->objectType);
+ if ($this->objectTypeProvider instanceof IRestrictedLikeObjectTypeProvider) {
+ if (!$this->objectTypeProvider->canViewLikes($this->likeableObject)) {
+ throw new PermissionDeniedException();
+ }
+ } elseif (!$this->objectTypeProvider->checkPermissions($this->likeableObject)) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateGetGroupedUserList()
+ {
+ $this->validateObjectParameters();
+
+ $this->readInteger('pageNo');
++
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getGroupedUserList()
+ {
+ // fetch number of pages
+ $sql = "SELECT COUNT(*)
+ FROM wcf" . WCF_N . "_like
+ WHERE objectID = ?
+ AND objectTypeID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ $this->parameters['data']['objectID'],
+ $this->objectType->objectTypeID,
+ ]);
+ $pageCount = \ceil($statement->fetchSingleColumn() / 20);
+
+ $sql = "SELECT userID, likeValue
+ FROM wcf" . WCF_N . "_like
+ WHERE objectID = ?
+ AND objectTypeID = ?
+ ORDER BY likeValue DESC, time DESC";
+ $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
+ $statement->execute([
+ $this->parameters['data']['objectID'],
+ $this->objectType->objectTypeID,
+ ]);
+ $data = [
+ Like::LIKE => [],
+ Like::DISLIKE => [],
+ ];
+ while ($row = $statement->fetchArray()) {
+ $data[$row['likeValue']][] = $row['userID'];
+ }
+
+ $values = [];
+ if (!empty($data[Like::LIKE])) {
+ $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
+ /** @noinspection PhpUndefinedMethodInspection */
+ $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
+ }
+ if (!empty($data[Like::DISLIKE])) {
+ $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
+ /** @noinspection PhpUndefinedMethodInspection */
+ $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
+ }
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign([
+ 'groupedUsers' => $values,
+ ]);
+
+ return [
+ 'containerID' => $this->parameters['data']['containerID'],
+ 'pageCount' => $pageCount,
+ 'template' => WCF::getTPL()->fetch('groupedUserList'),
+ ];
+ }
+
+ /**
+ * Validates parameters to load likes.
+ */
+ public function validateLoad()
+ {
+ if (!MODULE_LIKE) {
+ throw new IllegalLinkException();
+ }
+
+ $this->readInteger('lastLikeTime', true);
+ $this->readInteger('userID');
+ $this->readInteger('likeValue');
+ $this->readString('likeType');
+
+ $user = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
+
+ if ($user === null) {
+ throw new IllegalLinkException();
+ }
+
+ if ($user->isProtected()) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Loads a list of likes.
+ *
+ * @return array
+ */
+ public function load()
+ {
+ $likeList = new ViewableLikeList();
+ if ($this->parameters['lastLikeTime']) {
+ $likeList->getConditionBuilder()->add("like_table.time < ?", [$this->parameters['lastLikeTime']]);
+ }
+ if ($this->parameters['likeType'] == 'received') {
+ $likeList->getConditionBuilder()->add("like_table.objectUserID = ?", [$this->parameters['userID']]);
+ } else {
+ $likeList->getConditionBuilder()->add("like_table.userID = ?", [$this->parameters['userID']]);
+ }
+ $likeList->getConditionBuilder()->add("like_table.likeValue = ?", [$this->parameters['likeValue']]);
+ $likeList->readObjects();
+ if (!\count($likeList)) {
+ return [];
+ }
+
+ // parse template
+ WCF::getTPL()->assign([
+ 'likeList' => $likeList,
+ ]);
+
+ return [
+ 'lastLikeTime' => $likeList->getLastLikeTime(),
+ 'template' => WCF::getTPL()->fetch('userProfileLikeItem'),
+ ];
+ }
+
+ /**
+ * Copies likes from one object id to another.
+ */
+ public function copy()
+ {
+ $reactionAction = new ReactionAction([], 'copy', $this->getParameters());
+ $reactionAction->executeAction();
+ }
}
/**
* Executes media file-related actions.
- *
- * @author Matthias Schmidt
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\Media
- * @since 3.0
- *
- * @method Media create()
- * @method MediaEditor[] getObjects()
- * @method MediaEditor getSingleObject()
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Media
+ * @since 3.0
+ *
+ * @method Media create()
+ * @method MediaEditor[] getObjects()
+ * @method MediaEditor getSingleObject()
*/
-class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction, IUploadAction {
- /**
- * number of media files per media manager dialog page
- */
- const ITEMS_PER_MANAGER_DIALOG_PAGE = 50;
-
- /**
- * @inheritDoc
- */
- public function validateUpload() {
- WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
-
- $this->readBoolean('imagesOnly', true);
- $this->readInteger('categoryID', true);
-
- /** @noinspection PhpUndefinedMethodInspection */
- $this->parameters['__files']->validateFiles(new MediaUploadFileValidationStrategy($this->parameters['imagesOnly']));
-
- if ($this->parameters['categoryID']) {
- $category = CategoryHandler::getInstance()->getCategory($this->parameters['categoryID']);
- if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
- throw new UserInputException('categoryID');
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function upload() {
- $additionalData = ['username' => WCF::getUser()->username];
- if ($this->parameters['categoryID']) {
- $additionalData['categoryID'] = $this->parameters['categoryID'];
- }
-
- // save files
- $saveStrategy = new DefaultUploadFileSaveStrategy(self::class, [
- 'generateThumbnails' => true,
- 'rotateImages' => true
- ], $additionalData);
-
- /** @noinspection PhpUndefinedMethodInspection */
- $this->parameters['__files']->saveFiles($saveStrategy);
-
- /** @var Media[] $mediaFiles */
- $mediaFiles = $saveStrategy->getObjects();
-
- $result = [
- 'errors' => [],
- 'media' => []
- ];
-
- if (!empty($mediaFiles)) {
- $mediaIDs = $mediaToFileID = [];
- foreach ($mediaFiles as $internalFileID => $media) {
- $mediaIDs[] = $media->mediaID;
- $mediaToFileID[$media->mediaID] = $internalFileID;
- }
-
- // fetch media objects from database
- $mediaList = new ViewableMediaList();
- $mediaList->setObjectIDs($mediaIDs);
- $mediaList->readObjects();
-
- foreach ($mediaList as $media) {
- $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
- }
- }
-
- /** @var UploadFile[] $files */
- /** @noinspection PhpUndefinedMethodInspection */
- $files = $this->parameters['__files']->getFiles();
- foreach ($files as $file) {
- if ($file->getValidationErrorType()) {
- $result['errors'][$file->getInternalFileID()] = [
- 'filename' => $file->getFilename(),
- 'filesize' => $file->getFilesize(),
- 'errorType' => $file->getValidationErrorType()
- ];
- }
- }
-
- return $result;
- }
-
- /**
- * Generates thumbnails.
- */
- public function generateThumbnails() {
- if (empty($this->objects)) {
- $this->readObjects();
- }
-
- $saveStrategy = new DefaultUploadFileSaveStrategy(self::class);
-
- foreach ($this->getObjects() as $mediaEditor) {
- if ($mediaEditor->getDecoratedObject()->isImage) {
- $saveStrategy->generateThumbnails($mediaEditor->getDecoratedObject());
- }
- }
- }
-
- /**
- * Returns the data of the media file to be returned by AJAX requests.
- *
- * @param Media|ViewableMedia $media media files whose data will be returned
- * @return string[]
- */
- protected function getMediaData($media) {
- return [
- 'altText' => $media instanceof ViewableMedia ? $media->altText : [],
- 'caption' => $media instanceof ViewableMedia ? $media->caption : [],
- 'captionEnableHtml' => $media->captionEnableHtml,
- 'categoryID' => $media->categoryID,
- 'elementTag' => $media instanceof ViewableMedia ? $media->getElementTag($this->parameters['elementTagSize'] ?? 144) : '',
- 'elementTag48' => $media instanceof ViewableMedia ? $media->getElementTag(48) : '',
- 'fileHash' => $media->fileHash,
- 'filename' => $media->filename,
- 'filesize' => $media->filesize,
- 'formattedFilesize' => FileUtil::formatFilesize($media->filesize),
- 'fileType' => $media->fileType,
- 'height' => $media->height,
- 'languageID' => $media->languageID,
- 'imageDimensions' => $media->isImage ? WCF::getLanguage()->getDynamicVariable('wcf.media.imageDimensions.value', [
- 'media' => $media,
- ]) : '',
- 'isImage' => $media->isImage,
- 'isMultilingual' => $media->isMultilingual,
- 'largeThumbnailHeight' => $media->largeThumbnailHeight,
- 'largeThumbnailLink' => $media->largeThumbnailType ? $media->getThumbnailLink('large') : '',
- 'largeThumbnailType' => $media->largeThumbnailType,
- 'largeThumbnailWidth' => $media->largeThumbnailWidth,
- 'link' => $media->getLink(),
- 'mediaID' => $media->mediaID,
- 'mediumThumbnailHeight' => $media->mediumThumbnailHeight,
- 'mediumThumbnailLink' => $media->mediumThumbnailType ? $media->getThumbnailLink('medium') : '',
- 'mediumThumbnailType' => $media->mediumThumbnailType,
- 'mediumThumbnailWidth' => $media->mediumThumbnailWidth,
- 'smallThumbnailHeight' => $media->smallThumbnailHeight,
- 'smallThumbnailLink' => $media->smallThumbnailType ? $media->getThumbnailLink('small') : '',
- 'smallThumbnailTag' => $media->smallThumbnailType ? $media->getThumbnailTag('small') : '',
- 'smallThumbnailType' => $media->smallThumbnailType,
- 'smallThumbnailWidth' => $media->smallThumbnailWidth,
- 'tinyThumbnailHeight' => $media->tinyThumbnailHeight,
- 'tinyThumbnailLink' => $media->tinyThumbnailType ? $media->getThumbnailLink('tiny') : '',
- 'tinyThumbnailType' => $media->tinyThumbnailType,
- 'tinyThumbnailWidth' => $media->tinyThumbnailWidth,
- 'title' => $media instanceof ViewableMedia ? $media->title : [],
- 'uploadTime' => $media->uploadTime,
- 'userID' => $media->userID,
- 'userLink' => $media->userID ? LinkHandler::getInstance()->getLink('User', [
- 'id' => $media->userID,
- 'title' => $media->username
- ]) : '',
- 'userLinkElement' => $media instanceof ViewableMedia ? WCF::getTPL()->fetchString(
- WCF::getTPL()->getCompiler()->compileString('userLink', '{user object=$userProfile}')['template'],
- ['userProfile' => $media->getUserProfile()]
- ) : '',
- 'username' => $media->username,
- 'width' => $media->width
- ];
- }
-
- /**
- * Validates the 'getManagementDialog' action.
- */
- public function validateGetManagementDialog() {
- if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
- throw new PermissionDeniedException();
- }
-
- $this->readBoolean('imagesOnly', true);
-
- $this->readString('mode');
- if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
- throw new UserInputException('mode');
- }
- }
-
- /**
- * Returns the dialog to manage media.
- *
- * @return string[]
- */
- public function getManagementDialog() {
- $mediaList = new ViewableMediaList();
- if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
- $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
- }
- if ($this->parameters['imagesOnly']) {
- $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
- }
- $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
- $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
- $mediaList->readObjects();
-
- $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
- $categoryList->setMaxDepth(0);
-
- return [
- 'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')),
- 'media' => $this->getI18nMediaData($mediaList),
- 'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
- 'template' => WCF::getTPL()->fetch('mediaManager', 'wcf', [
- 'categoryList' => $categoryList,
- 'mediaList' => $mediaList,
- 'mode' => $this->parameters['mode']
- ])
- ];
- }
-
- /**
- * Returns the complete i18n data of the media files in the given list.
- *
- * @param MediaList $mediaList
- * @return array
- */
- protected function getI18nMediaData(MediaList $mediaList) {
- if (!count($mediaList)) return [];
-
- $conditionBuilder = new PreparedStatementConditionBuilder();
- $conditionBuilder->add('mediaID IN (?)', [$mediaList->getObjectIDs()]);
-
- $sql = "SELECT *
- FROM wcf".WCF_N."_media_content
- ".$conditionBuilder;
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute($conditionBuilder->getParameters());
-
- $mediaData = [];
- while ($row = $statement->fetchArray()) {
- if (!isset($mediaData[$row['mediaID']])) {
- $mediaData[$row['mediaID']] = [
- 'altText' => [],
- 'caption' => [],
- 'title' => []
- ];
- }
-
- $mediaData[$row['mediaID']]['altText'][intval($row['languageID'])] = $row['altText'];
- $mediaData[$row['mediaID']]['caption'][intval($row['languageID'])] = $row['caption'];
- $mediaData[$row['mediaID']]['title'][intval($row['languageID'])] = $row['title'];
- }
-
- $i18nMediaData = [];
- foreach ($mediaList as $media) {
- if (!isset($mediaData[$media->mediaID])) {
- $mediaData[$media->mediaID] = [];
- }
-
- $i18nMediaData[$media->mediaID] = array_merge($this->getMediaData($media), $mediaData[$media->mediaID]);
- }
-
- return $i18nMediaData;
- }
-
- /**
- * Validates the 'getEditorDialog' action.
- */
- public function validateGetEditorDialog() {
- WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
-
- $this->getSingleObject();
-
- if (!$this->getSingleObject()->canManage()) {
- throw new PermissionDeniedException();
- }
- }
-
- /**
- * Returns the template for the media editor.
- *
- * @return string[]
- */
- public function getEditorDialog() {
- $mediaList = new ViewableMediaList();
- $mediaList->setObjectIDs([$this->getSingleObject()->mediaID]);
- $mediaList->readObjects();
- $media = $mediaList->search($this->getSingleObject()->mediaID);
-
- I18nHandler::getInstance()->register('title_' . $media->mediaID);
- I18nHandler::getInstance()->register('caption_' . $media->mediaID);
- I18nHandler::getInstance()->register('altText_' . $media->mediaID);
- I18nHandler::getInstance()->assignVariables();
-
- $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
- $categoryList->setMaxDepth(0);
-
- return [
- 'availableLanguageCount' => count(LanguageFactory::getInstance()->getLanguages()),
- 'categoryIDs' => array_keys(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category')),
- 'mediaData' => $this->getI18nMediaData($mediaList)[$this->getSingleObject()->mediaID],
- 'template' => WCF::getTPL()->fetch('mediaEditor', 'wcf', [
- '__aclSimplePrefix' => 'mediaEditor_' . $media->mediaID . '_',
- '__aclInputName' => 'mediaEditor_' . $media->mediaID . '_aclValues',
- '__languageChooserPrefix' => 'mediaEditor_' . $media->mediaID . '_',
- 'aclValues' => SimpleAclHandler::getInstance()->getValues('com.woltlab.wcf.media', $media->mediaID),
- 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
- 'categoryList' => $categoryList,
- 'languageID' => WCF::getUser()->languageID,
- 'languages' => LanguageFactory::getInstance()->getLanguages(),
- 'media' => $media
- ])
- ];
- }
-
- /**
- * @inheritDoc
- */
- public function validateUpdate() {
- WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
-
- if (empty($this->objects)) {
- $this->readObjects();
-
- if (empty($this->objects)) {
- throw new UserInputException('objectIDs');
- }
- }
-
- if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
- foreach ($this->getObjects() as $media) {
- if ($media->userID != WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
- }
- }
-
- $this->readInteger('categoryID', true, 'data');
- $this->readInteger('languageID', true, 'data');
- $this->readBoolean('isMultilingual', true, 'data');
- $this->readInteger('captionEnableHtml', true, 'data');
-
- if (count(LanguageFactory::getInstance()->getLanguages()) > 1) {
- // languageID: convert zero to null
- if (!$this->parameters['data']['languageID']) $this->parameters['data']['languageID'] = null;
-
- // isMultilingual: convert boolean to integer
- $this->parameters['data']['isMultilingual'] = intval($this->parameters['data']['isMultilingual']);
- }
- else {
- $this->parameters['data']['isMultilingual'] = 0;
- $this->parameters['data']['languageID'] = WCF::getLanguage()->languageID;
- }
-
- // if data is not multilingual, a language id has to be given
- if (!$this->parameters['data']['isMultilingual'] && !$this->parameters['data']['languageID']) {
- throw new UserInputException('languageID');
- }
-
- // check language id
- if ($this->parameters['data']['languageID'] && !LanguageFactory::getInstance()->getLanguage($this->parameters['data']['languageID'])) {
- throw new UserInputException('languageID');
- }
-
- // check category id
- if ($this->parameters['data']['categoryID']) {
- $category = CategoryHandler::getInstance()->getCategory($this->parameters['data']['categoryID']);
- if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
- throw new UserInputException('categoryID');
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function update() {
- if (isset($this->parameters['data']['categoryID']) && $this->parameters['data']['categoryID'] === 0) {
- $this->parameters['data']['categoryID'] = null;
- }
-
- if (empty($this->objects)) {
- $this->readObjects();
- }
-
- parent::update();
-
- if (count($this->objects) == 1 && (isset($this->parameters['title']) || isset($this->parameters['caption']) || isset($this->parameters['altText']))) {
- $media = reset($this->objects);
-
- $isMultilingual = $media->isMultilingual;
- if (isset($this->parameters['data']['isMultilingual'])) {
- $isMultilingual = $this->parameters['data']['isMultilingual'];
- }
-
- $sql = "DELETE FROM wcf".WCF_N."_media_content
- WHERE mediaID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([$media->mediaID]);
-
- $sql = "INSERT INTO wcf".WCF_N."_media_content
- (mediaID, languageID, title, caption, altText)
- VALUES (?, ?, ?, ?, ?)";
- $statement = WCF::getDB()->prepareStatement($sql);
-
- if (!$isMultilingual) {
- $languageID = $media->languageID;
- if (isset($this->parameters['data']['languageID'])) {
- $languageID = $this->parameters['data']['languageID'];
- }
- $statement->execute([
- $media->mediaID,
- $languageID,
- isset($this->parameters['title'][$languageID]) ? mb_substr($this->parameters['title'][$languageID], 0, 255) : '',
- isset($this->parameters['caption'][$languageID]) ? $this->parameters['caption'][$languageID] : '',
- isset($this->parameters['altText'][$languageID]) ? mb_substr($this->parameters['altText'][$languageID], 0, 255) : ''
- ]);
- }
- else {
- $languages = LanguageFactory::getInstance()->getLanguages();
- foreach ($languages as $language) {
- $title = $caption = $altText = '';
- foreach (['title', 'caption', 'altText'] as $type) {
- if (isset($this->parameters[$type])) {
- if (is_array($this->parameters[$type])) {
- if (isset($this->parameters[$type][$language->languageID])) {
- /** @noinspection PhpVariableVariableInspection */
- $$type = $this->parameters[$type][$language->languageID];
- }
- }
- else {
- /** @noinspection PhpVariableVariableInspection */
- $$type = $this->parameters[$type];
- }
- }
- }
-
- $statement->execute([
- $media->mediaID,
- $language->languageID,
- mb_substr($title, 0, 255),
- $caption,
- mb_substr($altText, 0, 255)
- ]);
- }
- }
-
- if (!empty($this->parameters['aclValues'])) {
- SimpleAclHandler::getInstance()->setValues('com.woltlab.wcf.media', $media->mediaID, $this->parameters['aclValues']);
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function validateGetSearchResultList() {
- if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
- throw new PermissionDeniedException();
- }
-
- $this->readString('searchString', true);
- $this->readInteger('categoryID', true);
-
- $this->readBoolean('imagesOnly', true);
-
- $this->readString('mode');
- if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
- throw new UserInputException('mode');
- }
-
- $this->readInteger('pageNo', true);
- if (!$this->parameters['pageNo']) {
- $this->parameters['pageNo'] = 1;
- }
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * @inheritDoc
- */
- public function getSearchResultList() {
- $mediaList = new MediaList();
- $mediaList->addSearchConditions($this->parameters['searchString']);
- if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
- $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
- }
- if ($this->parameters['imagesOnly']) {
- $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
- }
- if ($this->parameters['categoryID']) {
- $mediaList->getConditionBuilder()->add('media.categoryID = ?', [$this->parameters['categoryID']]);
- }
- $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
- $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
- $mediaList->sqlOffset = ($this->parameters['pageNo'] - 1) * static::ITEMS_PER_MANAGER_DIALOG_PAGE;
- $mediaList->readObjectIDs();
-
- if (empty($mediaList->getObjectIDs())) {
- // check if page is requested that might have existed but does not exist anymore due to deleted
- // media files
- if ($this->parameters['pageNo'] > 1 && $this->parameters['searchString'] === '' && !$this->parameters['categoryID']) {
- // request media dialog page with highest page number
- $parameters = $this->parameters;
- $parameters['pageNo'] = ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE);
-
- return (new MediaAction($this->objects, 'getSearchResultList', $parameters))->executeAction()['returnValues'];
- }
-
- return [
- 'template' => WCF::getLanguage()->getDynamicVariable('wcf.media.search.noResults')
- ];
- }
-
- $viewableMediaList = new ViewableMediaList();
- $viewableMediaList->setObjectIDs($mediaList->getObjectIDs());
- $viewableMediaList->readObjects();
-
- return [
- 'media' => $this->getI18nMediaData($viewableMediaList),
- 'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
- 'pageNo' => $this->parameters['pageNo'],
- 'template' => WCF::getTPL()->fetch('mediaListItems', 'wcf', [
- 'mediaList' => $viewableMediaList,
- 'mode' => $this->parameters['mode']
- ])
- ];
- }
-
- /**
- * @inheritDoc
- */
- public function validateDelete() {
- WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
-
- if (empty($this->objects)) {
- $this->readObjects();
-
- if (empty($this->objects)) {
- throw new UserInputException('objectIDs');
- }
- }
-
- if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
- foreach ($this->getObjects() as $media) {
- if ($media->userID != WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function delete() {
- if (empty($this->objects)) {
- $this->readObjects();
- }
-
- foreach ($this->getObjects() as $mediaEditor) {
- $mediaEditor->deleteFiles();
- }
-
- parent::delete();
-
- $this->unmarkItems();
- }
-
- /**
- * Unmarks the media files with the given ids. If no media ids are given,
- * all media files currently loaded are unmarked.
- *
- * @param integer[] $mediaIDs ids of the media files to be unmarked
- */
- protected function unmarkItems(array $mediaIDs = []) {
- if (empty($mediaIDs)) {
- foreach ($this->getObjects() as $media) {
- $mediaIDs[] = $media->mediaID;
- }
- }
-
- if (!empty($mediaIDs)) {
- ClipboardHandler::getInstance()->unmark($mediaIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media'));
- }
- }
-
- /**
- * Validates the `getSetCategoryDialog` action.
- *
- * @throws PermissionDeniedException if user is not allowed to set category of media files
- * @throws IllegalLinkException if no media file categories exist
- */
- public function validateGetSetCategoryDialog() {
- if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia')) {
- throw new PermissionDeniedException();
- }
-
- if (empty(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category'))) {
- throw new IllegalLinkException();
- }
- }
-
- /**
- * Returns the dialog to set the category of multiple media files.
- *
- * @return string[]
- */
- public function getSetCategoryDialog() {
- $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
- $categoryList->setMaxDepth(0);
-
- return [
- 'template' => WCF::getTPL()->fetch('__mediaSetCategoryDialog', 'wcf', [
- 'categoryList' => $categoryList
- ])
- ];
- }
-
- /**
- * Validates the `setCategory` action.
- *
- * @throws PermissionDeniedException if user is not allowed to edit a requested media file
- * @throws UserInputException if no object ids are given
- */
- public function validateSetCategory() {
- $this->validateGetSetCategoryDialog();
-
- if (empty($this->objects)) {
- $this->readObjects();
-
- if (empty($this->objects)) {
- throw new UserInputException('objectIDs');
- }
- }
-
- if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
- foreach ($this->getObjects() as $media) {
- if ($media->userID != WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
- }
- }
-
- $this->readInteger('categoryID', true);
- }
-
- /**
- * Sets the category of multiple media files.
- */
- public function setCategory() {
- $conditionBuilder = new PreparedStatementConditionBuilder();
- $conditionBuilder->add('mediaID IN (?)', [$this->objectIDs]);
-
- $sql = "UPDATE wcf" . WCF_N . "_media
- SET categoryID = ?
- " . $conditionBuilder;
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array_merge(
- [$this->parameters['categoryID'] ?: null],
- $conditionBuilder->getParameters()
- ));
-
- $this->unmarkItems();
- }
-
- /**
- * Validates the `replaceFile` action.
- *
- * @since 5.3
- */
- public function validateReplaceFile() {
- WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
-
- $this->getSingleObject();
-
- /** @noinspection PhpUndefinedMethodInspection */
- $this->parameters['__files']->validateFiles(
- new MediaReplaceUploadFileValidationStrategy($this->getSingleObject()->getDecoratedObject())
- );
- }
-
- /**
- * Replaces the actual file of a media file.
- *
- * @return array
- * @since 5.3
- */
- public function replaceFile() {
- $saveStrategy = new DefaultUploadFileSaveStrategy(static::class, [
- 'action' => 'update',
- 'generateThumbnails' => true,
- 'object' => $this->getSingleObject()->getDecoratedObject(),
- 'rotateImages' => true,
- ], [
- 'fileUpdateTime' => TIME_NOW,
- 'userID' => $this->getSingleObject()->userID,
- 'username' => $this->getSingleObject()->username,
- ]);
-
- /** @noinspection PhpUndefinedMethodInspection */
- $this->parameters['__files']->saveFiles($saveStrategy);
-
- /** @var Media[] $mediaFiles */
- $mediaFiles = $saveStrategy->getObjects();
-
- $result = [
- 'errors' => [],
- 'media' => []
- ];
-
- if (!empty($mediaFiles)) {
- $mediaIDs = $mediaToFileID = [];
- foreach ($mediaFiles as $internalFileID => $media) {
- $mediaIDs[] = $media->mediaID;
- $mediaToFileID[$media->mediaID] = $internalFileID;
- }
-
- // fetch media objects from database
- $mediaList = new ViewableMediaList();
- $mediaList->setObjectIDs($mediaIDs);
- $mediaList->readObjects();
-
- foreach ($mediaList as $media) {
- $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
- }
- }
-
- /** @var UploadFile[] $files */
- /** @noinspection PhpUndefinedMethodInspection */
- $files = $this->parameters['__files']->getFiles();
- foreach ($files as $file) {
- if ($file->getValidationErrorType()) {
- $result['errors'][$file->getInternalFileID()] = [
- 'filename' => $file->getFilename(),
- 'filesize' => $file->getFilesize(),
- 'errorType' => $file->getValidationErrorType()
- ];
- }
- }
-
- $outdatedMediaFile = $this->getSingleObject();
- $updatedMediaFile = new Media($this->getSingleObject()->mediaID);
-
- // Delete *old* files using the non-updated local media editor object if the new file is
- // stored in a different location.
- if (empty($result['errors']) && $updatedMediaFile->getLocation() !== $outdatedMediaFile->getLocation()) {
- $outdatedMediaFile->deleteFiles();
- }
-
- return $result;
- }
+class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction, IUploadAction
+{
+ /**
+ * number of media files per media manager dialog page
+ */
+ const ITEMS_PER_MANAGER_DIALOG_PAGE = 50;
+
+ /**
+ * @inheritDoc
+ */
+ public function validateUpload()
+ {
+ WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
+
+ $this->readBoolean('imagesOnly', true);
+ $this->readInteger('categoryID', true);
+
+ /** @noinspection PhpUndefinedMethodInspection */
+ $this->parameters['__files']->validateFiles(new MediaUploadFileValidationStrategy($this->parameters['imagesOnly']));
+
+ if ($this->parameters['categoryID']) {
+ $category = CategoryHandler::getInstance()->getCategory($this->parameters['categoryID']);
+ if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
+ throw new UserInputException('categoryID');
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function upload()
+ {
+ $additionalData = ['username' => WCF::getUser()->username];
+ if ($this->parameters['categoryID']) {
+ $additionalData['categoryID'] = $this->parameters['categoryID'];
+ }
+
+ // save files
+ $saveStrategy = new DefaultUploadFileSaveStrategy(self::class, [
+ 'generateThumbnails' => true,
+ 'rotateImages' => true,
+ ], $additionalData);
+
+ /** @noinspection PhpUndefinedMethodInspection */
+ $this->parameters['__files']->saveFiles($saveStrategy);
+
+ /** @var Media[] $mediaFiles */
+ $mediaFiles = $saveStrategy->getObjects();
+
+ $result = [
+ 'errors' => [],
+ 'media' => [],
+ ];
+
+ if (!empty($mediaFiles)) {
+ $mediaIDs = $mediaToFileID = [];
+ foreach ($mediaFiles as $internalFileID => $media) {
+ $mediaIDs[] = $media->mediaID;
+ $mediaToFileID[$media->mediaID] = $internalFileID;
+ }
+
+ // fetch media objects from database
+ $mediaList = new ViewableMediaList();
+ $mediaList->setObjectIDs($mediaIDs);
+ $mediaList->readObjects();
+
+ foreach ($mediaList as $media) {
+ $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
+ }
+ }
+
+ /** @var UploadFile[] $files */
+ /** @noinspection PhpUndefinedMethodInspection */
+ $files = $this->parameters['__files']->getFiles();
+ foreach ($files as $file) {
+ if ($file->getValidationErrorType()) {
+ $result['errors'][$file->getInternalFileID()] = [
+ 'filename' => $file->getFilename(),
+ 'filesize' => $file->getFilesize(),
+ 'errorType' => $file->getValidationErrorType(),
+ ];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Generates thumbnails.
+ */
+ public function generateThumbnails()
+ {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ $saveStrategy = new DefaultUploadFileSaveStrategy(self::class);
+
+ foreach ($this->getObjects() as $mediaEditor) {
+ if ($mediaEditor->getDecoratedObject()->isImage) {
+ $saveStrategy->generateThumbnails($mediaEditor->getDecoratedObject());
+ }
+ }
+ }
+
+ /**
+ * Returns the data of the media file to be returned by AJAX requests.
+ *
+ * @param Media|ViewableMedia $media media files whose data will be returned
+ * @return string[]
+ */
+ protected function getMediaData($media)
+ {
+ return [
+ 'altText' => $media instanceof ViewableMedia ? $media->altText : [],
+ 'caption' => $media instanceof ViewableMedia ? $media->caption : [],
+ 'captionEnableHtml' => $media->captionEnableHtml,
+ 'categoryID' => $media->categoryID,
+ 'elementTag' => $media instanceof ViewableMedia ? $media->getElementTag($this->parameters['elementTagSize'] ?? 144) : '',
+ 'elementTag48' => $media instanceof ViewableMedia ? $media->getElementTag(48) : '',
+ 'fileHash' => $media->fileHash,
+ 'filename' => $media->filename,
+ 'filesize' => $media->filesize,
+ 'formattedFilesize' => FileUtil::formatFilesize($media->filesize),
+ 'fileType' => $media->fileType,
+ 'height' => $media->height,
+ 'languageID' => $media->languageID,
+ 'imageDimensions' => $media->isImage ? WCF::getLanguage()->getDynamicVariable(
+ 'wcf.media.imageDimensions.value',
+ [
+ 'media' => $media,
+ ]
+ ) : '',
+ 'isImage' => $media->isImage,
+ 'isMultilingual' => $media->isMultilingual,
+ 'largeThumbnailHeight' => $media->largeThumbnailHeight,
+ 'largeThumbnailLink' => $media->largeThumbnailType ? $media->getThumbnailLink('large') : '',
+ 'largeThumbnailType' => $media->largeThumbnailType,
+ 'largeThumbnailWidth' => $media->largeThumbnailWidth,
+ 'link' => $media->getLink(),
+ 'mediaID' => $media->mediaID,
+ 'mediumThumbnailHeight' => $media->mediumThumbnailHeight,
+ 'mediumThumbnailLink' => $media->mediumThumbnailType ? $media->getThumbnailLink('medium') : '',
+ 'mediumThumbnailType' => $media->mediumThumbnailType,
+ 'mediumThumbnailWidth' => $media->mediumThumbnailWidth,
+ 'smallThumbnailHeight' => $media->smallThumbnailHeight,
+ 'smallThumbnailLink' => $media->smallThumbnailType ? $media->getThumbnailLink('small') : '',
+ 'smallThumbnailTag' => $media->smallThumbnailType ? $media->getThumbnailTag('small') : '',
+ 'smallThumbnailType' => $media->smallThumbnailType,
+ 'smallThumbnailWidth' => $media->smallThumbnailWidth,
+ 'tinyThumbnailHeight' => $media->tinyThumbnailHeight,
+ 'tinyThumbnailLink' => $media->tinyThumbnailType ? $media->getThumbnailLink('tiny') : '',
+ 'tinyThumbnailType' => $media->tinyThumbnailType,
+ 'tinyThumbnailWidth' => $media->tinyThumbnailWidth,
+ 'title' => $media instanceof ViewableMedia ? $media->title : [],
+ 'uploadTime' => $media->uploadTime,
+ 'userID' => $media->userID,
+ 'userLink' => $media->userID ? LinkHandler::getInstance()->getLink('User', [
+ 'id' => $media->userID,
+ 'title' => $media->username,
+ ]) : '',
+ 'userLinkElement' => $media instanceof ViewableMedia ? WCF::getTPL()->fetchString(
+ WCF::getTPL()->getCompiler()->compileString('userLink', '{user object=$userProfile}')['template'],
+ ['userProfile' => $media->getUserProfile()]
+ ) : '',
+ 'username' => $media->username,
+ 'width' => $media->width,
+ ];
+ }
+
+ /**
+ * Validates the 'getManagementDialog' action.
+ */
+ public function validateGetManagementDialog()
+ {
+ if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
+ throw new PermissionDeniedException();
+ }
+
+ $this->readBoolean('imagesOnly', true);
+
+ $this->readString('mode');
+ if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
+ throw new UserInputException('mode');
+ }
+ }
+
+ /**
+ * Returns the dialog to manage media.
+ *
+ * @return string[]
+ */
+ public function getManagementDialog()
+ {
+ $mediaList = new ViewableMediaList();
+ if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
+ $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
+ }
+ if ($this->parameters['imagesOnly']) {
+ $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
+ }
+ $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
+ $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
+ $mediaList->readObjects();
+
+ $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
+ $categoryList->setMaxDepth(0);
+
+ return [
+ 'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')),
+ 'media' => $this->getI18nMediaData($mediaList),
+ 'pageCount' => \ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
+ 'template' => WCF::getTPL()->fetch('mediaManager', 'wcf', [
+ 'categoryList' => $categoryList,
+ 'mediaList' => $mediaList,
+ 'mode' => $this->parameters['mode'],
+ ]),
+ ];
+ }
+
+ /**
+ * Returns the complete i18n data of the media files in the given list.
+ *
+ * @param MediaList $mediaList
+ * @return array
+ */
+ protected function getI18nMediaData(MediaList $mediaList)
+ {
+ if (!\count($mediaList)) {
+ return [];
+ }
+
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add('mediaID IN (?)', [$mediaList->getObjectIDs()]);
+
+ $sql = "SELECT *
+ FROM wcf" . WCF_N . "_media_content
+ " . $conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ $mediaData = [];
+ while ($row = $statement->fetchArray()) {
+ if (!isset($mediaData[$row['mediaID']])) {
+ $mediaData[$row['mediaID']] = [
+ 'altText' => [],
+ 'caption' => [],
+ 'title' => [],
+ ];
+ }
+
+ $mediaData[$row['mediaID']]['altText'][\intval($row['languageID'])] = $row['altText'];
+ $mediaData[$row['mediaID']]['caption'][\intval($row['languageID'])] = $row['caption'];
+ $mediaData[$row['mediaID']]['title'][\intval($row['languageID'])] = $row['title'];
+ }
+
+ $i18nMediaData = [];
+ foreach ($mediaList as $media) {
+ if (!isset($mediaData[$media->mediaID])) {
+ $mediaData[$media->mediaID] = [];
+ }
+
+ $i18nMediaData[$media->mediaID] = \array_merge($this->getMediaData($media), $mediaData[$media->mediaID]);
+ }
+
+ return $i18nMediaData;
+ }
+
+ /**
+ * Validates the 'getEditorDialog' action.
+ */
+ public function validateGetEditorDialog()
+ {
+ WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
+
+ $this->getSingleObject();
+
+ if (!$this->getSingleObject()->canManage()) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Returns the template for the media editor.
+ *
+ * @return string[]
+ */
+ public function getEditorDialog()
+ {
+ $mediaList = new ViewableMediaList();
+ $mediaList->setObjectIDs([$this->getSingleObject()->mediaID]);
+ $mediaList->readObjects();
+ $media = $mediaList->search($this->getSingleObject()->mediaID);
+
+ I18nHandler::getInstance()->register('title_' . $media->mediaID);
+ I18nHandler::getInstance()->register('caption_' . $media->mediaID);
+ I18nHandler::getInstance()->register('altText_' . $media->mediaID);
+ I18nHandler::getInstance()->assignVariables();
+
+ $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
+ $categoryList->setMaxDepth(0);
+
+ return [
+ 'availableLanguageCount' => \count(LanguageFactory::getInstance()->getLanguages()),
+ 'categoryIDs' => \array_keys(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category')),
+ 'mediaData' => $this->getI18nMediaData($mediaList)[$this->getSingleObject()->mediaID],
+ 'template' => WCF::getTPL()->fetch('mediaEditor', 'wcf', [
+ '__aclSimplePrefix' => 'mediaEditor_' . $media->mediaID . '_',
+ '__aclInputName' => 'mediaEditor_' . $media->mediaID . '_aclValues',
+ '__languageChooserPrefix' => 'mediaEditor_' . $media->mediaID . '_',
+ 'aclValues' => SimpleAclHandler::getInstance()->getValues('com.woltlab.wcf.media', $media->mediaID),
+ 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
+ 'categoryList' => $categoryList,
+ 'languageID' => WCF::getUser()->languageID,
+ 'languages' => LanguageFactory::getInstance()->getLanguages(),
+ 'media' => $media,
+ ]),
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateUpdate()
+ {
+ WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
+ foreach ($this->getObjects() as $media) {
+ if ($media->userID != WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ $this->readInteger('categoryID', true, 'data');
+ $this->readInteger('languageID', true, 'data');
+ $this->readBoolean('isMultilingual', true, 'data');
+ $this->readInteger('captionEnableHtml', true, 'data');
+
+ if (\count(LanguageFactory::getInstance()->getLanguages()) > 1) {
+ // languageID: convert zero to null
+ if (!$this->parameters['data']['languageID']) {
+ $this->parameters['data']['languageID'] = null;
+ }
+
+ // isMultilingual: convert boolean to integer
+ $this->parameters['data']['isMultilingual'] = \intval($this->parameters['data']['isMultilingual']);
+ } else {
+ $this->parameters['data']['isMultilingual'] = 0;
+ $this->parameters['data']['languageID'] = WCF::getLanguage()->languageID;
+ }
+
+ // if data is not multilingual, a language id has to be given
+ if (!$this->parameters['data']['isMultilingual'] && !$this->parameters['data']['languageID']) {
+ throw new UserInputException('languageID');
+ }
+
+ // check language id
+ if ($this->parameters['data']['languageID'] && !LanguageFactory::getInstance()->getLanguage($this->parameters['data']['languageID'])) {
+ throw new UserInputException('languageID');
+ }
+
+ // check category id
+ if ($this->parameters['data']['categoryID']) {
+ $category = CategoryHandler::getInstance()->getCategory($this->parameters['data']['categoryID']);
+ if ($category === null || $category->getObjectType()->objectType !== 'com.woltlab.wcf.media.category') {
+ throw new UserInputException('categoryID');
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function update()
+ {
+ if (isset($this->parameters['data']['categoryID']) && $this->parameters['data']['categoryID'] === 0) {
+ $this->parameters['data']['categoryID'] = null;
+ }
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ parent::update();
+
+ if (\count($this->objects) == 1 && (isset($this->parameters['title']) || isset($this->parameters['caption']) || isset($this->parameters['altText']))) {
+ $media = \reset($this->objects);
+
+ $isMultilingual = $media->isMultilingual;
+ if (isset($this->parameters['data']['isMultilingual'])) {
+ $isMultilingual = $this->parameters['data']['isMultilingual'];
+ }
+
+ $sql = "DELETE FROM wcf" . WCF_N . "_media_content
+ WHERE mediaID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([$media->mediaID]);
+
+ $sql = "INSERT INTO wcf" . WCF_N . "_media_content
+ (mediaID, languageID, title, caption, altText)
+ VALUES (?, ?, ?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ if (!$isMultilingual) {
+ $languageID = $media->languageID;
+ if (isset($this->parameters['data']['languageID'])) {
+ $languageID = $this->parameters['data']['languageID'];
+ }
+ $statement->execute([
+ $media->mediaID,
+ $languageID,
+ isset($this->parameters['title'][$languageID]) ? \mb_substr(
+ $this->parameters['title'][$languageID],
+ 0,
+ 255
+ ) : '',
+ $this->parameters['caption'][$languageID] ?? '',
+ isset($this->parameters['altText'][$languageID]) ? \mb_substr(
+ $this->parameters['altText'][$languageID],
+ 0,
+ 255
+ ) : '',
+ ]);
+ } else {
+ $languages = LanguageFactory::getInstance()->getLanguages();
+ foreach ($languages as $language) {
+ $title = $caption = $altText = '';
+ foreach (['title', 'caption', 'altText'] as $type) {
+ if (isset($this->parameters[$type])) {
+ if (\is_array($this->parameters[$type])) {
+ if (isset($this->parameters[$type][$language->languageID])) {
+ /** @noinspection PhpVariableVariableInspection */
+ ${$type} = $this->parameters[$type][$language->languageID];
+ }
+ } else {
+ /** @noinspection PhpVariableVariableInspection */
+ ${$type} = $this->parameters[$type];
+ }
+ }
+ }
+
+ $statement->execute([
+ $media->mediaID,
+ $language->languageID,
+ \mb_substr($title, 0, 255),
+ $caption,
+ \mb_substr($altText, 0, 255),
+ ]);
+ }
+ }
+
+ if (!empty($this->parameters['aclValues'])) {
+ SimpleAclHandler::getInstance()->setValues(
+ 'com.woltlab.wcf.media',
+ $media->mediaID,
+ $this->parameters['aclValues']
+ );
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateGetSearchResultList()
+ {
+ if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
+ throw new PermissionDeniedException();
+ }
+
+ $this->readString('searchString', true);
+ $this->readInteger('categoryID', true);
+
+ $this->readBoolean('imagesOnly', true);
+
+ $this->readString('mode');
+ if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
+ throw new UserInputException('mode');
+ }
+
+ $this->readInteger('pageNo', true);
+ if (!$this->parameters['pageNo']) {
+ $this->parameters['pageNo'] = 1;
+ }
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getSearchResultList()
+ {
+ $mediaList = new MediaList();
+ $mediaList->addSearchConditions($this->parameters['searchString']);
+ if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
+ $mediaList->getConditionBuilder()->add('media.userID = ?', [WCF::getUser()->userID]);
+ }
+ if ($this->parameters['imagesOnly']) {
+ $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
+ }
+ if ($this->parameters['categoryID']) {
+ $mediaList->getConditionBuilder()->add('media.categoryID = ?', [$this->parameters['categoryID']]);
+ }
+ $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
+ $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
+ $mediaList->sqlOffset = ($this->parameters['pageNo'] - 1) * static::ITEMS_PER_MANAGER_DIALOG_PAGE;
+ $mediaList->readObjectIDs();
+
+ if (empty($mediaList->getObjectIDs())) {
+ // check if page is requested that might have existed but does not exist anymore due to deleted
+ // media files
+ if ($this->parameters['pageNo'] > 1 && $this->parameters['searchString'] === '' && !$this->parameters['categoryID']) {
+ // request media dialog page with highest page number
+ $parameters = $this->parameters;
+ $parameters['pageNo'] = \ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE);
+
+ return (new self($this->objects, 'getSearchResultList', $parameters))->executeAction()['returnValues'];
+ }
+
+ return [
+ 'template' => WCF::getLanguage()->getDynamicVariable('wcf.media.search.noResults'),
+ ];
+ }
+
+ $viewableMediaList = new ViewableMediaList();
+ $viewableMediaList->setObjectIDs($mediaList->getObjectIDs());
+ $viewableMediaList->readObjects();
+
+ return [
+ 'media' => $this->getI18nMediaData($viewableMediaList),
+ 'pageCount' => \ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
+ 'pageNo' => $this->parameters['pageNo'],
+ 'template' => WCF::getTPL()->fetch('mediaListItems', 'wcf', [
+ 'mediaList' => $viewableMediaList,
+ 'mode' => $this->parameters['mode'],
+ ]),
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateDelete()
+ {
+ WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
+ foreach ($this->getObjects() as $media) {
+ if ($media->userID != WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete()
+ {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->getObjects() as $mediaEditor) {
+ $mediaEditor->deleteFiles();
+ }
+
+ parent::delete();
+
+ $this->unmarkItems();
+ }
+
+ /**
+ * Unmarks the media files with the given ids. If no media ids are given,
+ * all media files currently loaded are unmarked.
+ *
+ * @param int[] $mediaIDs ids of the media files to be unmarked
+ */
+ protected function unmarkItems(array $mediaIDs = [])
+ {
+ if (empty($mediaIDs)) {
+ foreach ($this->getObjects() as $media) {
+ $mediaIDs[] = $media->mediaID;
+ }
+ }
+
+ if (!empty($mediaIDs)) {
+ ClipboardHandler::getInstance()->unmark(
+ $mediaIDs,
+ ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')
+ );
+ }
+ }
+
+ /**
+ * Validates the `getSetCategoryDialog` action.
+ *
+ * @throws PermissionDeniedException if user is not allowed to set category of media files
+ * @throws IllegalLinkException if no media file categories exist
+ */
+ public function validateGetSetCategoryDialog()
+ {
+ if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia')) {
+ throw new PermissionDeniedException();
+ }
+
+ if (empty(CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.media.category'))) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * Returns the dialog to set the category of multiple media files.
+ *
+ * @return string[]
+ */
+ public function getSetCategoryDialog()
+ {
+ $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
+ $categoryList->setMaxDepth(0);
+
+ return [
+ 'template' => WCF::getTPL()->fetch('__mediaSetCategoryDialog', 'wcf', [
+ 'categoryList' => $categoryList,
+ ]),
+ ];
+ }
+
+ /**
+ * Validates the `setCategory` action.
+ *
+ * @throws PermissionDeniedException if user is not allowed to edit a requested media file
+ * @throws UserInputException if no object ids are given
+ */
+ public function validateSetCategory()
+ {
+ $this->validateGetSetCategoryDialog();
+
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ if (WCF::getSession()->getPermission('admin.content.cms.canOnlyAccessOwnMedia')) {
+ foreach ($this->getObjects() as $media) {
+ if ($media->userID != WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ $this->readInteger('categoryID', true);
+ }
+
+ /**
+ * Sets the category of multiple media files.
+ */
+ public function setCategory()
+ {
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add('mediaID IN (?)', [$this->objectIDs]);
+
+ $sql = "UPDATE wcf" . WCF_N . "_media
+ SET categoryID = ?
+ " . $conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(\array_merge(
+ [$this->parameters['categoryID'] ?: null],
+ $conditionBuilder->getParameters()
+ ));
+
+ $this->unmarkItems();
+ }
+
+ /**
+ * Validates the `replaceFile` action.
+ *
+ * @since 5.3
+ */
+ public function validateReplaceFile()
+ {
+ WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
+
+ $this->getSingleObject();
+
+ /** @noinspection PhpUndefinedMethodInspection */
+ $this->parameters['__files']->validateFiles(
+ new MediaReplaceUploadFileValidationStrategy($this->getSingleObject()->getDecoratedObject())
+ );
+ }
+
+ /**
+ * Replaces the actual file of a media file.
+ *
+ * @return array
+ * @since 5.3
+ */
+ public function replaceFile()
+ {
+ $saveStrategy = new DefaultUploadFileSaveStrategy(static::class, [
+ 'action' => 'update',
+ 'generateThumbnails' => true,
+ 'object' => $this->getSingleObject()->getDecoratedObject(),
+ 'rotateImages' => true,
+ ], [
+ 'fileUpdateTime' => TIME_NOW,
+ 'userID' => $this->getSingleObject()->userID,
+ 'username' => $this->getSingleObject()->username,
+ // Reset thumbnail data in case the new file has no thumbnails.
+ 'tinyThumbnailType' => '',
+ 'tinyThumbnailSize' => 0,
+ 'tinyThumbnailWidth' => 0,
+ 'tinyThumbnailHeight' => 0,
+ 'smallThumbnailType' => '',
+ 'smallThumbnailSize' => 0,
+ 'smallThumbnailWidth' => 0,
+ 'smallThumbnailHeight' => 0,
+ 'mediumThumbnailType' => '',
+ 'mediumThumbnailSize' => 0,
+ 'mediumThumbnailWidth' => 0,
+ 'mediumThumbnailHeight' => 0,
+ 'largeThumbnailType' => '',
+ 'largeThumbnailSize' => 0,
+ 'largeThumbnailWidth' => 0,
+ 'largeThumbnailHeight' => 0,
+ ]);
+
+ /** @noinspection PhpUndefinedMethodInspection */
+ $this->parameters['__files']->saveFiles($saveStrategy);
+
+ /** @var Media[] $mediaFiles */
+ $mediaFiles = $saveStrategy->getObjects();
+
+ $result = [
+ 'errors' => [],
+ 'media' => [],
+ ];
+
+ if (!empty($mediaFiles)) {
+ $mediaIDs = $mediaToFileID = [];
+ foreach ($mediaFiles as $internalFileID => $media) {
+ $mediaIDs[] = $media->mediaID;
+ $mediaToFileID[$media->mediaID] = $internalFileID;
+ }
+
+ // fetch media objects from database
+ $mediaList = new ViewableMediaList();
+ $mediaList->setObjectIDs($mediaIDs);
+ $mediaList->readObjects();
+
+ foreach ($mediaList as $media) {
+ $result['media'][$mediaToFileID[$media->mediaID]] = $this->getMediaData($media);
+ }
+ }
+
+ /** @var UploadFile[] $files */
+ /** @noinspection PhpUndefinedMethodInspection */
+ $files = $this->parameters['__files']->getFiles();
+ foreach ($files as $file) {
+ if ($file->getValidationErrorType()) {
+ $result['errors'][$file->getInternalFileID()] = [
+ 'filename' => $file->getFilename(),
+ 'filesize' => $file->getFilesize(),
+ 'errorType' => $file->getValidationErrorType(),
+ ];
+ }
+ }
+
+ $outdatedMediaFile = $this->getSingleObject();
+ $updatedMediaFile = new Media($this->getSingleObject()->mediaID);
+
+ // Delete *old* files using the non-updated local media editor object if the new file is
+ // stored in a different location.
+ if (empty($result['errors']) && $updatedMediaFile->getLocation() !== $outdatedMediaFile->getLocation()) {
+ $outdatedMediaFile->deleteFiles();
+ }
+
+ return $result;
+ }
}
/**
* Executes follower-related actions.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\User\Follow
- *
- * @method UserFollow create()
- * @method UserFollowEditor[] getObjects()
- * @method UserFollowEditor getSingleObject()
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\User\Follow
+ *
+ * @method UserFollow create()
+ * @method UserFollowEditor[] getObjects()
+ * @method UserFollowEditor getSingleObject()
*/
-class UserFollowAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction {
- /**
- * @inheritDoc
- */
- protected $allowGuestAccess = ['getGroupedUserList'];
-
- /**
- * user profile object
- * @var UserProfile;
- */
- public $userProfile = null;
-
- /**
- * Validates given parameters.
- */
- public function validateFollow() {
- $this->readInteger('userID', false, 'data');
-
- if ($this->parameters['data']['userID'] == WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
-
- // check if current user is ignored by target user
- $sql = "SELECT ignoreID
- FROM wcf".WCF_N."_user_ignore
- WHERE userID = ?
- AND ignoreUserID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([
- $this->parameters['data']['userID'],
- WCF::getUser()->userID
- ]);
-
- $ignoreID = $statement->fetchSingleColumn();
- if ($ignoreID !== false) {
- throw new PermissionDeniedException();
- }
- }
-
- /**
- * Follows a user.
- *
- * @return array
- */
- public function follow() {
- /** @var UserFollow $follow */
- $follow = UserFollowEditor::createOrIgnore([
- 'userID' => WCF::getUser()->userID,
- 'followUserID' => $this->parameters['data']['userID'],
- 'time' => TIME_NOW,
- ]);
-
- if ($follow !== null) {
- // send notification
- UserNotificationHandler::getInstance()->fireEvent(
- 'following',
- 'com.woltlab.wcf.user.follow',
- new UserFollowUserNotificationObject($follow),
- [$follow->followUserID]
- );
-
- // fire activity event
- UserActivityEventHandler::getInstance()->fireEvent('com.woltlab.wcf.user.recentActivityEvent.follow', $this->parameters['data']['userID']);
-
- // reset storage
- UserStorageHandler::getInstance()->reset([$this->parameters['data']['userID']], 'followerUserIDs');
- UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
- }
-
- return [
- 'following' => 1
- ];
- }
-
- /**
- * @inheritDoc
- */
- public function validateUnfollow() {
- $this->validateFollow();
- }
-
- /**
- * Stops following a user.
- *
- * @return array
- */
- public function unfollow() {
- $follow = UserFollow::getFollow(WCF::getUser()->userID, $this->parameters['data']['userID']);
-
- if ($follow->followID) {
- $followEditor = new UserFollowEditor($follow);
- $followEditor->delete();
-
- // remove activity event
- UserActivityEventHandler::getInstance()->removeEvent('com.woltlab.wcf.user.recentActivityEvent.follow', $this->parameters['data']['userID']);
- }
-
- // reset storage
- UserStorageHandler::getInstance()->reset([$this->parameters['data']['userID']], 'followerUserIDs');
- UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
-
- return [
- 'following' => 0
- ];
- }
-
- /**
- * @inheritDoc
- */
- public function validateDelete() {
- // read objects
- if (empty($this->objects)) {
- $this->readObjects();
-
- if (empty($this->objects)) {
- throw new UserInputException('objectIDs');
- }
- }
-
- // validate ownership
- foreach ($this->getObjects() as $follow) {
- if ($follow->userID != WCF::getUser()->userID) {
- throw new PermissionDeniedException();
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function delete() {
- $returnValues = parent::delete();
-
- $followUserIDs = [];
- foreach ($this->getObjects() as $follow) {
- $followUserIDs[] = $follow->followUserID;
- // remove activity event
- UserActivityEventHandler::getInstance()->removeEvents('com.woltlab.wcf.user.recentActivityEvent.follow', [$follow->followUserID]);
- }
-
- // reset storage
- UserStorageHandler::getInstance()->reset($followUserIDs, 'followerUserIDs');
- UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
-
- return $returnValues;
- }
-
- /**
- * @inheritDoc
- */
- public function validateGetGroupedUserList() {
- $this->readInteger('pageNo');
- $this->readInteger('userID');
-
- $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
- if (!$this->userProfile) {
- throw new UserInputException('userID');
- }
- if ($this->userProfile->isProtected()) {
- throw new PermissionDeniedException();
- }
-
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * @inheritDoc
- */
- public function getGroupedUserList() {
- // resolve page count
- $sql = "SELECT COUNT(*)
- FROM wcf".WCF_N."_user_follow
- WHERE followUserID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([$this->parameters['userID']]);
- $pageCount = ceil($statement->fetchSingleColumn() / 20);
-
- // get user ids
- $sql = "SELECT userID
- FROM wcf".WCF_N."_user_follow
- WHERE followUserID = ?";
- $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
- $statement->execute([$this->parameters['userID']]);
- $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
-
- // create group
- $group = new GroupedUserList();
- $group->addUserIDs($userIDs);
-
- // load user profiles
- GroupedUserList::loadUsers();
-
- WCF::getTPL()->assign([
- 'groupedUsers' => [$group]
- ]);
-
- return [
- 'pageCount' => $pageCount,
- 'template' => WCF::getTPL()->fetch('groupedUserList')
- ];
- }
+class UserFollowAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction
+{
+ /**
+ * @inheritDoc
+ */
+ protected $allowGuestAccess = ['getGroupedUserList'];
+
+ /**
+ * user profile object
+ * @var UserProfile;
+ */
+ public $userProfile;
+
+ /**
+ * Validates given parameters.
+ */
+ public function validateFollow()
+ {
+ $this->readInteger('userID', false, 'data');
+
+ if ($this->parameters['data']['userID'] == WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+
+ // check if current user is ignored by target user
+ $sql = "SELECT ignoreID
+ FROM wcf" . WCF_N . "_user_ignore
+ WHERE userID = ?
+ AND ignoreUserID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ $this->parameters['data']['userID'],
+ WCF::getUser()->userID,
+ ]);
+
+ $ignoreID = $statement->fetchSingleColumn();
+ if ($ignoreID !== false) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Follows a user.
+ *
+ * @return array
+ */
+ public function follow()
+ {
+ /** @var UserFollow $follow */
+ $follow = UserFollowEditor::createOrIgnore([
+ 'userID' => WCF::getUser()->userID,
+ 'followUserID' => $this->parameters['data']['userID'],
+ 'time' => TIME_NOW,
+ ]);
+
+ if ($follow !== null) {
+ // send notification
+ UserNotificationHandler::getInstance()->fireEvent(
+ 'following',
+ 'com.woltlab.wcf.user.follow',
+ new UserFollowUserNotificationObject($follow),
+ [$follow->followUserID]
+ );
+
+ // fire activity event
+ UserActivityEventHandler::getInstance()->fireEvent(
+ 'com.woltlab.wcf.user.recentActivityEvent.follow',
+ $this->parameters['data']['userID']
+ );
+
+ // reset storage
+ UserStorageHandler::getInstance()->reset([$this->parameters['data']['userID']], 'followerUserIDs');
+ UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
+ }
+
+ return [
+ 'following' => 1,
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateUnfollow()
+ {
+ $this->validateFollow();
+ }
+
+ /**
+ * Stops following a user.
+ *
+ * @return array
+ */
+ public function unfollow()
+ {
+ $follow = UserFollow::getFollow(WCF::getUser()->userID, $this->parameters['data']['userID']);
+
+ if ($follow->followID) {
+ $followEditor = new UserFollowEditor($follow);
+ $followEditor->delete();
+
+ // remove activity event
+ UserActivityEventHandler::getInstance()->removeEvent(
+ 'com.woltlab.wcf.user.recentActivityEvent.follow',
+ $this->parameters['data']['userID']
+ );
+ }
+
+ // reset storage
+ UserStorageHandler::getInstance()->reset([$this->parameters['data']['userID']], 'followerUserIDs');
+ UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
+
+ return [
+ 'following' => 0,
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateDelete()
+ {
+ // read objects
+ if (empty($this->objects)) {
+ $this->readObjects();
+
+ if (empty($this->objects)) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ // validate ownership
+ foreach ($this->getObjects() as $follow) {
+ if ($follow->userID != WCF::getUser()->userID) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete()
+ {
+ $returnValues = parent::delete();
+
+ $followUserIDs = [];
+ foreach ($this->getObjects() as $follow) {
+ $followUserIDs[] = $follow->followUserID;
+ // remove activity event
+ UserActivityEventHandler::getInstance()->removeEvents(
+ 'com.woltlab.wcf.user.recentActivityEvent.follow',
+ [$follow->followUserID]
+ );
+ }
+
+ // reset storage
+ UserStorageHandler::getInstance()->reset($followUserIDs, 'followerUserIDs');
+ UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'followingUserIDs');
+
+ return $returnValues;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateGetGroupedUserList()
+ {
+ $this->readInteger('pageNo');
+ $this->readInteger('userID');
+
+ $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
++ if (!$this->userProfile) {
++ throw new UserInputException('userID');
++ }
+ if ($this->userProfile->isProtected()) {
+ throw new PermissionDeniedException();
+ }
++
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getGroupedUserList()
+ {
+ // resolve page count
+ $sql = "SELECT COUNT(*)
+ FROM wcf" . WCF_N . "_user_follow
+ WHERE followUserID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([$this->parameters['userID']]);
+ $pageCount = \ceil($statement->fetchSingleColumn() / 20);
+
+ // get user ids
+ $sql = "SELECT userID
+ FROM wcf" . WCF_N . "_user_follow
+ WHERE followUserID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
+ $statement->execute([$this->parameters['userID']]);
+ $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
+
+ // create group
+ $group = new GroupedUserList();
+ $group->addUserIDs($userIDs);
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign([
+ 'groupedUsers' => [$group],
+ ]);
+
+ return [
+ 'pageCount' => $pageCount,
+ 'template' => WCF::getTPL()->fetch('groupedUserList'),
+ ];
+ }
}
<?php
+
namespace wcf\data\user\follow;
+
use wcf\system\cache\runtime\UserProfileRuntimeCache;
use wcf\system\exception\PermissionDeniedException;
+ use wcf\system\exception\UserInputException;
use wcf\system\user\GroupedUserList;
use wcf\system\WCF;
/**
* Executes following-related actions.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\User\Follow
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\User\Follow
*/
-class UserFollowingAction extends UserFollowAction {
- /**
- * @inheritDoc
- */
- protected $className = UserFollowEditor::class;
-
- /**
- * @inheritDoc
- */
- public function validateGetGroupedUserList() {
- $this->readInteger('pageNo');
- $this->readInteger('userID');
-
- $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
- if (!$this->userProfile) {
- throw new UserInputException('userID');
- }
- if ($this->userProfile->isProtected()) {
- throw new PermissionDeniedException();
- }
+class UserFollowingAction extends UserFollowAction
+{
+ /**
+ * @inheritDoc
+ */
+ protected $className = UserFollowEditor::class;
+
+ /**
+ * @inheritDoc
+ */
+ public function validateGetGroupedUserList()
+ {
+ $this->readInteger('pageNo');
+ $this->readInteger('userID');
+
+ $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
++ if (!$this->userProfile) {
++ throw new UserInputException('userID');
++ }
+ if ($this->userProfile->isProtected()) {
+ throw new PermissionDeniedException();
+ }
++
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getGroupedUserList()
+ {
+ // resolve page count
+ $sql = "SELECT COUNT(*)
+ FROM wcf" . WCF_N . "_user_follow
+ WHERE userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([$this->parameters['userID']]);
+ $pageCount = \ceil($statement->fetchSingleColumn() / 20);
+
+ // get user ids
+ $sql = "SELECT followUserID
+ FROM wcf" . WCF_N . "_user_follow
+ WHERE userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
+ $statement->execute([$this->parameters['userID']]);
+ $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
+
+ // create group
+ $group = new GroupedUserList();
+ $group->addUserIDs($userIDs);
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign([
+ 'groupedUsers' => [$group],
+ ]);
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * @inheritDoc
- */
- public function getGroupedUserList() {
- // resolve page count
- $sql = "SELECT COUNT(*)
- FROM wcf".WCF_N."_user_follow
- WHERE userID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([$this->parameters['userID']]);
- $pageCount = ceil($statement->fetchSingleColumn() / 20);
-
- // get user ids
- $sql = "SELECT followUserID
- FROM wcf".WCF_N."_user_follow
- WHERE userID = ?";
- $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
- $statement->execute([$this->parameters['userID']]);
- $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
-
- // create group
- $group = new GroupedUserList();
- $group->addUserIDs($userIDs);
-
- // load user profiles
- GroupedUserList::loadUsers();
-
- WCF::getTPL()->assign([
- 'groupedUsers' => [$group]
- ]);
-
- return [
- 'pageCount' => $pageCount,
- 'template' => WCF::getTPL()->fetch('groupedUserList')
- ];
- }
+ return [
+ 'pageCount' => $pageCount,
+ 'template' => WCF::getTPL()->fetch('groupedUserList'),
+ ];
+ }
}
/**
* Executes profile visitor-related actions.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\User\Profile\Visitor
- *
- * @method UserProfileVisitor create()
- * @method UserProfileVisitorEditor[] getObjects()
- * @method UserProfileVisitorEditor getSingleObject()
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\User\Profile\Visitor
+ *
+ * @method UserProfileVisitor create()
+ * @method UserProfileVisitorEditor[] getObjects()
+ * @method UserProfileVisitorEditor getSingleObject()
*/
-class UserProfileVisitorAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction {
- /**
- * @inheritDoc
- */
- protected $allowGuestAccess = ['getGroupedUserList'];
-
- /**
- * user profile object
- * @var UserProfile;
- */
- public $userProfile = null;
-
- /**
- * @inheritDoc
- */
- public function validateGetGroupedUserList() {
- $this->readInteger('pageNo');
- $this->readInteger('userID');
-
- $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
- if (!$this->userProfile) {
- throw new UserInputException('userID');
- }
- if ($this->userProfile->isProtected()) {
- throw new PermissionDeniedException();
- }
+class UserProfileVisitorAction extends AbstractDatabaseObjectAction implements IGroupedUserListAction
+{
+ /**
+ * @inheritDoc
+ */
+ protected $allowGuestAccess = ['getGroupedUserList'];
+
+ /**
+ * user profile object
+ * @var UserProfile;
+ */
+ public $userProfile;
+
+ /**
+ * @inheritDoc
+ */
+ public function validateGetGroupedUserList()
+ {
+ $this->readInteger('pageNo');
+ $this->readInteger('userID');
+
+ $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
+ if (!$this->userProfile) {
+ throw new UserInputException('userID');
+ }
+ if ($this->userProfile->isProtected()) {
+ throw new PermissionDeniedException();
+ }
++
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getGroupedUserList()
+ {
+ // resolve page count
+ $sql = "SELECT COUNT(*)
+ FROM wcf" . WCF_N . "_user_profile_visitor
+ WHERE ownerID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([$this->parameters['userID']]);
+ $pageCount = \ceil($statement->fetchSingleColumn() / 20);
+
+ // get user ids
+ $sql = "SELECT userID
+ FROM wcf" . WCF_N . "_user_profile_visitor
+ WHERE ownerID = ?
+ ORDER BY time DESC";
+ $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
+ $statement->execute([$this->parameters['userID']]);
+ $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
+
+ // create group
+ $group = new GroupedUserList();
+ $group->addUserIDs($userIDs);
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign([
+ 'groupedUsers' => [$group],
+ ]);
+
+ return [
+ 'pageCount' => $pageCount,
+ 'template' => WCF::getTPL()->fetch('groupedUserList'),
+ ];
+ }
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * @inheritDoc
- */
- public function getGroupedUserList() {
- // resolve page count
- $sql = "SELECT COUNT(*)
- FROM wcf".WCF_N."_user_profile_visitor
- WHERE ownerID = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([$this->parameters['userID']]);
- $pageCount = ceil($statement->fetchSingleColumn() / 20);
-
- // get user ids
- $sql = "SELECT userID
- FROM wcf".WCF_N."_user_profile_visitor
- WHERE ownerID = ?
- ORDER BY time DESC";
- $statement = WCF::getDB()->prepareStatement($sql, 20, ($this->parameters['pageNo'] - 1) * 20);
- $statement->execute([$this->parameters['userID']]);
- $userIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
-
- // create group
- $group = new GroupedUserList();
- $group->addUserIDs($userIDs);
-
- // load user profiles
- GroupedUserList::loadUsers();
-
- WCF::getTPL()->assign([
- 'groupedUsers' => [$group]
- ]);
-
- return [
- 'pageCount' => $pageCount,
- 'template' => WCF::getTPL()->fetch('groupedUserList')
- ];
- }
-
- /**
- * Inserts a new visitor if it does not already exist, or updates it if it does.
- * @since 5.2
- */
- public function registerVisitor() {
- $sql = "INSERT INTO wcf".WCF_N."_user_profile_visitor
- (ownerID, userID, time)
- VALUES (?, ?, ?)
- ON DUPLICATE KEY UPDATE time = VALUES(time)";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute([
- $this->parameters['data']['ownerID'],
- $this->parameters['data']['userID'],
- $this->parameters['data']['time'] ?? TIME_NOW,
- ]);
- }
+ /**
+ * Inserts a new visitor if it does not already exist, or updates it if it does.
+ * @since 5.2
+ */
+ public function registerVisitor()
+ {
+ $sql = "INSERT INTO wcf" . WCF_N . "_user_profile_visitor
+ (ownerID, userID, time)
+ VALUES (?, ?, ?)
+ ON DUPLICATE KEY UPDATE time = VALUES(time)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ $this->parameters['data']['ownerID'],
+ $this->parameters['data']['userID'],
+ $this->parameters['data']['time'] ?? TIME_NOW,
+ ]);
+ }
}
use wcf\system\WCF;
/**
- * Provides user trophy actions.
+ * Provides user trophy actions.
*
- * @author Joshua Ruesweg
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\Data\User\Trophy
- * @since 3.1
+ * @author Joshua Ruesweg
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\User\Trophy
+ * @since 3.1
*
- * @method UserTrophyEditor[] getObjects()
- * @method UserTrophyEditor getSingleObject()
+ * @method UserTrophyEditor[] getObjects()
+ * @method UserTrophyEditor getSingleObject()
*/
-class UserTrophyAction extends AbstractDatabaseObjectAction {
- /**
- * @inheritDoc
- */
- protected $permissionsDelete = ['admin.trophy.canAwardTrophy'];
-
- /**
- * @inheritDoc
- */
- protected $allowGuestAccess = ['getGroupedUserTrophyList'];
-
- /**
- * @var UserProfile
- */
- public $userProfile;
-
- /**
- * @inheritDoc
- */
- public function create() {
- /** @var UserTrophy $userTrophy */
- $userTrophy = parent::create();
-
- if (!$userTrophy->getTrophy()->isDisabled()) {
- $userAction = new UserAction([$userTrophy->userID], 'update', [
- 'counters' => [
- 'trophyPoints' => 1
- ]
- ]);
- $userAction->executeAction();
-
- // checks if the user still has space to add special trophies
- if (count($userTrophy->getUserProfile()->getSpecialTrophies()) < $userTrophy->getUserProfile()->getPermission('user.profile.trophy.maxUserSpecialTrophies')) {
- $hasTrophy = false;
- foreach (UserTrophyList::getUserTrophies([$userTrophy->getUserProfile()->userID])[$userTrophy->getUserProfile()->userID] as $trophy) {
- if ($trophy->trophyID == $userTrophy->trophyID && $trophy->userTrophyID !== $userTrophy->userTrophyID) {
- $hasTrophy = true;
- break;
- }
- }
-
- if (!$hasTrophy) {
- $userProfileAction = new UserProfileAction([$userTrophy->getUserProfile()->getDecoratedObject()], 'updateSpecialTrophies', [
- 'trophyIDs' => array_unique(array_merge(array_map(function($trophy) {
- return $trophy->trophyID;
- }, $userTrophy->getUserProfile()->getSpecialTrophies()), [$userTrophy->trophyID]))
- ]);
- $userProfileAction->executeAction();
- }
- }
- }
-
- UserActivityEventHandler::getInstance()->fireEvent('com.woltlab.wcf.userTrophy.recentActivityEvent.trophyReceived', $userTrophy->getObjectID(), null, $userTrophy->userID);
-
- UserNotificationHandler::getInstance()->fireEvent('received', 'com.woltlab.wcf.userTrophy.notification', new UserTrophyNotificationObject($userTrophy), [
- $userTrophy->userID
- ]);
-
- return $userTrophy;
- }
-
- /**
- * @inheritDoc
- */
- public function validateDelete() {
- parent::validateDelete();
-
- /** @var UserTrophy $object */
- foreach ($this->objects as $object) {
- if ($object->getTrophy()->awardAutomatically) {
- throw new PermissionDeniedException();
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- public function delete() {
- if (empty($this->objects)) {
- $this->readObjects();
- }
-
- $trophyIDs = $userIDs = [];
- foreach ($this->getObjects() as $object) {
- $trophyIDs[] = $object->trophyID;
- $userIDs[] = $object->userID;
- }
-
- $returnValues = parent::delete();
-
- if (!empty($this->objects)) {
- // update user special trophies trophies
- $userTrophies = UserTrophyList::getUserTrophies($userIDs);
-
- foreach ($userTrophies as $userID => $trophies) {
- $userTrophyIDs = [];
- foreach ($trophies as $trophy) {
- $userTrophyIDs[] = $trophy->trophyID;
- }
-
- $conditionBuilder = new PreparedStatementConditionBuilder();
- if (!empty($userTrophyIDs)) $conditionBuilder->add('trophyID NOT IN (?)', [array_unique($userTrophyIDs)]);
- $conditionBuilder->add('userID = ?', [$userID]);
-
- $sql = "DELETE FROM wcf". WCF_N ."_user_special_trophy ". $conditionBuilder;
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute($conditionBuilder->getParameters());
-
- UserStorageHandler::getInstance()->reset([$userID], 'specialTrophies');
- }
-
- $updateUserTrophies = [];
- foreach ($this->getObjects() as $object) {
- if (!$object->getTrophy()->isDisabled()) {
- if (!isset($updateUserTrophies[$object->userID])) $updateUserTrophies[$object->userID] = 0;
- $updateUserTrophies[$object->userID]--;
- }
- }
-
- foreach ($updateUserTrophies as $userID => $count) {
- $userAction = new UserAction([$userID], 'update', [
- 'counters' => [
- 'trophyPoints' => $count
- ]
- ]);
- $userAction->executeAction();
- }
- }
-
- return $returnValues;
- }
-
- /**
- * Validates the getGroupedUserTrophyList method.
- */
- public function validateGetGroupedUserTrophyList() {
- if (!MODULE_TROPHY) {
- throw new IllegalLinkException();
- }
-
- WCF::getSession()->checkPermissions(['user.profile.trophy.canSeeTrophies']);
-
- $this->readInteger('pageNo');
- $this->readInteger('userID');
-
- $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
- if (!$this->userProfile) {
- throw new UserInputException('userID');
- }
- if (!$this->userProfile->isAccessible('canViewTrophies') && $this->userProfile->userID != WCF::getSession()->userID) {
- throw new PermissionDeniedException();
- }
-
- if ($this->parameters['pageNo'] < 1) {
- throw new UserInputException('pageNo');
- }
- }
-
- /**
- * Returns a viewable user trophy list for a specific user.
- */
- public function getGroupedUserTrophyList() {
- $userTrophyList = new UserTrophyList();
- $userTrophyList->getConditionBuilder()->add('userID = ?', [$this->parameters['userID']]);
- if (!empty($userTrophyList->sqlJoins)) $userTrophyList->sqlJoins .= ' ';
- if (!empty($userTrophyList->sqlConditionJoins)) $userTrophyList->sqlConditionJoins .= ' ';
- $userTrophyList->sqlJoins .= 'LEFT JOIN wcf'. WCF_N . '_trophy trophy ON user_trophy.trophyID = trophy.trophyID';
- $userTrophyList->sqlConditionJoins .= 'LEFT JOIN wcf'. WCF_N . '_trophy trophy ON user_trophy.trophyID = trophy.trophyID';
-
- // trophy category join
- $userTrophyList->sqlJoins .= ' LEFT JOIN wcf'. WCF_N . '_category category ON trophy.categoryID = category.categoryID';
- $userTrophyList->sqlConditionJoins .= ' LEFT JOIN wcf'. WCF_N . '_category category ON trophy.categoryID = category.categoryID';
-
- $userTrophyList->getConditionBuilder()->add('trophy.isDisabled = ?', [0]);
- $userTrophyList->getConditionBuilder()->add('category.isDisabled = ?', [0]);
- $userTrophyList->sqlLimit = 10;
- $userTrophyList->sqlOffset = ($this->parameters['pageNo'] - 1) * 10;
- $userTrophyList->sqlOrderBy = 'time DESC';
- $pageCount = ceil($userTrophyList->countObjects() / 10);
- $userTrophyList->readObjects();
-
- return [
- 'pageCount' => $pageCount,
- 'title' => WCF::getLanguage()->getDynamicVariable('wcf.user.trophy.dialogTitle', ['username' => $this->userProfile->username]),
- 'template' => WCF::getTPL()->fetch('groupedUserTrophyList', 'wcf', [
- 'userTrophyList' => $userTrophyList
- ])
- ];
- }
+class UserTrophyAction extends AbstractDatabaseObjectAction
+{
+ /**
+ * @inheritDoc
+ */
+ protected $permissionsDelete = ['admin.trophy.canAwardTrophy'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $allowGuestAccess = ['getGroupedUserTrophyList'];
+
+ /**
+ * @var UserProfile
+ */
+ public $userProfile;
+
+ /**
+ * @inheritDoc
+ */
+ public function create()
+ {
+ /** @var UserTrophy $userTrophy */
+ $userTrophy = parent::create();
+
+ if (!$userTrophy->getTrophy()->isDisabled()) {
+ $userAction = new UserAction([$userTrophy->userID], 'update', [
+ 'counters' => [
+ 'trophyPoints' => 1,
+ ],
+ ]);
+ $userAction->executeAction();
+
+ // checks if the user still has space to add special trophies
+ if (\count($userTrophy->getUserProfile()->getSpecialTrophies()) < $userTrophy->getUserProfile()->getPermission('user.profile.trophy.maxUserSpecialTrophies')) {
+ $hasTrophy = false;
+ foreach (UserTrophyList::getUserTrophies([$userTrophy->getUserProfile()->userID])[$userTrophy->getUserProfile()->userID] as $trophy) {
+ if ($trophy->trophyID == $userTrophy->trophyID && $trophy->userTrophyID !== $userTrophy->userTrophyID) {
+ $hasTrophy = true;
+ break;
+ }
+ }
+
+ if (!$hasTrophy) {
+ $userProfileAction = new UserProfileAction(
+ [$userTrophy->getUserProfile()->getDecoratedObject()],
+ 'updateSpecialTrophies',
+ [
+ 'trophyIDs' => \array_unique(\array_merge(\array_map(static function ($trophy) {
+ return $trophy->trophyID;
+ }, $userTrophy->getUserProfile()->getSpecialTrophies()), [$userTrophy->trophyID])),
+ ]
+ );
+ $userProfileAction->executeAction();
+ }
+ }
+ }
+
+ UserActivityEventHandler::getInstance()->fireEvent(
+ 'com.woltlab.wcf.userTrophy.recentActivityEvent.trophyReceived',
+ $userTrophy->getObjectID(),
+ null,
+ $userTrophy->userID
+ );
+
+ UserNotificationHandler::getInstance()->fireEvent(
+ 'received',
+ 'com.woltlab.wcf.userTrophy.notification',
+ new UserTrophyNotificationObject($userTrophy),
+ [
+ $userTrophy->userID,
+ ]
+ );
+
+ return $userTrophy;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateDelete()
+ {
+ parent::validateDelete();
+
+ /** @var UserTrophy $object */
+ foreach ($this->objects as $object) {
+ if ($object->getTrophy()->awardAutomatically) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete()
+ {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ $trophyIDs = $userIDs = [];
+ foreach ($this->getObjects() as $object) {
+ $trophyIDs[] = $object->trophyID;
+ $userIDs[] = $object->userID;
+ }
+
+ $returnValues = parent::delete();
+
+ if (!empty($this->objects)) {
+ // update user special trophies trophies
+ $userTrophies = UserTrophyList::getUserTrophies($userIDs);
+
+ foreach ($userTrophies as $userID => $trophies) {
+ $userTrophyIDs = [];
+ foreach ($trophies as $trophy) {
+ $userTrophyIDs[] = $trophy->trophyID;
+ }
+
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ if (!empty($userTrophyIDs)) {
+ $conditionBuilder->add('trophyID NOT IN (?)', [\array_unique($userTrophyIDs)]);
+ }
+ $conditionBuilder->add('userID = ?', [$userID]);
+
+ $sql = "DELETE FROM wcf" . WCF_N . "_user_special_trophy
+ " . $conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ UserStorageHandler::getInstance()->reset([$userID], 'specialTrophies');
+ }
+
+ $updateUserTrophies = [];
+ foreach ($this->getObjects() as $object) {
+ if (!$object->getTrophy()->isDisabled()) {
+ if (!isset($updateUserTrophies[$object->userID])) {
+ $updateUserTrophies[$object->userID] = 0;
+ }
+ $updateUserTrophies[$object->userID]--;
+ }
+ }
+
+ foreach ($updateUserTrophies as $userID => $count) {
+ $userAction = new UserAction([$userID], 'update', [
+ 'counters' => [
+ 'trophyPoints' => $count,
+ ],
+ ]);
+ $userAction->executeAction();
+ }
+ }
+
+ return $returnValues;
+ }
+
+ /**
+ * Validates the getGroupedUserTrophyList method.
+ */
+ public function validateGetGroupedUserTrophyList()
+ {
+ if (!MODULE_TROPHY) {
+ throw new IllegalLinkException();
+ }
+
+ WCF::getSession()->checkPermissions(['user.profile.trophy.canSeeTrophies']);
+
+ $this->readInteger('pageNo');
+ $this->readInteger('userID');
+
+ $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->parameters['userID']);
+ if (!$this->userProfile) {
+ throw new UserInputException('userID');
+ }
+ if (!$this->userProfile->isAccessible('canViewTrophies') && $this->userProfile->userID != WCF::getSession()->userID) {
+ throw new PermissionDeniedException();
+ }
++
++ if ($this->parameters['pageNo'] < 1) {
++ throw new UserInputException('pageNo');
++ }
+ }
+
+ /**
+ * Returns a viewable user trophy list for a specific user.
+ */
+ public function getGroupedUserTrophyList()
+ {
+ $userTrophyList = new UserTrophyList();
+ $userTrophyList->getConditionBuilder()->add('userID = ?', [$this->parameters['userID']]);
+ if (!empty($userTrophyList->sqlJoins)) {
+ $userTrophyList->sqlJoins .= ' ';
+ }
+ if (!empty($userTrophyList->sqlConditionJoins)) {
+ $userTrophyList->sqlConditionJoins .= ' ';
+ }
+ $userTrophyList->sqlJoins .= '
+ LEFT JOIN wcf' . WCF_N . '_trophy trophy
+ ON user_trophy.trophyID = trophy.trophyID';
+ $userTrophyList->sqlConditionJoins .= '
+ LEFT JOIN wcf' . WCF_N . '_trophy trophy
+ ON user_trophy.trophyID = trophy.trophyID';
+
+ // trophy category join
+ $userTrophyList->sqlJoins .= '
+ LEFT JOIN wcf' . WCF_N . '_category category
+ ON trophy.categoryID = category.categoryID';
+ $userTrophyList->sqlConditionJoins .= '
+ LEFT JOIN wcf' . WCF_N . '_category category
+ ON trophy.categoryID = category.categoryID';
+
+ $userTrophyList->getConditionBuilder()->add('trophy.isDisabled = ?', [0]);
+ $userTrophyList->getConditionBuilder()->add('category.isDisabled = ?', [0]);
+ $userTrophyList->sqlLimit = 10;
+ $userTrophyList->sqlOffset = ($this->parameters['pageNo'] - 1) * 10;
+ $userTrophyList->sqlOrderBy = 'time DESC';
+ $pageCount = \ceil($userTrophyList->countObjects() / 10);
+ $userTrophyList->readObjects();
+
+ return [
+ 'pageCount' => $pageCount,
+ 'title' => WCF::getLanguage()->getDynamicVariable(
+ 'wcf.user.trophy.dialogTitle',
+ ['username' => $this->userProfile->username]
+ ),
+ 'template' => WCF::getTPL()->fetch('groupedUserTrophyList', 'wcf', [
+ 'userTrophyList' => $userTrophyList,
+ ]),
+ ];
+ }
}
/**
* Abstract implementation of a database access class using PDO.
- *
- * @author Marcel Werk
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package WoltLabSuite\Core\System\Database
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database
*/
-abstract class Database {
- /**
- * name of the class used for prepared statements
- * @var string
- */
- protected $preparedStatementClassName = PreparedStatement::class;
-
- /**
- * name of the database editor class
- * @var string
- */
- protected $editorClassName = DatabaseEditor::class;
-
- /**
- * sql server hostname
- * @var string
- */
- protected $host = '';
-
- /**
- * sql server post
- * @var integer
- */
- protected $port = 0;
-
- /**
- * sql server login name
- * @var string
- */
- protected $user = '';
-
- /**
- * sql server login password
- * @var string
- */
- protected $password = '';
-
- /**
- * database name
- * @var string
- */
- protected $database = '';
-
- /**
- * enables failsafe connection
- * @var boolean
- */
- protected $failsafeTest = false;
-
- /**
- * number of executed queries
- * @var integer
- */
- protected $queryCount = 0;
-
- /**
- * database editor object
- * @var DatabaseEditor
- */
- protected $editor = null;
-
- /**
- * pdo object
- * @var \PDO
- */
- protected $pdo = null;
-
- /**
- * amount of active transactions
- * @var integer
- */
- protected $activeTransactions = 0;
-
- /**
- * attempts to create the database after the connection has been established
- * @var boolean
- */
- protected $tryToCreateDatabase = false;
-
- /**
- * default driver options passed to the PDO constructor
- * @var array
- */
- protected $defaultDriverOptions = [];
-
- /**
- * Creates a Database Object.
- *
- * @param string $host SQL database server host address
- * @param string $user SQL database server username
- * @param string $password SQL database server password
- * @param string $database SQL database server database name
- * @param integer $port SQL database server port
- * @param boolean $failsafeTest
- * @param boolean $tryToCreateDatabase
- * @param array $defaultDriverOptions
- */
- public function __construct($host, $user, $password, $database, $port, $failsafeTest = false, $tryToCreateDatabase = false, $defaultDriverOptions = []) {
- $this->host = $host;
- $this->port = $port;
- $this->user = $user;
- $this->password = $password;
- $this->database = $database;
- $this->failsafeTest = $failsafeTest;
- $this->tryToCreateDatabase = $tryToCreateDatabase;
- $this->defaultDriverOptions = $defaultDriverOptions;
-
- // connect database
- $this->connect();
- }
-
- public function enableDebugMode() {
- $this->preparedStatementClassName = DebugPreparedStatement::class;
- }
-
- /**
- * Connects to database server.
- */
- abstract public function connect();
-
- /**
- * Returns ID from last insert.
- *
- * @param string $table
- * @param string $field
- * @return integer
- * @throws DatabaseException
- */
- public function getInsertID($table, $field) {
- try {
- return $this->pdo->lastInsertId();
- }
- catch (\PDOException $e) {
- throw new GenericDatabaseException("Cannot fetch last insert id", $e);
- }
- }
-
- /**
- * Initiates a transaction.
- *
- * @return boolean true on success
- * @throws DatabaseTransactionException
- */
- public function beginTransaction() {
- try {
- if ($this->activeTransactions === 0) {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("BEGIN", Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->beginTransaction();
- }
- else {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("SAVEPOINT level".$this->activeTransactions, Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->exec("SAVEPOINT level".$this->activeTransactions) !== false;
- }
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->stop();
-
- $this->activeTransactions++;
-
- return $result;
- }
- catch (\PDOException $e) {
- throw new DatabaseTransactionException("Could not begin transaction", $e);
- }
- }
-
- /**
- * Commits a transaction and returns true if the transaction was successful.
- *
- * @return boolean
- * @throws DatabaseTransactionException
- */
- public function commitTransaction() {
- if ($this->activeTransactions === 0) return false;
-
- try {
- $this->activeTransactions--;
-
- if ($this->activeTransactions === 0) {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("COMMIT", Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->commit();
- }
- else {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("RELEASE SAVEPOINT level".$this->activeTransactions, Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->exec("RELEASE SAVEPOINT level".$this->activeTransactions) !== false;
- }
-
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->stop();
-
- return $result;
- }
- catch (\PDOException $e) {
- throw new DatabaseTransactionException("Could not commit transaction", $e);
- }
- }
-
- /**
- * Rolls back a transaction and returns true if the rollback was successful.
- *
- * @return boolean
- * @throws DatabaseTransactionException
- */
- public function rollBackTransaction() {
- if ($this->activeTransactions === 0) return false;
-
- try {
- $this->activeTransactions--;
- if ($this->activeTransactions === 0) {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("ROLLBACK", Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->rollBack();
- }
- else {
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start("ROLLBACK TO SAVEPOINT level".$this->activeTransactions, Benchmark::TYPE_SQL_QUERY);
- $result = $this->pdo->exec("ROLLBACK TO SAVEPOINT level".$this->activeTransactions) !== false;
- }
-
- if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->stop();
-
- return $result;
- }
- catch (\PDOException $e) {
- throw new DatabaseTransactionException("Could not roll back transaction", $e);
- }
- }
-
- /**
- * Prepares a statement for execution and returns a statement object.
- *
- * @param string $statement
- * @param integer $limit
- * @param integer $offset
- * @return PreparedStatement
- * @throws DatabaseQueryException
- */
- public function prepareStatement($statement, $limit = 0, $offset = 0) {
- $statement = $this->handleLimitParameter($statement, $limit, $offset);
-
- try {
- // Append routing information of the current request as a comment.
- // This allows the system administrator to find offending requests
- // in MySQL's slow query log and / or MySQL's process list.
- // Note: This is meant to be run unconditionally in production to be
- // useful. Thus the code to retrieve the request information
- // must be absolutely lightweight.
- static $requestInformation = null;
- if ($requestInformation === null) {
- $requestInformation = '';
- if (defined('ENABLE_PRODUCTION_DEBUG_MODE') && ENABLE_PRODUCTION_DEBUG_MODE && isset($_SERVER['REQUEST_URI'])) {
- $requestInformation = $_SERVER['REQUEST_URI'];
- if ($requestId = \wcf\getRequestId()) {
- $requestInformation = substr($requestInformation, 0, 70);
- $requestInformation .= ' ('.$requestId.')';
- }
- if (isset($_REQUEST['className']) && isset($_REQUEST['actionName'])) {
- $requestInformation = substr($requestInformation, 0, 90);
- $requestInformation .= ' ('.$_REQUEST['className'].':'.$_REQUEST['actionName'].')';
- }
- $requestInformation = substr($requestInformation, 0, 180);
- }
- }
-
- $pdoStatement = $this->pdo->prepare($statement.($requestInformation ? " -- ".$this->pdo->quote($requestInformation) : ''));
-
- return new $this->preparedStatementClassName($this, $pdoStatement, $statement);
- }
- catch (\PDOException $e) {
- throw new DatabaseQueryException("Could not prepare statement '".$statement."'", $e);
- }
- }
-
- /**
- * Handles the limit and offset parameter in SELECT queries.
- * This is a default implementation compatible to MySQL and PostgreSQL.
- * Other database implementations should override this function.
- *
- * @param string $query
- * @param integer $limit
- * @param integer $offset
- * @return string
- */
- public function handleLimitParameter($query, $limit = 0, $offset = 0) {
- $limit = \intval($limit);
- $offset = \intval($offset);
- if ($limit < 0) {
- throw new \InvalidArgumentException('The limit must not be negative.');
- }
- if ($offset < 0) {
- throw new \InvalidArgumentException('The offset must not be negative.');
- }
-
- if ($limit != 0) {
- $query = preg_replace('~(\s+FOR\s+UPDATE\s*)?$~', " LIMIT " . $limit . ($offset ? " OFFSET " . $offset : '') . "\\0", $query, 1);
- }
-
- return $query;
- }
-
- /**
- * Returns the number of the last error.
- *
- * @return integer
- */
- public function getErrorNumber() {
- if ($this->pdo !== null) return $this->pdo->errorCode();
- return 0;
- }
-
- /**
- * Returns the description of the last error.
- *
- * @return string
- */
- public function getErrorDesc() {
- if ($this->pdo !== null) {
- $errorInfoArray = $this->pdo->errorInfo();
- if (isset($errorInfoArray[2])) return $errorInfoArray[2];
- }
- return '';
- }
-
- /**
- * Returns the current database type.
- *
- * @return string
- */
- public function getDBType() {
- return get_class($this);
- }
-
- /**
- * Escapes a string for use in sql query.
- *
- * @param string $string
- * @return string
- */
- public function escapeString($string) {
- return addslashes($string);
- }
-
- /**
- * Returns the sql version.
- *
- * @return string
- */
- public function getVersion() {
- try {
- if ($this->pdo !== null) {
- return $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION);
- }
- }
- catch (\PDOException $e) {}
-
- return 'unknown';
- }
-
- /**
- * Returns the database name.
- *
- * @return string
- */
- public function getDatabaseName() {
- return $this->database;
- }
-
- /**
- * Returns the name of the database user.
- *
- * @return string user name
- */
- public function getUser() {
- return $this->user;
- }
-
- /**
- * Returns the amount of executed sql queries.
- *
- * @return integer
- */
- public function getQueryCount() {
- return $this->queryCount;
- }
-
- /**
- * Increments the query counter by one.
- */
- public function incrementQueryCount() {
- $this->queryCount++;
- }
-
- /**
- * Returns a database editor object.
- *
- * @return DatabaseEditor
- */
- public function getEditor() {
- if ($this->editor === null) {
- $this->editor = new $this->editorClassName($this);
- }
-
- return $this->editor;
- }
-
- /**
- * Returns true if this database type is supported.
- *
- * @return boolean
- */
- public static function isSupported() {
- return false;
- }
-
- /**
- * Sets default connection attributes.
- */
- protected function setAttributes() {
- $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
- $this->pdo->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL);
- $this->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false);
- }
+abstract class Database
+{
+ /**
+ * name of the class used for prepared statements
+ * @var string
+ */
+ protected $preparedStatementClassName = PreparedStatement::class;
+
+ /**
+ * name of the database editor class
+ * @var string
+ */
+ protected $editorClassName = DatabaseEditor::class;
+
+ /**
+ * sql server hostname
+ * @var string
+ */
+ protected $host = '';
+
+ /**
+ * sql server post
+ * @var int
+ */
+ protected $port = 0;
+
+ /**
+ * sql server login name
+ * @var string
+ */
+ protected $user = '';
+
+ /**
+ * sql server login password
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * database name
+ * @var string
+ */
+ protected $database = '';
+
+ /**
+ * enables failsafe connection
+ * @var bool
+ */
+ protected $failsafeTest = false;
+
+ /**
+ * number of executed queries
+ * @var int
+ */
+ protected $queryCount = 0;
+
+ /**
+ * database editor object
+ * @var DatabaseEditor
+ */
+ protected $editor;
+
+ /**
+ * pdo object
+ * @var \PDO
+ */
+ protected $pdo;
+
+ /**
+ * amount of active transactions
+ * @var int
+ */
+ protected $activeTransactions = 0;
+
+ /**
+ * attempts to create the database after the connection has been established
+ * @var bool
+ */
+ protected $tryToCreateDatabase = false;
+
+ /**
+ * default driver options passed to the PDO constructor
+ * @var array
+ */
+ protected $defaultDriverOptions = [];
+
+ /**
+ * Creates a Database Object.
+ *
+ * @param string $host SQL database server host address
+ * @param string $user SQL database server username
+ * @param string $password SQL database server password
+ * @param string $database SQL database server database name
+ * @param int $port SQL database server port
+ * @param bool $failsafeTest
+ * @param bool $tryToCreateDatabase
+ * @param array $defaultDriverOptions
+ */
+ public function __construct(
+ $host,
+ $user,
+ $password,
+ $database,
+ $port,
+ $failsafeTest = false,
+ $tryToCreateDatabase = false,
+ $defaultDriverOptions = []
+ ) {
+ $this->host = $host;
+ $this->port = $port;
+ $this->user = $user;
+ $this->password = $password;
+ $this->database = $database;
+ $this->failsafeTest = $failsafeTest;
+ $this->tryToCreateDatabase = $tryToCreateDatabase;
+ $this->defaultDriverOptions = $defaultDriverOptions;
+
+ // connect database
+ $this->connect();
+ }
+
+ public function enableDebugMode()
+ {
+ $this->preparedStatementClassName = DebugPreparedStatement::class;
+ }
+
+ /**
+ * Connects to database server.
+ */
+ abstract public function connect();
+
+ /**
+ * Returns ID from last insert.
+ *
+ * @param string $table
+ * @param string $field
+ * @return int
+ * @throws DatabaseException
+ */
+ public function getInsertID($table, $field)
+ {
+ try {
+ return $this->pdo->lastInsertId();
+ } catch (\PDOException $e) {
+ throw new GenericDatabaseException("Cannot fetch last insert id", $e);
+ }
+ }
+
+ /**
+ * Initiates a transaction.
+ *
+ * @return bool true on success
+ * @throws DatabaseTransactionException
+ */
+ public function beginTransaction()
+ {
+ try {
+ if ($this->activeTransactions === 0) {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start("BEGIN", Benchmark::TYPE_SQL_QUERY);
+ }
+ $result = $this->pdo->beginTransaction();
+ } else {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start(
+ "SAVEPOINT level" . $this->activeTransactions,
+ Benchmark::TYPE_SQL_QUERY
+ );
+ }
+ $result = $this->pdo->exec("SAVEPOINT level" . $this->activeTransactions) !== false;
+ }
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->stop();
+ }
+
+ $this->activeTransactions++;
+
+ return $result;
+ } catch (\PDOException $e) {
+ throw new DatabaseTransactionException("Could not begin transaction", $e);
+ }
+ }
+
+ /**
+ * Commits a transaction and returns true if the transaction was successful.
+ *
+ * @return bool
+ * @throws DatabaseTransactionException
+ */
+ public function commitTransaction()
+ {
+ if ($this->activeTransactions === 0) {
+ return false;
+ }
+
+ try {
+ $this->activeTransactions--;
+
+ if ($this->activeTransactions === 0) {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start("COMMIT", Benchmark::TYPE_SQL_QUERY);
+ }
+ $result = $this->pdo->commit();
+ } else {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start(
+ "RELEASE SAVEPOINT level" . $this->activeTransactions,
+ Benchmark::TYPE_SQL_QUERY
+ );
+ }
+ $result = $this->pdo->exec("RELEASE SAVEPOINT level" . $this->activeTransactions) !== false;
+ }
+
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->stop();
+ }
+
+ return $result;
+ } catch (\PDOException $e) {
+ throw new DatabaseTransactionException("Could not commit transaction", $e);
+ }
+ }
+
+ /**
+ * Rolls back a transaction and returns true if the rollback was successful.
+ *
+ * @return bool
+ * @throws DatabaseTransactionException
+ */
+ public function rollBackTransaction()
+ {
+ if ($this->activeTransactions === 0) {
+ return false;
+ }
+
+ try {
+ $this->activeTransactions--;
+ if ($this->activeTransactions === 0) {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start("ROLLBACK", Benchmark::TYPE_SQL_QUERY);
+ }
+ $result = $this->pdo->rollBack();
+ } else {
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->start(
+ "ROLLBACK TO SAVEPOINT level" . $this->activeTransactions,
+ Benchmark::TYPE_SQL_QUERY
+ );
+ }
+ $result = $this->pdo->exec("ROLLBACK TO SAVEPOINT level" . $this->activeTransactions) !== false;
+ }
+
+ if (WCF::benchmarkIsEnabled()) {
+ Benchmark::getInstance()->stop();
+ }
+
+ return $result;
+ } catch (\PDOException $e) {
+ throw new DatabaseTransactionException("Could not roll back transaction", $e);
+ }
+ }
+
+ /**
+ * Prepares a statement for execution and returns a statement object.
+ *
+ * @param string $statement
+ * @param int $limit
+ * @param int $offset
+ * @return PreparedStatement
+ * @throws DatabaseQueryException
+ */
+ public function prepareStatement($statement, $limit = 0, $offset = 0)
+ {
+ $statement = $this->handleLimitParameter($statement, $limit, $offset);
+
+ try {
+ // Append routing information of the current request as a comment.
+ // This allows the system administrator to find offending requests
+ // in MySQL's slow query log and / or MySQL's process list.
+ // Note: This is meant to be run unconditionally in production to be
+ // useful. Thus the code to retrieve the request information
+ // must be absolutely lightweight.
+ static $requestInformation = null;
+ if ($requestInformation === null) {
+ $requestInformation = '';
+ if (
+ \defined('ENABLE_PRODUCTION_DEBUG_MODE')
+ && ENABLE_PRODUCTION_DEBUG_MODE
+ && isset($_SERVER['REQUEST_URI'])
+ ) {
+ $requestInformation = $_SERVER['REQUEST_URI'];
+ if ($requestId = \wcf\getRequestId()) {
+ $requestInformation = \substr($requestInformation, 0, 70);
+ $requestInformation .= ' (' . $requestId . ')';
+ }
+ if (isset($_REQUEST['className']) && isset($_REQUEST['actionName'])) {
+ $requestInformation = \substr($requestInformation, 0, 90);
+ $requestInformation .= ' (' . $_REQUEST['className'] . ':' . $_REQUEST['actionName'] . ')';
+ }
+ $requestInformation = \substr($requestInformation, 0, 180);
+ }
+ }
+
+ $pdoStatement = $this->pdo->prepare(
+ $statement . ($requestInformation ? " -- " . $this->pdo->quote($requestInformation) : '')
+ );
+
+ return new $this->preparedStatementClassName($this, $pdoStatement, $statement);
+ } catch (\PDOException $e) {
+ throw new DatabaseQueryException("Could not prepare statement '" . $statement . "'", $e);
+ }
+ }
+
+ /**
+ * Prepares a statement for execution and returns a statement object.
+ *
+ * In contrast to `prepareStatement()`, for all installed apps, `app1_` is replaced with
+ * `app{WCF_N}_`.
+ *
+ * @since 5.4
+ */
+ public function prepare(string $statement, int $limit = 0, int $offset = 0): PreparedStatement
+ {
+ static $regex = null;
+ if ($regex === null) {
+ $abbreviations = \implode(
+ '|',
+ \array_map(static function (Application $app): string {
+ return \preg_quote($app->getAbbreviation(), '~');
+ }, ApplicationHandler::getInstance()->getApplications())
+ );
+
+ $regex = "~(\\b(?:{$abbreviations}))1_~";
+ }
+
+ $statement = \preg_replace(
+ $regex,
+ '${1}' . WCF_N . '_',
+ $statement
+ );
+
+ return $this->prepareStatement($statement, $limit, $offset);
+ }
+
+ /**
+ * Handles the limit and offset parameter in SELECT queries.
+ * This is a default implementation compatible to MySQL and PostgreSQL.
+ * Other database implementations should override this function.
+ *
+ * @param string $query
+ * @param int $limit
+ * @param int $offset
+ * @return string
+ */
+ public function handleLimitParameter($query, $limit = 0, $offset = 0)
+ {
++ $limit = \intval($limit);
++ $offset = \intval($offset);
++ if ($limit < 0) {
++ throw new \InvalidArgumentException('The limit must not be negative.');
++ }
++ if ($offset < 0) {
++ throw new \InvalidArgumentException('The offset must not be negative.');
++ }
++
+ if ($limit != 0) {
+ $query = \preg_replace(
+ '~(\s+FOR\s+UPDATE\s*)?$~',
+ " LIMIT " . $limit . ($offset ? " OFFSET " . $offset : '') . "\\0",
+ $query,
+ 1
+ );
+ }
+
+ return $query;
+ }
+
+ /**
+ * Returns the number of the last error.
+ *
+ * @return int
+ */
+ public function getErrorNumber()
+ {
+ if ($this->pdo !== null) {
+ return $this->pdo->errorCode();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns the description of the last error.
+ *
+ * @return string
+ */
+ public function getErrorDesc()
+ {
+ if ($this->pdo !== null) {
+ $errorInfoArray = $this->pdo->errorInfo();
+ if (isset($errorInfoArray[2])) {
+ return $errorInfoArray[2];
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns the current database type.
+ *
+ * @return string
+ */
+ public function getDBType()
+ {
+ return \get_class($this);
+ }
+
+ /**
+ * Escapes a string for use in sql query.
+ *
+ * @param string $string
+ * @return string
+ */
+ public function escapeString($string)
+ {
+ return \addslashes($string);
+ }
+
+ /**
+ * Returns the sql version.
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ try {
+ if ($this->pdo !== null) {
+ return $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION);
+ }
+ } catch (\PDOException $e) {
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * Returns the database name.
+ *
+ * @return string
+ */
+ public function getDatabaseName()
+ {
+ return $this->database;
+ }
+
+ /**
+ * Returns the name of the database user.
+ *
+ * @return string user name
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * Returns the amount of executed sql queries.
+ *
+ * @return int
+ */
+ public function getQueryCount()
+ {
+ return $this->queryCount;
+ }
+
+ /**
+ * Increments the query counter by one.
+ */
+ public function incrementQueryCount()
+ {
+ $this->queryCount++;
+ }
+
+ /**
+ * Returns a database editor object.
+ *
+ * @return DatabaseEditor
+ */
+ public function getEditor()
+ {
+ if ($this->editor === null) {
+ $this->editor = new $this->editorClassName($this);
+ }
+
+ return $this->editor;
+ }
+
+ /**
+ * Returns true if this database type is supported.
+ *
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return false;
+ }
+
+ /**
+ * Sets default connection attributes.
+ */
+ protected function setAttributes()
+ {
+ $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $this->pdo->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL);
+ $this->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false);
+ }
}