Merge branch 'api'
This commit is contained in:
commit
021514aabd
281 changed files with 15747 additions and 3530 deletions
|
@ -1,14 +1,11 @@
|
||||||
[chibi]
|
|
||||||
enableCache=1
|
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
dbDriver = "sqlite"
|
dbDriver = "sqlite"
|
||||||
dbLocation = "./data/db.sqlite"
|
dbLocation = "./data/db.sqlite"
|
||||||
dbUser = "test"
|
dbUser = "test"
|
||||||
dbPass = "test"
|
dbPass = "test"
|
||||||
filesPath = "./data/files/"
|
filesPath = "./data/files/"
|
||||||
thumbsPath = "./data/thumbs/"
|
thumbsPath = "./public_html/thumbs/"
|
||||||
logsPath = "./data/logs/"
|
logsPath = "./data/logs/{yyyy}-{mm}.log"
|
||||||
mediaPath = "./public_html/media/"
|
mediaPath = "./public_html/media/"
|
||||||
title = "szurubooru"
|
title = "szurubooru"
|
||||||
salt = "1A2/$_4xVa"
|
salt = "1A2/$_4xVa"
|
||||||
|
@ -17,6 +14,7 @@ salt = "1A2/$_4xVa"
|
||||||
featuredPostMaxDays=7
|
featuredPostMaxDays=7
|
||||||
debugQueries=0
|
debugQueries=0
|
||||||
logAnonymousUploads=1
|
logAnonymousUploads=1
|
||||||
|
githubLink = http://github.com/rr-/szurubooru
|
||||||
|
|
||||||
[help]
|
[help]
|
||||||
title=Help
|
title=Help
|
||||||
|
@ -42,6 +40,14 @@ showDislikedPostsDefault=1
|
||||||
maxSearchTokens=4
|
maxSearchTokens=4
|
||||||
maxRelatedPosts=50
|
maxRelatedPosts=50
|
||||||
|
|
||||||
|
[tags]
|
||||||
|
minLength = 1
|
||||||
|
maxLength = 64
|
||||||
|
regex = "/^[()\[\]a-zA-Z0-9_.-]+$/i"
|
||||||
|
|
||||||
|
[posts]
|
||||||
|
maxSourceLength = 200
|
||||||
|
|
||||||
[comments]
|
[comments]
|
||||||
minLength = 5
|
minLength = 5
|
||||||
maxLength = 2000
|
maxLength = 2000
|
||||||
|
@ -70,17 +76,31 @@ passwordResetEmailSubject = "{host} - password reset"
|
||||||
passwordResetEmailBody = "Hello,{nl}{nl}You received this e-mail because someone requested a password reset for user with this e-mail address at {host}. If it's you, visit {link} to finish password reset process, otherwise you may ignore and delete this e-mail.{nl}{nl}Kind regards,{nl}{host} mailing system"
|
passwordResetEmailBody = "Hello,{nl}{nl}You received this e-mail because someone requested a password reset for user with this e-mail address at {host}. If it's you, visit {link} to finish password reset process, otherwise you may ignore and delete this e-mail.{nl}{nl}Kind regards,{nl}{host} mailing system"
|
||||||
|
|
||||||
[privileges]
|
[privileges]
|
||||||
uploadPost=registered
|
registerAccount=anonymous
|
||||||
|
;registerAccount=nobody
|
||||||
|
|
||||||
listPosts=anonymous
|
listPosts=anonymous
|
||||||
|
listPosts.safe=anonymous
|
||||||
listPosts.sketchy=registered
|
listPosts.sketchy=registered
|
||||||
listPosts.unsafe=registered
|
listPosts.unsafe=registered
|
||||||
listPosts.hidden=moderator
|
listPosts.hidden=moderator
|
||||||
viewPost=anonymous
|
viewPost=anonymous
|
||||||
|
viewPost.safe=anonymous
|
||||||
viewPost.sketchy=registered
|
viewPost.sketchy=registered
|
||||||
viewPost.unsafe=registered
|
viewPost.unsafe=registered
|
||||||
viewPost.hidden=moderator
|
viewPost.hidden=moderator
|
||||||
retrievePost=anonymous
|
retrievePost=anonymous
|
||||||
favoritePost=registered
|
favoritePost=registered
|
||||||
|
|
||||||
|
addPost=registered
|
||||||
|
addPostSafety=registered
|
||||||
|
addPostTags=registered
|
||||||
|
addPostThumb=power-user
|
||||||
|
addPostSource=registered
|
||||||
|
addPostRelations=power-user
|
||||||
|
addPostContent=registered
|
||||||
|
|
||||||
|
editPost=registered
|
||||||
editPostSafety.own=registered
|
editPostSafety.own=registered
|
||||||
editPostSafety.all=moderator
|
editPostSafety.all=moderator
|
||||||
editPostTags=registered
|
editPostTags=registered
|
||||||
|
@ -88,7 +108,8 @@ editPostThumb=moderator
|
||||||
editPostSource=moderator
|
editPostSource=moderator
|
||||||
editPostRelations.own=registered
|
editPostRelations.own=registered
|
||||||
editPostRelations.all=moderator
|
editPostRelations.all=moderator
|
||||||
editPostFile=moderator
|
editPostContent=moderator
|
||||||
|
|
||||||
massTag.own=registered
|
massTag.own=registered
|
||||||
massTag.all=power-user
|
massTag.all=power-user
|
||||||
hidePost=moderator
|
hidePost=moderator
|
||||||
|
@ -99,16 +120,17 @@ flagPost=registered
|
||||||
|
|
||||||
listUsers=registered
|
listUsers=registered
|
||||||
viewUser=registered
|
viewUser=registered
|
||||||
viewUserEmail.all=admin
|
|
||||||
viewUserEmail.own=registered
|
viewUserEmail.own=registered
|
||||||
changeUserPassword.own=registered
|
viewUserEmail.all=admin
|
||||||
changeUserPassword.all=admin
|
editUserPassword.own=registered
|
||||||
changeUserEmail.own=registered
|
editUserPassword.all=admin
|
||||||
changeUserEmail.all=admin
|
editUserEmail.own=registered
|
||||||
changeUserAccessRank=admin
|
editUserEmail.all=admin
|
||||||
changeUserName=moderator
|
editUserEmailNoConfirm=admin
|
||||||
changeUserSettings.all=nobody
|
editUserAccessRank=admin
|
||||||
changeUserSettings.own=registered
|
editUserName=moderator
|
||||||
|
editUserSettings.own=registered
|
||||||
|
editUserSettings.all=nobody
|
||||||
acceptUserRegistration=moderator
|
acceptUserRegistration=moderator
|
||||||
banUser.own=nobody
|
banUser.own=nobody
|
||||||
banUser.all=admin
|
banUser.all=admin
|
||||||
|
|
|
@ -39,8 +39,8 @@ Command | Description
|
||||||
[search]id:1,2,3[/search] | having specific post ID | `ids` |
|
[search]id:1,2,3[/search] | having specific post ID | `ids` |
|
||||||
[search]idmin:5[/search] | posts with ID greater than or equal to @5 | `id_min` |
|
[search]idmin:5[/search] | posts with ID greater than or equal to @5 | `id_min` |
|
||||||
[search]idmax:5[/search] | posts with ID less than or equal to @5 | `id_max` |
|
[search]idmax:5[/search] | posts with ID less than or equal to @5 | `id_max` |
|
||||||
[search]type:img[/search] | only image posts | - |
|
[search]type:img[/search] | only image posts | `type:image` |
|
||||||
[search]type:swf[/search] | only Flash posts | - |
|
[search]type:flash[/search] | only Flash posts | `type:swf` |
|
||||||
[search]type:yt[/search] | only Youtube posts | `type:youtube` |
|
[search]type:yt[/search] | only Youtube posts | `type:youtube` |
|
||||||
[search]special:liked[/search] | posts liked by currently logged in user | `special:likes`, `special:like` |
|
[search]special:liked[/search] | posts liked by currently logged in user | `special:likes`, `special:like` |
|
||||||
[search]special:disliked[/search] | posts disliked by currently logged in user | `special:dislikes`, `special:dislike` |
|
[search]special:disliked[/search] | posts disliked by currently logged in user | `special:dislikes`, `special:dislike` |
|
||||||
|
|
|
@ -8,4 +8,4 @@ Your actions related to posts (uploading, tagging, etc.) are logged, along with
|
||||||
|
|
||||||
# Cookies
|
# Cookies
|
||||||
|
|
||||||
Cookies are used to store your session data and browsing preferences, such as endless scrolling or visibility of NSFW posts.
|
Cookies are used to store your session data in order to keep you logged in and personalize your web experience.
|
||||||
|
|
5
init.php
5
init.php
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
require_once 'src/core.php';
|
require_once 'src/core.php';
|
||||||
$config = getConfig();
|
$config = Core::getConfig();
|
||||||
$fontsPath = TextHelper::absolutePath($config->main->mediaPath . DS . 'fonts');
|
$fontsPath = TextHelper::absolutePath($config->main->mediaPath . DS . 'fonts');
|
||||||
$libPath = TextHelper::absolutePath($config->main->mediaPath . DS . 'lib');
|
$libPath = TextHelper::absolutePath($config->main->mediaPath . DS . 'lib');
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ function download($source, $destination = null)
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$version = exec('git describe --tags --always --dirty');
|
||||||
|
$branch = exec('git rev-parse --abbrev-ref HEAD');
|
||||||
|
PropertyModel::set(PropertyModel::EngineVersion, $version . '@' . $branch);
|
||||||
|
|
||||||
|
|
||||||
//jQuery
|
//jQuery
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 45c662d0a4b32e09399b5b68ac53aaa3f1a29911
|
Subproject commit a6c610e5c68220cf672debe765752662807c0d39
|
|
@ -1 +1 @@
|
||||||
Subproject commit 22910a186efbcb9bc86a3ae3eb6f4aff34096406
|
Subproject commit 6bb18c1c6ed7ea952ae7a8dab792d5364a334201
|
|
@ -1,10 +1,14 @@
|
||||||
DirectorySlash Off
|
DirectorySlash Off
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
|
|
||||||
|
ErrorDocument 403 /fatal-error/403
|
||||||
|
ErrorDocument 404 /fatal-error/404
|
||||||
|
ErrorDocument 500 /fatal-error/500
|
||||||
|
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
ErrorDocument 403 /dispatch.php?request=error/http&code=403
|
RewriteCond %{DOCUMENT_ROOT}/thumbs/$1.thumb -f
|
||||||
ErrorDocument 404 /dispatch.php?request=error/http&code=404
|
RewriteRule ^/?post/(.*)/thumb/?$ /thumbs/$1.thumb
|
||||||
ErrorDocument 500 /dispatch.php?request=error/http&code=500
|
RewriteRule ^/?thumbs/(.*).thumb - [L,T=image/jpeg]
|
||||||
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
|
|
@ -1,211 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
$startTime = microtime(true);
|
|
||||||
|
|
||||||
require_once '../src/core.php';
|
require_once '../src/core.php';
|
||||||
|
|
||||||
$query = rtrim($_SERVER['REQUEST_URI'], '/');
|
$dispatcher = new Dispatcher();
|
||||||
|
$dispatcher->run();
|
||||||
//prepare context
|
|
||||||
$context = new StdClass;
|
|
||||||
$context->startTime = $startTime;
|
|
||||||
$context->query = $query;
|
|
||||||
|
|
||||||
function renderView()
|
|
||||||
{
|
|
||||||
$context = getContext();
|
|
||||||
\Chibi\View::render($context->layoutName, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContext()
|
|
||||||
{
|
|
||||||
global $context;
|
|
||||||
return $context;
|
|
||||||
}
|
|
||||||
|
|
||||||
$context->simpleControllerName = null;
|
|
||||||
$context->simpleActionName = null;
|
|
||||||
|
|
||||||
\Chibi\Router::setObserver(function($route, $args)
|
|
||||||
{
|
|
||||||
$context = getContext();
|
|
||||||
$context->route = $route;
|
|
||||||
list ($className, $methodName) = $route->destination;
|
|
||||||
|
|
||||||
$context->simpleControllerName = TextCaseConverter::convert(
|
|
||||||
str_replace('Controller', '', $className),
|
|
||||||
TextCaseConverter::CAMEL_CASE,
|
|
||||||
TextCaseConverter::SPINAL_CASE);
|
|
||||||
|
|
||||||
$context->simpleActionName = TextCaseConverter::convert(
|
|
||||||
str_replace('Action', '', $methodName),
|
|
||||||
TextCaseConverter::CAMEL_CASE,
|
|
||||||
TextCaseConverter::SPINAL_CASE);
|
|
||||||
|
|
||||||
$context->viewName = sprintf(
|
|
||||||
'%s-%s',
|
|
||||||
$context->simpleControllerName,
|
|
||||||
$context->simpleActionName);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (['GET', 'POST'] as $method)
|
|
||||||
{
|
|
||||||
\Chibi\Router::register(['IndexController', 'indexAction'], $method, '');
|
|
||||||
\Chibi\Router::register(['IndexController', 'indexAction'], $method, '/index');
|
|
||||||
\Chibi\Router::register(['IndexController', 'helpAction'], $method, '/help');
|
|
||||||
\Chibi\Router::register(['IndexController', 'helpAction'], $method, '/help/{tab}');
|
|
||||||
\Chibi\Router::register(['LogController', 'listAction'], $method, '/logs');
|
|
||||||
\Chibi\Router::register(['LogController', 'viewAction'], $method, '/log/{name}', ['name' => '[0-9a-zA-Z._-]+']);
|
|
||||||
\Chibi\Router::register(['LogController', 'viewAction'], $method, '/log/{name}/{page}', ['name' => '[0-9a-zA-Z._-]+', 'page' => '\d*']);
|
|
||||||
\Chibi\Router::register(['LogController', 'viewAction'], $method, '/log/{name}/{page}/{filter}', ['name' => '[0-9a-zA-Z._-]+', 'page' => '\d*', 'filter' => '.*']);
|
|
||||||
\Chibi\Router::register(['AuthController', 'loginAction'], $method, '/auth/login');
|
|
||||||
\Chibi\Router::register(['AuthController', 'logoutAction'], $method, '/auth/logout');
|
|
||||||
\Chibi\Router::register(['AuthController', 'loginAction'], 'POST', '/auth/login');
|
|
||||||
\Chibi\Router::register(['AuthController', 'logoutAction'], 'POST', '/auth/logout');
|
|
||||||
\Chibi\Router::register(['CommentController', 'listAction'], $method, '/comments');
|
|
||||||
\Chibi\Router::register(['CommentController', 'listAction'], $method, '/comments/{page}', ['page' => '\d+']);
|
|
||||||
\Chibi\Router::register(['CommentController', 'addAction'], $method, '/post/{postId}/add-comment', ['postId' => '\d+']);
|
|
||||||
\Chibi\Router::register(['CommentController', 'deleteAction'], $method, '/comment/{id}/delete', ['id' => '\d+']);
|
|
||||||
\Chibi\Router::register(['CommentController', 'editAction'], $method, '/comment/{id}/edit', ['id' => '\d+']);
|
|
||||||
|
|
||||||
$postValidation =
|
|
||||||
[
|
|
||||||
'tag' => '[^\/]*',
|
|
||||||
'enable' => '0|1',
|
|
||||||
'source' => 'posts|mass-tag',
|
|
||||||
'query' => '[^\/]*',
|
|
||||||
'additionalInfo' => '[^\/]*',
|
|
||||||
'score' => '-1|0|1',
|
|
||||||
];
|
|
||||||
|
|
||||||
\Chibi\Router::register(['PostController', 'uploadAction'], $method, '/posts/upload', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'listAction'], $method, '/{source}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'listAction'], $method, '/{source}/{query}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'listAction'], $method, '/{source}/{query}/{page}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'listAction'], $method, '/{source}/{additionalInfo}/{query}/{page}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'toggleTagAction'], $method, '/post/{id}/toggle-tag/{tag}/{enable}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'favoritesAction'], $method, '/favorites', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'favoritesAction'], $method, '/favorites/{page}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'upvotedAction'], $method, '/upvoted', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'upvotedAction'], $method, '/upvoted/{page}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'randomAction'], $method, '/random', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'randomAction'], $method, '/random/{page}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'viewAction'], $method, '/post/{id}', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'retrieveAction'], $method, '/post/{name}/retrieve', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'thumbAction'], $method, '/post/{name}/thumb', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'removeFavoriteAction'], $method, '/post/{id}/rem-fav', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'addFavoriteAction'], $method, '/post/{id}/add-fav', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'deleteAction'], $method, '/post/{id}/delete', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'hideAction'], $method, '/post/{id}/hide', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'unhideAction'], $method, '/post/{id}/unhide', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'editAction'], $method, '/post/{id}/edit', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'flagAction'], $method, '/post/{id}/flag', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'featureAction'], $method, '/post/{id}/feature', $postValidation);
|
|
||||||
\Chibi\Router::register(['PostController', 'scoreAction'], $method, '/post/{id}/score/{score}', $postValidation);
|
|
||||||
|
|
||||||
$tagValidation =
|
|
||||||
[
|
|
||||||
'page' => '\d*',
|
|
||||||
'filter' => '[^\/]+',
|
|
||||||
];
|
|
||||||
|
|
||||||
\Chibi\Router::register(['TagController', 'listAction'], $method, '/tags', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'listAction'], $method, '/tags/{filter}', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'listAction'], $method, '/tags/{page}', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'listAction'], $method, '/tags/{filter}/{page}', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'autoCompleteAction'], $method, '/tags-autocomplete', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'relatedAction'], $method, '/tags-related', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'mergeAction'], $method, '/tags-merge', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'renameAction'], $method, '/tags-rename', $tagValidation);
|
|
||||||
\Chibi\Router::register(['TagController', 'massTagRedirectAction'], $method, '/mass-tag-redirect', $tagValidation);
|
|
||||||
|
|
||||||
$userValidations =
|
|
||||||
[
|
|
||||||
'name' => '[^\/]+',
|
|
||||||
'page' => '\d*',
|
|
||||||
'tab' => 'favs|uploads',
|
|
||||||
'filter' => '[^\/]+',
|
|
||||||
];
|
|
||||||
|
|
||||||
\Chibi\Router::register(['UserController', 'registrationAction'], $method, '/register', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'viewAction'], $method, '/user/{name}/{tab}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'viewAction'], $method, '/user/{name}/{tab}/{page}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'listAction'], $method, '/users', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'listAction'], $method, '/users/{page}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'listAction'], $method, '/users/{filter}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'listAction'], $method, '/users/{filter}/{page}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'flagAction'], $method, '/user/{name}/flag', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'banAction'], $method, '/user/{name}/ban', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'unbanAction'], $method, '/user/{name}/unban', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'acceptRegistrationAction'], $method, '/user/{name}/accept-registration', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'deleteAction'], $method, '/user/{name}/delete', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'settingsAction'], $method, '/user/{name}/settings', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'editAction'], $method, '/user/{name}/edit', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'activationAction'], $method, '/activation/{token}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'activationProxyAction'], $method, '/activation-proxy', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'activationProxyAction'], $method, '/activation-proxy/{token}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'passwordResetAction'], $method, '/password-reset/{token}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'passwordResetProxyAction'], $method, '/password-reset-proxy', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'passwordResetProxyAction'], $method, '/password-reset-proxy/{token}', $userValidations);
|
|
||||||
\Chibi\Router::register(['UserController', 'toggleSafetyAction'], $method, '/user/toggle-safety/{safety}', $userValidations);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assets::setTitle($config->main->title);
|
|
||||||
|
|
||||||
$context->handleExceptions = false;
|
|
||||||
$context->json = isset($_GET['json']);
|
|
||||||
$context->layoutName = $context->json
|
|
||||||
? 'layout-json'
|
|
||||||
: 'layout-normal';
|
|
||||||
$context->viewName = '';
|
|
||||||
$context->transport = new StdClass;
|
|
||||||
|
|
||||||
session_start();
|
|
||||||
if (!Auth::isLoggedIn())
|
|
||||||
Auth::tryAutoLogin();
|
|
||||||
|
|
||||||
register_shutdown_function(function()
|
|
||||||
{
|
|
||||||
$error = error_get_last();
|
|
||||||
if ($error !== null)
|
|
||||||
\Chibi\Util\Headers::setCode(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
\Chibi\Router::run($query);
|
|
||||||
renderView();
|
|
||||||
AuthController::observeWorkFinish();
|
|
||||||
}
|
|
||||||
catch (\Chibi\UnhandledRouteException $e)
|
|
||||||
{
|
|
||||||
throw new SimpleNotFoundException($query . ' not found.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (\Chibi\MissingViewFileException $e)
|
|
||||||
{
|
|
||||||
$context->json = true;
|
|
||||||
$context->layoutName = 'layout-json';
|
|
||||||
renderView();
|
|
||||||
}
|
|
||||||
catch (SimpleException $e)
|
|
||||||
{
|
|
||||||
if ($e instanceof SimpleNotFoundException)
|
|
||||||
\Chibi\Util\Headers::setCode(404);
|
|
||||||
else
|
|
||||||
\Chibi\Util\Headers::setCode(400);
|
|
||||||
Messenger::message($e->getMessage(), false);
|
|
||||||
if (!$context->handleExceptions)
|
|
||||||
$context->viewName = 'message';
|
|
||||||
renderView();
|
|
||||||
}
|
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
\Chibi\Util\Headers::setCode(400);
|
|
||||||
Messenger::message($e->getMessage());
|
|
||||||
$context->transport->exception = $e;
|
|
||||||
$context->transport->queries = \Chibi\Database::getLogs();
|
|
||||||
$context->viewName = 'error-exception';
|
|
||||||
renderView();
|
|
||||||
}
|
|
||||||
|
|
|
@ -145,6 +145,12 @@ footer span:not(:last-of-type):after {
|
||||||
footer a {
|
footer a {
|
||||||
color: silver;
|
color: silver;
|
||||||
}
|
}
|
||||||
|
footer .left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
footer .right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,12 +260,12 @@ button {
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
height: 34px;
|
padding: 3px 5px;
|
||||||
|
height: 30px;
|
||||||
}
|
}
|
||||||
label,
|
label,
|
||||||
input,
|
input,
|
||||||
select {
|
select {
|
||||||
padding: 5px;
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 11pt;
|
font-size: 11pt;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +296,7 @@ button:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
margin: 0.25em 0;
|
margin: 0 0 0.5em 0;
|
||||||
clear: left;
|
clear: left;
|
||||||
}
|
}
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
|
@ -313,12 +319,19 @@ ul.tagit {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
ul.tagit li.tagit-new {
|
||||||
|
padding: 1px 0 !important;
|
||||||
|
}
|
||||||
|
ul.tagit li.tagit-choice {
|
||||||
|
padding: 1px 20px 1px 5px !important;
|
||||||
}
|
}
|
||||||
ul.tagit input {
|
ul.tagit input {
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
line-height: auto !important;
|
line-height: auto !important;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
margin: -4px 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
.related-tags {
|
.related-tags {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
|
|
||||||
.post .ops a {
|
.post .ops a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 0.5em;
|
margin-left: 1.5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.post a span {
|
.post a span {
|
||||||
|
@ -117,11 +117,11 @@
|
||||||
.post .file-name strong {
|
.post .file-name strong {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 50%;
|
max-width: 100%;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: text-bottom;
|
||||||
padding: 0.5em 0;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.safety-safe {
|
.safety-safe {
|
||||||
|
@ -155,6 +155,9 @@ ul.tagit {
|
||||||
.post .form-wrapper {
|
.post .form-wrapper {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.post form {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#lightbox {
|
#lightbox {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -23,13 +23,17 @@ embed {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
#sidebar .tags li {
|
#sidebar .tags li a {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 90%;
|
||||||
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
#sidebar .tags li .count {
|
#sidebar .tags li .count {
|
||||||
padding-left: 0.5em;
|
padding-left: 0.5em;
|
||||||
color: silver;
|
color: silver;
|
||||||
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
#around {
|
#around {
|
||||||
|
|
18
public_html/media/css/static-api.css
Normal file
18
public_html/media/css/static-api.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#content pre {
|
||||||
|
background: ghostwhite;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-left: 0.2em solid silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapsue;
|
||||||
|
}
|
||||||
|
#content th,
|
||||||
|
#content td {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
#content tbody:nth-child(2n) {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ $(function()
|
||||||
formDom.addClass('inactive');
|
formDom.addClass('inactive');
|
||||||
formDom.find(':input').attr('readonly', true);
|
formDom.find(':input').attr('readonly', true);
|
||||||
|
|
||||||
var url = formDom.attr('action') + '?json';
|
var url = formDom.attr('action');
|
||||||
var fd = new FormData(formDom[0]);
|
var fd = new FormData(formDom[0]);
|
||||||
|
|
||||||
var preview = false;
|
var preview = false;
|
||||||
|
@ -36,7 +36,6 @@ $(function()
|
||||||
data: fd,
|
data: fd,
|
||||||
processData: false,
|
processData: false,
|
||||||
contentType: false,
|
contentType: false,
|
||||||
type: 'POST',
|
|
||||||
|
|
||||||
success: function(data)
|
success: function(data)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +50,7 @@ $(function()
|
||||||
formDom.find('.preview').hide();
|
formDom.find('.preview').hide();
|
||||||
var cb = function()
|
var cb = function()
|
||||||
{
|
{
|
||||||
$.get(window.location.href, function(data)
|
$.get(window.location.href).success(function(data)
|
||||||
{
|
{
|
||||||
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
|
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
|
||||||
$('body').trigger('dom-update');
|
$('body').trigger('dom-update');
|
||||||
|
@ -84,7 +83,7 @@ $(function()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax(ajaxData);
|
postJSON(ajaxData);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.comment .edit a').bindOnce('edit-comment', 'click', function(e)
|
$('.comment .edit a').bindOnce('edit-comment', 'click', function(e)
|
||||||
|
@ -100,7 +99,7 @@ $(function()
|
||||||
|
|
||||||
if (formDom.length == 0)
|
if (formDom.length == 0)
|
||||||
{
|
{
|
||||||
$.get($(this).attr('href'), function(data)
|
$.get($(this).attr('href')).success(function(data)
|
||||||
{
|
{
|
||||||
var otherForm = $(data).find('form.edit-comment');
|
var otherForm = $(data).find('form.edit-comment');
|
||||||
otherForm.hide();
|
otherForm.hide();
|
||||||
|
|
|
@ -32,6 +32,24 @@ function rememberLastSearchQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
//core functionalities, prototypes
|
//core functionalities, prototypes
|
||||||
|
function getJSON(data)
|
||||||
|
{
|
||||||
|
if (typeof(data.headers) === 'undefined')
|
||||||
|
data.headers = {};
|
||||||
|
data.headers['X-Ajax'] = '1';
|
||||||
|
data.type = 'GET';
|
||||||
|
return $.ajax(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
function postJSON(data)
|
||||||
|
{
|
||||||
|
if (typeof(data.headers) === 'undefined')
|
||||||
|
data.headers = {};
|
||||||
|
data.headers['X-Ajax'] = '1';
|
||||||
|
data.type = 'POST';
|
||||||
|
return $.ajax(data);
|
||||||
|
};
|
||||||
|
|
||||||
$.fn.hasAttr = function(name)
|
$.fn.hasAttr = function(name)
|
||||||
{
|
{
|
||||||
return this.attr(name) !== undefined;
|
return this.attr(name) !== undefined;
|
||||||
|
@ -50,34 +68,6 @@ $.fn.bindOnce = function(name, eventName, callback)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//safety trigger
|
|
||||||
$(function()
|
|
||||||
{
|
|
||||||
$('.safety a').click(function(e)
|
|
||||||
{
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var aDom = $(this);
|
|
||||||
if (aDom.hasClass('inactive'))
|
|
||||||
return;
|
|
||||||
aDom.addClass('inactive');
|
|
||||||
|
|
||||||
var url = $(this).attr('href') + '?json';
|
|
||||||
$.get(url).success(function(data)
|
|
||||||
{
|
|
||||||
window.location.reload();
|
|
||||||
}).error(function(xhr)
|
|
||||||
{
|
|
||||||
alert(xhr.responseJSON
|
|
||||||
? xhr.responseJSON.message
|
|
||||||
: 'Fatal error');
|
|
||||||
aDom.removeClass('inactive');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//basic event listeners
|
//basic event listeners
|
||||||
$(function()
|
$(function()
|
||||||
{
|
{
|
||||||
|
@ -111,8 +101,8 @@ $(function()
|
||||||
return;
|
return;
|
||||||
aDom.addClass('inactive');
|
aDom.addClass('inactive');
|
||||||
|
|
||||||
var url = $(this).attr('href') + '?json';
|
var url = $(this).attr('href');
|
||||||
$.post(url, {submit: 1}).success(function(data)
|
postJSON({ url: url }).success(function(data)
|
||||||
{
|
{
|
||||||
if (aDom.hasAttr('data-redirect-url'))
|
if (aDom.hasAttr('data-redirect-url'))
|
||||||
window.location.href = aDom.attr('data-redirect-url');
|
window.location.href = aDom.attr('data-redirect-url');
|
||||||
|
@ -201,21 +191,26 @@ function split(val)
|
||||||
|
|
||||||
function retrieveTags(searchTerm, cb)
|
function retrieveTags(searchTerm, cb)
|
||||||
{
|
{
|
||||||
var options = { search: searchTerm };
|
var options =
|
||||||
$.getJSON('/tags-autocomplete?json', options, function(data)
|
|
||||||
{
|
{
|
||||||
var tags = $.map(data.tags.slice(0, 15), function(tag)
|
url: '/tags-autocomplete',
|
||||||
|
data: { search: searchTerm }
|
||||||
|
};
|
||||||
|
getJSON(options)
|
||||||
|
.success(function(data)
|
||||||
{
|
{
|
||||||
var ret =
|
var tags = $.map(data.tags.slice(0, 15), function(tag)
|
||||||
{
|
{
|
||||||
label: tag.name + ' (' + tag.count + ')',
|
var ret =
|
||||||
value: tag.name,
|
{
|
||||||
};
|
label: tag.name + ' (' + tag.count + ')',
|
||||||
return ret;
|
value: tag.name,
|
||||||
});
|
};
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
cb(tags);
|
cb(tags);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(function()
|
$(function()
|
||||||
|
@ -277,8 +272,17 @@ function attachTagIt(target)
|
||||||
{
|
{
|
||||||
var targetTagit = ui.tag.parents('.tagit');
|
var targetTagit = ui.tag.parents('.tagit');
|
||||||
var context = target.tagit('assignedTags');
|
var context = target.tagit('assignedTags');
|
||||||
options = { context: context, tag: ui.tagLabel };
|
var options =
|
||||||
if (targetTagit.siblings('.related-tags:eq(0)').data('for') == options.tag)
|
{
|
||||||
|
url: '/tags-related',
|
||||||
|
data:
|
||||||
|
{
|
||||||
|
context: context,
|
||||||
|
tag: ui.tagLabel
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (targetTagit.siblings('.related-tags:eq(0)').data('for') == options.data.tag)
|
||||||
{
|
{
|
||||||
targetTagit.siblings('.related-tags').slideUp(function()
|
targetTagit.siblings('.related-tags').slideUp(function()
|
||||||
{
|
{
|
||||||
|
@ -287,7 +291,7 @@ function attachTagIt(target)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.getJSON('/tags-related?json', options, function(data)
|
getJSON(options).success(function(data)
|
||||||
{
|
{
|
||||||
var list = $('<ul>');
|
var list = $('<ul>');
|
||||||
$.each(data.tags, function(i, tag)
|
$.each(data.tags, function(i, tag)
|
||||||
|
|
|
@ -15,7 +15,7 @@ function scrolled()
|
||||||
if (pageNext != null && pageNext != pageDone)
|
if (pageNext != null && pageNext != pageDone)
|
||||||
{
|
{
|
||||||
$(document).data('page-done', pageNext);
|
$(document).data('page-done', pageNext);
|
||||||
$.get(pageNext, [], function(response)
|
$.get(pageNext).success(function(response)
|
||||||
{
|
{
|
||||||
var dom = $(response);
|
var dom = $(response);
|
||||||
var nextPage = dom.find('.paginator .next:not(.disabled) a').attr('href');
|
var nextPage = dom.find('.paginator .next:not(.disabled) a').attr('href');
|
||||||
|
|
|
@ -12,9 +12,9 @@ $(function()
|
||||||
aDom.addClass('inactive');
|
aDom.addClass('inactive');
|
||||||
|
|
||||||
var enable = !aDom.parents('.post').hasClass('tagged');
|
var enable = !aDom.parents('.post').hasClass('tagged');
|
||||||
var url = $(this).attr('href') + '?json';
|
var url = $(this).attr('href');
|
||||||
url = url.replace('_enable_', enable ? '1' : '0');
|
url = url.replace(/\/[01]\/?$/, '/' + (enable ? '1' : '0'));
|
||||||
$.get(url, {submit: 1}).success(function(data)
|
postJSON({ url: url }).success(function(data)
|
||||||
{
|
{
|
||||||
aDom.removeClass('inactive');
|
aDom.removeClass('inactive');
|
||||||
aDom.parents('.post').removeClass('tagged');
|
aDom.parents('.post').removeClass('tagged');
|
||||||
|
|
|
@ -90,7 +90,7 @@ $(function()
|
||||||
}
|
}
|
||||||
|
|
||||||
var postDom = posts.first();
|
var postDom = posts.first();
|
||||||
var url = postDom.find('form').attr('action') + '?json';
|
var url = postDom.find('form').attr('action');
|
||||||
var fd = new FormData(postDom.find('form').get(0));
|
var fd = new FormData(postDom.find('form').get(0));
|
||||||
|
|
||||||
fd.append('file', postDom.data('file'));
|
fd.append('file', postDom.data('file'));
|
||||||
|
@ -104,7 +104,6 @@ $(function()
|
||||||
processData: false,
|
processData: false,
|
||||||
contentType: false,
|
contentType: false,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
type: 'POST',
|
|
||||||
success: function(data)
|
success: function(data)
|
||||||
{
|
{
|
||||||
postDom.slideUp(function()
|
postDom.slideUp(function()
|
||||||
|
@ -125,7 +124,7 @@ $(function()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax(ajaxData);
|
postJSON(ajaxData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFinished()
|
function uploadFinished()
|
||||||
|
@ -166,6 +165,7 @@ $(function()
|
||||||
{
|
{
|
||||||
handleInputs(files, function(postDom, file)
|
handleInputs(files, function(postDom, file)
|
||||||
{
|
{
|
||||||
|
postDom.data('url', '');
|
||||||
postDom.data('file', file);
|
postDom.data('file', file);
|
||||||
$('.file-name strong', postDom).text(file.name);
|
$('.file-name strong', postDom).text(file.name);
|
||||||
|
|
||||||
|
@ -198,11 +198,13 @@ $(function()
|
||||||
handleInputs(urls, function(postDom, url)
|
handleInputs(urls, function(postDom, url)
|
||||||
{
|
{
|
||||||
postDom.data('url', url);
|
postDom.data('url', url);
|
||||||
|
postDom.data('file', '');
|
||||||
postDom.find('[name=source]').val(url);
|
postDom.find('[name=source]').val(url);
|
||||||
if (matches = url.match(/watch.*?=([a-zA-Z0-9_-]+)/))
|
if (matches = url.match(/watch.*?=([a-zA-Z0-9_-]+)/))
|
||||||
{
|
{
|
||||||
postDom.find('.file-name strong').text(url);
|
postDom.find('.file-name strong').text(url);
|
||||||
$.getJSON('http://gdata.youtube.com/feeds/api/videos/' + matches[1] + '?v=2&alt=jsonc', function(data)
|
var url = 'http://gdata.youtube.com/feeds/api/videos/' + matches[1] + '?v=2&alt=jsonc';
|
||||||
|
getJSON({ url: url }).success(function(data)
|
||||||
{
|
{
|
||||||
postDom.find('.file-name strong')
|
postDom.find('.file-name strong')
|
||||||
.text(data.data.title);
|
.text(data.data.title);
|
||||||
|
|
|
@ -67,7 +67,7 @@ $(function()
|
||||||
|
|
||||||
$('.comments.unit a.simple-action').data('callback', function()
|
$('.comments.unit a.simple-action').data('callback', function()
|
||||||
{
|
{
|
||||||
$.get(window.location.href, function(data)
|
$.get(window.location.href).success(function(data)
|
||||||
{
|
{
|
||||||
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
|
$('.comments-wrapper').replaceWith($(data).find('.comments-wrapper'));
|
||||||
$('body').trigger('dom-update');
|
$('body').trigger('dom-update');
|
||||||
|
@ -76,7 +76,7 @@ $(function()
|
||||||
|
|
||||||
$('#sidebar a.simple-action').data('callback', function()
|
$('#sidebar a.simple-action').data('callback', function()
|
||||||
{
|
{
|
||||||
$.get(window.location.href, function(data)
|
$.get(window.location.href).success(function(data)
|
||||||
{
|
{
|
||||||
$('#sidebar').replaceWith($(data).find('#sidebar'));
|
$('#sidebar').replaceWith($(data).find('#sidebar'));
|
||||||
$('body').trigger('dom-update');
|
$('body').trigger('dom-update');
|
||||||
|
@ -97,7 +97,7 @@ $(function()
|
||||||
formDom.addClass('inactive');
|
formDom.addClass('inactive');
|
||||||
formDom.find(':input').attr('readonly', true);
|
formDom.find(':input').attr('readonly', true);
|
||||||
|
|
||||||
var url = formDom.attr('action') + '?json';
|
var url = formDom.attr('action');
|
||||||
var fd = new FormData(formDom[0]);
|
var fd = new FormData(formDom[0]);
|
||||||
|
|
||||||
var ajaxData =
|
var ajaxData =
|
||||||
|
@ -112,7 +112,7 @@ $(function()
|
||||||
{
|
{
|
||||||
disableExitConfirmation();
|
disableExitConfirmation();
|
||||||
|
|
||||||
$.get(window.location.href, function(data)
|
$.get(window.location.href).success(function(data)
|
||||||
{
|
{
|
||||||
$('#sidebar').replaceWith($(data).find('#sidebar'));
|
$('#sidebar').replaceWith($(data).find('#sidebar'));
|
||||||
$('#edit-token').replaceWith($(data).find('#edit-token'));
|
$('#edit-token').replaceWith($(data).find('#edit-token'));
|
||||||
|
@ -132,7 +132,7 @@ $(function()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax(ajaxData);
|
postJSON(ajaxData);
|
||||||
});
|
});
|
||||||
|
|
||||||
Mousetrap.bind('a', function()
|
Mousetrap.bind('a', function()
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../src/core.php';
|
require_once __DIR__ . '/../src/core.php';
|
||||||
|
|
||||||
function usage()
|
Access::disablePrivilegeChecking();
|
||||||
{
|
|
||||||
echo 'Usage: ' . basename(__FILE__);
|
|
||||||
echo ' QUERY' . PHP_EOL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
array_shift($argv);
|
array_shift($argv);
|
||||||
if (empty($argv))
|
|
||||||
usage() and die;
|
|
||||||
|
|
||||||
$query = array_shift($argv);
|
$query = array_shift($argv);
|
||||||
$posts = Model_Post::getEntities($query, null, null);
|
$posts = PostSearchService::getEntities($query, null, null);
|
||||||
foreach ($posts as $post)
|
foreach ($posts as $post)
|
||||||
{
|
{
|
||||||
echo implode("\t",
|
echo implode("\t",
|
||||||
[
|
[
|
||||||
$post->id,
|
$post->getId(),
|
||||||
$post->name,
|
$post->getName(),
|
||||||
Model_Post::getFullPath($post->name),
|
$post->tryGetWorkingFullPath(),
|
||||||
$post->mimeType,
|
$post->getMimeType(),
|
||||||
]). PHP_EOL;
|
]). PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../src/core.php';
|
require_once __DIR__ . '/../src/core.php';
|
||||||
|
|
||||||
|
Access::disablePrivilegeChecking();
|
||||||
|
|
||||||
function usage()
|
function usage()
|
||||||
{
|
{
|
||||||
echo 'Usage: ' . basename(__FILE__);
|
echo 'Usage: ' . basename(__FILE__);
|
||||||
|
@ -33,7 +35,7 @@ switch ($action)
|
||||||
$func = function($name) use ($dir)
|
$func = function($name) use ($dir)
|
||||||
{
|
{
|
||||||
echo $name . PHP_EOL;
|
echo $name . PHP_EOL;
|
||||||
$srcPath = Model_Post::getFullPath($name);
|
$srcPath = PostModel::getFullPath($name);
|
||||||
$dstPath = $dir . DS . $name;
|
$dstPath = $dir . DS . $name;
|
||||||
rename($srcPath, $dstPath);
|
rename($srcPath, $dstPath);
|
||||||
};
|
};
|
||||||
|
@ -43,7 +45,7 @@ switch ($action)
|
||||||
$func = function($name)
|
$func = function($name)
|
||||||
{
|
{
|
||||||
echo $name . PHP_EOL;
|
echo $name . PHP_EOL;
|
||||||
$srcPath = Model_Post::getFullPath($name);
|
$srcPath = PostModel::getFullPath($name);
|
||||||
unlink($srcPath);
|
unlink($srcPath);
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
@ -53,13 +55,13 @@ switch ($action)
|
||||||
}
|
}
|
||||||
|
|
||||||
$names = [];
|
$names = [];
|
||||||
foreach (R::findAll('post') as $post)
|
foreach (PostSearchService::getEntities(null, null, null) as $post)
|
||||||
{
|
{
|
||||||
$names []= $post->name;
|
$names []= $post->getName();
|
||||||
}
|
}
|
||||||
$names = array_flip($names);
|
$names = array_flip($names);
|
||||||
|
|
||||||
$config = getConfig();
|
$config = Core::getConfig();
|
||||||
foreach (glob(TextHelper::absolutePath($config->main->filesPath) . DS . '*') as $name)
|
foreach (glob(TextHelper::absolutePath($config->main->filesPath) . DS . '*') as $name)
|
||||||
{
|
{
|
||||||
$name = basename($name);
|
$name = basename($name);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../src/core.php';
|
require_once __DIR__ . '/../src/core.php';
|
||||||
|
|
||||||
|
Access::disablePrivilegeChecking();
|
||||||
|
|
||||||
function usage()
|
function usage()
|
||||||
{
|
{
|
||||||
echo 'Usage: ' . basename(__FILE__);
|
echo 'Usage: ' . basename(__FILE__);
|
||||||
|
@ -14,10 +16,10 @@ if (empty($argv))
|
||||||
|
|
||||||
function printUser($user)
|
function printUser($user)
|
||||||
{
|
{
|
||||||
echo 'ID: ' . $user->id . PHP_EOL;
|
echo 'ID: ' . $user->getId() . PHP_EOL;
|
||||||
echo 'Name: ' . $user->name . PHP_EOL;
|
echo 'Name: ' . $user->getName() . PHP_EOL;
|
||||||
echo 'E-mail: ' . $user->email_unconfirmed . PHP_EOL;
|
echo 'E-mail: ' . $user->getUnconfirmedEmail() . PHP_EOL;
|
||||||
echo 'Date joined: ' . date('Y-m-d H:i:s', $user->join_date) . PHP_EOL;
|
echo 'Date joined: ' . date('Y-m-d H:i:s', $user->getJoinTime()) . PHP_EOL;
|
||||||
echo PHP_EOL;
|
echo PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +34,7 @@ switch ($action)
|
||||||
$func = function($user)
|
$func = function($user)
|
||||||
{
|
{
|
||||||
printUser($user);
|
printUser($user);
|
||||||
Model_User::remove($user);
|
UserModel::remove($user);
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -40,8 +42,13 @@ switch ($action)
|
||||||
die('Unknown action' . PHP_EOL);
|
die('Unknown action' . PHP_EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = R::find('user', 'email_confirmed IS NULL AND DATETIME(join_date) < DATETIME("now", "-21 days")');
|
$users = UserSearchService::getEntities(null, null, null);
|
||||||
foreach ($rows as $user)
|
foreach ($users as $user)
|
||||||
{
|
{
|
||||||
$func($user);
|
if (!$user->getConfirmedEmail()
|
||||||
|
and !$user->getLastLoginTime()
|
||||||
|
and ((time() - $user->getJoinTime()) > 21 * 24 * 60 * 60))
|
||||||
|
{
|
||||||
|
$func($user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
116
src/Access.php
116
src/Access.php
|
@ -2,101 +2,119 @@
|
||||||
class Access
|
class Access
|
||||||
{
|
{
|
||||||
private static $privileges = [];
|
private static $privileges = [];
|
||||||
|
private static $checkPrivileges = true;
|
||||||
|
|
||||||
public static function init()
|
public static function init()
|
||||||
{
|
{
|
||||||
self::$privileges = [];
|
self::$privileges = [];
|
||||||
foreach (getConfig()->privileges as $key => $minAccessRankName)
|
foreach (Core::getConfig()->privileges as $key => $minAccessRankName)
|
||||||
{
|
{
|
||||||
if (strpos($key, '.') === false)
|
if (strpos($key, '.') === false)
|
||||||
$key .= '.';
|
$key .= '.';
|
||||||
list ($privilegeName, $subPrivilegeName) = explode('.', $key);
|
list ($privilegeName, $subPrivilegeName) = explode('.', $key);
|
||||||
|
$minAccessRank = new AccessRank(TextHelper::resolveConstant($minAccessRankName, 'AccessRank'));
|
||||||
|
|
||||||
$privilegeName = TextCaseConverter::convert($privilegeName,
|
if (!in_array($privilegeName, Privilege::getAllConstants()))
|
||||||
TextCaseConverter::CAMEL_CASE,
|
throw new Exception('Invalid privilege name in config: ' . $privilegeName);
|
||||||
TextCaseConverter::SPINAL_CASE);
|
|
||||||
$subPrivilegeName = TextCaseConverter::convert($subPrivilegeName,
|
|
||||||
TextCaseConverter::CAMEL_CASE,
|
|
||||||
TextCaseConverter::SPINAL_CASE);
|
|
||||||
|
|
||||||
$key = rtrim($privilegeName . '.' . $subPrivilegeName, '.');
|
if (!isset(self::$privileges[$privilegeName]))
|
||||||
|
|
||||||
$minAccessRank = TextHelper::resolveConstant($minAccessRankName, 'AccessRank');
|
|
||||||
self::$privileges[$key] = $minAccessRank;
|
|
||||||
|
|
||||||
if (!isset(self::$privileges[$privilegeName]) or
|
|
||||||
self::$privileges[$privilegeName] > $minAccessRank)
|
|
||||||
{
|
{
|
||||||
self::$privileges[$privilegeName] = $minAccessRank;
|
self::$privileges[$privilegeName] = [];
|
||||||
|
self::$privileges[$privilegeName][null] = $minAccessRank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::$privileges[$privilegeName][$subPrivilegeName] = $minAccessRank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function check($privilege, $subPrivilege = null)
|
public static function check(Privilege $privilege, $user = null)
|
||||||
{
|
{
|
||||||
if (php_sapi_name() == 'cli')
|
if (!self::$checkPrivileges)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
$user = Auth::getCurrentUser();
|
if ($user === null)
|
||||||
$minAccessRank = AccessRank::Admin;
|
$user = Auth::getCurrentUser();
|
||||||
|
|
||||||
$key = TextCaseConverter::convert(Privilege::toString($privilege),
|
$minAccessRank = new AccessRank(AccessRank::Nobody);
|
||||||
TextCaseConverter::CAMEL_CASE,
|
|
||||||
TextCaseConverter::SPINAL_CASE);
|
|
||||||
|
|
||||||
if (isset(self::$privileges[$key]))
|
if (isset(self::$privileges[$privilege->primary][$privilege->secondary]))
|
||||||
{
|
$minAccessRank = self::$privileges[$privilege->primary][$privilege->secondary];
|
||||||
$minAccessRank = self::$privileges[$key];
|
|
||||||
}
|
|
||||||
if ($subPrivilege != null)
|
|
||||||
{
|
|
||||||
$key2 = $key . '.' . strtolower($subPrivilege);
|
|
||||||
if (isset(self::$privileges[$key2]))
|
|
||||||
{
|
|
||||||
$minAccessRank = self::$privileges[$key2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return intval($user->accessRank) >= $minAccessRank;
|
elseif (isset(self::$privileges[$privilege->primary][null]))
|
||||||
|
$minAccessRank = self::$privileges[$privilege->primary][null];
|
||||||
|
|
||||||
|
return $user->getAccessRank()->toInteger() >= $minAccessRank->toInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function checkEmailConfirmation($user = null)
|
||||||
|
{
|
||||||
|
if (!self::$checkPrivileges)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ($user === null)
|
||||||
|
$user = Auth::getCurrentUser();
|
||||||
|
|
||||||
|
if (!$user->getConfirmedEmail())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function assertAuthentication()
|
public static function assertAuthentication()
|
||||||
{
|
{
|
||||||
if (!Auth::isLoggedIn())
|
if (!Auth::isLoggedIn())
|
||||||
throw new SimpleException('Not logged in');
|
self::fail('Not logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function assert($privilege, $subPrivilege = null)
|
public static function assert(Privilege $privilege, $user = null)
|
||||||
{
|
{
|
||||||
if (!self::check($privilege, $subPrivilege))
|
if (!self::check($privilege, $user))
|
||||||
throw new SimpleException('Insufficient privileges');
|
self::fail('Insufficient privileges (' . $privilege->toDisplayString() . ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function assertEmailConfirmation()
|
public static function assertEmailConfirmation($user = null)
|
||||||
{
|
{
|
||||||
$user = Auth::getCurrentUser();
|
if (!self::checkEmailConfirmation($user))
|
||||||
if (!$user->emailConfirmed)
|
self::fail('Need e-mail address confirmation to continue');
|
||||||
throw new SimpleException('Need e-mail address confirmation to continue');
|
}
|
||||||
|
|
||||||
|
public static function fail($message)
|
||||||
|
{
|
||||||
|
throw new AccessException($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getIdentity($user)
|
public static function getIdentity($user)
|
||||||
{
|
{
|
||||||
if (!$user)
|
if (!$user)
|
||||||
return 'all';
|
return 'all';
|
||||||
return $user->id == Auth::getCurrentUser()->id ? 'own' : 'all';
|
return $user->getId() == Auth::getCurrentUser()->getId() ? 'own' : 'all';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAllowedSafety()
|
public static function getAllowedSafety()
|
||||||
{
|
{
|
||||||
if (php_sapi_name() == 'cli')
|
if (!self::$checkPrivileges)
|
||||||
return PostSafety::getAll();
|
return PostSafety::getAll();
|
||||||
|
|
||||||
return array_filter(PostSafety::getAll(), function($safety)
|
return array_filter(PostSafety::getAll(), function($safety)
|
||||||
{
|
{
|
||||||
return Access::check(Privilege::ListPosts, PostSafety::toString($safety))
|
return Access::check(new Privilege(Privilege::ListPosts, $safety->toString()))
|
||||||
and Auth::getCurrentUser()->hasEnabledSafety($safety);
|
and Auth::getCurrentUser()->getSettings()->hasEnabledSafety($safety);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Access::init();
|
public static function getAllDefinedSubPrivileges($privilege)
|
||||||
|
{
|
||||||
|
if (!isset(self::$privileges[$privilege]))
|
||||||
|
return null;
|
||||||
|
return self::$privileges[$privilege];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function disablePrivilegeChecking()
|
||||||
|
{
|
||||||
|
self::$checkPrivileges = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function enablePrivilegeChecking()
|
||||||
|
{
|
||||||
|
self::$checkPrivileges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
src/AccessException.php
Normal file
4
src/AccessException.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
class AccessException extends SimpleException
|
||||||
|
{
|
||||||
|
}
|
117
src/Api/Api.php
Normal file
117
src/Api/Api.php
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<?php
|
||||||
|
final class Api
|
||||||
|
{
|
||||||
|
public static function getUrl()
|
||||||
|
{
|
||||||
|
return \Chibi\Router::linkTo(['ApiController', 'runAction']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function run(IJob $job, $jobArgs)
|
||||||
|
{
|
||||||
|
$user = Auth::getCurrentUser();
|
||||||
|
|
||||||
|
return \Chibi\Database::transaction(function() use ($job, $jobArgs)
|
||||||
|
{
|
||||||
|
$job->setArguments($jobArgs);
|
||||||
|
|
||||||
|
self::checkArguments($job);
|
||||||
|
|
||||||
|
$job->prepare();
|
||||||
|
|
||||||
|
self::checkPrivileges($job);
|
||||||
|
|
||||||
|
return $job->execute();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function runMultiple($jobs)
|
||||||
|
{
|
||||||
|
$statuses = [];
|
||||||
|
\Chibi\Database::transaction(function() use ($jobs, &$statuses)
|
||||||
|
{
|
||||||
|
foreach ($jobs as $jobItem)
|
||||||
|
{
|
||||||
|
list ($job, $jobArgs) = $jobItem;
|
||||||
|
$statuses []= self::run($job, $jobArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function checkArguments(IJob $job)
|
||||||
|
{
|
||||||
|
self::runArgumentCheck($job, $job->getRequiredArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function checkPrivileges(IJob $job)
|
||||||
|
{
|
||||||
|
if ($job->isAuthenticationRequired())
|
||||||
|
Access::assertAuthentication();
|
||||||
|
|
||||||
|
if ($job->isConfirmedEmailRequired())
|
||||||
|
Access::assertEmailConfirmation();
|
||||||
|
|
||||||
|
$mainPrivilege = $job->getRequiredMainPrivilege();
|
||||||
|
$subPrivileges = $job->getRequiredSubPrivileges();
|
||||||
|
if (!is_array($subPrivileges))
|
||||||
|
$subPrivileges = [$subPrivileges];
|
||||||
|
|
||||||
|
if ($mainPrivilege !== null)
|
||||||
|
{
|
||||||
|
Access::assert(new Privilege($mainPrivilege));
|
||||||
|
foreach ($subPrivileges as $subPrivilege)
|
||||||
|
Access::assert(new Privilege($mainPrivilege, $subPrivilege));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function runArgumentCheck(IJob $job, $item)
|
||||||
|
{
|
||||||
|
if (is_array($item))
|
||||||
|
throw new Exception('Argument definition cannot be an array.');
|
||||||
|
elseif ($item instanceof JobArgsNestedStruct)
|
||||||
|
{
|
||||||
|
if ($item instanceof JobArgsAlternative)
|
||||||
|
{
|
||||||
|
$success = false;
|
||||||
|
foreach ($item->args as $subItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
self::runArgumentCheck($job, $subItem);
|
||||||
|
$success = true;
|
||||||
|
}
|
||||||
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$success)
|
||||||
|
throw new ApiJobUnsatisfiedException($job);
|
||||||
|
}
|
||||||
|
elseif ($item instanceof JobArgsConjunction)
|
||||||
|
{
|
||||||
|
foreach ($item->args as $subItem)
|
||||||
|
!self::runArgumentCheck($job, $subItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($item === null)
|
||||||
|
return;
|
||||||
|
elseif (!$job->hasArgument($item))
|
||||||
|
throw new ApiJobUnsatisfiedException($job, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllJobClassNames()
|
||||||
|
{
|
||||||
|
$pathToJobs = Core::getConfig()->rootDir . DS . 'src' . DS . 'Api' . DS . 'Jobs';
|
||||||
|
$directory = new RecursiveDirectoryIterator($pathToJobs);
|
||||||
|
$iterator = new RecursiveIteratorIterator($directory);
|
||||||
|
$regex = new RegexIterator($iterator, '/^.+Job\.php$/i');
|
||||||
|
$files = array_keys(iterator_to_array($regex));
|
||||||
|
|
||||||
|
\Chibi\Util\Reflection::loadClasses($files);
|
||||||
|
return array_filter(get_declared_classes(), function($x)
|
||||||
|
{
|
||||||
|
$class = new ReflectionClass($x);
|
||||||
|
return !$class->isAbstract() and $class->isSubClassOf('AbstractJob');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
30
src/Api/ApiFileInput.php
Normal file
30
src/Api/ApiFileInput.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Used for serializing files passed in POST requests to job arguments
|
||||||
|
*/
|
||||||
|
class ApiFileInput
|
||||||
|
{
|
||||||
|
public $filePath;
|
||||||
|
public $fileName;
|
||||||
|
public $originalPath;
|
||||||
|
|
||||||
|
public function __construct($filePath, $fileName)
|
||||||
|
{
|
||||||
|
$tmpPath = tempnam(sys_get_temp_dir(), 'upload') . '.dat';
|
||||||
|
$this->originalPath = $tmpPath;
|
||||||
|
|
||||||
|
//php "security" bullshit
|
||||||
|
if (is_uploaded_file($filePath))
|
||||||
|
move_uploaded_file($filePath, $tmpPath);
|
||||||
|
else
|
||||||
|
copy($filePath, $tmpPath);
|
||||||
|
|
||||||
|
$this->filePath = $tmpPath;
|
||||||
|
$this->fileName = $fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
TransferHelper::remove($this->originalPath);
|
||||||
|
}
|
||||||
|
}
|
30
src/Api/ApiFileOutput.php
Normal file
30
src/Api/ApiFileOutput.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Used for serializing files output from jobs
|
||||||
|
*/
|
||||||
|
class ApiFileOutput implements ISerializable
|
||||||
|
{
|
||||||
|
public $fileContent;
|
||||||
|
public $fileName;
|
||||||
|
public $lastModified;
|
||||||
|
public $mimeType;
|
||||||
|
|
||||||
|
public function __construct($filePath, $fileName)
|
||||||
|
{
|
||||||
|
$this->fileContent = file_get_contents($filePath);
|
||||||
|
$this->fileName = $fileName;
|
||||||
|
$this->lastModified = filemtime($filePath);
|
||||||
|
$this->mimeType = mime_content_type($filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serializeToArray()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
'name ' => $this->fileName,
|
||||||
|
'modification-time' => $this->lastModified,
|
||||||
|
'mime-type' => $this->mimeType,
|
||||||
|
'content' => base64_encode(gzencode($this->fileContent)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
10
src/Api/ApiJobUnsatisfiedException.php
Normal file
10
src/Api/ApiJobUnsatisfiedException.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
class ApiJobUnsatisfiedException extends SimpleException
|
||||||
|
{
|
||||||
|
public function __construct(IJob $job, $arg = null)
|
||||||
|
{
|
||||||
|
parent::__construct('%s cannot be run due to unsatisfied execution conditions (%s).',
|
||||||
|
get_class($job),
|
||||||
|
$arg);
|
||||||
|
}
|
||||||
|
}
|
8
src/Api/ApiMissingArgumentException.php
Normal file
8
src/Api/ApiMissingArgumentException.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
class ApiMissingArgumentException extends SimpleException
|
||||||
|
{
|
||||||
|
public function __construct($argumentName)
|
||||||
|
{
|
||||||
|
parent::__construct('Expected argument "' . $argumentName . '" was not specified');
|
||||||
|
}
|
||||||
|
}
|
41
src/Api/Common/EntityRetrievers/CommentRetriever.php
Normal file
41
src/Api/Common/EntityRetrievers/CommentRetriever.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
class CommentRetriever implements IEntityRetriever
|
||||||
|
{
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
public function __construct(IJob $job)
|
||||||
|
{
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJob()
|
||||||
|
{
|
||||||
|
return $this->job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tryRetrieve()
|
||||||
|
{
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_COMMENT_ENTITY))
|
||||||
|
return $this->job->getArgument(JobArgs::ARG_COMMENT_ENTITY);
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_COMMENT_ID))
|
||||||
|
return CommentModel::getById($this->job->getArgument(JobArgs::ARG_COMMENT_ID));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieve()
|
||||||
|
{
|
||||||
|
$comment = $this->tryRetrieve();
|
||||||
|
if ($comment)
|
||||||
|
return $comment;
|
||||||
|
throw new ApiJobUnsatisfiedException($this->job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_COMMENT_ID,
|
||||||
|
JobArgs::ARG_COMMENT_ENTITY);
|
||||||
|
}
|
||||||
|
}
|
9
src/Api/Common/EntityRetrievers/IEntityRetriever.php
Normal file
9
src/Api/Common/EntityRetrievers/IEntityRetriever.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
interface IEntityRetriever
|
||||||
|
{
|
||||||
|
public function __construct(IJob $job);
|
||||||
|
public function getJob();
|
||||||
|
public function tryRetrieve();
|
||||||
|
public function retrieve();
|
||||||
|
public function getRequiredArguments();
|
||||||
|
}
|
45
src/Api/Common/EntityRetrievers/PostRetriever.php
Normal file
45
src/Api/Common/EntityRetrievers/PostRetriever.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
class PostRetriever implements IEntityRetriever
|
||||||
|
{
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
public function __construct(IJob $job)
|
||||||
|
{
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJob()
|
||||||
|
{
|
||||||
|
return $this->job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tryRetrieve()
|
||||||
|
{
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_POST_ENTITY))
|
||||||
|
return $this->job->getArgument(JobArgs::ARG_POST_ENTITY);
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_POST_ID))
|
||||||
|
return PostModel::getById($this->job->getArgument(JobArgs::ARG_POST_ID));
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_POST_NAME))
|
||||||
|
return PostModel::getByName($this->job->getArgument(JobArgs::ARG_POST_NAME));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieve()
|
||||||
|
{
|
||||||
|
$post = $this->tryRetrieve();
|
||||||
|
if ($post)
|
||||||
|
return $post;
|
||||||
|
throw new ApiJobUnsatisfiedException($this->job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_POST_ID,
|
||||||
|
JobArgs::ARG_POST_NAME,
|
||||||
|
JobArgs::ARG_POST_ENTITY);
|
||||||
|
}
|
||||||
|
}
|
41
src/Api/Common/EntityRetrievers/SafePostRetriever.php
Normal file
41
src/Api/Common/EntityRetrievers/SafePostRetriever.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
class SafePostRetriever implements IEntityRetriever
|
||||||
|
{
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
public function __construct(IJob $job)
|
||||||
|
{
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJob()
|
||||||
|
{
|
||||||
|
return $this->job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tryRetrieve()
|
||||||
|
{
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_POST_ENTITY))
|
||||||
|
return $this->job->getArgument(JobArgs::ARG_POST_ENTITY);
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_POST_NAME))
|
||||||
|
return PostModel::getByName($this->job->getArgument(JobArgs::ARG_POST_NAME));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieve()
|
||||||
|
{
|
||||||
|
$post = $this->tryRetrieve();
|
||||||
|
if ($post)
|
||||||
|
return $post;
|
||||||
|
throw new ApiJobUnsatisfiedException($this->job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_POST_NAME,
|
||||||
|
JobArgs::ARG_POST_ENTITY);
|
||||||
|
}
|
||||||
|
}
|
45
src/Api/Common/EntityRetrievers/UserRetriever.php
Normal file
45
src/Api/Common/EntityRetrievers/UserRetriever.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
class UserRetriever implements IEntityRetriever
|
||||||
|
{
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
public function __construct(IJob $job)
|
||||||
|
{
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJob()
|
||||||
|
{
|
||||||
|
return $this->job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tryRetrieve()
|
||||||
|
{
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_USER_ENTITY))
|
||||||
|
return $this->job->getArgument(JobArgs::ARG_USER_ENTITY);
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_USER_EMAIL))
|
||||||
|
return UserModel::getByEmail($this->job->getArgument(JobArgs::ARG_USER_EMAIL));
|
||||||
|
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_USER_NAME))
|
||||||
|
return UserModel::getByName($this->job->getArgument(JobArgs::ARG_USER_NAME));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function retrieve()
|
||||||
|
{
|
||||||
|
$user = $this->tryRetrieve();
|
||||||
|
if ($user)
|
||||||
|
return $user;
|
||||||
|
throw new ApiJobUnsatisfiedException($this->job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_USER_NAME,
|
||||||
|
JobArgs::ARG_USER_EMAIL,
|
||||||
|
JobArgs::ARG_USER_ENTITY);
|
||||||
|
}
|
||||||
|
}
|
50
src/Api/Common/JobPager.php
Normal file
50
src/Api/Common/JobPager.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
class JobPager
|
||||||
|
{
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
public function __construct(IJob $job)
|
||||||
|
{
|
||||||
|
$this->pageSize = 20;
|
||||||
|
$this->job = $job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPageSize($newPageSize)
|
||||||
|
{
|
||||||
|
$this->pageSize = $newPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPageSize()
|
||||||
|
{
|
||||||
|
return $this->pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPageNumber()
|
||||||
|
{
|
||||||
|
if ($this->job->hasArgument(JobArgs::ARG_PAGE_NUMBER))
|
||||||
|
return (int) $this->job->getArgument(JobArgs::ARG_PAGE_NUMBER);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Optional(JobArgs::ARG_PAGE_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serialize($entities, $totalEntityCount)
|
||||||
|
{
|
||||||
|
$pageSize = $this->getPageSize();
|
||||||
|
$pageNumber = $this->getPageNumber();
|
||||||
|
|
||||||
|
$pageCount = (int) ceil($totalEntityCount / $pageSize);
|
||||||
|
$pageNumber = $this->getPageNumber();
|
||||||
|
$pageNumber = min($pageCount, $pageNumber);
|
||||||
|
|
||||||
|
$ret = new StdClass;
|
||||||
|
$ret->entities = $entities;
|
||||||
|
$ret->entityCount = (int) $totalEntityCount;
|
||||||
|
$ret->page = (int) $pageNumber;
|
||||||
|
$ret->pageCount = (int) $pageCount;
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
72
src/Api/JobArgs/JobArgs.php
Normal file
72
src/Api/JobArgs/JobArgs.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
class JobArgs
|
||||||
|
{
|
||||||
|
const ARG_ANONYMOUS = 'anonymous';
|
||||||
|
|
||||||
|
const ARG_PAGE_NUMBER = 'page-number';
|
||||||
|
const ARG_QUERY = 'query';
|
||||||
|
const ARG_TOKEN = 'token';
|
||||||
|
|
||||||
|
const ARG_USER_ENTITY = 'user';
|
||||||
|
#const ARG_USER_ID = 'user-id';
|
||||||
|
const ARG_USER_NAME = 'user-name';
|
||||||
|
const ARG_USER_EMAIL = 'user-email';
|
||||||
|
|
||||||
|
const ARG_POST_ENTITY = 'post';
|
||||||
|
const ARG_POST_ID = 'post-id';
|
||||||
|
const ARG_POST_NAME = 'post-name';
|
||||||
|
|
||||||
|
const ARG_TAG_NAME = 'tag-name';
|
||||||
|
const ARG_TAG_NAMES = 'tag-names';
|
||||||
|
|
||||||
|
const ARG_COMMENT_ENTITY = 'comment';
|
||||||
|
const ARG_COMMENT_ID = 'comment-id';
|
||||||
|
|
||||||
|
const ARG_LOG_ID = 'log-id';
|
||||||
|
|
||||||
|
const ARG_NEW_TEXT = 'new-text';
|
||||||
|
const ARG_NEW_STATE = 'new-state';
|
||||||
|
|
||||||
|
const ARG_NEW_POST_CONTENT = 'new-post-content';
|
||||||
|
const ARG_NEW_POST_CONTENT_URL = 'new-post-content-url';
|
||||||
|
const ARG_NEW_RELATED_POST_IDS = 'new-related-post-ids';
|
||||||
|
const ARG_NEW_SAFETY = 'new-safety';
|
||||||
|
const ARG_NEW_SOURCE = 'new-source';
|
||||||
|
const ARG_NEW_THUMB_CONTENT = 'new-thumb-content';
|
||||||
|
const ARG_NEW_TAG_NAMES = 'new-tag-names';
|
||||||
|
|
||||||
|
const ARG_NEW_ACCESS_RANK = 'new-access-rank';
|
||||||
|
const ARG_NEW_EMAIL = 'new-email';
|
||||||
|
const ARG_NEW_USER_NAME = 'new-user-name';
|
||||||
|
const ARG_NEW_PASSWORD = 'new-password';
|
||||||
|
const ARG_NEW_SETTINGS = 'new-settings';
|
||||||
|
|
||||||
|
const ARG_NEW_POST_SCORE = 'new-post-score';
|
||||||
|
const ARG_SOURCE_TAG_NAME = 'source-tag-name';
|
||||||
|
const ARG_TARGET_TAG_NAME = 'target-tag-name';
|
||||||
|
|
||||||
|
public static function Alternative()
|
||||||
|
{
|
||||||
|
return JobArgsAlternative::factory(func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Conjunction()
|
||||||
|
{
|
||||||
|
return JobArgsConjunction::factory(func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Optional()
|
||||||
|
{
|
||||||
|
return JobArgsOptional::factory(func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInternalArguments()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
self::ARG_POST_ENTITY,
|
||||||
|
self::ARG_USER_ENTITY,
|
||||||
|
self::ARG_COMMENT_ENTITY
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
25
src/Api/JobArgs/JobArgsAlternative.php
Normal file
25
src/Api/JobArgs/JobArgsAlternative.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
class JobArgsAlternative extends JobArgsNestedStruct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* simplifies the structure as much as possible
|
||||||
|
* and returns new class or existing args.
|
||||||
|
*/
|
||||||
|
public static function factory(array $args)
|
||||||
|
{
|
||||||
|
$finalArgs = [];
|
||||||
|
|
||||||
|
foreach ($args as $arg)
|
||||||
|
{
|
||||||
|
if ($arg instanceof self)
|
||||||
|
$finalArgs = array_merge($finalArgs, $arg->args);
|
||||||
|
elseif ($arg !== null)
|
||||||
|
$finalArgs []= $arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($finalArgs) == 1)
|
||||||
|
return $finalArgs[0];
|
||||||
|
else
|
||||||
|
return new self($finalArgs);
|
||||||
|
}
|
||||||
|
}
|
25
src/Api/JobArgs/JobArgsConjunction.php
Normal file
25
src/Api/JobArgs/JobArgsConjunction.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
class JobArgsConjunction extends JobArgsNestedStruct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Simplifies the structure as much as possible
|
||||||
|
* and returns new class or existing args.
|
||||||
|
*/
|
||||||
|
public static function factory(array $args)
|
||||||
|
{
|
||||||
|
$finalArgs = [];
|
||||||
|
|
||||||
|
foreach ($args as $arg)
|
||||||
|
{
|
||||||
|
if ($arg instanceof self)
|
||||||
|
$finalArgs = array_merge($finalArgs, $arg->args);
|
||||||
|
elseif ($arg !== null)
|
||||||
|
$finalArgs []= $arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($finalArgs) == 1)
|
||||||
|
return $finalArgs[0];
|
||||||
|
else
|
||||||
|
return new self($finalArgs);
|
||||||
|
}
|
||||||
|
}
|
19
src/Api/JobArgs/JobArgsNestedStruct.php
Normal file
19
src/Api/JobArgs/JobArgsNestedStruct.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
class JobArgsNestedStruct
|
||||||
|
{
|
||||||
|
public $args;
|
||||||
|
|
||||||
|
protected function __construct(array $args)
|
||||||
|
{
|
||||||
|
usort($args, function($arg1, $arg2)
|
||||||
|
{
|
||||||
|
return strnatcasecmp(serialize($arg1), serialize($arg2));
|
||||||
|
});
|
||||||
|
$this->args = $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function factory(array $args)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
20
src/Api/JobArgs/JobArgsOptional.php
Normal file
20
src/Api/JobArgs/JobArgsOptional.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
class JobArgsOptional extends JobArgsNestedStruct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Simplifies the structure as much as possible
|
||||||
|
* and returns new class or existing args.
|
||||||
|
*/
|
||||||
|
public static function factory(array $args)
|
||||||
|
{
|
||||||
|
$args = array_filter($args, function($arg)
|
||||||
|
{
|
||||||
|
return $arg !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (count($args) == 0)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return new self($args);
|
||||||
|
}
|
||||||
|
}
|
83
src/Api/Jobs/AbstractJob.php
Normal file
83
src/Api/Jobs/AbstractJob.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
abstract class AbstractJob implements IJob
|
||||||
|
{
|
||||||
|
const CONTEXT_NORMAL = 1;
|
||||||
|
const CONTEXT_BATCH_EDIT = 2;
|
||||||
|
const CONTEXT_BATCH_ADD = 3;
|
||||||
|
|
||||||
|
protected $arguments = [];
|
||||||
|
protected $context = self::CONTEXT_NORMAL;
|
||||||
|
protected $subJobs;
|
||||||
|
|
||||||
|
public function prepare()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract function execute();
|
||||||
|
|
||||||
|
public abstract function getRequiredArguments();
|
||||||
|
|
||||||
|
public function isAvailableToPublic()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
$name = get_called_class();
|
||||||
|
$name = str_replace('Job', '', $name);
|
||||||
|
$name = TextCaseConverter::convert(
|
||||||
|
$name,
|
||||||
|
TextCaseConverter::UPPER_CAMEL_CASE,
|
||||||
|
TextCaseConverter::SPINAL_CASE);
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubJob(IJob $subJob)
|
||||||
|
{
|
||||||
|
$this->subJobs []= $subJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubJobs()
|
||||||
|
{
|
||||||
|
return $this->subJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContext()
|
||||||
|
{
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContext($context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArgument($key)
|
||||||
|
{
|
||||||
|
if (!$this->hasArgument($key))
|
||||||
|
throw new ApiMissingArgumentException($key);
|
||||||
|
|
||||||
|
return $this->arguments[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArguments()
|
||||||
|
{
|
||||||
|
return $this->arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasArgument($key)
|
||||||
|
{
|
||||||
|
return isset($this->arguments[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setArgument($key, $value)
|
||||||
|
{
|
||||||
|
$this->arguments[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setArguments(array $arguments)
|
||||||
|
{
|
||||||
|
$this->arguments = $arguments;
|
||||||
|
}
|
||||||
|
}
|
57
src/Api/Jobs/CommentJobs/AddCommentJob.php
Normal file
57
src/Api/Jobs/CommentJobs/AddCommentJob.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
class AddCommentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$user = Auth::getCurrentUser();
|
||||||
|
$text = $this->getArgument(JobArgs::ARG_NEW_TEXT);
|
||||||
|
|
||||||
|
$comment = CommentModel::spawn();
|
||||||
|
$comment->setCommenter($user);
|
||||||
|
$comment->setPost($post);
|
||||||
|
$comment->setCreationTime(time());
|
||||||
|
$comment->setText($text);
|
||||||
|
|
||||||
|
CommentModel::save($comment);
|
||||||
|
Logger::log('{user} commented on {post}', [
|
||||||
|
'user' => TextHelper::reprUser($user),
|
||||||
|
'post' => TextHelper::reprPost($comment->getPost())]);
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::AddComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return Core::getConfig()->registration->needEmailForCommenting;
|
||||||
|
}
|
||||||
|
}
|
47
src/Api/Jobs/CommentJobs/DeleteCommentJob.php
Normal file
47
src/Api/Jobs/CommentJobs/DeleteCommentJob.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
class DeleteCommentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $commentRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->commentRetriever = new CommentRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$comment = $this->commentRetriever->retrieve();
|
||||||
|
$post = $comment->getPost();
|
||||||
|
|
||||||
|
CommentModel::remove($comment);
|
||||||
|
|
||||||
|
Logger::log('{user} removed comment from {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->commentRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::DeleteComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->commentRetriever->retrieve()->getCommenter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
52
src/Api/Jobs/CommentJobs/EditCommentJob.php
Normal file
52
src/Api/Jobs/CommentJobs/EditCommentJob.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
class EditCommentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $commentRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->commentRetriever = new CommentRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$comment = $this->commentRetriever->retrieve();
|
||||||
|
|
||||||
|
$comment->setCreationTime(time());
|
||||||
|
$comment->setText($this->getArgument(JobArgs::ARG_NEW_TEXT));
|
||||||
|
|
||||||
|
CommentModel::save($comment);
|
||||||
|
Logger::log('{user} edited comment in {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($comment->getPost())]);
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->commentRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->commentRetriever->retrieve()->getCommenter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
60
src/Api/Jobs/CommentJobs/ListCommentsJob.php
Normal file
60
src/Api/Jobs/CommentJobs/ListCommentsJob.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
class ListCommentsJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->comments->commentsPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$query = 'comment_min:1 order:comment_date,desc';
|
||||||
|
|
||||||
|
$posts = PostSearchService::getEntities($query, $pageSize, $page);
|
||||||
|
$postCount = PostSearchService::getEntityCount($query);
|
||||||
|
|
||||||
|
PostModel::preloadTags($posts);
|
||||||
|
PostModel::preloadComments($posts);
|
||||||
|
$comments = [];
|
||||||
|
foreach ($posts as $post)
|
||||||
|
$comments = array_merge($comments, $post->getComments());
|
||||||
|
CommentModel::preloadCommenters($comments);
|
||||||
|
|
||||||
|
return $this->pager->serialize($posts, $postCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->pager->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
63
src/Api/Jobs/CommentJobs/PreviewCommentJob.php
Normal file
63
src/Api/Jobs/CommentJobs/PreviewCommentJob.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
class PreviewCommentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $commentRetriever;
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->commentRetriever = new CommentRetriever($this);
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = Auth::getCurrentUser();
|
||||||
|
$text = $this->getArgument(JobArgs::ARG_NEW_TEXT);
|
||||||
|
|
||||||
|
$comment = $this->commentRetriever->tryRetrieve();
|
||||||
|
if (!$comment)
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$comment = CommentModel::spawn();
|
||||||
|
$comment->setPost($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment->setCommenter($user);
|
||||||
|
$comment->setCreationTime(time());
|
||||||
|
$comment->setText($text);
|
||||||
|
|
||||||
|
$comment->validate();
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
JobArgs::ARG_NEW_TEXT,
|
||||||
|
JobArgs::Alternative(
|
||||||
|
$this->commentRetriever->getRequiredArguments(),
|
||||||
|
$this->postRetriever->getRequiredArguments()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::AddComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return Core::getConfig()->registration->needEmailForCommenting;
|
||||||
|
}
|
||||||
|
}
|
34
src/Api/Jobs/GetPropertyJob.php
Normal file
34
src/Api/Jobs/GetPropertyJob.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
class GetPropertyJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
return PropertyModel::get($this->getArgument(JobArgs::ARG_QUERY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::ARG_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
src/Api/Jobs/IJob.php
Normal file
19
src/Api/Jobs/IJob.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
interface IJob
|
||||||
|
{
|
||||||
|
public function prepare();
|
||||||
|
public function execute();
|
||||||
|
|
||||||
|
public function getRequiredArguments();
|
||||||
|
public function getRequiredMainPrivilege();
|
||||||
|
public function getRequiredSubPrivileges();
|
||||||
|
public function isAuthenticationRequired();
|
||||||
|
public function isConfirmedEmailRequired();
|
||||||
|
public function isAvailableToPublic();
|
||||||
|
|
||||||
|
public function getArgument($key);
|
||||||
|
public function getArguments();
|
||||||
|
public function hasArgument($key);
|
||||||
|
public function setArgument($key, $value);
|
||||||
|
public function setArguments(array $arguments);
|
||||||
|
}
|
5
src/Api/Jobs/IPagedJob.php
Normal file
5
src/Api/Jobs/IPagedJob.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
interface IPagedJob
|
||||||
|
{
|
||||||
|
public function getPager();
|
||||||
|
}
|
80
src/Api/Jobs/LogJobs/GetLogJob.php
Normal file
80
src/Api/Jobs/LogJobs/GetLogJob.php
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
class GetLogJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->browsing->logsPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$name = $this->getArgument(JobArgs::ARG_LOG_ID);
|
||||||
|
$query = $this->hasArgument(JobArgs::ARG_QUERY)
|
||||||
|
? $this->getArgument(JobArgs::ARG_QUERY)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
//parse input
|
||||||
|
$page = max(1, intval($page));
|
||||||
|
$name = str_replace(['/', '\\'], '', $name); //paranoia mode
|
||||||
|
$path = TextHelper::absolutePath(dirname(Core::getConfig()->main->logsPath) . DS . $name);
|
||||||
|
if (!file_exists($path))
|
||||||
|
throw new SimpleNotFoundException('Specified log doesn\'t exist');
|
||||||
|
|
||||||
|
//load lines
|
||||||
|
$lines = file_get_contents($path);
|
||||||
|
$lines = trim($lines);
|
||||||
|
$lines = explode(PHP_EOL, str_replace(["\r", "\n"], PHP_EOL, $lines));
|
||||||
|
$lines = array_reverse($lines);
|
||||||
|
|
||||||
|
if (!empty($query))
|
||||||
|
{
|
||||||
|
$lines = array_filter($lines, function($line) use ($query)
|
||||||
|
{
|
||||||
|
return stripos($line, $query) !== false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineCount = count($lines);
|
||||||
|
$lines = array_slice($lines, ($page - 1) * $pageSize, $pageSize);
|
||||||
|
|
||||||
|
return $this->pager->serialize($lines, $lineCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->pager->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_LOG_ID,
|
||||||
|
JobArgs::Optional(JobArgs::ARG_QUERY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ViewLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
44
src/Api/Jobs/LogJobs/ListLogsJob.php
Normal file
44
src/Api/Jobs/LogJobs/ListLogsJob.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
class ListLogsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$path = TextHelper::absolutePath(Core::getConfig()->main->logsPath);
|
||||||
|
|
||||||
|
$logs = [];
|
||||||
|
foreach (glob(dirname($path) . DS . '*.log') as $log)
|
||||||
|
$logs []= basename($log);
|
||||||
|
|
||||||
|
usort($logs, function($a, $b)
|
||||||
|
{
|
||||||
|
return strnatcasecmp($b, $a); //reverse natcasesort
|
||||||
|
});
|
||||||
|
|
||||||
|
return $logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
88
src/Api/Jobs/PostJobs/AddPostJob.php
Normal file
88
src/Api/Jobs/PostJobs/AddPostJob.php
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
class AddPostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addSubJob(new EditPostSafetyJob());
|
||||||
|
$this->addSubJob(new EditPostTagsJob());
|
||||||
|
$this->addSubJob(new EditPostSourceJob());
|
||||||
|
$this->addSubJob(new EditPostRelationsJob());
|
||||||
|
$this->addSubJob(new EditPostContentJob());
|
||||||
|
$this->addSubJob(new EditPostThumbJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = PostModel::spawn();
|
||||||
|
|
||||||
|
$anonymous = false;
|
||||||
|
if ($this->hasArgument(JobArgs::ARG_ANONYMOUS))
|
||||||
|
$anonymous = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_ANONYMOUS));
|
||||||
|
|
||||||
|
if (Auth::isLoggedIn() and !$anonymous)
|
||||||
|
$post->setUploader(Auth::getCurrentUser());
|
||||||
|
|
||||||
|
PostModel::forgeId($post);
|
||||||
|
|
||||||
|
$arguments = $this->getArguments();
|
||||||
|
$arguments[JobArgs::ARG_POST_ENTITY] = $post;
|
||||||
|
|
||||||
|
Logger::bufferChanges();
|
||||||
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
|
{
|
||||||
|
$subJob->setContext(AbstractJob::CONTEXT_BATCH_ADD);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Api::run($subJob, $arguments);
|
||||||
|
}
|
||||||
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Logger::discardBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//save the post to db if everything went okay
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
Logger::log('{user} added {post} (tags: {tags}, safety: {safety}, source: {source})', [
|
||||||
|
'user' => ($anonymous and !Core::getConfig()->misc->logAnonymousUploads)
|
||||||
|
? TextHelper::reprUser(UserModel::getAnonymousName())
|
||||||
|
: TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'tags' => TextHelper::reprTags($post->getTags()),
|
||||||
|
'safety' => $post->getSafety()->toString(),
|
||||||
|
'source' => $post->getSource()]);
|
||||||
|
|
||||||
|
Logger::flush();
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Optional(JobArgs::ARG_ANONYMOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::AddPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return Core::getConfig()->registration->needEmailForUploading;
|
||||||
|
}
|
||||||
|
}
|
46
src/Api/Jobs/PostJobs/DeletePostJob.php
Normal file
46
src/Api/Jobs/PostJobs/DeletePostJob.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
class DeletePostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
PostModel::remove($post);
|
||||||
|
|
||||||
|
Logger::log('{user} deleted {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->postRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::DeletePost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
66
src/Api/Jobs/PostJobs/EditPostContentJob.php
Normal file
66
src/Api/Jobs/PostJobs/EditPostContentJob.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
class EditPostContentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
if ($this->hasArgument(JobArgs::ARG_NEW_POST_CONTENT_URL))
|
||||||
|
{
|
||||||
|
$url = $this->getArgument(JobArgs::ARG_NEW_POST_CONTENT_URL);
|
||||||
|
$post->setContentFromUrl($url);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$file = $this->getArgument(JobArgs::ARG_NEW_POST_CONTENT);
|
||||||
|
$post->setContentFromPath($file->filePath, $file->fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
Logger::log('{user} changed contents of {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_NEW_POST_CONTENT,
|
||||||
|
JobArgs::ARG_NEW_POST_CONTENT_URL));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostContent
|
||||||
|
: Privilege::EditPostContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
68
src/Api/Jobs/PostJobs/EditPostJob.php
Normal file
68
src/Api/Jobs/PostJobs/EditPostJob.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
class EditPostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
$this->addSubJob(new EditPostSafetyJob());
|
||||||
|
$this->addSubJob(new EditPostTagsJob());
|
||||||
|
$this->addSubJob(new EditPostSourceJob());
|
||||||
|
$this->addSubJob(new EditPostRelationsJob());
|
||||||
|
$this->addSubJob(new EditPostContentJob());
|
||||||
|
$this->addSubJob(new EditPostThumbJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
$arguments = $this->getArguments();
|
||||||
|
$arguments[JobArgs::ARG_POST_ENTITY] = $post;
|
||||||
|
|
||||||
|
Logger::bufferChanges();
|
||||||
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
|
{
|
||||||
|
$subJob->setContext(self::CONTEXT_BATCH_EDIT);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Api::run($subJob, $arguments);
|
||||||
|
}
|
||||||
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PostModel::save($post);
|
||||||
|
Logger::flush();
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->postRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
75
src/Api/Jobs/PostJobs/EditPostRelationsJob.php
Normal file
75
src/Api/Jobs/PostJobs/EditPostRelationsJob.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
class EditPostRelationsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$relatedPostIds = $this->getArgument(JobArgs::ARG_NEW_RELATED_POST_IDS);
|
||||||
|
|
||||||
|
if (!is_array($relatedPostIds))
|
||||||
|
throw new SimpleException('Expected array');
|
||||||
|
|
||||||
|
$relatedPosts = PostModel::getAllByIds($relatedPostIds);
|
||||||
|
|
||||||
|
$oldRelatedIds = array_map(function($post) { return $post->getId(); }, $post->getRelations());
|
||||||
|
$post->setRelations($relatedPosts);
|
||||||
|
$newRelatedIds = array_map(function($post) { return $post->getId(); }, $post->getRelations());
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
foreach (array_diff($oldRelatedIds, $newRelatedIds) as $post2id)
|
||||||
|
{
|
||||||
|
Logger::log('{user} removed relation between {post} and {post2}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'post2' => TextHelper::reprPost($post2id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_diff($newRelatedIds, $oldRelatedIds) as $post2id)
|
||||||
|
{
|
||||||
|
Logger::log('{user} added relation between {post} and {post2}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'post2' => TextHelper::reprPost($post2id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_RELATED_POST_IDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostRelations
|
||||||
|
: Privilege::EditPostRelations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
61
src/Api/Jobs/PostJobs/EditPostSafetyJob.php
Normal file
61
src/Api/Jobs/PostJobs/EditPostSafetyJob.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
class EditPostSafetyJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$newSafety = new PostSafety($this->getArgument(JobArgs::ARG_NEW_SAFETY));
|
||||||
|
|
||||||
|
$oldSafety = $post->getSafety();
|
||||||
|
$post->setSafety($newSafety);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
if ($oldSafety != $newSafety)
|
||||||
|
{
|
||||||
|
Logger::log('{user} changed safety of {post} to {safety}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'safety' => $post->getSafety()->toString()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_SAFETY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostSafety
|
||||||
|
: Privilege::EditPostSafety;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
61
src/Api/Jobs/PostJobs/EditPostSourceJob.php
Normal file
61
src/Api/Jobs/PostJobs/EditPostSourceJob.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
class EditPostSourceJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$newSource = $this->getArgument(JobArgs::ARG_NEW_SOURCE);
|
||||||
|
|
||||||
|
$oldSource = $post->getSource();
|
||||||
|
$post->setSource($newSource);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
if ($oldSource != $newSource)
|
||||||
|
{
|
||||||
|
Logger::log('{user} changed source of {post} to {source}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'source' => $post->getSource()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostSource
|
||||||
|
: Privilege::EditPostSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
78
src/Api/Jobs/PostJobs/EditPostTagsJob.php
Normal file
78
src/Api/Jobs/PostJobs/EditPostTagsJob.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
class EditPostTagsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$tagNames = $this->getArgument(JobArgs::ARG_NEW_TAG_NAMES);
|
||||||
|
|
||||||
|
if (!is_array($tagNames))
|
||||||
|
throw new SimpleException('Expected array');
|
||||||
|
|
||||||
|
$tags = TagModel::spawnFromNames($tagNames);
|
||||||
|
|
||||||
|
$oldTags = array_map(function($tag) { return $tag->getName(); }, $post->getTags());
|
||||||
|
$post->setTags($tags);
|
||||||
|
$newTags = array_map(function($tag) { return $tag->getName(); }, $post->getTags());
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
{
|
||||||
|
PostModel::save($post);
|
||||||
|
TagModel::removeUnused();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_diff($oldTags, $newTags) as $tag)
|
||||||
|
{
|
||||||
|
Logger::log('{user} untagged {post} with {tag}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_diff($newTags, $oldTags) as $tag)
|
||||||
|
{
|
||||||
|
Logger::log('{user} tagged {post} with {tag}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_TAG_NAMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostTags
|
||||||
|
: Privilege::EditPostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
56
src/Api/Jobs/PostJobs/EditPostThumbJob.php
Normal file
56
src/Api/Jobs/PostJobs/EditPostThumbJob.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
class EditPostThumbJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$file = $this->getArgument(JobArgs::ARG_NEW_THUMB_CONTENT);
|
||||||
|
|
||||||
|
$post->setCustomThumbnailFromPath($file->filePath);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
Logger::log('{user} changed thumb of {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_THUMB_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostThumb
|
||||||
|
: Privilege::EditPostThumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
60
src/Api/Jobs/PostJobs/FeaturePostJob.php
Normal file
60
src/Api/Jobs/PostJobs/FeaturePostJob.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
class FeaturePostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
PropertyModel::set(PropertyModel::FeaturedPostId, $post->getId());
|
||||||
|
PropertyModel::set(PropertyModel::FeaturedPostUnixTime, time());
|
||||||
|
|
||||||
|
$anonymous = false;
|
||||||
|
if ($this->hasArgument(JobArgs::ARG_ANONYMOUS))
|
||||||
|
$anonymous = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_ANONYMOUS));
|
||||||
|
|
||||||
|
PropertyModel::set(PropertyModel::FeaturedPostUserName,
|
||||||
|
$anonymous
|
||||||
|
? null
|
||||||
|
: Auth::getCurrentUser()->getName());
|
||||||
|
|
||||||
|
Logger::log('{user} featured {post} on main page', [
|
||||||
|
'user' => TextHelper::reprPost(PropertyModel::get(PropertyModel::FeaturedPostUserName)),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::Optional(JobArgs::ARG_ANONYMOUS));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::FeaturePost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
53
src/Api/Jobs/PostJobs/FlagPostJob.php
Normal file
53
src/Api/Jobs/PostJobs/FlagPostJob.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
class FlagPostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$key = TextHelper::reprPost($post);
|
||||||
|
|
||||||
|
$flagged = SessionHelper::get('flagged', []);
|
||||||
|
if (in_array($key, $flagged))
|
||||||
|
throw new SimpleException('You already flagged this post');
|
||||||
|
$flagged []= $key;
|
||||||
|
SessionHelper::set('flagged', $flagged);
|
||||||
|
|
||||||
|
Logger::log('{user} flagged {post} for moderator attention', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->postRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::FlagPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
62
src/Api/Jobs/PostJobs/GetPostContentJob.php
Normal file
62
src/Api/Jobs/PostJobs/GetPostContentJob.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
class GetPostContentJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new SafePostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$config = Core::getConfig();
|
||||||
|
|
||||||
|
$path = $post->tryGetWorkingFullPath();
|
||||||
|
if (!$path)
|
||||||
|
throw new SimpleNotFoundException('Post file does not exist');
|
||||||
|
|
||||||
|
$fileName = sprintf('%s_%s_%s.%s',
|
||||||
|
$config->main->title,
|
||||||
|
$post->getId(),
|
||||||
|
join(',', array_map(function($tag) { return $tag->getName(); }, $post->getTags())),
|
||||||
|
TextHelper::resolveMimeType($post->getMimeType()) ?: 'dat');
|
||||||
|
$fileName = preg_replace('/[[:^print:]]/', '', $fileName);
|
||||||
|
|
||||||
|
return new ApiFileOutput($path, $fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->postRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ViewPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$privileges = [];
|
||||||
|
|
||||||
|
if ($post->isHidden())
|
||||||
|
$privileges []= 'hidden';
|
||||||
|
|
||||||
|
$privileges []= $post->getSafety()->toString();
|
||||||
|
|
||||||
|
return $privileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
54
src/Api/Jobs/PostJobs/GetPostJob.php
Normal file
54
src/Api/Jobs/PostJobs/GetPostJob.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
class GetPostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
CommentModel::preloadCommenters($post->getComments());
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ViewPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$privileges = [];
|
||||||
|
|
||||||
|
if ($post->isHidden())
|
||||||
|
$privileges []= 'hidden';
|
||||||
|
|
||||||
|
$privileges []= $post->getSafety()->toString();
|
||||||
|
|
||||||
|
return $privileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
65
src/Api/Jobs/PostJobs/GetPostThumbJob.php
Normal file
65
src/Api/Jobs/PostJobs/GetPostThumbJob.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
class GetPostThumbJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new SafePostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
//optimize - save extra query to DB
|
||||||
|
if ($this->hasArgument(JobArgs::ARG_POST_NAME))
|
||||||
|
$name = $this->getArgument(JobArgs::ARG_POST_NAME);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$name = $post->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = PostModel::tryGetWorkingThumbPath($name);
|
||||||
|
if (!$path)
|
||||||
|
{
|
||||||
|
$post = PostModel::getByName($name);
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
$post->generateThumb();
|
||||||
|
$path = PostModel::tryGetWorkingThumbPath($name);
|
||||||
|
|
||||||
|
if (!$path)
|
||||||
|
{
|
||||||
|
$path = Core::getConfig()->main->mediaPath . DS . 'img' . DS . 'thumb.jpg';
|
||||||
|
$path = TextHelper::absolutePath($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ApiFileOutput($path, 'thumbnail.jpg');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->postRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
59
src/Api/Jobs/PostJobs/ListPostsJob.php
Normal file
59
src/Api/Jobs/PostJobs/ListPostsJob.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
class ListPostsJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->browsing->postsPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$query = $this->hasArgument(JobArgs::ARG_QUERY)
|
||||||
|
? $this->getArgument(JobArgs::ARG_QUERY)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
$posts = PostSearchService::getEntities($query, $pageSize, $page);
|
||||||
|
$postCount = PostSearchService::getEntityCount($query);
|
||||||
|
|
||||||
|
PostModel::preloadTags($posts);
|
||||||
|
|
||||||
|
return $this->pager->serialize($posts, $postCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->pager->getRequiredArguments(),
|
||||||
|
JobArgs::Optional(JobArgs::ARG_QUERY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
47
src/Api/Jobs/PostJobs/ScorePostJob.php
Normal file
47
src/Api/Jobs/PostJobs/ScorePostJob.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
class ScorePostJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$score = TextHelper::toInteger($this->getArgument(JobArgs::ARG_NEW_POST_SCORE));
|
||||||
|
|
||||||
|
UserModel::updateUserScore(Auth::getCurrentUser(), $post, $score);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_POST_SCORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ScorePost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
55
src/Api/Jobs/PostJobs/TogglePostFavoriteJob.php
Normal file
55
src/Api/Jobs/PostJobs/TogglePostFavoriteJob.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
class TogglePostFavoriteJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$favorite = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_NEW_STATE));
|
||||||
|
|
||||||
|
if ($favorite)
|
||||||
|
{
|
||||||
|
UserModel::updateUserScore(Auth::getCurrentUser(), $post, 1);
|
||||||
|
UserModel::addToUserFavorites(Auth::getCurrentUser(), $post);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UserModel::removeFromUserFavorites(Auth::getCurrentUser(), $post);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::FavoritePost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
88
src/Api/Jobs/PostJobs/TogglePostTagJob.php
Normal file
88
src/Api/Jobs/PostJobs/TogglePostTagJob.php
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
class TogglePostTagJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$tagName = $this->getArgument(JobArgs::ARG_TAG_NAME);
|
||||||
|
$enable = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_NEW_STATE));
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
|
||||||
|
$tags = $post->getTags();
|
||||||
|
|
||||||
|
if ($enable)
|
||||||
|
{
|
||||||
|
$tag = TagModel::tryGetByName($tagName);
|
||||||
|
if ($tag === null)
|
||||||
|
{
|
||||||
|
$tag = TagModel::spawn();
|
||||||
|
$tag->setName($tagName);
|
||||||
|
TagModel::save($tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tags []= $tag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($tags as $i => $tag)
|
||||||
|
if ($tag->getName() == $tagName)
|
||||||
|
unset($tags[$i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$post->setTags($tags);
|
||||||
|
PostModel::save($post);
|
||||||
|
TagModel::removeUnused();
|
||||||
|
|
||||||
|
if ($enable)
|
||||||
|
{
|
||||||
|
Logger::log('{user} tagged {post} with {tag}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::log('{user} untagged {post} with {tag}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post),
|
||||||
|
'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::Conjunction(
|
||||||
|
JobArgs::ARG_TAG_NAME,
|
||||||
|
Jobargs::ARG_NEW_STATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditPostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
55
src/Api/Jobs/PostJobs/TogglePostVisibilityJob.php
Normal file
55
src/Api/Jobs/PostJobs/TogglePostVisibilityJob.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
class TogglePostVisibilityJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $postRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$post = $this->postRetriever->retrieve();
|
||||||
|
$visible = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_NEW_STATE));
|
||||||
|
|
||||||
|
$post->setHidden(!$visible);
|
||||||
|
PostModel::save($post);
|
||||||
|
|
||||||
|
Logger::log(
|
||||||
|
$visible
|
||||||
|
? '{user} unhidden {post}'
|
||||||
|
: '{user} hidden {post}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'post' => TextHelper::reprPost($post)]);
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->postRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::HidePost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->postRetriever->retrieve()->getUploader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
59
src/Api/Jobs/TagJobs/ListRelatedTagsJob.php
Normal file
59
src/Api/Jobs/TagJobs/ListRelatedTagsJob.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
class ListRelatedTagsJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->browsing->tagsRelated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$tag = $this->getArgument(JobArgs::ARG_TAG_NAME);
|
||||||
|
$otherTags = $this->hasArgument(JobArgs::ARG_TAG_NAMES) ? $this->getArgument(JobArgs::ARG_TAG_NAMES) : [];
|
||||||
|
|
||||||
|
$tags = TagSearchService::getRelatedTags($tag);
|
||||||
|
$tagCount = count($tags);
|
||||||
|
$tags = array_filter($tags, function($tag) use ($otherTags) { return !in_array($tag->getName(), $otherTags); });
|
||||||
|
$tags = array_slice($tags, 0, $pageSize);
|
||||||
|
|
||||||
|
return $this->pager->serialize($tags, $tagCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->pager->getRequiredArguments(),
|
||||||
|
Jobargs::ARG_TAG_NAME,
|
||||||
|
JobArgs::Optional(JobArgs::ARG_TAG_NAMES));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
57
src/Api/Jobs/TagJobs/ListTagsJob.php
Normal file
57
src/Api/Jobs/TagJobs/ListTagsJob.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
class ListTagsJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->browsing->tagsPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$query = $this->hasArgument(JobArgs::ARG_QUERY)
|
||||||
|
? $this->getArgument(JobArgs::ARG_QUERY)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
$tags = TagSearchService::getEntities($query, $pageSize, $page);
|
||||||
|
$tagCount = TagSearchService::getEntityCount($query);
|
||||||
|
|
||||||
|
return $this->pager->serialize($tags, $tagCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->pager->getRequiredArguments(),
|
||||||
|
JobArgs::Optional(JobArgs::ARG_QUERY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
44
src/Api/Jobs/TagJobs/MergeTagsJob.php
Normal file
44
src/Api/Jobs/TagJobs/MergeTagsJob.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
class MergeTagsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$sourceTag = $this->getArgument(JobArgs::ARG_SOURCE_TAG_NAME);
|
||||||
|
$targetTag = $this->getArgument(JobArgs::ARG_TARGET_TAG_NAME);
|
||||||
|
|
||||||
|
TagModel::removeUnused();
|
||||||
|
TagModel::merge($sourceTag, $targetTag);
|
||||||
|
|
||||||
|
Logger::log('{user} merged {source} with {target}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'source' => TextHelper::reprTag($sourceTag),
|
||||||
|
'target' => TextHelper::reprTag($targetTag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
JobArgs::ARG_SOURCE_TAG_NAME,
|
||||||
|
JobArgs::ARG_TARGET_TAG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::MergeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
44
src/Api/Jobs/TagJobs/RenameTagsJob.php
Normal file
44
src/Api/Jobs/TagJobs/RenameTagsJob.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
class RenameTagsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$sourceTag = $this->getArgument(JobArgs::ARG_SOURCE_TAG_NAME);
|
||||||
|
$targetTag = $this->getArgument(JobArgs::ARG_TARGET_TAG_NAME);
|
||||||
|
|
||||||
|
TagModel::removeUnused();
|
||||||
|
TagModel::rename($sourceTag, $targetTag);
|
||||||
|
|
||||||
|
Logger::log('{user} renamed {source} to {target}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'source' => TextHelper::reprTag($sourceTag),
|
||||||
|
'target' => TextHelper::reprTag($targetTag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
JobArgs::ARG_SOURCE_TAG_NAME,
|
||||||
|
JobArgs::ARG_TARGET_TAG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::RenameTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
49
src/Api/Jobs/UserJobs/AcceptUserRegistrationJob.php
Normal file
49
src/Api/Jobs/UserJobs/AcceptUserRegistrationJob.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
class AcceptUserRegistrationJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
|
||||||
|
$user->setStaffConfirmed(true);
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{user} confirmed {subject}\'s account', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::AcceptUserRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
102
src/Api/Jobs/UserJobs/ActivateUserEmailJob.php
Normal file
102
src/Api/Jobs/UserJobs/ActivateUserEmailJob.php
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
class ActivateUserEmailJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
if (!$this->hasArgument(JobArgs::ARG_TOKEN))
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
|
||||||
|
if (empty($user->getUnconfirmedEmail()))
|
||||||
|
{
|
||||||
|
if (!empty($user->getConfirmedEmail()))
|
||||||
|
throw new SimpleException('E-mail was already confirmed; activation skipped');
|
||||||
|
else
|
||||||
|
throw new SimpleException('This user has no e-mail specified; activation cannot proceed');
|
||||||
|
}
|
||||||
|
|
||||||
|
self::sendEmail($user);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$tokenText = $this->getArgument(JobArgs::ARG_TOKEN);
|
||||||
|
$token = TokenModel::getByToken($tokenText);
|
||||||
|
TokenModel::checkValidity($token);
|
||||||
|
|
||||||
|
$user = $token->getUser();
|
||||||
|
$user->confirmEmail();
|
||||||
|
$token->setUsed(true);
|
||||||
|
TokenModel::save($token);
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{subject} just activated account', [
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
JobArgs::ARG_TOKEN,
|
||||||
|
$this->userRetriever->getRequiredArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAvailableToPublic()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sendEmail($user)
|
||||||
|
{
|
||||||
|
$regConfig = Core::getConfig()->registration;
|
||||||
|
|
||||||
|
if (!$regConfig->confirmationEmailEnabled)
|
||||||
|
{
|
||||||
|
$user->confirmEmail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail = new Mail();
|
||||||
|
$mail->body = $regConfig->confirmationEmailBody;
|
||||||
|
$mail->subject = $regConfig->confirmationEmailSubject;
|
||||||
|
$mail->senderName = $regConfig->confirmationEmailSenderName;
|
||||||
|
$mail->senderEmail = $regConfig->confirmationEmailSenderEmail;
|
||||||
|
$mail->recipientEmail = $user->getUnconfirmedEmail();
|
||||||
|
|
||||||
|
return Mailer::sendMailWithTokenLink(
|
||||||
|
$user,
|
||||||
|
['UserController', 'activationAction'],
|
||||||
|
$mail);
|
||||||
|
}
|
||||||
|
}
|
87
src/Api/Jobs/UserJobs/AddUserJob.php
Normal file
87
src/Api/Jobs/UserJobs/AddUserJob.php
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
class AddUserJob extends AbstractJob
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addSubJob(new EditUserAccessRankJob());
|
||||||
|
$this->addSubJob(new EditUserNameJob());
|
||||||
|
$this->addSubJob(new EditUserPasswordJob());
|
||||||
|
$this->addSubJob(new EditUserEmailJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$firstUser = UserModel::getCount() == 0;
|
||||||
|
|
||||||
|
$user = UserModel::spawn();
|
||||||
|
$user->setJoinTime(time());
|
||||||
|
$user->setStaffConfirmed($firstUser);
|
||||||
|
UserModel::forgeId($user);
|
||||||
|
|
||||||
|
if ($firstUser)
|
||||||
|
{
|
||||||
|
$user->setAccessRank(new AccessRank(AccessRank::Admin));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$user->setAccessRank(new AccessRank(AccessRank::Registered));
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments = $this->getArguments();
|
||||||
|
$arguments[JobArgs::ARG_USER_ENTITY] = $user;
|
||||||
|
|
||||||
|
Logger::bufferChanges();
|
||||||
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
|
{
|
||||||
|
$subJob->setContext(self::CONTEXT_BATCH_ADD);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Api::run($subJob, $arguments);
|
||||||
|
}
|
||||||
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Logger::discardBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//save the user to db if everything went okay
|
||||||
|
UserModel::save($user);
|
||||||
|
EditUserEmailJob::observeSave($user);
|
||||||
|
|
||||||
|
Logger::log('{subject} just signed up', [
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
Logger::flush();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::RegisterAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
47
src/Api/Jobs/UserJobs/DeleteUserJob.php
Normal file
47
src/Api/Jobs/UserJobs/DeleteUserJob.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
class DeleteUserJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
|
||||||
|
$name = $user->getName();
|
||||||
|
UserModel::remove($user);
|
||||||
|
|
||||||
|
Logger::log('{user} removed {subject}\'s account', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($name)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::DeleteUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
59
src/Api/Jobs/UserJobs/EditUserAccessRankJob.php
Normal file
59
src/Api/Jobs/UserJobs/EditUserAccessRankJob.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
class EditUserAccessRankJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$newAccessRank = new AccessRank($this->getArgument(JobArgs::ARG_NEW_ACCESS_RANK));
|
||||||
|
|
||||||
|
$oldAccessRank = $user->getAccessRank();
|
||||||
|
if ($oldAccessRank == $newAccessRank)
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
$user->setAccessRank($newAccessRank);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{user} changed {subject}\'s access rank to {rank}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user),
|
||||||
|
'rank' => $newAccessRank->toDisplayString()]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_ACCESS_RANK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditUserAccessRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
82
src/Api/Jobs/UserJobs/EditUserEmailJob.php
Normal file
82
src/Api/Jobs/UserJobs/EditUserEmailJob.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
class EditUserEmailJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
if (Core::getConfig()->registration->needEmailForRegistering)
|
||||||
|
if (!$this->hasArgument(JobArgs::ARG_NEW_EMAIL) or empty($this->getArgument(JobArgs::ARG_NEW_EMAIL)))
|
||||||
|
throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.');
|
||||||
|
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$newEmail = $this->getArgument(JobArgs::ARG_NEW_EMAIL);
|
||||||
|
|
||||||
|
$oldEmail = $user->getConfirmedEmail();
|
||||||
|
if ($oldEmail == $newEmail)
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
$user->setUnconfirmedEmail($newEmail);
|
||||||
|
$user->setConfirmedEmail(null);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
{
|
||||||
|
UserModel::save($user);
|
||||||
|
self::observeSave($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::log('{user} changed {subject}\'s e-mail to {mail}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user),
|
||||||
|
'mail' => $newEmail]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function observeSave($user)
|
||||||
|
{
|
||||||
|
if (Access::check(new Privilege(Privilege::EditUserEmailNoConfirm), $user))
|
||||||
|
{
|
||||||
|
$user->confirmEmail();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!empty($user->getUnconfirmedEmail()))
|
||||||
|
ActivateUserEmailJob::sendEmail($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::RegisterAccount
|
||||||
|
: Privilege::EditUserEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
88
src/Api/Jobs/UserJobs/EditUserJob.php
Normal file
88
src/Api/Jobs/UserJobs/EditUserJob.php
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
class EditUserJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
$this->addSubJob(new EditUserAccessRankJob());
|
||||||
|
$this->addSubJob(new EditUserNameJob());
|
||||||
|
$this->addSubJob(new EditUserPasswordJob());
|
||||||
|
$this->addSubJob(new EditUserEmailJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canEditAnything($user)
|
||||||
|
{
|
||||||
|
$this->privileges = [];
|
||||||
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$subJob->setArgument(JobArgs::ARG_USER_ENTITY, $user);
|
||||||
|
Api::checkPrivileges($subJob);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (AccessException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
|
||||||
|
$arguments = $this->getArguments();
|
||||||
|
$arguments[JobArgs::ARG_USER_ENTITY] = $user;
|
||||||
|
|
||||||
|
Logger::bufferChanges();
|
||||||
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
|
{
|
||||||
|
$subJob->setContext(self::CONTEXT_BATCH_EDIT);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Api::run($subJob, $arguments);
|
||||||
|
}
|
||||||
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
{
|
||||||
|
UserModel::save($user);
|
||||||
|
EditUserEmailJob::observeSave($user);
|
||||||
|
Logger::flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
61
src/Api/Jobs/UserJobs/EditUserNameJob.php
Normal file
61
src/Api/Jobs/UserJobs/EditUserNameJob.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
class EditUserNameJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$newName = $this->getArgument(JobArgs::ARG_NEW_USER_NAME);
|
||||||
|
|
||||||
|
$oldName = $user->getName();
|
||||||
|
if ($oldName == $newName)
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
$user->setName($newName);
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{user} renamed {old} to {new}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'old' => TextHelper::reprUser($oldName),
|
||||||
|
'new' => TextHelper::reprUser($newName)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_USER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::RegisterAccount
|
||||||
|
: Privilege::EditUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
60
src/Api/Jobs/UserJobs/EditUserPasswordJob.php
Normal file
60
src/Api/Jobs/UserJobs/EditUserPasswordJob.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
class EditUserPasswordJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$newPassword = $this->getArgument(JobArgs::ARG_NEW_PASSWORD);
|
||||||
|
|
||||||
|
$oldPasswordHash = $user->getPasswordHash();
|
||||||
|
$user->setPassword($newPassword);
|
||||||
|
$newPasswordHash = $user->getPasswordHash();
|
||||||
|
if ($oldPasswordHash == $newPasswordHash)
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{user} changed {subject}\'s password', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return $this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::RegisterAccount
|
||||||
|
: Privilege::EditUserPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
56
src/Api/Jobs/UserJobs/EditUserSettingsJob.php
Normal file
56
src/Api/Jobs/UserJobs/EditUserSettingsJob.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
class EditUserSettingsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$newSettings = $this->getArgument(JobArgs::ARG_NEW_SETTINGS);
|
||||||
|
|
||||||
|
if (!is_array($newSettings))
|
||||||
|
throw new SimpleException('Expected array');
|
||||||
|
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
foreach ($newSettings as $key => $value)
|
||||||
|
{
|
||||||
|
$user->getSettings()->set($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->getAccessRank()->toInteger() == AccessRank::Anonymous)
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
return UserModel::save($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_SETTINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditUserSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
53
src/Api/Jobs/UserJobs/FlagUserJob.php
Normal file
53
src/Api/Jobs/UserJobs/FlagUserJob.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
class FlagUserJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$key = TextHelper::reprUser($user);
|
||||||
|
|
||||||
|
$flagged = SessionHelper::get('flagged', []);
|
||||||
|
if (in_array($key, $flagged))
|
||||||
|
throw new SimpleException('You already flagged this user');
|
||||||
|
$flagged []= $key;
|
||||||
|
SessionHelper::set('flagged', $flagged);
|
||||||
|
|
||||||
|
Logger::log('{user} flagged {subject} for moderator attention', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::FlagUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
40
src/Api/Jobs/UserJobs/GetUserJob.php
Normal file
40
src/Api/Jobs/UserJobs/GetUserJob.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
class GetUserJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ViewUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
41
src/Api/Jobs/UserJobs/GetUserSettingsJob.php
Normal file
41
src/Api/Jobs/UserJobs/GetUserSettingsJob.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
class GetUserSettingsJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
return $user->getSettings()->getAllAsArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return $this->userRetriever->getRequiredArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::EditUserSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
57
src/Api/Jobs/UserJobs/ListUsersJob.php
Normal file
57
src/Api/Jobs/UserJobs/ListUsersJob.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
class ListUsersJob extends AbstractJob implements IPagedJob
|
||||||
|
{
|
||||||
|
protected $pager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->pager = new JobPager($this);
|
||||||
|
$this->pager->setPageSize(Core::getConfig()->browsing->usersPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPager()
|
||||||
|
{
|
||||||
|
return $this->pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$pageSize = $this->pager->getPageSize();
|
||||||
|
$page = $this->pager->getPageNumber();
|
||||||
|
$filter = $this->hasArgument(JobArgs::ARG_QUERY)
|
||||||
|
? $this->getArgument(JobArgs::ARG_QUERY)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
$users = UserSearchService::getEntities($filter, $pageSize, $page);
|
||||||
|
$userCount = UserSearchService::getEntityCount($filter);
|
||||||
|
|
||||||
|
return $this->pager->serialize($users, $userCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->pager->getRequiredArguments(),
|
||||||
|
JobArgs::Optional(JobArgs::ARG_QUERY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::ListUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
100
src/Api/Jobs/UserJobs/PasswordResetJob.php
Normal file
100
src/Api/Jobs/UserJobs/PasswordResetJob.php
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
class PasswordResetJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
if (!$this->hasArgument(JobArgs::ARG_TOKEN))
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
|
||||||
|
if (empty($user->getConfirmedEmail()))
|
||||||
|
throw new SimpleException('This user has no e-mail confirmed; password reset cannot proceed');
|
||||||
|
|
||||||
|
self::sendEmail($user);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$tokenText = $this->getArgument(JobArgs::ARG_TOKEN);
|
||||||
|
$token = TokenModel::getByToken($tokenText);
|
||||||
|
TokenModel::checkValidity($token);
|
||||||
|
|
||||||
|
$alphabet = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
|
||||||
|
$newPassword = join('', array_map(function($x) use ($alphabet)
|
||||||
|
{
|
||||||
|
return $alphabet[$x];
|
||||||
|
}, array_rand($alphabet, 8)));
|
||||||
|
|
||||||
|
$user = $token->getUser();
|
||||||
|
$user->setPassword($newPassword);
|
||||||
|
$token->setUsed(true);
|
||||||
|
TokenModel::save($token);
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log('{subject} just reset password', [
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
$x = new StdClass;
|
||||||
|
$x->user = $user;
|
||||||
|
$x->newPassword = $newPassword;
|
||||||
|
return $x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Alternative(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAvailableToPublic()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sendEmail($user)
|
||||||
|
{
|
||||||
|
$regConfig = Core::getConfig()->registration;
|
||||||
|
|
||||||
|
$mail = new Mail();
|
||||||
|
$mail->body = $regConfig->passwordResetEmailBody;
|
||||||
|
$mail->subject = $regConfig->passwordResetEmailSubject;
|
||||||
|
$mail->senderName = $regConfig->passwordResetEmailSenderName;
|
||||||
|
$mail->senderEmail = $regConfig->passwordResetEmailSenderEmail;
|
||||||
|
$mail->recipientEmail = $user->getConfirmedEmail();
|
||||||
|
|
||||||
|
return Mailer::sendMailWithTokenLink(
|
||||||
|
$user,
|
||||||
|
['UserController', 'passwordResetAction'],
|
||||||
|
$mail);
|
||||||
|
}
|
||||||
|
}
|
58
src/Api/Jobs/UserJobs/ToggleUserBanJob.php
Normal file
58
src/Api/Jobs/UserJobs/ToggleUserBanJob.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
class ToggleUserBanJob extends AbstractJob
|
||||||
|
{
|
||||||
|
protected $userRetriever;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRetriever = new UserRetriever($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$user = $this->userRetriever->retrieve();
|
||||||
|
$banned = TextHelper::toBoolean($this->getArgument(JobArgs::ARG_NEW_STATE));
|
||||||
|
|
||||||
|
if ($banned)
|
||||||
|
$user->ban();
|
||||||
|
else
|
||||||
|
$user->unban();
|
||||||
|
UserModel::save($user);
|
||||||
|
|
||||||
|
Logger::log(
|
||||||
|
$banned
|
||||||
|
? '{user} banned {subject}'
|
||||||
|
: '{user} unbanned {subject}', [
|
||||||
|
'user' => TextHelper::reprUser(Auth::getCurrentUser()),
|
||||||
|
'subject' => TextHelper::reprUser($user)]);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredArguments()
|
||||||
|
{
|
||||||
|
return JobArgs::Conjunction(
|
||||||
|
$this->userRetriever->getRequiredArguments(),
|
||||||
|
JobArgs::ARG_NEW_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredMainPrivilege()
|
||||||
|
{
|
||||||
|
return Privilege::BanUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSubPrivileges()
|
||||||
|
{
|
||||||
|
return Access::getIdentity($this->userRetriever->retrieve());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfirmedEmailRequired()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
95
src/Assert.php
Normal file
95
src/Assert.php
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<?php
|
||||||
|
class Assert
|
||||||
|
{
|
||||||
|
public function throws($callback, $expectedMessage)
|
||||||
|
{
|
||||||
|
$success = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$callback();
|
||||||
|
$success = true;
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
if (stripos($e->getMessage(), $expectedMessage) === false)
|
||||||
|
{
|
||||||
|
$this->fail('Assertion failed. Expected: "' . $expectedMessage . '", got: "' . $e->getMessage() . '"'
|
||||||
|
. PHP_EOL . $e->getTraceAsString() . PHP_EOL . '---' . PHP_EOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($success)
|
||||||
|
$this->fail('Assertion failed. Expected exception, got nothing');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doesNotThrow($callback)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$ret = $callback();
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
$this->fail('Assertion failed. Expected nothing, got exception: "' . $e->getMessage() . '"'
|
||||||
|
. PHP_EOL . $e->getTraceAsString() . PHP_EOL . '---' . PHP_EOL);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNull($actual)
|
||||||
|
{
|
||||||
|
if ($actual !== null)
|
||||||
|
$this->fail('Assertion failed. Expected: NULL, got: "' . $this->dumpVar($actual) . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNotNull($actual)
|
||||||
|
{
|
||||||
|
if ($actual === null)
|
||||||
|
$this->fail('Assertion failed. Expected: not NULL, got: "' . $this->dumpVar($actual) . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isTrue($actual)
|
||||||
|
{
|
||||||
|
return $this->areEqual(1, intval(boolval($actual)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFalse($actual)
|
||||||
|
{
|
||||||
|
return $this->areEqual(0, intval(boolval($actual)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function areEqual($expected, $actual)
|
||||||
|
{
|
||||||
|
if ($expected !== $actual)
|
||||||
|
$this->fail('Assertion failed. Expected: "' . $this->dumpVar($expected) . '", got: "' . $this->dumpVar($actual) . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function areEquivalent($expected, $actual)
|
||||||
|
{
|
||||||
|
if ($expected != $actual)
|
||||||
|
$this->fail('Assertion failed. Expected: "' . $this->dumpVar($expected) . '", got: "' . $this->dumpVar($actual) . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function areNotEqual($expected, $actual)
|
||||||
|
{
|
||||||
|
if ($expected === $actual)
|
||||||
|
$this->fail('Assertion failed. Specified objects are equal');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function areNotEquivalent($expected, $actual)
|
||||||
|
{
|
||||||
|
if ($expected == $actual)
|
||||||
|
$this->fail('Assertion failed. Specified objects are equivalent');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dumpVar($var)
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
var_dump($var);
|
||||||
|
return trim(ob_get_clean());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fail($message)
|
||||||
|
{
|
||||||
|
throw new SimpleException($message);
|
||||||
|
}
|
||||||
|
}
|
38
src/Auth.php
38
src/Auth.php
|
@ -10,25 +10,25 @@ class Auth
|
||||||
|
|
||||||
public static function login($name, $password, $remember)
|
public static function login($name, $password, $remember)
|
||||||
{
|
{
|
||||||
$config = getConfig();
|
$config = Core::getConfig();
|
||||||
$context = getContext();
|
$context = Core::getContext();
|
||||||
|
|
||||||
$dbUser = UserModel::findByNameOrEmail($name, false);
|
$user = UserModel::tryGetByEmail($name);
|
||||||
if ($dbUser === null)
|
if ($user === null)
|
||||||
throw new SimpleException('Invalid username');
|
$user = UserModel::getByName($name);
|
||||||
|
|
||||||
$passwordHash = UserModel::hashPassword($password, $dbUser->passSalt);
|
$passwordHash = UserModel::hashPassword($password, $user->getPasswordSalt());
|
||||||
if ($passwordHash != $dbUser->passHash)
|
if ($passwordHash != $user->getPasswordHash())
|
||||||
throw new SimpleException('Invalid password');
|
throw new SimpleException('Invalid password');
|
||||||
|
|
||||||
if (!$dbUser->staffConfirmed and $config->registration->staffActivation)
|
if (!$user->isStaffConfirmed() and $config->registration->staffActivation)
|
||||||
throw new SimpleException('Staff hasn\'t confirmed your registration yet');
|
throw new SimpleException('Staff hasn\'t confirmed your registration yet');
|
||||||
|
|
||||||
if ($dbUser->banned)
|
if ($user->isBanned())
|
||||||
throw new SimpleException('You are banned');
|
throw new SimpleException('You are banned');
|
||||||
|
|
||||||
if ($config->registration->needEmailForRegistering)
|
if ($config->registration->needEmailForRegistering)
|
||||||
Access::requireEmail($dbUser);
|
Access::assertEmailConfirmation($user);
|
||||||
|
|
||||||
if ($remember)
|
if ($remember)
|
||||||
{
|
{
|
||||||
|
@ -36,14 +36,17 @@ class Auth
|
||||||
setcookie('auth', TextHelper::encrypt($token), time() + 365 * 24 * 3600, '/');
|
setcookie('auth', TextHelper::encrypt($token), time() + 365 * 24 * 3600, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setCurrentUser($dbUser);
|
self::setCurrentUser($user);
|
||||||
|
|
||||||
$dbUser->lastLoginDate = time();
|
$user->setLastLoginTime(time());
|
||||||
UserModel::save($dbUser);
|
UserModel::save($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tryAutoLogin()
|
public static function tryAutoLogin()
|
||||||
{
|
{
|
||||||
|
if (self::isLoggedIn())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!isset($_COOKIE['auth']))
|
if (!isset($_COOKIE['auth']))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -73,14 +76,14 @@ class Auth
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$_SESSION['logged-in'] = $user->accessRank != AccessRank::Anonymous;
|
$_SESSION['logged-in'] = $user->getAccessRank()->toInteger() != AccessRank::Anonymous;
|
||||||
$_SESSION['user'] = serialize($user);
|
$_SESSION['user'] = serialize($user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getCurrentUser()
|
public static function getCurrentUser()
|
||||||
{
|
{
|
||||||
return self::isLoggedIn()
|
return isset($_SESSION['user'])
|
||||||
? unserialize($_SESSION['user'])
|
? unserialize($_SESSION['user'])
|
||||||
: self::getAnonymousUser();
|
: self::getAnonymousUser();
|
||||||
}
|
}
|
||||||
|
@ -88,8 +91,9 @@ class Auth
|
||||||
private static function getAnonymousUser()
|
private static function getAnonymousUser()
|
||||||
{
|
{
|
||||||
$dummy = UserModel::spawn();
|
$dummy = UserModel::spawn();
|
||||||
$dummy->name = UserModel::getAnonymousName();
|
$dummy->setId(null);
|
||||||
$dummy->accessRank = AccessRank::Anonymous;
|
$dummy->setName(UserModel::getAnonymousName());
|
||||||
|
$dummy->setAccessRank(new AccessRank(AccessRank::Anonymous));
|
||||||
return $dummy;
|
return $dummy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
82
src/Controllers/AbstractController.php
Normal file
82
src/Controllers/AbstractController.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
class AbstractController
|
||||||
|
{
|
||||||
|
protected $assets;
|
||||||
|
private $layoutName;
|
||||||
|
private static $isRendered;
|
||||||
|
|
||||||
|
public function isAjax()
|
||||||
|
{
|
||||||
|
return isset($_SERVER['HTTP_X_AJAX']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->switchLayout('layout-normal');
|
||||||
|
|
||||||
|
$this->assets = new Assets();
|
||||||
|
$this->assets->setTitle(Core::getConfig()->main->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if ($this->isAjax())
|
||||||
|
$this->renderAjax();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderAjax()
|
||||||
|
{
|
||||||
|
$this->switchLayout('layout-json');
|
||||||
|
$this->renderView(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderFile()
|
||||||
|
{
|
||||||
|
$this->switchLayout('layout-file');
|
||||||
|
$this->renderView(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderView($viewName)
|
||||||
|
{
|
||||||
|
//no matter which controller runs it (including ErrorController), render only once
|
||||||
|
if (self::isRendered())
|
||||||
|
return;
|
||||||
|
|
||||||
|
self::markAsRendered();
|
||||||
|
$context = Core::getContext();
|
||||||
|
if ($viewName !== null)
|
||||||
|
$context->viewName = $viewName;
|
||||||
|
View::renderTopLevel($this->layoutName, $this->assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function redirectToLastVisitedUrl($filter = null)
|
||||||
|
{
|
||||||
|
$targetUrl = SessionHelper::getLastVisitedUrl($filter);
|
||||||
|
if (!$targetUrl)
|
||||||
|
$targetUrl = \Chibi\Router::linkTo(['StaticPagesController', 'mainPageView']);
|
||||||
|
$this->redirect($targetUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function redirect($url)
|
||||||
|
{
|
||||||
|
if (!$this->isAjax())
|
||||||
|
\Chibi\Util\Url::forward($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static function isRendered()
|
||||||
|
{
|
||||||
|
return self::$isRendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function markAsRendered()
|
||||||
|
{
|
||||||
|
self::$isRendered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function switchLayout($layoutName)
|
||||||
|
{
|
||||||
|
$this->layoutName = $layoutName;
|
||||||
|
}
|
||||||
|
}
|
61
src/Controllers/ApiController.php
Normal file
61
src/Controllers/ApiController.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
class ApiController extends AbstractController
|
||||||
|
{
|
||||||
|
public function runAction()
|
||||||
|
{
|
||||||
|
$context = Core::getContext();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Auth::isLoggedIn())
|
||||||
|
{
|
||||||
|
$auth = InputHelper::get('auth');
|
||||||
|
if ($auth)
|
||||||
|
{
|
||||||
|
Auth::login($auth['user'], $auth['pass'], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$jobName = InputHelper::get('name');
|
||||||
|
$jobArgs = InputHelper::get('args');
|
||||||
|
|
||||||
|
$job = $this->jobFromName($jobName);
|
||||||
|
if (!$job)
|
||||||
|
throw new SimpleException('Unknown job: ' . $jobName);
|
||||||
|
if (!$job->isAvailableToPublic())
|
||||||
|
throw new SimpleException('This job is unavailable for public.');
|
||||||
|
|
||||||
|
if (isset($_FILES['args']))
|
||||||
|
{
|
||||||
|
foreach (array_keys($_FILES['args']['name']) as $key)
|
||||||
|
{
|
||||||
|
$jobArgs[$key] = new ApiFileInput(
|
||||||
|
$_FILES['args']['tmp_name'][$key],
|
||||||
|
$_FILES['args']['name'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$context->transport->status = Api::run($job, $jobArgs);
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
Messenger::fail($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->renderAjax();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function jobFromName($jobName)
|
||||||
|
{
|
||||||
|
$jobClassNames = Api::getAllJobClassNames();
|
||||||
|
foreach ($jobClassNames as $className)
|
||||||
|
{
|
||||||
|
$job = (new ReflectionClass($className))->newInstance();
|
||||||
|
if ($job->getName() == $jobName)
|
||||||
|
return $job;
|
||||||
|
$job = null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,59 +1,35 @@
|
||||||
<?php
|
<?php
|
||||||
class AuthController
|
class AuthController extends AbstractController
|
||||||
{
|
{
|
||||||
|
public function loginView()
|
||||||
|
{
|
||||||
|
if (Auth::isLoggedIn())
|
||||||
|
$this->redirectToLastVisitedUrl('auth');
|
||||||
|
else
|
||||||
|
$this->renderView('auth-login');
|
||||||
|
}
|
||||||
|
|
||||||
public function loginAction()
|
public function loginAction()
|
||||||
{
|
{
|
||||||
$context = getContext();
|
try
|
||||||
$context->handleExceptions = true;
|
|
||||||
|
|
||||||
//check if already logged in
|
|
||||||
if (Auth::isLoggedIn())
|
|
||||||
{
|
{
|
||||||
self::redirectAfterLog();
|
$suppliedName = InputHelper::get('name');
|
||||||
return;
|
$suppliedPassword = InputHelper::get('password');
|
||||||
|
$remember = boolval(InputHelper::get('remember'));
|
||||||
|
Auth::login($suppliedName, $suppliedPassword, $remember);
|
||||||
|
}
|
||||||
|
catch (SimpleException $e)
|
||||||
|
{
|
||||||
|
Messenger::fail($e->getMessage());
|
||||||
|
$this->renderView('auth-login');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InputHelper::get('submit'))
|
$this->redirectToLastVisitedUrl('auth');
|
||||||
return;
|
|
||||||
|
|
||||||
$suppliedName = InputHelper::get('name');
|
|
||||||
$suppliedPassword = InputHelper::get('password');
|
|
||||||
$remember = boolval(InputHelper::get('remember'));
|
|
||||||
$dbUser = Auth::login($suppliedName, $suppliedPassword, $remember);
|
|
||||||
self::redirectAfterLog();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logoutAction()
|
public function logoutAction()
|
||||||
{
|
{
|
||||||
$context = getContext();
|
|
||||||
$context->viewName = null;
|
|
||||||
$context->layoutName = null;
|
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
\Chibi\Util\Url::forward(\Chibi\Router::linkTo(['IndexController', 'indexAction']));
|
$this->redirectToLastVisitedUrl('auth');
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function observeWorkFinish()
|
|
||||||
{
|
|
||||||
if (strpos(\Chibi\Util\Headers::get('Content-Type'), 'text/html') === false)
|
|
||||||
return;
|
|
||||||
if (\Chibi\Util\Headers::getCode() != 200)
|
|
||||||
return;
|
|
||||||
$context = getContext();
|
|
||||||
if ($context->simpleControllerName == 'auth')
|
|
||||||
return;
|
|
||||||
$_SESSION['login-redirect-url'] = $context->query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function redirectAfterLog()
|
|
||||||
{
|
|
||||||
if (isset($_SESSION['login-redirect-url']))
|
|
||||||
{
|
|
||||||
\Chibi\Util\Url::forward(\Chibi\Util\Url::makeAbsolute($_SESSION['login-redirect-url']));
|
|
||||||
unset($_SESSION['login-redirect-url']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
\Chibi\Util\Url::forward(\Chibi\Router::linkTo(['IndexController', 'indexAction']));
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +1,97 @@
|
||||||
<?php
|
<?php
|
||||||
class CommentController
|
class CommentController extends AbstractController
|
||||||
{
|
{
|
||||||
public function listAction($page)
|
public function listView($page = 1)
|
||||||
{
|
{
|
||||||
Access::assert(Privilege::ListComments);
|
$ret = Api::run(
|
||||||
|
new ListCommentsJob(),
|
||||||
|
[
|
||||||
|
JobArgs::ARG_PAGE_NUMBER => $page,
|
||||||
|
]);
|
||||||
|
|
||||||
$page = max(1, intval($page));
|
$context = Core::getContext();
|
||||||
$commentsPerPage = intval(getConfig()->comments->commentsPerPage);
|
$context->transport->posts = $ret->entities;
|
||||||
$searchQuery = 'comment_min:1 order:comment_date,desc';
|
$context->transport->paginator = $ret;
|
||||||
|
$this->renderView('comment-list');
|
||||||
$posts = PostSearchService::getEntities($searchQuery, $commentsPerPage, $page);
|
|
||||||
$postCount = PostSearchService::getEntityCount($searchQuery);
|
|
||||||
$pageCount = ceil($postCount / $commentsPerPage);
|
|
||||||
PostModel::preloadTags($posts);
|
|
||||||
PostModel::preloadComments($posts);
|
|
||||||
$comments = [];
|
|
||||||
foreach ($posts as $post)
|
|
||||||
$comments = array_merge($comments, $post->getComments());
|
|
||||||
CommentModel::preloadCommenters($comments);
|
|
||||||
|
|
||||||
$context = getContext();
|
|
||||||
$context->postGroups = true;
|
|
||||||
$context->transport->posts = $posts;
|
|
||||||
$context->transport->paginator = new StdClass;
|
|
||||||
$context->transport->paginator->page = $page;
|
|
||||||
$context->transport->paginator->pageCount = $pageCount;
|
|
||||||
$context->transport->paginator->entityCount = $postCount;
|
|
||||||
$context->transport->paginator->entities = $posts;
|
|
||||||
$context->transport->paginator->params = func_get_args();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addAction($postId)
|
public function addAction()
|
||||||
{
|
{
|
||||||
$context = getContext();
|
if (InputHelper::get('sender') == 'preview')
|
||||||
Access::assert(Privilege::AddComment);
|
|
||||||
if (getConfig()->registration->needEmailForCommenting)
|
|
||||||
Access::assertEmailConfirmation();
|
|
||||||
|
|
||||||
$post = PostModel::findById($postId);
|
|
||||||
$context->transport->post = $post;
|
|
||||||
|
|
||||||
if (!InputHelper::get('submit'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$text = InputHelper::get('text');
|
|
||||||
$text = CommentModel::validateText($text);
|
|
||||||
|
|
||||||
$comment = CommentModel::spawn();
|
|
||||||
$comment->setPost($post);
|
|
||||||
if (Auth::isLoggedIn())
|
|
||||||
$comment->setCommenter(Auth::getCurrentUser());
|
|
||||||
else
|
|
||||||
$comment->setCommenter(null);
|
|
||||||
$comment->commentDate = time();
|
|
||||||
$comment->text = $text;
|
|
||||||
|
|
||||||
if (InputHelper::get('sender') != 'preview')
|
|
||||||
{
|
{
|
||||||
CommentModel::save($comment);
|
$comment = Api::run(
|
||||||
LogHelper::log('{user} commented on {post}', ['post' => TextHelper::reprPost($post->id)]);
|
new PreviewCommentJob(),
|
||||||
|
[
|
||||||
|
JobArgs::ARG_POST_ID => InputHelper::get('post-id'),
|
||||||
|
JobArgs::ARG_NEW_TEXT => InputHelper::get('text')
|
||||||
|
]);
|
||||||
|
|
||||||
|
Core::getContext()->transport->textPreview = $comment->getTextMarkdown();
|
||||||
|
$this->renderAjax();
|
||||||
}
|
}
|
||||||
$context->transport->textPreview = $comment->getText();
|
else
|
||||||
|
{
|
||||||
|
Api::run(
|
||||||
|
new AddCommentJob(),
|
||||||
|
[
|
||||||
|
JobArgs::ARG_POST_ID => InputHelper::get('post-id'),
|
||||||
|
JobArgs::ARG_NEW_TEXT => InputHelper::get('text')
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->isAjax())
|
||||||
|
$this->renderAjax();
|
||||||
|
else
|
||||||
|
$this->redirectToLastVisitedUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function editView($id)
|
||||||
|
{
|
||||||
|
Core::getContext()->transport->comment = CommentModel::getById($id);
|
||||||
|
$this->renderView('comment-edit');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function editAction($id)
|
public function editAction($id)
|
||||||
{
|
{
|
||||||
$context = getContext();
|
if (InputHelper::get('sender') == 'preview')
|
||||||
$comment = CommentModel::findById($id);
|
|
||||||
$context->transport->comment = $comment;
|
|
||||||
|
|
||||||
Access::assert(
|
|
||||||
Privilege::EditComment,
|
|
||||||
Access::getIdentity($comment->getCommenter()));
|
|
||||||
|
|
||||||
if (!InputHelper::get('submit'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$text = InputHelper::get('text');
|
|
||||||
$text = CommentModel::validateText($text);
|
|
||||||
|
|
||||||
$comment->text = $text;
|
|
||||||
|
|
||||||
if (InputHelper::get('sender') != 'preview')
|
|
||||||
{
|
{
|
||||||
CommentModel::save($comment);
|
$comment = Api::run(
|
||||||
LogHelper::log('{user} edited comment in {post}', [
|
new PreviewCommentJob(),
|
||||||
'post' => TextHelper::reprPost($comment->getPost())]);
|
[
|
||||||
|
JobArgs::ARG_COMMENT_ID => $id,
|
||||||
|
JobArgs::ARG_NEW_TEXT => InputHelper::get('text')
|
||||||
|
]);
|
||||||
|
|
||||||
|
Core::getContext()->transport->textPreview = $comment->getTextMarkdown();
|
||||||
|
$this->renderAjax();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Api::run(
|
||||||
|
new EditCommentJob(),
|
||||||
|
[
|
||||||
|
JobArgs::ARG_COMMENT_ID => $id,
|
||||||
|
JobArgs::ARG_NEW_TEXT => InputHelper::get('text')
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->isAjax())
|
||||||
|
$this->renderAjax();
|
||||||
|
else
|
||||||
|
$this->redirectToLastVisitedUrl('comment/');
|
||||||
}
|
}
|
||||||
$context->transport->textPreview = $comment->getText();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteAction($id)
|
public function deleteAction($id)
|
||||||
{
|
{
|
||||||
$comment = CommentModel::findById($id);
|
$comment = Api::run(
|
||||||
|
new DeleteCommentJob(),
|
||||||
|
[
|
||||||
|
JobArgs::ARG_COMMENT_ID => $id,
|
||||||
|
]);
|
||||||
|
|
||||||
Access::assert(
|
if ($this->isAjax())
|
||||||
Privilege::DeleteComment,
|
$this->renderAjax();
|
||||||
Access::getIdentity($comment->getCommenter()));
|
else
|
||||||
|
$this->redirectToLastVisitedUrl('comment/');
|
||||||
CommentModel::remove($comment);
|
|
||||||
|
|
||||||
LogHelper::log('{user} removed comment from {post}', [
|
|
||||||
'post' => TextHelper::reprPost($comment->getPost())]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/Controllers/ErrorController.php
Normal file
30
src/Controllers/ErrorController.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
class ErrorController extends AbstractController
|
||||||
|
{
|
||||||
|
public function simpleExceptionView(Exception $exception)
|
||||||
|
{
|
||||||
|
if ($exception instanceof SimpleNotFoundException)
|
||||||
|
\Chibi\Util\Headers::setCode(404);
|
||||||
|
else
|
||||||
|
\Chibi\Util\Headers::setCode(400);
|
||||||
|
Messenger::fail($exception->getMessage());
|
||||||
|
|
||||||
|
if ($this->isAjax())
|
||||||
|
$this->renderAjax();
|
||||||
|
else
|
||||||
|
$this->renderView('message');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seriousExceptionView(Exception $exception)
|
||||||
|
{
|
||||||
|
\Chibi\Util\Headers::setCode(400);
|
||||||
|
Messenger::fail($exception->getMessage());
|
||||||
|
$context->transport->exception = $exception;
|
||||||
|
$context->transport->queries = \Chibi\Database::getLogs();
|
||||||
|
|
||||||
|
if ($this->isAjax())
|
||||||
|
$this->renderAjax();
|
||||||
|
else
|
||||||
|
$this->renderView('error-exception');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
class IndexController
|
|
||||||
{
|
|
||||||
public function indexAction()
|
|
||||||
{
|
|
||||||
$context = getContext();
|
|
||||||
$context->transport->postCount = PostModel::getCount();
|
|
||||||
|
|
||||||
$featuredPost = $this->getFeaturedPost();
|
|
||||||
if ($featuredPost)
|
|
||||||
{
|
|
||||||
$context->featuredPost = $featuredPost;
|
|
||||||
$context->featuredPostDate = PropertyModel::get(PropertyModel::FeaturedPostDate);
|
|
||||||
$context->featuredPostUser = UserModel::findByNameOrEmail(
|
|
||||||
PropertyModel::get(PropertyModel::FeaturedPostUserName),
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function helpAction($tab = null)
|
|
||||||
{
|
|
||||||
$config = getConfig();
|
|
||||||
$context = getContext();
|
|
||||||
if (empty($config->help->paths) or empty($config->help->title))
|
|
||||||
throw new SimpleException('Help is disabled');
|
|
||||||
$tab = $tab ?: array_keys($config->help->subTitles)[0];
|
|
||||||
if (!isset($config->help->paths[$tab]))
|
|
||||||
throw new SimpleException('Invalid tab');
|
|
||||||
$context->path = TextHelper::absolutePath($config->help->paths[$tab]);
|
|
||||||
$context->tab = $tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getFeaturedPost()
|
|
||||||
{
|
|
||||||
$config = getConfig();
|
|
||||||
$featuredPostRotationTime = $config->misc->featuredPostMaxDays * 24 * 3600;
|
|
||||||
|
|
||||||
$featuredPostId = PropertyModel::get(PropertyModel::FeaturedPostId);
|
|
||||||
$featuredPostDate = PropertyModel::get(PropertyModel::FeaturedPostDate);
|
|
||||||
|
|
||||||
//check if too old
|
|
||||||
if (!$featuredPostId or $featuredPostDate + $featuredPostRotationTime < time())
|
|
||||||
return PropertyModel::featureNewPost();
|
|
||||||
|
|
||||||
//check if post was deleted
|
|
||||||
$featuredPost = PostModel::findById($featuredPostId, false);
|
|
||||||
if (!$featuredPost)
|
|
||||||
return PropertyModel::featureNewPost();
|
|
||||||
|
|
||||||
return $featuredPost;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue