Added posts listing (closed #7)
This commit is contained in:
parent
15eb2342b9
commit
a16a2d3235
10 changed files with 156 additions and 4 deletions
|
@ -49,5 +49,8 @@ minUserNameLength = 1
|
||||||
maxUserNameLength = 32
|
maxUserNameLength = 32
|
||||||
usersPerPage = 20
|
usersPerPage = 20
|
||||||
|
|
||||||
|
[posts]
|
||||||
|
postsPerPage = 40
|
||||||
|
|
||||||
[misc]
|
[misc]
|
||||||
thumbnailCropStyle = outside
|
thumbnailCropStyle = outside
|
||||||
|
|
26
public_html/css/post-list.css
Normal file
26
public_html/css/post-list.css
Normal 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;
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
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() {
|
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);
|
||||||
|
|
8
public_html/templates/post-list-item.tpl
Normal file
8
public_html/templates/post-list-item.tpl
Normal 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>
|
8
public_html/templates/post-list.tpl
Normal file
8
public_html/templates/post-list.tpl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<div id="post-list">
|
||||||
|
<div class="pagination-content">
|
||||||
|
<div class="wrapper">
|
||||||
|
<ul class="posts">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -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);
|
||||||
|
|
12
src/Dao/Services/PostSearchService.php
Normal file
12
src/Dao/Services/PostSearchService.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue