diff --git a/Controller/Conversation.php b/Controller/Conversation.php index f1ae54f6..9b8258cc 100644 --- a/Controller/Conversation.php +++ b/Controller/Conversation.php @@ -115,7 +115,7 @@ public function actionPostAttachments() $attachmentPlugin = $this->plugin('Xfrocks\Api:Attachment'); $tempHash = $attachmentPlugin->getAttachmentTempHash($contentData); - return $attachmentPlugin->doUpload($tempHash, 'conversation_message', $contentData); + return $attachmentPlugin->doUploadAndRespond($tempHash, 'conversation_message', $contentData); } protected function assertViewableConversation($conversationId, array $extraWith = []) diff --git a/Controller/ConversationMessage.php b/Controller/ConversationMessage.php index 76a417b1..7f488ec4 100644 --- a/Controller/ConversationMessage.php +++ b/Controller/ConversationMessage.php @@ -208,7 +208,7 @@ public function actionPostAttachments() $attachmentPlugin = $this->plugin('Xfrocks\Api:Attachment'); $tempHash = $attachmentPlugin->getAttachmentTempHash($context); - return $attachmentPlugin->doUpload($tempHash, 'conversation_message', $context); + return $attachmentPlugin->doUploadAndRespond($tempHash, 'conversation_message', $context); } public function actionPostReport(ParameterBag $params) diff --git a/Controller/Post.php b/Controller/Post.php index 354a00a1..cc3089fa 100644 --- a/Controller/Post.php +++ b/Controller/Post.php @@ -278,7 +278,7 @@ public function actionPostAttachments() $attachmentPlugin = $this->plugin('Xfrocks\Api:Attachment'); $tempHash = $attachmentPlugin->getAttachmentTempHash($context); - return $attachmentPlugin->doUpload($tempHash, 'post', $context); + return $attachmentPlugin->doUploadAndRespond($tempHash, 'post', $context); } public function actionGetLikes(ParameterBag $params) diff --git a/Controller/Thread.php b/Controller/Thread.php index 9d4f4f5c..c6286c35 100644 --- a/Controller/Thread.php +++ b/Controller/Thread.php @@ -203,7 +203,7 @@ public function actionPostAttachments() $attachmentPlugin = $this->plugin('Xfrocks\Api:Attachment'); $tempHash = $attachmentPlugin->getAttachmentTempHash($context); - return $attachmentPlugin->doUpload($tempHash, 'post', $context); + return $attachmentPlugin->doUploadAndRespond($tempHash, 'post', $context); } public function actionGetFollowers(ParameterBag $params) diff --git a/ControllerPlugin/Attachment.php b/ControllerPlugin/Attachment.php index 12a0f2b5..70002b0b 100644 --- a/ControllerPlugin/Attachment.php +++ b/ControllerPlugin/Attachment.php @@ -45,7 +45,15 @@ public function doUpload($hash, $contentType, $context, $formField = 'file') if (!$attachment) { throw $this->controller->exception($this->controller->noPermission($error)); } + return $attachment; + } + + public function doUploadAndRespond($hash, $contentType, $context, $formField = 'file') + { + $attachment = $this->doUpload($hash, $contentType, $context, $formField); + /** @var AbstractController $controller */ + $controller = $this->controller; $lazyTransformer = $controller->transformEntityLazily($attachment); $lazyTransformer->addCallbackPreTransform(function ($context) use ($hash) { /** @var TransformContext $context */ @@ -64,10 +72,9 @@ public function getAttachmentTempHash(array $contentData = []) $params = $controller->params(); $prefix = ''; - $inputHash = $params['attachment_hash']; - if (!empty($inputHash)) { - $prefix = sprintf('hash%s', $inputHash); + if (!empty($params['attachment_hash'])) { + $prefix = sprintf('hash%s', $params['attachment_hash']); } elseif (!empty($contentData['post_id'])) { $prefix = sprintf('post%d', $contentData['post_id']); } elseif (!empty($contentData['thread_id'])) { @@ -80,6 +87,10 @@ public function getAttachmentTempHash(array $contentData = []) $prefix = sprintf('message%d', $contentData['message_id']); } elseif (!empty($contentData['conversation_id'])) { $prefix = sprintf('conversation%d', $contentData['conversation_id']); + } elseif (!empty($contentData['media_album_id'])) { + $prefix = sprintf('media_album%d', $contentData['media_album_id']); + } elseif (!empty($contentData['media_category_id'])) { + $prefix = sprintf('media_category%d', $contentData['media_category_id']); } /** @var Session $session */ diff --git a/Listener.php b/Listener.php index 3de31e0c..c7c4f2ed 100644 --- a/Listener.php +++ b/Listener.php @@ -42,6 +42,12 @@ public static function appSetup($app) if (!empty($addOnCache['XFRM'])) { $extension->addClassExtension('Xfrocks\Api\Data\Modules', 'Xfrocks\Api\XFRM\Data\Modules'); } + + $addOnCache = $container['addon.cache']; + $extension = $app->extension(); + if (!empty($addOnCache['XFMG'])) { + $extension->addClassExtension('Xfrocks\Api\Data\Modules', 'Xfrocks\Api\XFMG\Data\Modules'); + } } /** diff --git a/XFMG/Controller/AbstractController.php b/XFMG/Controller/AbstractController.php new file mode 100644 index 00000000..ab631178 --- /dev/null +++ b/XFMG/Controller/AbstractController.php @@ -0,0 +1,81 @@ +assertRecordExists( + 'XFMG:Album', + $albumId, + $extraWith, + 'xfmg_requested_album_not_found' + ); + + if (!$album->canView($error)) { + throw $this->exception($this->noPermission($error)); + } + + return $album; + } + + /** + * @param int $categoryId + * @param array $extraWith + * @return \XFMG\Entity\Category + * @throws \XF\Mvc\Reply\Exception + */ + protected function assertViewableCategory($categoryId, array $extraWith = []) + { + /** @var \XFMG\Entity\Category $category */ + $category = $this->assertRecordExists( + 'XFMG:Category', + $categoryId, + $extraWith, + 'xfmg_requested_category_not_found' + ); + + if (!$category->canView($error)) { + throw $this->exception($this->noPermission($error)); + } + + return $category; + } + + /** + * @param int $mediaId + * @param array $extraWith + * @return \XFMG\Entity\MediaItem + * @throws \XF\Mvc\Reply\Exception + */ + protected function assertViewableMediaItem($mediaId, array $extraWith = []) + { + /** @var \XFMG\Entity\MediaItem $item */ + $item = $this->assertRecordExists( + 'XFMG:MediaItem', + $mediaId, + $extraWith, + 'xfmg_requested_media_item_not_found' + ); + + if (!$item->canView($error)) { + throw $this->exception($this->noPermission($error)); + } + + return $item; + } +} \ No newline at end of file diff --git a/XFMG/Controller/Album.php b/XFMG/Controller/Album.php new file mode 100644 index 00000000..82707b2e --- /dev/null +++ b/XFMG/Controller/Album.php @@ -0,0 +1,256 @@ +album_id) { + return $this->actionSingle($params->album_id); + } + + $params = $this->params() + ->define('category_id', 'int', '', -1) + ->define('user_id', 'uint') + ->defineOrder([ + 'natural' => ['create_date', 'asc'], + 'natural_reverse' => ['create_date', 'desc'], + 'album_last_update_date' => ['last_update_date', 'asc'], + 'album_last_update_date_reverse' => ['last_update_date', 'desc'], + 'album_rating' => ['rating_avg', 'asc'], + 'album_rating_reverse' => ['rating_avg', 'desc'], + 'album_comment_count' => ['comment_count', 'asc'], + 'album_comment_count_reverse' => ['comment_count', 'desc'], + ]) + ->definePageNav(); + + /** @var \XFMG\Finder\Album $finder */ + $finder = $this->finder('XFMG:Album'); + $this->applyFilters($finder, $params); + $params->sortFinder($finder); + $params->limitFinderByPage($finder); + + $total = $finder->total(); + $albums = $total > 0 ? $this->transformFinderLazily($finder) : []; + + $data = [ + 'albums' => $albums, + 'albums_total' => $total + ]; + + PageNav::addLinksToData($data, $params, $total, 'albums'); + + return $this->api($data); + } + + protected function actionSingle($albumId) + { + $album = $this->assertViewableAlbum($albumId); + + $data = [ + 'album' => $this->transformEntityLazily($album) + ]; + + return $this->api($data); + } + + public function actionPostIndex(ParameterBag $params) + { + /** @var \XFMG\XF\Entity\User $visitor */ + $visitor = \XF::visitor(); + if (!$visitor->canCreateAlbum()) + { + return $this->noPermission(); + } + + $params = $this->defineAlbumParams() + ->define('category_id', 'uint', 'category of the new album') + ; + + /** @var \XFMG\Service\Album\Creator $creator */ + $creator = $this->service('XFMG:Album\Creator'); + + if (!empty($params['category_id'])) { + $category = $this->assertViewableCategory($params['category_id']); + if (!$category->canCreateAlbum()) + { + return $this->noPermission(); + } + $creator->setCategory($category); + } + $creator->setTitle($params['title'], $params['description']); + $creator->setViewPrivacy($params['view_privacy'], $params['view_users']); + $creator->setAddPrivacy($params['add_privacy'], $params['add_users']); + + $creator->checkForSpam(); + + if (!$creator->validate($errors)) + { + return $this->error($errors); + } + $album = $creator->save(); + + $creator->sendNotifications(); + + /** @var \XFMG\Repository\AlbumWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:AlbumWatch'); + $watchRepo->autoWatchAlbum($album, \XF::visitor(), true); + + return $this->actionSingle($album->album_id); + } + + public function actionPutIndex(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + if (!$album->canEdit($error)) + { + return $this->noPermission($error); + } + + $params = $this->defineAlbumParams(); + $title = $params['title']; + $description = $params['description']; + + /** @var \XFMG\Service\Album\Editor $editor */ + $editor = $this->service('XFMG:Album\Editor', $album); + + if (!empty($title) && !empty($description)) { + $editor->setTitle($title, $description); + } + if ($album->canChangePrivacy()) + { + $editor->setViewPrivacy($params['view_privacy'], $params['view_users']); + $editor->setAddPrivacy($params['add_privacy'], $params['add_users']); + } + + $editor->checkForSpam(); + + if (!$editor->validate($errors)) + { + return $this->error($errors); + } + $editor->save(); + + return $this->actionSingle($album->album_id); + } + + public function actionDeleteIndex(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + if (!$album->canDelete('soft', $error)) + { + return $this->noPermission($error); + } + + /** @var \XFMG\Service\Album\Deleter $deleter */ + $deleter = $this->service('XFMG:Album\Deleter', $album); + $deleter->delete('soft'); + + return $this->actionSingle($album->album_id); + } + + public function actionPostLikes(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + if (!$album->canLike($error)) { + return $this->noPermission($error); + } + + $visitor = \XF::visitor(); + if (empty($album->Likes[$visitor->user_id])) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $album->getEntityContentType(); + $likeRepo->toggleLike($contentType, $album->album_id, $visitor); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionDeleteLikes(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + if (!$album->canLike($error)) { + return $this->noPermission($error); + } + + $visitor = \XF::visitor(); + if (!empty($album->Likes[$visitor->user_id])) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $album->getEntityContentType(); + $likeRepo->toggleLike($contentType, $album->album_id, $visitor); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionPostFollowers(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + if (!$album->canWatch($error)) { + return $this->noPermission($error); + } + + $params = $this->params() + ->define('notify_on', 'str', 'comment|media|media_comment', 'media_comment') + ->define('send_alert', 'bool', '', true) + ->define('send_email', 'bool', '', true) + ; + + $action = 'watch'; + $config = [ + 'notify_on' => $params['notify_on'], + 'send_alert' => $params['send_alert'], + 'send_email' => $params['send_email'], + ]; + $visitor = \XF::visitor(); + + /** @var \XFMG\Repository\AlbumWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:AlbumWatch'); + $watchRepo->setWatchState($album, $visitor, $action, $config); + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionDeleteFollowers(ParameterBag $params) + { + $album = $this->assertViewableAlbum($params->album_id); + + $visitor = \XF::visitor(); + + /** @var \XFMG\Repository\AlbumWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:AlbumWatch'); + $watchRepo->setWatchState($album, $visitor, 'delete'); + + return $this->message(\XF::phrase('changes_saved')); + } + + protected function defineAlbumParams() + { + return $this->params() + ->define('title', 'str', 'new title of the album') + ->define('description', 'str', 'new description of the album') + ->define('view_privacy', 'str', 'public, members, or private', 'public') + ->define('view_users', 'str', 'specific users who can view this album', null) + ->define('add_privacy', 'str', 'public, members, or private', 'private') + ->define('add_users', 'str', 'specific users who can add media to this album', null) + ; + } + + protected function applyFilters(\XFMG\Finder\Album $finder, Params $params) + { + if ($params['category_id'] > -1) { + $finder->inCategory($params['category_id']); + } + if ($params['user_id'] > 0) { + $finder->byUser($params['user_id']); + } + } +} diff --git a/XFMG/Controller/Comment.php b/XFMG/Controller/Comment.php new file mode 100644 index 00000000..af09359b --- /dev/null +++ b/XFMG/Controller/Comment.php @@ -0,0 +1,231 @@ +comment_id) { + return $this->actionSingle($params->comment_id); + } + + $params = $this->params() + ->define('media_id', 'int', '') + ->define('album_id', 'int', '') + ->defineOrder([ + 'natural' => ['comment_date', 'asc'], + 'natural_reverse' => ['comment_date', 'desc'], + ]) + ->definePageNav(); + + $content = $this->assertViewableContent($params); + + /** @var \XFMG\Finder\Comment $finder */ + $finder = $this->finder('XFMG:Comment'); + $finder->forContent($content); + $params->sortFinder($finder); + $params->limitFinderByPage($finder); + + $total = $finder->total(); + $comments = $total > 0 ? $this->transformFinderLazily($finder) : []; + + $data = [ + 'comments' => $comments, + 'comments_total' => $total + ]; + + PageNav::addLinksToData($data, $params, $total, 'comments'); + + return $this->api($data); + } + + protected function actionSingle($commentId) + { + $comment = $this->assertViewableComment($commentId); + + $data = [ + 'comment' => $this->transformEntityLazily($comment) + ]; + + return $this->api($data); + } + + public function actionPostIndex(ParameterBag $params) + { + $params = $this->params() + ->define('media_id', 'int', '') + ->define('album_id', 'int', '') + ->define('message', 'str', 'comment message') + ; + + $content = $this->assertViewableAndCommentableContent($params); + + /** @var \XFMG\Service\Comment\Creator $creator */ + $creator = $this->service('XFMG:Comment\Creator', $content); + $creator->setMessage($params['message']); + $creator->checkForSpam(); + + if (!$creator->validate($errors)) + { + return $this->error($errors); + } + /** @var \XFMG\Entity\Comment $comment */ + $comment = $creator->save(); + + $this->finalizeCommentCreate($creator); + + return $this->actionSingle($comment->comment_id); + } + + public function actionPutIndex(ParameterBag $params) + { + $comment = $this->assertViewableComment($params->comment_id); + if (!$comment->canEdit($error)) { + return $this->noPermission($error); + } + + $params = $this->params() + ->define('message', 'str', 'comment message') + ; + + /** @var \XFMG\Service\Comment\Editor $editor */ + $editor = $this->service('XFMG:Comment\Editor', $comment); + $editor->setMessage($params['message']); + $editor->checkForSpam(); + + if (!$editor->validate($errors)) + { + return $this->error($errors); + } + /** @var \XFMG\Entity\Comment $comment */ + $comment = $editor->save(); + + return $this->actionSingle($comment->comment_id); + } + + public function actionDeleteIndex(ParameterBag $params) + { + $comment = $this->assertViewableComment($params->comment_id); + if (!$comment->canDelete('soft', $error)) { + return $this->noPermission($error); + } + + /** @var \XFMG\Service\Comment\Deleter $deleter */ + $deleter = $this->service('XFMG:Comment\Deleter', $comment); + $deleter->delete('soft'); + + return $this->actionSingle($comment->comment_id); + } + + public function actionPostLikes(ParameterBag $params) + { + $comment = $this->assertViewableComment($params->comment_id); + if (!$comment->canLike($error)) { + return $this->noPermission($error); + } + + if (!$comment->isLiked()) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $comment->getEntityContentType(); + $likeRepo->toggleLike($contentType, $comment->comment_id, \XF::visitor()); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionDeleteLikes(ParameterBag $params) + { + $comment = $this->assertViewableComment($params->comment_id); + if (!$comment->canLike($error)) { + return $this->noPermission($error); + } + + if ($comment->isLiked()) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $comment->getEntityContentType(); + $likeRepo->toggleLike($contentType, $comment->comment_id, \XF::visitor()); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + protected function finalizeCommentCreate(\XFMG\Service\Comment\Creator $creator) + { + $creator->sendNotifications(); + + $content = $creator->getContent(); + $content->draft_comment->delete(); + + $visitor = \XF::visitor(); + + if ($visitor->user_id != $content->user_id) + { + if ($content->content_type == 'xfmg_media') + { + /** @var \XFMG\Repository\MediaWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:MediaWatch'); + $watchRepo->autoWatchMediaItem($content, $visitor); + } + else + { + /** @var \XFMG\Repository\AlbumWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:AlbumWatch'); + $watchRepo->autoWatchAlbum($content, $visitor); + } + } + } + + protected function assertViewableContent(Params $params) + { + if ($params['media_id'] > 0) { + $content = $this->assertViewableMediaItem($params['media_id']); + } elseif ($params['album_id'] > 0) { + $content = $this->assertViewableAlbum($params['album_id']); + } else { + throw $this->exception($this->noPermission()); + } + if (!$content->canViewComments($error)) { + throw $this->exception($this->noPermission($error)); + } + return $content; + } + + protected function assertViewableAndCommentableContent(Params $params) + { + $content = $this->assertViewableContent($params); + if (!$content->canAddComment($error)) { + throw $this->exception($this->noPermission($error)); + } + return $content; + } + + protected function assertViewableComment($commentId, array $extraWith = []) + { + /** @var \XFMG\Entity\Comment $comment */ + $comment = $this->assertRecordExists( + 'XFMG:Comment', + $commentId, + $extraWith, + 'xfmg_requested_comment_not_found' + ); + + if (!$comment->canView($error)) { + throw $this->exception($this->noPermission($error)); + } + + return $comment; + } +} diff --git a/XFMG/Controller/Media.php b/XFMG/Controller/Media.php new file mode 100644 index 00000000..7dbc033e --- /dev/null +++ b/XFMG/Controller/Media.php @@ -0,0 +1,231 @@ +media_id) { + return $this->actionSingle($params->media_id); + } + + $params = $this->params() + ->defineOrder([ + 'natural' => ['media_date', 'asc'], + 'natural_reverse' => ['media_date', 'desc'], + 'media_rating' => ['rating_avg', 'asc'], + 'media_rating_reverse' => ['rating_avg', 'desc'], + 'media_comment_count' => ['comment_count', 'asc'], + 'media_comment_count_reverse' => ['comment_count', 'desc'], + ]) + ->definePageNav(); + + /** @var \XFMG\Finder\MediaItem $finder */ + $finder = $this->finder('XFMG:MediaItem'); + $params->sortFinder($finder); + $params->limitFinderByPage($finder); + + $total = $finder->total(); + $items = $total > 0 ? $this->transformFinderLazily($finder) : []; + + $data = [ + 'items' => $items, + 'items_total' => $total + ]; + + PageNav::addLinksToData($data, $params, $total, 'items'); + + return $this->api($data); + } + + protected function actionSingle($itemId) + { + $item = $this->assertViewableMediaItem($itemId); + + $data = [ + 'item' => $this->transformEntityLazily($item) + ]; + + return $this->api($data); + } + + public function actionPostIndex() + { + $params = $this + ->params() + ->define('album_id', 'uint', 'id of the target album') + ->define('category_id', 'uint', 'id of the target category') + ->define('title', 'str', 'title of the new media') + ->define('description', 'str', 'description of the new media') + ->defineFile('file', 'binary data of the attachment'); + + if (!empty($params['album_id'])) { + $container = $this->assertViewableAlbum($params['album_id']); + $context = ['media_album_id' => $params['album_id']]; + } else if (!empty($params['category_id'])) { + $container = $this->assertViewableCategory($params['category_id']); + $context = ['media_category_id' => $params['category_id']]; + } else { + return $this->noPermission(); + } + + if (!$container->canAddMedia($error)) { + return $this->noPermission($error); + } + + /** @var \Xfrocks\Api\ControllerPlugin\Attachment $attachmentPlugin */ + $attachmentPlugin = $this->plugin('Xfrocks\Api:Attachment'); + $tempHash = $attachmentPlugin->getAttachmentTempHash($context); + $attachment = $attachmentPlugin->doUpload($tempHash, 'xfmg_media', $context); + + /** @var \XFMG\Entity\MediaTemp $tempMedia */ + $mediaTemp = $this->em()->findOne('XFMG:MediaTemp', ['attachment_id' => $attachment->attachment_id]); + + /** @var \XFMG\Service\Media\Creator $creator */ + $creator = $this->service('XFMG:Media\Creator', $mediaTemp); + $creator->setContainer($container); + $creator->setTitle($params['title'], $params['description']); + $creator->setAttachment($attachment->attachment_id, $attachment->temp_hash); + + $creator->checkForSpam(); + + if (!$creator->validate($errors)) { + return $this->error($errors); + } + + /** @var \XFMG\Entity\MediaItem $item */ + $item = $creator->save(); + + /** @var \XFMG\Repository\MediaWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:MediaWatch'); + $watchRepo->autoWatchMediaItem($item, \XF::visitor(), true); + + // Clear entity cache + $this->em()->detachEntity($item); + $this->em()->detachEntity($attachment); + + return $this->actionSingle($item->media_id); + } + + public function actionPutIndex(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + if (!$item->canEdit($error)) { + return $this->noPermission($error); + } + + $params = $this->params() + ->define('title', 'str', 'title of the new media') + ->define('description', 'str', 'description of the new media'); + + /** @var \XFMG\Service\Media\Editor $editor */ + $editor = $this->service('XFMG:Media\Editor', $item); + $editor->setTitle($params['title'], $params['description']); + $editor->checkForSpam(); + + if (!$editor->validate($errors)) + { + return $this->error($errors); + } + /** @var \XFMG\Entity\MediaItem $item */ + $item = $editor->save(); + + return $this->actionSingle($item->media_id); + } + + public function actionDeleteIndex(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + if (!$item->canDelete('soft', $error)) + { + return $this->noPermission($error); + } + + /** @var \XFMG\Service\Media\Deleter $deleter */ + $deleter = $this->service('XFMG:Media\Deleter', $item); + $deleter->delete('soft'); + + return $this->actionSingle($item->media_id); + } + + public function actionPostLikes(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + if (!$item->canLike($error)) { + return $this->noPermission($error); + } + + $visitor = \XF::visitor(); + if (empty($item->Likes[$visitor->user_id])) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $item->getEntityContentType(); + $likeRepo->toggleLike($contentType, $item->media_id, $visitor); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionDeleteLikes(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + if (!$item->canLike($error)) { + return $this->noPermission($error); + } + + $visitor = \XF::visitor(); + if (!empty($item->Likes[$visitor->user_id])) { + /** @var \XF\Repository\LikedContent $likeRepo */ + $likeRepo = $this->repository('XF:LikedContent'); + $contentType = $item->getEntityContentType(); + $likeRepo->toggleLike($contentType, $item->media_id, $visitor); + } + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionPostFollowers(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + if (!$item->canWatch($error)) { + return $this->noPermission($error); + } + + $params = $this->params() + ->define('send_alert', 'bool', '', true) + ->define('send_email', 'bool', '', true) + ; + + $action = 'watch'; + $config = [ + 'notify_on' => 'comment', + 'send_alert' => $params['send_alert'], + 'send_email' => $params['send_email'], + ]; + $visitor = \XF::visitor(); + + /** @var \XFMG\Repository\MediaWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:MediaWatch'); + $watchRepo->setWatchState($item, $visitor, $action, $config); + + return $this->message(\XF::phrase('changes_saved')); + } + + public function actionDeleteFollowers(ParameterBag $params) + { + $item = $this->assertViewableMediaItem($params->media_id); + + $visitor = \XF::visitor(); + + /** @var \XFMG\Repository\MediaWatch $watchRepo */ + $watchRepo = $this->repository('XFMG:MediaWatch'); + $watchRepo->setWatchState($item, $visitor, 'delete'); + + return $this->message(\XF::phrase('changes_saved')); + } +} diff --git a/XFMG/Data/Modules.php b/XFMG/Data/Modules.php new file mode 100644 index 00000000..241fa537 --- /dev/null +++ b/XFMG/Data/Modules.php @@ -0,0 +1,59 @@ +addController( + 'Xfrocks\Api\XFMG\Controller\Album', + 'media', + 'albums/:int/', + null, + 'albums' + ); + $this->addController( + 'Xfrocks\Api\XFMG\Controller\Comment', + 'media', + 'comments/:int/', + null, + 'comments' + ); + $this->addController( + 'Xfrocks\Api\XFMG\Controller\Media', + 'media', + ':int/' + ); + + $this->register('xfmg', 2018100101); + } + + /** + * @param AbstractController $controller + * @return array + */ + public function getDataForApiIndex($controller) + { + $data = parent::getDataForApiIndex($controller); + + $app = $controller->app(); + $apiRouter = $app->router('api'); + $data['links']['media'] = $apiRouter->buildLink('media'); + $data['links']['media/albums'] = $apiRouter->buildLink('media/albums'); + + return $data; + } +} + +if (false) { + // phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses + // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps + class XFCP_Modules extends \Xfrocks\Api\Data\Modules + { + } +} diff --git a/XFMG/Transform/Album.php b/XFMG/Transform/Album.php new file mode 100644 index 00000000..b5565b80 --- /dev/null +++ b/XFMG/Transform/Album.php @@ -0,0 +1,106 @@ +getSource(); + switch ($key) { + case self::DYNAMIC_KEY_IS_LIKED: + return $album->isLiked(); + case self::DYNAMIC_KEY_IS_FOLLOWED: + return !empty($album->Watch[\XF::visitor()->user_id]); + case self::DYNAMIC_KEY_IS_DELETED: + return $album->album_state == 'deleted'; + } + return null; + } + + public function collectPermissions($context) + { + /** @var \XFMG\Entity\Album $album */ + $album = $context->getSource(); + + $permissions = [ + self::PERM_DELETE => $album->canDelete(), + self::PERM_EDIT => $album->canEdit(), + self::PERM_LIKE => $album->canLike(), + self::PERM_FOLLOW => $album->canWatch(), + self::PERM_ADD_MEDIA => $album->canAddMedia(), + self::PERM_COMMENT => $album->canAddComment(), + ]; + + return $permissions; + } + + public function collectLinks($context) + { + /** @var \XFMG\Entity\Album $album */ + $album = $context->getSource(); + + $links = [ + self::LINK_PERMALINK => $this->buildPublicLink('media/albums', $album), + self::LINK_DETAIL => $this->buildApiLink('media/albums', $album), + self::LINK_LIKES => $this->buildApiLink('media/albums/likes', $album), + self::LINK_FOLLOWERS => $this->buildApiLink('media/albums/followers', $album), + ]; + + return $links; + } + + public function getMappings($context) + { + return [ + 'album_id' => self::KEY_ID, + 'category_id' => self::KEY_CATEGORY_ID, + 'title' => self::KEY_TITLE, + 'description' => self::KEY_DESCRIPTION, + 'user_id' => self::KEY_USER_ID, + 'username' => self::KEY_USERNAME, + 'create_date' => self::KEY_CREATE_DATE, + 'last_update_date' => self::KEY_LAST_UPDATE_DATE, + 'media_count' => self::KEY_MEDIA_COUNT, + 'likes' => self::KEY_LIKE_COUNT, + 'comment_count' => self::KEY_COMMENT_COUNT, + 'rating_count' => self::KEY_RATING_COUNT, + 'rating_avg' => self::KEY_RATING, + + self::DYNAMIC_KEY_IS_LIKED, + self::DYNAMIC_KEY_IS_FOLLOWED, + self::DYNAMIC_KEY_IS_DELETED, + ]; + } +} \ No newline at end of file diff --git a/XFMG/Transform/Comment.php b/XFMG/Transform/Comment.php new file mode 100644 index 00000000..2a1867ef --- /dev/null +++ b/XFMG/Transform/Comment.php @@ -0,0 +1,104 @@ +getSource(); + + switch ($key) { + case self::DYNAMIC_KEY_BODY_HTML: + return $this->renderBbCodeHtml($key, $comment->message, $comment); + case self::DYNAMIC_KEY_BODY_PLAIN: + return $this->renderBbCodePlainText($comment->message); + case self::DYNAMIC_KEY_IS_DELETED: + return $comment->comment_state === 'deleted'; + case self::DYNAMIC_KEY_IS_IGNORED: + if (!\XF::visitor()->user_id) { + return false; + } + + return $comment->isIgnored(); + case self::DYNAMIC_KEY_IS_LIKED: + return $comment->isLiked(); + } + + return null; + } + + public function collectPermissions($context) + { + /** @var \XFMG\Entity\Comment $comment */ + $comment = $context->getSource(); + + $permissions = [ + self::PERM_DELETE => $comment->canDelete(), + self::PERM_EDIT => $comment->canEdit(), + self::PERM_LIKE => $comment->canLike(), + ]; + + return $permissions; + } + + public function collectLinks($context) + { + /** @var \XFMG\Entity\Comment $comment */ + $comment = $context->getSource(); + + $links = [ + self::LINK_DETAIL => $this->buildApiLink('media/comments', $comment), + self::LINK_LIKES => $this->buildApiLink('media/comments/likes', $comment), + ]; + + return $links; + } + + public function getMappings($context) + { + return [ + 'comment_id' => self::KEY_ID, + 'content_id' => self::KEY_CONTENT_ID, + 'content_type' => self::KEY_CONTENT_TYPE, + 'message' => self::KEY_BODY, + 'comment_date' => self::KEY_COMMENT_DATE, + 'last_edit_date' => self::KEY_LAST_EDIT_DATE, + 'user_id' => self::KEY_USER_ID, + 'username' => self::KEY_USERNAME, + 'likes' => self::KEY_LIKE_COUNT, + + self::DYNAMIC_KEY_BODY_HTML, + self::DYNAMIC_KEY_BODY_PLAIN, + self::DYNAMIC_KEY_IS_DELETED, + self::DYNAMIC_KEY_IS_LIKED, + ]; + } +} diff --git a/XFMG/Transform/MediaItem.php b/XFMG/Transform/MediaItem.php new file mode 100644 index 00000000..51a0eada --- /dev/null +++ b/XFMG/Transform/MediaItem.php @@ -0,0 +1,149 @@ +getParentSourceValue('media_id'); + } + + return null; + } + + public function attachmentCollectLinks($context, array &$links) + { + /** @var \XFMG\Entity\MediaItem $item */ + $item = $context->getParentSource(); + $links[self::ATTACHMENT__LINK_MEDIA] = $this->buildApiLink('media', $item); + $links[self::ATTACHMENT__LINK_THUMBNAIL] = $item->getCurrentThumbnailUrl(); + } + + public function attachmentCollectPermissions($context, array &$permissions) + { + } + + public function attachmentGetMappings($context, array &$mappings) + { + $mappings[] = self::ATTACHMENT__DYNAMIC_KEY_ID; + } + + public function calculateDynamicValue($context, $key) + { + /** @var \XFMG\Entity\MediaItem $item */ + $item = $context->getSource(); + switch ($key) { + case self::DYNAMIC_KEY_ATTACHMENT: + if (!$item->Attachment) { + return null; + } + + return $this->transformer->transformEntityRelation($context, $key, $item, 'Attachment'); + case self::DYNAMIC_KEY_IS_LIKED: + return $item->isLiked(); + case self::DYNAMIC_KEY_IS_FOLLOWED: + return !empty($item->Watch[\XF::visitor()->user_id]); + case self::DYNAMIC_KEY_IS_DELETED: + return $item->media_state == 'deleted'; + } + return null; + } + + public function collectLinks($context) + { + /** @var \XFMG\Entity\MediaItem $item */ + $item = $context->getSource(); + + $links = [ + self::LINK_PERMALINK => $this->buildPublicLink('media', $item), + self::LINK_DETAIL => $this->buildApiLink('media', $item), + self::LINK_LIKES => $this->buildApiLink('media/likes', $item), + self::LINK_FOLLOWERS => $this->buildApiLink('media/followers', $item), + self::LINK_ALBUM => $this->buildPublicLink('media', $item), + ]; + + return $links; + } + + public function collectPermissions($context) + { + /** @var \XFMG\Entity\MediaItem $item */ + $item = $context->getSource(); + + $permissions = [ + self::PERM_DELETE => $item->canDelete(), + self::PERM_EDIT => $item->canEdit(), + self::PERM_LIKE => $item->canLike(), + self::PERM_FOLLOW => $item->canWatch(), + self::PERM_COMMENT => $item->canAddComment(), + ]; + + return $permissions; + } + + public function getMappings($context) + { + return [ + 'media_id' => self::KEY_ID, + 'media_type' => self::KEY_MEDIA_TYPE, + 'title' => self::KEY_TITLE, + 'description' => self::KEY_DESCRIPTION, + 'exif_data' => self::KEY_EXIF_DATA, + 'album_id' => self::KEY_ALBUM_ID, + 'user_id' => self::KEY_USER_ID, + 'username' => self::KEY_USERNAME, + 'media_date' => self::KEY_MEDIA_DATE, + 'last_edit_date' => self::KEY_LAST_EDIT_DATE, + 'likes' => self::KEY_LIKE_COUNT, + 'comment_count' => self::KEY_COMMENT_COUNT, + 'rating_count' => self::KEY_RATING_COUNT, + 'rating_avg' => self::KEY_RATING, + + self::DYNAMIC_KEY_ATTACHMENT, + self::DYNAMIC_KEY_IS_LIKED, + self::DYNAMIC_KEY_IS_FOLLOWED, + self::DYNAMIC_KEY_IS_DELETED, + ]; + } +}