Added posts listing (closed #7)

This commit is contained in:
Marcin Kurczewski 2014-09-17 18:34:57 +02:00
parent 15eb2342b9
commit a16a2d3235
10 changed files with 156 additions and 4 deletions

View file

@ -49,5 +49,8 @@ minUserNameLength = 1
maxUserNameLength = 32 maxUserNameLength = 32
usersPerPage = 20 usersPerPage = 20
[posts]
postsPerPage = 40
[misc] [misc]
thumbnailCropStyle = outside thumbnailCropStyle = outside

View file

@ -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;
}

View file

@ -29,6 +29,7 @@
<link rel="stylesheet" type="text/css" href="/css/post-upload.css"/> <link rel="stylesheet" type="text/css" href="/css/post-upload.css"/>
<link rel="stylesheet" type="text/css" href="/css/user-list.css"/> <link rel="stylesheet" type="text/css" href="/css/user-list.css"/>
<link rel="stylesheet" type="text/css" href="/css/user.css"/> <link rel="stylesheet" type="text/css" href="/css/user.css"/>
<link rel="stylesheet" type="text/css" href="/css/post-list.css"/>
<!-- /build --> <!-- /build -->
</head> </head>

View file

@ -2,26 +2,91 @@ var App = App || {};
App.Presenters = App.Presenters || {}; App.Presenters = App.Presenters || {};
App.Presenters.PostListPresenter = function( App.Presenters.PostListPresenter = function(
_,
jQuery, jQuery,
topNavigationPresenter) { util,
promise,
auth,
router,
pagedCollectionPresenter,
topNavigationPresenter,
messagePresenter) {
var $el = jQuery('#content'); var $el = jQuery('#content');
var listTemplate;
var itemTemplate;
function init(args) { function init(args) {
topNavigationPresenter.select('posts'); topNavigationPresenter.select('posts');
topNavigationPresenter.changeTitle('Posts'); topNavigationPresenter.changeTitle('Posts');
promise.waitAll(
util.promiseTemplate('post-list'),
util.promiseTemplate('post-list-item')).then(function(listHtml, itemHtml) {
listTemplate = _.template(listHtml);
itemTemplate = _.template(itemHtml);
render(); 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() { 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 { return {
init: init, init: init,
reinit: reinit,
render: render, 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);

View file

@ -0,0 +1,8 @@
<li class="post post-type-<%= post.contentType %> ">
<a class="link"
title="<%= _.map(post.tags, function(tag) { return '#' + tag; }).join(', ') %>"
href="#/post/<%= post.id %>">
<img class="thumb" src="/data/thumbnails/160x160/posts/<%= post.name %>" alt="@<%= post.id %>"/>
</a>
</li>

View file

@ -0,0 +1,8 @@
<div id="post-list">
<div class="pagination-content">
<div class="wrapper">
<ul class="posts">
</ul>
</div>
</div>
</div>

View file

@ -22,9 +22,21 @@ final class PostController extends AbstractController
public function registerRoutes(\Szurubooru\Router $router) public function registerRoutes(\Szurubooru\Router $router)
{ {
$router->get('/api/posts', [$this, 'getFiltered']);
$router->post('/api/posts', [$this, 'createPost']); $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() public function createPost()
{ {
$this->privilegeService->assertPrivilege(\Szurubooru\Privilege::UPLOAD_POSTS); $this->privilegeService->assertPrivilege(\Szurubooru\Privilege::UPLOAD_POSTS);

View file

@ -0,0 +1,12 @@
<?php
namespace Szurubooru\Dao\Services;
class PostSearchService extends AbstractSearchService
{
public function __construct(
\Szurubooru\DatabaseConnection $databaseConnection,
\Szurubooru\Dao\PostDao $postDao)
{
parent::__construct($databaseConnection, $postDao);
}
}

View file

@ -7,6 +7,7 @@ class PostService
private $validator; private $validator;
private $transactionManager; private $transactionManager;
private $postDao; private $postDao;
private $postSearchService;
private $fileService; private $fileService;
private $timeService; private $timeService;
private $authService; private $authService;
@ -16,6 +17,7 @@ class PostService
\Szurubooru\Validator $validator, \Szurubooru\Validator $validator,
\Szurubooru\Dao\TransactionManager $transactionManager, \Szurubooru\Dao\TransactionManager $transactionManager,
\Szurubooru\Dao\PostDao $postDao, \Szurubooru\Dao\PostDao $postDao,
\Szurubooru\Dao\Services\PostSearchService $postSearchService,
\Szurubooru\Services\AuthService $authService, \Szurubooru\Services\AuthService $authService,
\Szurubooru\Services\TimeService $timeService, \Szurubooru\Services\TimeService $timeService,
\Szurubooru\Services\FileService $fileService) \Szurubooru\Services\FileService $fileService)
@ -24,6 +26,7 @@ class PostService
$this->validator = $validator; $this->validator = $validator;
$this->transactionManager = $transactionManager; $this->transactionManager = $transactionManager;
$this->postDao = $postDao; $this->postDao = $postDao;
$this->postSearchService = $postSearchService;
$this->fileService = $fileService; $this->fileService = $fileService;
$this->timeService = $timeService; $this->timeService = $timeService;
$this->authService = $authService; $this->authService = $authService;
@ -41,6 +44,17 @@ class PostService
return $this->transactionManager->rollback($transactionFunc); 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) public function createPost(\Szurubooru\FormData\UploadFormData $formData)
{ {
$transactionFunc = function() use ($formData) $transactionFunc = function() use ($formData)

View file

@ -7,6 +7,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
private $validatorMock; private $validatorMock;
private $transactionManagerMock; private $transactionManagerMock;
private $postDaoMock; private $postDaoMock;
private $postSearchServiceMock;
private $authServiceMock; private $authServiceMock;
private $timeServiceMock; private $timeServiceMock;
private $fileServiceMock; private $fileServiceMock;
@ -17,6 +18,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
$this->validatorMock = $this->mock(\Szurubooru\Validator::class); $this->validatorMock = $this->mock(\Szurubooru\Validator::class);
$this->transactionManagerMock = $this->mockTransactionManager(); $this->transactionManagerMock = $this->mockTransactionManager();
$this->postDaoMock = $this->mock(\Szurubooru\Dao\PostDao::class); $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->authServiceMock = $this->mock(\Szurubooru\Services\AuthService::class);
$this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class); $this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class);
$this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class); $this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class);
@ -181,6 +183,7 @@ class PostServiceTest extends \Szurubooru\Tests\AbstractTestCase
$this->validatorMock, $this->validatorMock,
$this->transactionManagerMock, $this->transactionManagerMock,
$this->postDaoMock, $this->postDaoMock,
$this->postSearchServiceMock,
$this->authServiceMock, $this->authServiceMock,
$this->timeServiceMock, $this->timeServiceMock,
$this->fileServiceMock); $this->fileServiceMock);