From a16a2d3235880ec8e5887d524d432034cc606da9 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 17 Sep 2014 18:34:57 +0200 Subject: [PATCH] Added posts listing (closed #7) --- data/config.ini | 3 + public_html/css/post-list.css | 26 +++++++ public_html/index.html | 1 + .../js/Presenters/PostListPresenter.js | 73 ++++++++++++++++++- public_html/templates/post-list-item.tpl | 8 ++ public_html/templates/post-list.tpl | 8 ++ src/Controllers/PostController.php | 12 +++ src/Dao/Services/PostSearchService.php | 12 +++ src/Services/PostService.php | 14 ++++ tests/Services/PostServiceTest.php | 3 + 10 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 public_html/css/post-list.css create mode 100644 public_html/templates/post-list-item.tpl create mode 100644 public_html/templates/post-list.tpl create mode 100644 src/Dao/Services/PostSearchService.php diff --git a/data/config.ini b/data/config.ini index c3b3dd70..2fc95160 100644 --- a/data/config.ini +++ b/data/config.ini @@ -49,5 +49,8 @@ minUserNameLength = 1 maxUserNameLength = 32 usersPerPage = 20 +[posts] +postsPerPage = 40 + [misc] thumbnailCropStyle = outside diff --git a/public_html/css/post-list.css b/public_html/css/post-list.css new file mode 100644 index 00000000..efa9e935 --- /dev/null +++ b/public_html/css/post-list.css @@ -0,0 +1,26 @@ +#post-list { + text-align: center; +} + +#post-list .wrapper { + display: inline-block; + margin: 0 auto; +} + +#post-list ul { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +#post-list li a { + display: block; + margin: 0.4em; +} +#post-list li img { + display: block; + box-shadow: 0 0 0 1px #999; +} diff --git a/public_html/index.html b/public_html/index.html index 44a3766d..16bce1de 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -29,6 +29,7 @@ + diff --git a/public_html/js/Presenters/PostListPresenter.js b/public_html/js/Presenters/PostListPresenter.js index bf5ad9b0..02993c62 100644 --- a/public_html/js/Presenters/PostListPresenter.js +++ b/public_html/js/Presenters/PostListPresenter.js @@ -2,26 +2,91 @@ var App = App || {}; App.Presenters = App.Presenters || {}; App.Presenters.PostListPresenter = function( + _, jQuery, - topNavigationPresenter) { + util, + promise, + auth, + router, + pagedCollectionPresenter, + topNavigationPresenter, + messagePresenter) { var $el = jQuery('#content'); + var listTemplate; + var itemTemplate; function init(args) { topNavigationPresenter.select('posts'); topNavigationPresenter.changeTitle('Posts'); - render(); + + promise.waitAll( + util.promiseTemplate('post-list'), + util.promiseTemplate('post-list-item')).then(function(listHtml, itemHtml) { + listTemplate = _.template(listHtml); + itemTemplate = _.template(itemHtml); + + render(); + reinit(args); + }); + } + + function reinit(args) { + var searchArgs = util.parseComplexRouteArgs(args.searchArgs); + searchArgs.order = searchArgs.order; + + updateActiveOrder(searchArgs.order); + initPaginator(searchArgs); + } + + function initPaginator(searchArgs) { + pagedCollectionPresenter.init({ + page: searchArgs.page, + searchParams: {order: searchArgs.order}, + baseUri: '#/posts', + backendUri: '/posts', + updateCallback: function(data, clear) { + renderPosts(data.entities, clear); + return $el.find('.pagination-content'); + }, + failCallback: function(response) { + $el.empty(); + messagePresenter.showError($el, response.json && response.json.error || response); + }}); } function render() { - $el.html('Post list placeholder'); + $el.html(listTemplate()); + } + + function updateActiveOrder(activeOrder) { + $el.find('.order li a').removeClass('active'); + $el.find('.order [data-order="' + activeOrder + '"]').addClass('active'); + } + + function renderPosts(posts, clear) { + var $target = $el.find('.posts'); + + var all = ''; + _.each(posts, function(post) { + all += itemTemplate({ + post: post, + }); + }); + + if (clear) { + $target.html(all); + } else { + $target.append(all); + } } return { init: init, + reinit: reinit, render: render, }; }; -App.DI.register('postListPresenter', ['jQuery', 'topNavigationPresenter'], App.Presenters.PostListPresenter); +App.DI.register('postListPresenter', ['_', 'jQuery', 'util', 'promise', 'auth', 'router', 'pagedCollectionPresenter', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.PostListPresenter); diff --git a/public_html/templates/post-list-item.tpl b/public_html/templates/post-list-item.tpl new file mode 100644 index 00000000..b9f6d63e --- /dev/null +++ b/public_html/templates/post-list-item.tpl @@ -0,0 +1,8 @@ +
  • + + + @<%= post.id %> + +
  • diff --git a/public_html/templates/post-list.tpl b/public_html/templates/post-list.tpl new file mode 100644 index 00000000..c1f6b1dc --- /dev/null +++ b/public_html/templates/post-list.tpl @@ -0,0 +1,8 @@ +
    +
    +
    +
      +
    +
    +
    +
    diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 91f9eb52..f2ff5380 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -22,9 +22,21 @@ final class PostController extends AbstractController public function registerRoutes(\Szurubooru\Router $router) { + $router->get('/api/posts', [$this, 'getFiltered']); $router->post('/api/posts', [$this, 'createPost']); } + public function getFiltered() + { + $formData = new \Szurubooru\FormData\SearchFormData($this->inputReader); + $searchResult = $this->postService->getFiltered($formData); + $entities = $this->postViewProxy->fromArray($searchResult->entities); + return [ + 'data' => $entities, + 'pageSize' => $searchResult->filter->pageSize, + 'totalRecords' => $searchResult->totalRecords]; + } + public function createPost() { $this->privilegeService->assertPrivilege(\Szurubooru\Privilege::UPLOAD_POSTS); diff --git a/src/Dao/Services/PostSearchService.php b/src/Dao/Services/PostSearchService.php new file mode 100644 index 00000000..1594e54a --- /dev/null +++ b/src/Dao/Services/PostSearchService.php @@ -0,0 +1,12 @@ +validator = $validator; $this->transactionManager = $transactionManager; $this->postDao = $postDao; + $this->postSearchService = $postSearchService; $this->fileService = $fileService; $this->timeService = $timeService; $this->authService = $authService; @@ -41,6 +44,17 @@ class PostService return $this->transactionManager->rollback($transactionFunc); } + public function getFiltered(\Szurubooru\FormData\SearchFormData $formData) + { + $transactionFunc = function() use ($formData) + { + $this->validator->validate($formData); + $searchFilter = new \Szurubooru\Dao\SearchFilter($this->config->posts->postsPerPage, $formData); + return $this->postSearchService->getFiltered($searchFilter); + }; + return $this->transactionManager->rollback($transactionFunc); + } + public function createPost(\Szurubooru\FormData\UploadFormData $formData) { $transactionFunc = function() use ($formData) diff --git a/tests/Services/PostServiceTest.php b/tests/Services/PostServiceTest.php index 713a05dd..abc7eb70 100644 --- a/tests/Services/PostServiceTest.php +++ b/tests/Services/PostServiceTest.php @@ -7,6 +7,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase private $validatorMock; private $transactionManagerMock; private $postDaoMock; + private $postSearchServiceMock; private $authServiceMock; private $timeServiceMock; private $fileServiceMock; @@ -17,6 +18,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase $this->validatorMock = $this->mock(\Szurubooru\Validator::class); $this->transactionManagerMock = $this->mockTransactionManager(); $this->postDaoMock = $this->mock(\Szurubooru\Dao\PostDao::class); + $this->postSearchServiceMock = $this->mock(\Szurubooru\Dao\Services\PostSearchService::class); $this->authServiceMock = $this->mock(\Szurubooru\Services\AuthService::class); $this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class); $this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class); @@ -181,6 +183,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase $this->validatorMock, $this->transactionManagerMock, $this->postDaoMock, + $this->postSearchServiceMock, $this->authServiceMock, $this->timeServiceMock, $this->fileServiceMock);