Refactor of controllers and models
- Most of model-related code moved from controllers to model classes, much fewer calls to R::whatever() in controllers - Post editing and uploading shares the same code, thus making implementing stuff easier in the future - Added support for default bean wiring, no more calls to R::preload() all over the place - More robust concurrent post editing detection
This commit is contained in:
parent
0a5169a7d6
commit
d8997edc57
14 changed files with 785 additions and 527 deletions
|
@ -90,6 +90,8 @@ editPostThumb=moderator
|
||||||
editPostSource=moderator
|
editPostSource=moderator
|
||||||
editPostRelations.own=registered
|
editPostRelations.own=registered
|
||||||
editPostRelations.all=moderator
|
editPostRelations.all=moderator
|
||||||
|
editPostFile.all=moderator
|
||||||
|
editPostFile.own=moderator
|
||||||
hidePost.own=moderator
|
hidePost.own=moderator
|
||||||
hidePost.all=moderator
|
hidePost.all=moderator
|
||||||
deletePost.own=moderator
|
deletePost.own=moderator
|
||||||
|
|
|
@ -25,7 +25,6 @@ class CommentController
|
||||||
$page = max(1, min($pageCount, $page));
|
$page = max(1, min($pageCount, $page));
|
||||||
$comments = Model_Comment::getEntities(null, $commentsPerPage, $page);
|
$comments = Model_Comment::getEntities(null, $commentsPerPage, $page);
|
||||||
|
|
||||||
R::preload($comments, ['commenter' => 'user', 'post', 'post.uploader' => 'user']);
|
|
||||||
$this->context->postGroups = true;
|
$this->context->postGroups = true;
|
||||||
$this->context->transport->paginator = new StdClass;
|
$this->context->transport->paginator = new StdClass;
|
||||||
$this->context->transport->paginator->page = $page;
|
$this->context->transport->paginator->page = $page;
|
||||||
|
@ -55,7 +54,7 @@ class CommentController
|
||||||
$text = InputHelper::get('text');
|
$text = InputHelper::get('text');
|
||||||
$text = Model_Comment::validateText($text);
|
$text = Model_Comment::validateText($text);
|
||||||
|
|
||||||
$comment = R::dispense('comment');
|
$comment = Model_Comment::create();
|
||||||
$comment->post = $post;
|
$comment->post = $post;
|
||||||
if ($this->context->loggedIn)
|
if ($this->context->loggedIn)
|
||||||
$comment->commenter = $this->context->user;
|
$comment->commenter = $this->context->user;
|
||||||
|
@ -63,7 +62,7 @@ class CommentController
|
||||||
$comment->text = $text;
|
$comment->text = $text;
|
||||||
if (InputHelper::get('sender') != 'preview')
|
if (InputHelper::get('sender') != 'preview')
|
||||||
{
|
{
|
||||||
R::store($comment);
|
Model_Comment::save($comment);
|
||||||
LogHelper::logEvent('comment-add', '{user} commented on {post}', ['post' => TextHelper::reprPost($post->id)]);
|
LogHelper::logEvent('comment-add', '{user} commented on {post}', ['post' => TextHelper::reprPost($post->id)]);
|
||||||
}
|
}
|
||||||
$this->context->transport->textPreview = $comment->getText();
|
$this->context->transport->textPreview = $comment->getText();
|
||||||
|
@ -80,10 +79,10 @@ class CommentController
|
||||||
public function deleteAction($id)
|
public function deleteAction($id)
|
||||||
{
|
{
|
||||||
$comment = Model_Comment::locate($id);
|
$comment = Model_Comment::locate($id);
|
||||||
R::preload($comment, ['commenter' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::DeleteComment, PrivilegesHelper::getIdentitySubPrivilege($comment->commenter));
|
PrivilegesHelper::confirmWithException(Privilege::DeleteComment, PrivilegesHelper::getIdentitySubPrivilege($comment->commenter));
|
||||||
|
Model_Comment::remove($comment);
|
||||||
|
|
||||||
LogHelper::logEvent('comment-del', '{user} removed comment from {post}', ['post' => TextHelper::reprPost($comment->post)]);
|
LogHelper::logEvent('comment-del', '{user} removed comment from {post}', ['post' => TextHelper::reprPost($comment->post)]);
|
||||||
R::trash($comment);
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ class IndexController
|
||||||
{
|
{
|
||||||
$this->context->subTitle = 'home';
|
$this->context->subTitle = 'home';
|
||||||
$this->context->stylesheets []= 'index-index.css';
|
$this->context->stylesheets []= 'index-index.css';
|
||||||
$this->context->transport->postCount = R::$f->begin()->select('count(1)')->as('count')->from('post')->get('row')['count'];
|
$this->context->transport->postCount = Model_Post::getAllPostCount();
|
||||||
|
|
||||||
$featuredPostRotationTime = $this->config->misc->featuredPostMaxDays * 24 * 3600;
|
$featuredPostRotationTime = $this->config->misc->featuredPostMaxDays * 24 * 3600;
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,18 @@ class PostController
|
||||||
$callback();
|
$callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function serializeTags($post)
|
private static function serializePost($post)
|
||||||
{
|
{
|
||||||
$x = [];
|
$x = [];
|
||||||
foreach ($post->sharedTag as $tag)
|
foreach ($post->sharedTag as $tag)
|
||||||
$x []= $tag->name;
|
$x []= TextHelper::reprTag($tag->name);
|
||||||
|
foreach ($post->via('crossref')->sharedPost as $relatedPost)
|
||||||
|
$x []= TextHelper::reprPost($relatedPost);
|
||||||
|
$x []= $post->safety;
|
||||||
|
$x []= $post->source;
|
||||||
|
$x []= $post->file_hash;
|
||||||
natcasesort($x);
|
natcasesort($x);
|
||||||
$x = join('', $x);
|
$x = join(' ', $x);
|
||||||
return md5($x);
|
return md5($x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +125,6 @@ class PostController
|
||||||
public function toggleTagAction($id, $tag)
|
public function toggleTagAction($id, $tag)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['uploader' => 'user']);
|
|
||||||
$this->context->transport->post = $post;
|
$this->context->transport->post = $post;
|
||||||
|
|
||||||
$tagRow = Model_Tag::locate($tag, false);
|
$tagRow = Model_Tag::locate($tag, false);
|
||||||
|
@ -146,7 +150,7 @@ class PostController
|
||||||
$dbTags = Model_Tag::insertOrUpdate($tags);
|
$dbTags = Model_Tag::insertOrUpdate($tags);
|
||||||
$post->sharedTag = $dbTags;
|
$post->sharedTag = $dbTags;
|
||||||
|
|
||||||
R::store($post);
|
Model_Post::save($post);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,173 +196,38 @@ class PostController
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
/* file contents */
|
R::transaction(function()
|
||||||
if (isset($_FILES['file']))
|
|
||||||
{
|
{
|
||||||
$suppliedFile = $_FILES['file'];
|
$post = Model_Post::create();
|
||||||
self::handleUploadErrors($suppliedFile);
|
|
||||||
$origName = basename($suppliedFile['name']);
|
|
||||||
$sourcePath = $suppliedFile['tmp_name'];
|
|
||||||
}
|
|
||||||
elseif (InputHelper::get('url'))
|
|
||||||
{
|
|
||||||
$url = InputHelper::get('url');
|
|
||||||
$origName = $url;
|
|
||||||
if (!preg_match('/^https?:\/\//', $url))
|
|
||||||
throw new SimpleException('Invalid URL "' . $url . '"');
|
|
||||||
|
|
||||||
if (preg_match('/youtube.com\/watch.*?=([a-zA-Z0-9_-]+)/', $url, $matches))
|
//basic stuff
|
||||||
{
|
|
||||||
$origName = $matches[1];
|
|
||||||
$postType = PostType::Youtube;
|
|
||||||
$sourcePath = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sourcePath = tempnam(sys_get_temp_dir(), 'upload') . '.dat';
|
|
||||||
|
|
||||||
//warning: low level sh*t ahead
|
|
||||||
//download the URL $url into $sourcePath
|
|
||||||
$maxBytes = TextHelper::stripBytesUnits(ini_get('upload_max_filesize'));
|
|
||||||
set_time_limit(0);
|
|
||||||
$urlFP = fopen($url, 'rb');
|
|
||||||
if (!$urlFP)
|
|
||||||
throw new SimpleException('Cannot open URL for reading');
|
|
||||||
$sourceFP = fopen($sourcePath, 'w+b');
|
|
||||||
if (!$sourceFP)
|
|
||||||
{
|
|
||||||
fclose($urlFP);
|
|
||||||
throw new SimpleException('Cannot open file for writing');
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!feof($urlFP))
|
|
||||||
{
|
|
||||||
$buffer = fread($urlFP, 4 * 1024);
|
|
||||||
if (fwrite($sourceFP, $buffer) === false)
|
|
||||||
throw new SimpleException('Cannot write into file');
|
|
||||||
fflush($sourceFP);
|
|
||||||
if (ftell($sourceFP) > $maxBytes)
|
|
||||||
throw new SimpleException('File is too big (maximum allowed size: ' . TextHelper::useBytesUnits($maxBytes) . ')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
fclose($urlFP);
|
|
||||||
fclose($sourceFP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* file details */
|
|
||||||
$mimeType = null;
|
|
||||||
if ($sourcePath)
|
|
||||||
{
|
|
||||||
if (function_exists('mime_content_type'))
|
|
||||||
$mimeType = mime_content_type($sourcePath);
|
|
||||||
else
|
|
||||||
$mimeType = $suppliedFile['type'];
|
|
||||||
}
|
|
||||||
$imageWidth = null;
|
|
||||||
$imageHeight = null;
|
|
||||||
switch ($mimeType)
|
|
||||||
{
|
|
||||||
case 'image/gif':
|
|
||||||
case 'image/png':
|
|
||||||
case 'image/jpeg':
|
|
||||||
$postType = PostType::Image;
|
|
||||||
list ($imageWidth, $imageHeight) = getimagesize($sourcePath);
|
|
||||||
break;
|
|
||||||
case 'application/x-shockwave-flash':
|
|
||||||
$postType = PostType::Flash;
|
|
||||||
list ($imageWidth, $imageHeight) = getimagesize($sourcePath);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!isset($postType))
|
|
||||||
throw new SimpleException('Invalid file type "' . $mimeType . '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sourcePath)
|
|
||||||
{
|
|
||||||
$fileSize = filesize($sourcePath);
|
|
||||||
$fileHash = md5_file($sourcePath);
|
|
||||||
$duplicatedPost = R::findOne('post', 'file_hash = ?', [$fileHash]);
|
|
||||||
if ($duplicatedPost !== null)
|
|
||||||
throw new SimpleException('Duplicate upload: @' . $duplicatedPost->id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$fileSize = 0;
|
|
||||||
$fileHash = null;
|
|
||||||
if ($postType == PostType::Youtube)
|
|
||||||
{
|
|
||||||
$duplicatedPost = R::findOne('post', 'orig_name = ?', [$origName]);
|
|
||||||
if ($duplicatedPost !== null)
|
|
||||||
throw new SimpleException('Duplicate upload: @' . $duplicatedPost->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
$name = md5(mt_rand() . uniqid());
|
|
||||||
$path = $this->config->main->filesPath . DS . $name;
|
|
||||||
}
|
|
||||||
while (file_exists($path));
|
|
||||||
|
|
||||||
|
|
||||||
/* safety */
|
|
||||||
$suppliedSafety = InputHelper::get('safety');
|
|
||||||
$suppliedSafety = Model_Post::validateSafety($suppliedSafety);
|
|
||||||
|
|
||||||
/* tags */
|
|
||||||
$suppliedTags = InputHelper::get('tags');
|
|
||||||
$suppliedTags = Model_Tag::validateTags($suppliedTags);
|
|
||||||
$dbTags = Model_Tag::insertOrUpdate($suppliedTags);
|
|
||||||
|
|
||||||
/* source */
|
|
||||||
$suppliedSource = InputHelper::get('source');
|
|
||||||
$suppliedSource = Model_Post::validateSource($suppliedSource);
|
|
||||||
|
|
||||||
/* anonymous */
|
|
||||||
$anonymous = InputHelper::get('anonymous');
|
$anonymous = InputHelper::get('anonymous');
|
||||||
|
|
||||||
/* db storage */
|
|
||||||
$dbPost = R::dispense('post');
|
|
||||||
$dbPost->type = $postType;
|
|
||||||
$dbPost->name = $name;
|
|
||||||
$dbPost->orig_name = $origName;
|
|
||||||
$dbPost->file_hash = $fileHash;
|
|
||||||
$dbPost->file_size = $fileSize;
|
|
||||||
$dbPost->mime_type = $mimeType;
|
|
||||||
$dbPost->safety = $suppliedSafety;
|
|
||||||
$dbPost->source = $suppliedSource;
|
|
||||||
$dbPost->hidden = false;
|
|
||||||
$dbPost->upload_date = time();
|
|
||||||
$dbPost->image_width = $imageWidth;
|
|
||||||
$dbPost->image_height = $imageHeight;
|
|
||||||
if ($this->context->loggedIn and !$anonymous)
|
if ($this->context->loggedIn and !$anonymous)
|
||||||
$dbPost->uploader = $this->context->user;
|
$post->uploader = $this->context->user;
|
||||||
$dbPost->ownFavoritee = [];
|
|
||||||
$dbPost->sharedTag = $dbTags;
|
|
||||||
|
|
||||||
if ($sourcePath)
|
//store the post to get the ID in the logs
|
||||||
{
|
Model_Post::save($post);
|
||||||
if (is_uploaded_file($sourcePath))
|
|
||||||
move_uploaded_file($sourcePath, $path);
|
|
||||||
else
|
|
||||||
rename($sourcePath, $path);
|
|
||||||
}
|
|
||||||
R::store($dbPost);
|
|
||||||
|
|
||||||
|
//log
|
||||||
|
LogHelper::bufferChanges();
|
||||||
$fmt = ($anonymous and !$this->config->misc->logAnonymousUploads)
|
$fmt = ($anonymous and !$this->config->misc->logAnonymousUploads)
|
||||||
? 'someone'
|
? 'someone'
|
||||||
: '{user}';
|
: '{user}';
|
||||||
$fmt .= ' added {post} tagged with {tags} marked as {safety}';
|
$fmt .= ' added {post}';
|
||||||
LogHelper::logEvent('post-new', $fmt, [
|
LogHelper::logEvent('post-new', $fmt, ['post' => TextHelper::reprPost($post)]);
|
||||||
'post' => TextHelper::reprPost($dbPost),
|
|
||||||
'tags' => join(', ', array_map(['TextHelper', 'reprTag'], $dbTags)),
|
//after logging basic info, do the editing stuff
|
||||||
'safety' => PostSafety::toString($dbPost->safety)]);
|
$this->doEdit($post, true);
|
||||||
|
|
||||||
|
//this basically means that user didn't specify file nor url
|
||||||
|
if (empty($post->type))
|
||||||
|
throw new SimpleException('No post type detected; upload faled');
|
||||||
|
|
||||||
|
LogHelper::flush();
|
||||||
|
|
||||||
|
//finish
|
||||||
|
Model_Post::save($post);
|
||||||
|
});
|
||||||
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
|
@ -372,111 +241,21 @@ class PostController
|
||||||
public function editAction($id)
|
public function editAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['uploader' => 'user']);
|
|
||||||
$this->context->transport->post = $post;
|
$this->context->transport->post = $post;
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
|
$editToken = InputHelper::get('edit-token');
|
||||||
|
if ($editToken != self::serializePost($post))
|
||||||
|
throw new SimpleException('This post was already edited by someone else in the meantime');
|
||||||
|
|
||||||
LogHelper::bufferChanges();
|
LogHelper::bufferChanges();
|
||||||
|
$this->doEdit($post, false);
|
||||||
|
LogHelper::flush();
|
||||||
|
|
||||||
/* safety */
|
Model_Post::save($post);
|
||||||
$suppliedSafety = InputHelper::get('safety');
|
|
||||||
if ($suppliedSafety !== null and $suppliedSafety != $post->safety)
|
|
||||||
{
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::EditPostSafety, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
|
||||||
$suppliedSafety = Model_Post::validateSafety($suppliedSafety);
|
|
||||||
$post->safety = $suppliedSafety;
|
|
||||||
$edited = true;
|
|
||||||
LogHelper::logEvent('post-edit', '{user} changed safety for {post} to {safety}', ['post' => TextHelper::reprPost($post), 'safety' => PostSafety::toString($post->safety)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* tags */
|
|
||||||
$suppliedTags = InputHelper::get('tags');
|
|
||||||
if ($suppliedTags !== null)
|
|
||||||
{
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::EditPostTags, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
|
||||||
$currentToken = self::serializeTags($post);
|
|
||||||
if (InputHelper::get('tags-token') != $currentToken)
|
|
||||||
throw new SimpleException('Someone else has changed the tags in the meantime');
|
|
||||||
|
|
||||||
$suppliedTags = Model_Tag::validateTags($suppliedTags);
|
|
||||||
$dbTags = Model_Tag::insertOrUpdate($suppliedTags);
|
|
||||||
|
|
||||||
$oldTags = array_map(function($tag) { return $tag->name; }, $post->sharedTag);
|
|
||||||
$post->sharedTag = $dbTags;
|
|
||||||
$edited = true;
|
|
||||||
|
|
||||||
foreach (array_diff($oldTags, $suppliedTags) as $tag)
|
|
||||||
LogHelper::logEvent('post-tag-del', '{user} untagged {post} with {tag}', ['post' => TextHelper::reprPost($post), 'tag' => TextHelper::reprTag($tag)]);
|
|
||||||
foreach (array_diff($suppliedTags, $oldTags) as $tag)
|
|
||||||
LogHelper::logEvent('post-tag-add', '{user} tagged {post} with {tag}', ['post' => TextHelper::reprPost($post), 'tag' => TextHelper::reprTag($tag)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* thumbnail */
|
|
||||||
if (!empty($_FILES['thumb']['name']))
|
|
||||||
{
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::EditPostThumb, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
|
||||||
$suppliedFile = $_FILES['thumb'];
|
|
||||||
self::handleUploadErrors($suppliedFile);
|
|
||||||
|
|
||||||
$mimeType = mime_content_type($suppliedFile['tmp_name']);
|
|
||||||
if (!in_array($mimeType, ['image/gif', 'image/png', 'image/jpeg']))
|
|
||||||
throw new SimpleException('Invalid thumbnail type "' . $mimeType . '"');
|
|
||||||
list ($imageWidth, $imageHeight) = getimagesize($suppliedFile['tmp_name']);
|
|
||||||
if ($imageWidth != $this->config->browsing->thumbWidth)
|
|
||||||
throw new SimpleException('Invalid thumbnail width (should be ' . $this->config->browsing->thumbWidth . ')');
|
|
||||||
if ($imageWidth != $this->config->browsing->thumbHeight)
|
|
||||||
throw new SimpleException('Invalid thumbnail width (should be ' . $this->config->browsing->thumbHeight . ')');
|
|
||||||
|
|
||||||
$path = $this->config->main->thumbsPath . DS . $post->name . '.custom';
|
|
||||||
move_uploaded_file($suppliedFile['tmp_name'], $path);
|
|
||||||
LogHelper::logEvent('post-edit', '{user} added custom thumb for {post}', ['post' => TextHelper::reprPost($post)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* source */
|
|
||||||
$suppliedSource = InputHelper::get('source');
|
|
||||||
if ($suppliedSource !== null and $suppliedSource != $post->source)
|
|
||||||
{
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::EditPostSource, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
|
||||||
$suppliedSource = Model_Post::validateSource($suppliedSource);
|
|
||||||
$post->source = $suppliedSource;
|
|
||||||
$edited = true;
|
|
||||||
LogHelper::logEvent('post-edit', '{user} changed source for {post} to {source}', ['post' => TextHelper::reprPost($post), 'source' => $post->source]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* relations */
|
|
||||||
$suppliedRelations = InputHelper::get('relations');
|
|
||||||
if ($suppliedRelations !== null)
|
|
||||||
{
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::EditPostRelations, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
|
||||||
$relatedIds = array_filter(preg_split('/\D/', $suppliedRelations));
|
|
||||||
$relatedPosts = [];
|
|
||||||
foreach ($relatedIds as $relatedId)
|
|
||||||
{
|
|
||||||
if ($relatedId == $post->id)
|
|
||||||
continue;
|
|
||||||
if (count($relatedPosts) > $this->config->browsing->maxRelatedPosts)
|
|
||||||
throw new SimpleException('Too many related posts (maximum: ' . $this->config->browsing->maxRelatedPosts . ')');
|
|
||||||
$relatedPosts []= Model_Post::locate($relatedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldRelatedIds = array_map(function($post) { return $post->id; }, $post->via('crossref')->sharedPost);
|
|
||||||
$post->via('crossref')->sharedPost = $relatedPosts;
|
|
||||||
|
|
||||||
foreach (array_diff($oldRelatedIds, $relatedIds) as $post2id)
|
|
||||||
LogHelper::logEvent('post-relation-del', '{user} removed relation between {post} and {post2}', ['post' => TextHelper::reprPost($post), 'post2' => TextHelper::reprPost($post2id)]);
|
|
||||||
foreach (array_diff($relatedIds, $oldRelatedIds) as $post2id)
|
|
||||||
LogHelper::logEvent('post-relation-add', '{user} added relation between {post} and {post2}', ['post' => TextHelper::reprPost($post), 'post2' => TextHelper::reprPost($post2id)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
R::store($post);
|
|
||||||
Model_Tag::removeUnused();
|
Model_Tag::removeUnused();
|
||||||
|
|
||||||
LogHelper::flush();
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,13 +293,12 @@ class PostController
|
||||||
public function hideAction($id)
|
public function hideAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['uploader' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
$post->hidden = true;
|
$post->setHidden(true);
|
||||||
R::store($post);
|
Model_Post::save($post);
|
||||||
|
|
||||||
LogHelper::logEvent('post-hide', '{user} hidden {post}', ['post' => TextHelper::reprPost($post)]);
|
LogHelper::logEvent('post-hide', '{user} hidden {post}', ['post' => TextHelper::reprPost($post)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
|
@ -535,13 +313,12 @@ class PostController
|
||||||
public function unhideAction($id)
|
public function unhideAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['uploader' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
$post->hidden = false;
|
$post->setHidden(false);
|
||||||
R::store($post);
|
Model_Post::save($post);
|
||||||
|
|
||||||
LogHelper::logEvent('post-unhide', '{user} unhidden {post}', ['post' => TextHelper::reprPost($post)]);
|
LogHelper::logEvent('post-unhide', '{user} unhidden {post}', ['post' => TextHelper::reprPost($post)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
|
@ -556,23 +333,11 @@ class PostController
|
||||||
public function deleteAction($id)
|
public function deleteAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['uploader' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::DeletePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
PrivilegesHelper::confirmWithException(Privilege::DeletePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
//remove stuff from auxiliary tables
|
Model_Post::remove($post);
|
||||||
R::trashAll(R::find('postscore', 'post_id = ?', [$post->id]));
|
|
||||||
R::trashAll(R::find('crossref', 'post_id = ? OR post2_id = ?', [$post->id, $post->id]));
|
|
||||||
foreach ($post->ownComment as $comment)
|
|
||||||
{
|
|
||||||
$comment->post = null;
|
|
||||||
R::store($comment);
|
|
||||||
}
|
|
||||||
$post->ownFavoritee = [];
|
|
||||||
$post->sharedTag = [];
|
|
||||||
R::store($post);
|
|
||||||
R::trash($post);
|
|
||||||
|
|
||||||
LogHelper::logEvent('post-delete', '{user} deleted {post}', ['post' => TextHelper::reprPost($id)]);
|
LogHelper::logEvent('post-delete', '{user} deleted {post}', ['post' => TextHelper::reprPost($id)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
|
@ -588,7 +353,6 @@ class PostController
|
||||||
public function addFavoriteAction($id)
|
public function addFavoriteAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['favoritee' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
|
@ -596,12 +360,8 @@ class PostController
|
||||||
if (!$this->context->loggedIn)
|
if (!$this->context->loggedIn)
|
||||||
throw new SimpleException('Not logged in');
|
throw new SimpleException('Not logged in');
|
||||||
|
|
||||||
foreach ($post->via('favoritee')->sharedUser as $fav)
|
$this->context->user->addToFavorites($post);
|
||||||
if ($fav->id == $this->context->user->id)
|
Model_User::save($this->context->user);
|
||||||
throw new SimpleException('Already in favorites');
|
|
||||||
|
|
||||||
$post->link('favoritee')->user = $this->context->user;
|
|
||||||
R::store($post);
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,7 +373,6 @@ class PostController
|
||||||
public function remFavoriteAction($id)
|
public function remFavoriteAction($id)
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, ['favoritee' => 'user']);
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
||||||
|
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
|
@ -621,16 +380,8 @@ class PostController
|
||||||
if (!$this->context->loggedIn)
|
if (!$this->context->loggedIn)
|
||||||
throw new SimpleException('Not logged in');
|
throw new SimpleException('Not logged in');
|
||||||
|
|
||||||
$finalKey = null;
|
$this->context->user->remFromFavorites($post);
|
||||||
foreach ($post->ownFavoritee as $key => $fav)
|
Model_User::save($this->context->user);
|
||||||
if ($fav->user->id == $this->context->user->id)
|
|
||||||
$finalKey = $key;
|
|
||||||
|
|
||||||
if ($finalKey === null)
|
|
||||||
throw new SimpleException('Not in favorites');
|
|
||||||
|
|
||||||
unset ($post->ownFavoritee[$finalKey]);
|
|
||||||
R::store($post);
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -651,15 +402,8 @@ class PostController
|
||||||
if (!$this->context->loggedIn)
|
if (!$this->context->loggedIn)
|
||||||
throw new SimpleException('Not logged in');
|
throw new SimpleException('Not logged in');
|
||||||
|
|
||||||
$p = R::findOne('postscore', 'post_id = ? AND user_id = ?', [$post->id, $this->context->user->id]);
|
$this->context->user->score($post, $score);
|
||||||
if (!$p)
|
Model_User::save($this->context->user);
|
||||||
{
|
|
||||||
$p = R::dispense('postscore');
|
|
||||||
$p->post = $post;
|
|
||||||
$p->user = $this->context->user;
|
|
||||||
}
|
|
||||||
$p->score = $score;
|
|
||||||
R::store($p);
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -690,7 +434,6 @@ class PostController
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($id);
|
$post = Model_Post::locate($id);
|
||||||
R::preload($post, [
|
R::preload($post, [
|
||||||
'uploader' => 'user',
|
|
||||||
'tag',
|
'tag',
|
||||||
'comment',
|
'comment',
|
||||||
'ownComment.commenter' => 'user']);
|
'ownComment.commenter' => 'user']);
|
||||||
|
@ -728,19 +471,8 @@ class PostController
|
||||||
$buildNextPostQuery($nextPostQuery, $id, true);
|
$buildNextPostQuery($nextPostQuery, $id, true);
|
||||||
$nextPost = $nextPostQuery->get('row');
|
$nextPost = $nextPostQuery->get('row');
|
||||||
|
|
||||||
$favorite = false;
|
$favorite = $this->context->user->hasFavorited($post);
|
||||||
$score = null;
|
$score = $this->context->user->getScore($post);
|
||||||
if ($this->context->loggedIn)
|
|
||||||
{
|
|
||||||
foreach ($post->ownFavoritee as $fav)
|
|
||||||
if ($fav->user->id == $this->context->user->id)
|
|
||||||
$favorite = true;
|
|
||||||
|
|
||||||
$s = R::findOne('postscore', 'post_id = ? AND user_id = ?', [$post->id, $this->context->user->id]);
|
|
||||||
if ($s)
|
|
||||||
$score = intval($s->score);
|
|
||||||
}
|
|
||||||
|
|
||||||
$flagged = in_array(TextHelper::reprPost($post), SessionHelper::get('flagged', []));
|
$flagged = in_array(TextHelper::reprPost($post), SessionHelper::get('flagged', []));
|
||||||
|
|
||||||
$this->context->pageThumb = \Chibi\UrlHelper::route('post', 'thumb', ['name' => $post->name]);
|
$this->context->pageThumb = \Chibi\UrlHelper::route('post', 'thumb', ['name' => $post->name]);
|
||||||
|
@ -754,7 +486,7 @@ class PostController
|
||||||
$this->context->transport->post = $post;
|
$this->context->transport->post = $post;
|
||||||
$this->context->transport->prevPostId = $prevPost ? $prevPost['id'] : null;
|
$this->context->transport->prevPostId = $prevPost ? $prevPost['id'] : null;
|
||||||
$this->context->transport->nextPostId = $nextPost ? $nextPost['id'] : null;
|
$this->context->transport->nextPostId = $nextPost ? $nextPost['id'] : null;
|
||||||
$this->context->transport->tagsToken = self::serializeTags($post);
|
$this->context->transport->editToken = self::serializePost($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -765,98 +497,25 @@ class PostController
|
||||||
*/
|
*/
|
||||||
public function thumbAction($name, $width = null, $height = null)
|
public function thumbAction($name, $width = null, $height = null)
|
||||||
{
|
{
|
||||||
$dstWidth = $width === null ? $this->config->browsing->thumbWidth : $width;
|
$path = Model_Post::getThumbCustomPath($name, $width, $height);
|
||||||
$dstHeight = $height === null ? $this->config->browsing->thumbHeight : $height;
|
|
||||||
$dstWidth = min(1000, max(1, $dstWidth));
|
|
||||||
$dstHeight = min(1000, max(1, $dstHeight));
|
|
||||||
|
|
||||||
$this->context->layoutName = 'layout-file';
|
|
||||||
|
|
||||||
$path = $this->config->main->thumbsPath . DS . $name . '.custom';
|
|
||||||
if (!file_exists($path))
|
if (!file_exists($path))
|
||||||
$path = $this->config->main->thumbsPath . DS . $name . '-' . $dstWidth . 'x' . $dstHeight . '.default';
|
{
|
||||||
|
$path = Model_Post::getThumbDefaultPath($name, $width, $height);
|
||||||
if (!file_exists($path))
|
if (!file_exists($path))
|
||||||
{
|
{
|
||||||
$post = Model_Post::locate($name);
|
$post = Model_Post::locate($name);
|
||||||
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ListPosts);
|
PrivilegesHelper::confirmWithException(Privilege::ListPosts);
|
||||||
PrivilegesHelper::confirmWithException(Privilege::ListPosts, PostSafety::toString($post->safety));
|
PrivilegesHelper::confirmWithException(Privilege::ListPosts, PostSafety::toString($post->safety));
|
||||||
$srcPath = $this->config->main->filesPath . DS . $post->name;
|
$post->makeThumb($width, $height);
|
||||||
|
if (!file_exists($path))
|
||||||
if ($post->type == PostType::Youtube)
|
|
||||||
{
|
|
||||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
|
||||||
$contents = file_get_contents('http://img.youtube.com/vi/' . $post->orig_name . '/mqdefault.jpg');
|
|
||||||
file_put_contents($tmpPath, $contents);
|
|
||||||
if (file_exists($tmpPath))
|
|
||||||
$srcImage = imagecreatefromjpeg($tmpPath);
|
|
||||||
}
|
|
||||||
else switch ($post->mime_type)
|
|
||||||
{
|
|
||||||
case 'image/jpeg':
|
|
||||||
$srcImage = imagecreatefromjpeg($srcPath);
|
|
||||||
break;
|
|
||||||
case 'image/png':
|
|
||||||
$srcImage = imagecreatefrompng($srcPath);
|
|
||||||
break;
|
|
||||||
case 'image/gif':
|
|
||||||
$srcImage = imagecreatefromgif($srcPath);
|
|
||||||
break;
|
|
||||||
case 'application/x-shockwave-flash':
|
|
||||||
$srcImage = null;
|
|
||||||
exec('which dump-gnash', $tmp, $exitCode);
|
|
||||||
if ($exitCode == 0)
|
|
||||||
{
|
|
||||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
|
||||||
exec('dump-gnash --screenshot last --screenshot-file ' . $tmpPath . ' -1 -r1 --max-advances 15 ' . $srcPath);
|
|
||||||
if (file_exists($tmpPath))
|
|
||||||
$srcImage = imagecreatefrompng($tmpPath);
|
|
||||||
}
|
|
||||||
if (!$srcImage)
|
|
||||||
{
|
|
||||||
exec('which swfrender', $tmp, $exitCode);
|
|
||||||
if ($exitCode == 0)
|
|
||||||
{
|
|
||||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
|
||||||
exec('swfrender ' . $srcPath . ' -o ' . $tmpPath);
|
|
||||||
if (file_exists($tmpPath))
|
|
||||||
$srcImage = imagecreatefrompng($tmpPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($srcImage))
|
|
||||||
{
|
|
||||||
switch ($this->config->browsing->thumbStyle)
|
|
||||||
{
|
|
||||||
case 'outside':
|
|
||||||
$dstImage = ThumbnailHelper::cropOutside($srcImage, $dstWidth, $dstHeight);
|
|
||||||
break;
|
|
||||||
case 'inside':
|
|
||||||
$dstImage = ThumbnailHelper::cropInside($srcImage, $dstWidth, $dstHeight);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new SimpleException('Unknown thumbnail crop style');
|
|
||||||
}
|
|
||||||
|
|
||||||
imagejpeg($dstImage, $path);
|
|
||||||
imagedestroy($srcImage);
|
|
||||||
imagedestroy($dstImage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$path = $this->config->main->mediaPath . DS . 'img' . DS . 'thumb.jpg';
|
$path = $this->config->main->mediaPath . DS . 'img' . DS . 'thumb.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($tmpPath))
|
|
||||||
unlink($tmpPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_readable($path))
|
if (!is_readable($path))
|
||||||
throw new SimpleException('Thumbnail file is not readable');
|
throw new SimpleException('Thumbnail file is not readable');
|
||||||
|
|
||||||
|
$this->context->layoutName = 'layout-file';
|
||||||
$this->context->transport->cacheDaysToLive = 30;
|
$this->context->transport->cacheDaysToLive = 30;
|
||||||
$this->context->transport->mimeType = 'image/jpeg';
|
$this->context->transport->mimeType = 'image/jpeg';
|
||||||
$this->context->transport->fileHash = 'thumb' . md5($name . filemtime($path));
|
$this->context->transport->fileHash = 'thumb' . md5($name . filemtime($path));
|
||||||
|
@ -873,7 +532,6 @@ class PostController
|
||||||
{
|
{
|
||||||
$this->context->layoutName = 'layout-file';
|
$this->context->layoutName = 'layout-file';
|
||||||
$post = Model_Post::locate($name, true);
|
$post = Model_Post::locate($name, true);
|
||||||
R::preload($post, ['tag']);
|
|
||||||
|
|
||||||
PrivilegesHelper::confirmWithException(Privilege::RetrievePost);
|
PrivilegesHelper::confirmWithException(Privilege::RetrievePost);
|
||||||
PrivilegesHelper::confirmWithException(Privilege::RetrievePost, PostSafety::toString($post->safety));
|
PrivilegesHelper::confirmWithException(Privilege::RetrievePost, PostSafety::toString($post->safety));
|
||||||
|
@ -902,4 +560,117 @@ class PostController
|
||||||
$this->context->transport->fileHash = 'post' . $post->file_hash;
|
$this->context->transport->fileHash = 'post' . $post->file_hash;
|
||||||
$this->context->transport->filePath = $path;
|
$this->context->transport->filePath = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function doEdit($post, $isNew)
|
||||||
|
{
|
||||||
|
/* file contents */
|
||||||
|
if (isset($_FILES['file']))
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostFile, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$suppliedFile = $_FILES['file'];
|
||||||
|
self::handleUploadErrors($suppliedFile);
|
||||||
|
|
||||||
|
$srcPath = $suppliedFile['tmp_name'];
|
||||||
|
$post->setContentFromPath($srcPath);
|
||||||
|
|
||||||
|
if (!$isNew)
|
||||||
|
LogHelper::logEvent('post-edit', '{user} changed contents of {post}', ['post' => TextHelper::reprPost($post)]);
|
||||||
|
}
|
||||||
|
elseif (InputHelper::get('url'))
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostFile, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$url = InputHelper::get('url');
|
||||||
|
$post->setContentFromUrl($url);
|
||||||
|
|
||||||
|
if (!$isNew)
|
||||||
|
LogHelper::logEvent('post-edit', '{user} changed contents of {post}', ['post' => TextHelper::reprPost($post)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* safety */
|
||||||
|
$suppliedSafety = InputHelper::get('safety');
|
||||||
|
if ($suppliedSafety !== null)
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostSafety, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$oldSafety = $post->safety;
|
||||||
|
$post->setSafety($suppliedSafety);
|
||||||
|
$newSafety = $post->safety;
|
||||||
|
|
||||||
|
if ($oldSafety != $newSafety)
|
||||||
|
LogHelper::logEvent('post-edit', '{user} changed safety for {post} to {safety}', ['post' => TextHelper::reprPost($post), 'safety' => PostSafety::toString($post->safety)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tags */
|
||||||
|
$suppliedTags = InputHelper::get('tags');
|
||||||
|
if ($suppliedTags !== null)
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostTags, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$oldTags = array_map(function($tag) { return $tag->name; }, $post->sharedTag);
|
||||||
|
$post->setTagsFromText($suppliedTags);
|
||||||
|
$newTags = array_map(function($tag) { return $tag->name; }, $post->sharedTag);
|
||||||
|
|
||||||
|
foreach (array_diff($oldTags, $newTags) as $tag)
|
||||||
|
LogHelper::logEvent('post-tag-del', '{user} untagged {post} with {tag}', ['post' => TextHelper::reprPost($post), 'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
|
||||||
|
foreach (array_diff($newTags, $oldTags) as $tag)
|
||||||
|
LogHelper::logEvent('post-tag-add', '{user} tagged {post} with {tag}', ['post' => TextHelper::reprPost($post), 'tag' => TextHelper::reprTag($tag)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* source */
|
||||||
|
$suppliedSource = InputHelper::get('source');
|
||||||
|
if ($suppliedSource !== null)
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostSource, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$oldSource = $post->source;
|
||||||
|
$post->setSource($suppliedSource);
|
||||||
|
$newSource = $post->source;
|
||||||
|
|
||||||
|
if ($oldSource != $newSource)
|
||||||
|
LogHelper::logEvent('post-edit', '{user} changed source for {post} to {source}', ['post' => TextHelper::reprPost($post), 'source' => $post->source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relations */
|
||||||
|
$suppliedRelations = InputHelper::get('relations');
|
||||||
|
if ($suppliedRelations !== null)
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostRelations, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$oldRelatedIds = array_map(function($post) { return $post->id; }, $post->via('crossref')->sharedPost);
|
||||||
|
$post->setRelationsFromText($suppliedRelations);
|
||||||
|
$newRelatedIds = array_map(function($post) { return $post->id; }, $post->via('crossref')->sharedPost);
|
||||||
|
|
||||||
|
foreach (array_diff($oldRelatedIds, $newRelatedIds) as $post2id)
|
||||||
|
LogHelper::logEvent('post-relation-del', '{user} removed relation between {post} and {post2}', ['post' => TextHelper::reprPost($post), 'post2' => TextHelper::reprPost($post2id)]);
|
||||||
|
|
||||||
|
foreach (array_diff($newRelatedIds, $oldRelatedIds) as $post2id)
|
||||||
|
LogHelper::logEvent('post-relation-add', '{user} added relation between {post} and {post2}', ['post' => TextHelper::reprPost($post), 'post2' => TextHelper::reprPost($post2id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* thumbnail */
|
||||||
|
if (!empty($_FILES['thumb']['name']))
|
||||||
|
{
|
||||||
|
if (!$isNew)
|
||||||
|
PrivilegesHelper::confirmWithException(Privilege::EditPostThumb, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||||
|
|
||||||
|
$suppliedFile = $_FILES['thumb'];
|
||||||
|
self::handleUploadErrors($suppliedFile);
|
||||||
|
|
||||||
|
$srcPath = $suppliedFile['tmp_name'];
|
||||||
|
$post->setCustomThumbnailFromPath($srcPath);
|
||||||
|
|
||||||
|
LogHelper::logEvent('post-edit', '{user} changed thumb for {post}', ['post' => TextHelper::reprPost($post)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ class TagController
|
||||||
PrivilegesHelper::confirmWithException(Privilege::MergeTags);
|
PrivilegesHelper::confirmWithException(Privilege::MergeTags);
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
|
Model_Tag::removeUnused();
|
||||||
|
|
||||||
$suppliedSourceTag = InputHelper::get('source-tag');
|
$suppliedSourceTag = InputHelper::get('source-tag');
|
||||||
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
||||||
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
||||||
|
@ -60,9 +62,9 @@ class TagController
|
||||||
if ($postTag->id == $sourceTag->id)
|
if ($postTag->id == $sourceTag->id)
|
||||||
unset($post->sharedTag[$key]);
|
unset($post->sharedTag[$key]);
|
||||||
$post->sharedTag []= $targetTag;
|
$post->sharedTag []= $targetTag;
|
||||||
R::store($post);
|
Model_Post::save($post);
|
||||||
}
|
}
|
||||||
R::trash($sourceTag);
|
Model_Tag::remove($sourceTag);
|
||||||
|
|
||||||
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list'));
|
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list'));
|
||||||
LogHelper::logEvent('tag-merge', '{user} merged {source} with {target}', ['source' => TextHelper::reprTag($suppliedSourceTag), 'target' => TextHelper::reprTag($suppliedTargetTag)]);
|
LogHelper::logEvent('tag-merge', '{user} merged {source} with {target}', ['source' => TextHelper::reprTag($suppliedSourceTag), 'target' => TextHelper::reprTag($suppliedTargetTag)]);
|
||||||
|
@ -83,6 +85,8 @@ class TagController
|
||||||
PrivilegesHelper::confirmWithException(Privilege::MergeTags);
|
PrivilegesHelper::confirmWithException(Privilege::MergeTags);
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
|
Model_Tag::removeUnused();
|
||||||
|
|
||||||
$suppliedSourceTag = InputHelper::get('source-tag');
|
$suppliedSourceTag = InputHelper::get('source-tag');
|
||||||
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
||||||
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
||||||
|
@ -95,7 +99,7 @@ class TagController
|
||||||
throw new SimpleException('Target tag already exists');
|
throw new SimpleException('Target tag already exists');
|
||||||
|
|
||||||
$sourceTag->name = $suppliedTargetTag;
|
$sourceTag->name = $suppliedTargetTag;
|
||||||
R::store($sourceTag);
|
Model_Tag::save($sourceTag);
|
||||||
|
|
||||||
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list'));
|
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('tag', 'list'));
|
||||||
LogHelper::logEvent('tag-rename', '{user} renamed {source} to {target}', ['source' => TextHelper::reprTag($suppliedSourceTag), 'target' => TextHelper::reprTag($suppliedTargetTag)]);
|
LogHelper::logEvent('tag-rename', '{user} renamed {source} to {target}', ['source' => TextHelper::reprTag($suppliedSourceTag), 'target' => TextHelper::reprTag($suppliedTargetTag)]);
|
||||||
|
|
|
@ -184,7 +184,7 @@ class UserController
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
$user->banned = true;
|
$user->banned = true;
|
||||||
R::store($user);
|
Model_User::save($user);
|
||||||
|
|
||||||
LogHelper::logEvent('ban', '{user} banned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
LogHelper::logEvent('ban', '{user} banned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
|
@ -205,7 +205,7 @@ class UserController
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
$user->banned = false;
|
$user->banned = false;
|
||||||
R::store($user);
|
Model_User::save($user);
|
||||||
|
|
||||||
LogHelper::logEvent('unban', '{user} unbanned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
LogHelper::logEvent('unban', '{user} unbanned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
|
@ -225,7 +225,7 @@ class UserController
|
||||||
if (InputHelper::get('submit'))
|
if (InputHelper::get('submit'))
|
||||||
{
|
{
|
||||||
$user->staff_confirmed = true;
|
$user->staff_confirmed = true;
|
||||||
R::store($user);
|
Model_User::save($user);
|
||||||
LogHelper::logEvent('reg-accept', '{user} confirmed account for {subject}', ['subject' => TextHelper::reprUser($user)]);
|
LogHelper::logEvent('reg-accept', '{user} confirmed account for {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
|
@ -257,22 +257,11 @@ class UserController
|
||||||
if ($suppliedPasswordHash != $user->pass_hash)
|
if ($suppliedPasswordHash != $user->pass_hash)
|
||||||
throw new SimpleException('Must supply valid password');
|
throw new SimpleException('Must supply valid password');
|
||||||
}
|
}
|
||||||
R::trashAll(R::find('postscore', 'user_id = ?', [$user->id]));
|
|
||||||
foreach ($user->alias('commenter')->ownComment as $comment)
|
$oldId = $user->id;
|
||||||
{
|
Model_User::remove($user);
|
||||||
$comment->commenter = null;
|
if ($oldId == $this->context->user->id)
|
||||||
R::store($comment);
|
|
||||||
}
|
|
||||||
foreach ($user->alias('uploader')->ownPost as $post)
|
|
||||||
{
|
|
||||||
$post->uploader = null;
|
|
||||||
R::store($post);
|
|
||||||
}
|
|
||||||
$user->ownFavoritee = [];
|
|
||||||
if ($user->id == $this->context->user->id)
|
|
||||||
AuthController::doLogOut();
|
AuthController::doLogOut();
|
||||||
R::store($user);
|
|
||||||
R::trash($user);
|
|
||||||
|
|
||||||
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index'));
|
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index'));
|
||||||
LogHelper::logEvent('user-del', '{user} removed account for {subject}', ['subject' => TextHelper::reprUser($name)]);
|
LogHelper::logEvent('user-del', '{user} removed account for {subject}', ['subject' => TextHelper::reprUser($name)]);
|
||||||
|
@ -305,7 +294,7 @@ class UserController
|
||||||
|
|
||||||
$user->enableEndlessScrolling(InputHelper::get('endless-scrolling'));
|
$user->enableEndlessScrolling(InputHelper::get('endless-scrolling'));
|
||||||
|
|
||||||
R::store($user);
|
Model_User::save($user);
|
||||||
if ($user->id == $this->context->user->id)
|
if ($user->id == $this->context->user->id)
|
||||||
$this->context->user = $user;
|
$this->context->user = $user;
|
||||||
AuthController::doReLog();
|
AuthController::doReLog();
|
||||||
|
@ -394,7 +383,7 @@ class UserController
|
||||||
if ($suppliedPasswordHash != $currentPasswordHash)
|
if ($suppliedPasswordHash != $currentPasswordHash)
|
||||||
throw new SimpleException('Must supply valid current password');
|
throw new SimpleException('Must supply valid current password');
|
||||||
}
|
}
|
||||||
R::store($user);
|
Model_User::save($user);
|
||||||
|
|
||||||
if ($confirmMail)
|
if ($confirmMail)
|
||||||
self::sendEmailChangeConfirmation($user);
|
self::sendEmailChangeConfirmation($user);
|
||||||
|
@ -478,7 +467,7 @@ class UserController
|
||||||
|
|
||||||
AuthController::doReLog();
|
AuthController::doReLog();
|
||||||
if (!$this->context->user->anonymous)
|
if (!$this->context->user->anonymous)
|
||||||
R::store($this->context->user);
|
Model_User::save($this->context->user);
|
||||||
|
|
||||||
StatusHelper::success();
|
StatusHelper::success();
|
||||||
}
|
}
|
||||||
|
@ -523,9 +512,8 @@ class UserController
|
||||||
throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.');
|
throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.');
|
||||||
|
|
||||||
//register the user
|
//register the user
|
||||||
$dbUser = R::dispense('user');
|
$dbUser = Model_User::create();
|
||||||
$dbUser->name = $suppliedName;
|
$dbUser->name = $suppliedName;
|
||||||
$dbUser->pass_salt = md5(mt_rand() . uniqid());
|
|
||||||
$dbUser->pass_hash = Model_User::hashPassword($suppliedPassword, $dbUser->pass_salt);
|
$dbUser->pass_hash = Model_User::hashPassword($suppliedPassword, $dbUser->pass_salt);
|
||||||
$dbUser->email_unconfirmed = $suppliedEmail;
|
$dbUser->email_unconfirmed = $suppliedEmail;
|
||||||
|
|
||||||
|
@ -546,7 +534,7 @@ class UserController
|
||||||
}
|
}
|
||||||
|
|
||||||
//save the user to db if everything went okay
|
//save the user to db if everything went okay
|
||||||
R::store($dbUser);
|
Model_User::save($dbUser);
|
||||||
|
|
||||||
if (!empty($dbUser->email_unconfirmed))
|
if (!empty($dbUser->email_unconfirmed))
|
||||||
self::sendEmailChangeConfirmation($dbUser);
|
self::sendEmailChangeConfirmation($dbUser);
|
||||||
|
@ -589,7 +577,7 @@ class UserController
|
||||||
$dbUser->email_unconfirmed = null;
|
$dbUser->email_unconfirmed = null;
|
||||||
$dbToken->used = true;
|
$dbToken->used = true;
|
||||||
R::store($dbToken);
|
R::store($dbToken);
|
||||||
R::store($dbUser);
|
Model_User::save($dbUser);
|
||||||
|
|
||||||
LogHelper::logEvent('user-activation', '{subject} just activated account', ['subject' => TextHelper::reprUser($dbUser)]);
|
LogHelper::logEvent('user-activation', '{subject} just activated account', ['subject' => TextHelper::reprUser($dbUser)]);
|
||||||
$message = 'Activation completed successfully.';
|
$message = 'Activation completed successfully.';
|
||||||
|
@ -626,7 +614,7 @@ class UserController
|
||||||
$dbUser->pass_hash = Model_User::hashPassword($randomPassword, $dbUser->pass_salt);
|
$dbUser->pass_hash = Model_User::hashPassword($randomPassword, $dbUser->pass_salt);
|
||||||
$dbToken->used = true;
|
$dbToken->used = true;
|
||||||
R::store($dbToken);
|
R::store($dbToken);
|
||||||
R::store($dbUser);
|
Model_User::save($dbUser);
|
||||||
|
|
||||||
LogHelper::logEvent('user-pass-reset', '{subject} just reset password', ['subject' => TextHelper::reprUser($dbUser)]);
|
LogHelper::logEvent('user-pass-reset', '{subject} just reset password', ['subject' => TextHelper::reprUser($dbUser)]);
|
||||||
$message = 'Password reset successful. Your new password is **' . $randomPassword . '**.';
|
$message = 'Password reset successful. Your new password is **' . $randomPassword . '**.';
|
||||||
|
|
|
@ -50,4 +50,34 @@ abstract class AbstractModel extends RedBean_SimpleModel
|
||||||
$dbQuery->from($table);
|
$dbQuery->from($table);
|
||||||
return intval($dbQuery->get('row')['count']);
|
return intval($dbQuery->get('row')['count']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function create()
|
||||||
|
{
|
||||||
|
return R::dispense(static::getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function remove($entity)
|
||||||
|
{
|
||||||
|
R::trash($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function save($entity)
|
||||||
|
{
|
||||||
|
R::store($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FUSE methods for RedBeanPHP, preventing some aliasing errors */
|
||||||
|
public function open()
|
||||||
|
{
|
||||||
|
$this->preload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function after_update()
|
||||||
|
{
|
||||||
|
$this->preload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preload()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,13 @@ class Model_Comment extends AbstractModel
|
||||||
return 'Model_Comment_QueryBuilder';
|
return 'Model_Comment_QueryBuilder';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function preload()
|
||||||
|
{
|
||||||
|
R::preload($this->bean, ['commenter' => 'user', 'post', 'post.uploader' => 'user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function locate($key, $throw = true)
|
public static function locate($key, $throw = true)
|
||||||
{
|
{
|
||||||
$comment = R::findOne(self::getTableName(), 'id = ?', [$key]);
|
$comment = R::findOne(self::getTableName(), 'id = ?', [$key]);
|
||||||
|
@ -23,6 +30,8 @@ class Model_Comment extends AbstractModel
|
||||||
return $comment;
|
return $comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function validateText($text)
|
public static function validateText($text)
|
||||||
{
|
{
|
||||||
$text = trim($text);
|
$text = trim($text);
|
||||||
|
|
|
@ -1,6 +1,30 @@
|
||||||
<?php
|
<?php
|
||||||
class Model_Post extends AbstractModel
|
class Model_Post extends AbstractModel
|
||||||
{
|
{
|
||||||
|
protected static $config;
|
||||||
|
|
||||||
|
public static function initModel()
|
||||||
|
{
|
||||||
|
self::$config = \Chibi\Registry::getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTableName()
|
||||||
|
{
|
||||||
|
return 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getQueryBuilder()
|
||||||
|
{
|
||||||
|
return 'Model_Post_QueryBuilder';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preload()
|
||||||
|
{
|
||||||
|
R::preload($this->bean, ['uploader' => 'user','favoritee' => 'user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function locate($key, $disallowNumeric = false, $throw = true)
|
public static function locate($key, $disallowNumeric = false, $throw = true)
|
||||||
{
|
{
|
||||||
if (is_numeric($key) and !$disallowNumeric)
|
if (is_numeric($key) and !$disallowNumeric)
|
||||||
|
@ -26,6 +50,42 @@ class Model_Post extends AbstractModel
|
||||||
return $post;
|
return $post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function create()
|
||||||
|
{
|
||||||
|
$post = R::dispense(self::getTableName());
|
||||||
|
$post->hidden = false;
|
||||||
|
$post->upload_date = time();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$post->name = md5(mt_rand() . uniqid());
|
||||||
|
}
|
||||||
|
while (file_exists(self::getFullPath($post->name)));
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function remove($post)
|
||||||
|
{
|
||||||
|
//remove stuff from auxiliary tables
|
||||||
|
R::trashAll(R::find('postscore', 'post_id = ?', [$post->id]));
|
||||||
|
R::trashAll(R::find('crossref', 'post_id = ? OR post2_id = ?', [$post->id, $post->id]));
|
||||||
|
foreach ($post->ownComment as $comment)
|
||||||
|
{
|
||||||
|
$comment->post = null;
|
||||||
|
R::store($comment);
|
||||||
|
}
|
||||||
|
$post->ownFavoritee = [];
|
||||||
|
$post->sharedTag = [];
|
||||||
|
R::store($post);
|
||||||
|
R::trash($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function save($post)
|
||||||
|
{
|
||||||
|
R::store($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function validateSafety($safety)
|
public static function validateSafety($safety)
|
||||||
{
|
{
|
||||||
$safety = intval($safety);
|
$safety = intval($safety);
|
||||||
|
@ -47,14 +107,48 @@ class Model_Post extends AbstractModel
|
||||||
return $source;
|
return $source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTableName()
|
private static function validateThumbSize($width, $height)
|
||||||
{
|
{
|
||||||
return 'post';
|
$width = $width === null ? self::$config->browsing->thumbWidth : $width;
|
||||||
|
$height = $height === null ? self::$config->browsing->thumbHeight : $height;
|
||||||
|
$width = min(1000, max(1, $width));
|
||||||
|
$height = min(1000, max(1, $height));
|
||||||
|
return [$width, $height];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getQueryBuilder()
|
public static function getAllPostCount()
|
||||||
{
|
{
|
||||||
return 'Model_Post_QueryBuilder';
|
return R::$f
|
||||||
|
->begin()
|
||||||
|
->select('count(1)')
|
||||||
|
->as('count')
|
||||||
|
->from(self::getTableName())
|
||||||
|
->get('row')['count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getThumbPathTokenized($text, $name, $width = null, $height = null)
|
||||||
|
{
|
||||||
|
list ($width, $height) = self::validateThumbSize($width, $height);
|
||||||
|
|
||||||
|
return TextHelper::replaceTokens($text, [
|
||||||
|
'fullpath' => self::$config->main->thumbsPath . DS . $name,
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getThumbCustomPath($name, $width = null, $height = null)
|
||||||
|
{
|
||||||
|
return self::getThumbPathTokenized('{fullpath}.custom', $name, $width, $height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getThumbDefaultPath($name, $width = null, $height = null)
|
||||||
|
{
|
||||||
|
return self::getThumbPathTokenized('{fullpath}-{width}x{height}.default', $name, $width, $height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getFullPath($name)
|
||||||
|
{
|
||||||
|
return self::$config->main->filesPath . DS . $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isTaggedWith($tagName)
|
public function isTaggedWith($tagName)
|
||||||
|
@ -65,4 +159,269 @@ class Model_Post extends AbstractModel
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setHidden($hidden)
|
||||||
|
{
|
||||||
|
$this->hidden = boolval($hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setSafety($safety)
|
||||||
|
{
|
||||||
|
$this->safety = self::validateSafety($safety);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setSource($source)
|
||||||
|
{
|
||||||
|
$this->source = self::validateSource($source);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setTagsFromText($tagsText)
|
||||||
|
{
|
||||||
|
$tagNames = Model_Tag::validateTags($tagsText);
|
||||||
|
$dbTags = Model_Tag::insertOrUpdate($tagNames);
|
||||||
|
|
||||||
|
$this->sharedTag = $dbTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setRelationsFromText($relationsText)
|
||||||
|
{
|
||||||
|
$relatedIds = array_filter(preg_split('/\D/', $relationsText));
|
||||||
|
|
||||||
|
$relatedPosts = [];
|
||||||
|
foreach ($relatedIds as $relatedId)
|
||||||
|
{
|
||||||
|
if ($relatedId == $this->id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (count($relatedPosts) > self::$config->browsing->maxRelatedPosts)
|
||||||
|
throw new SimpleException('Too many related posts (maximum: ' . self::$config->browsing->maxRelatedPosts . ')');
|
||||||
|
|
||||||
|
$relatedPosts []= self::locate($relatedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->bean->via('crossref')->sharedPost = $relatedPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setCustomThumbnailFromPath($srcPath)
|
||||||
|
{
|
||||||
|
$mimeType = mime_content_type($srcPath);
|
||||||
|
if (!in_array($mimeType, ['image/gif', 'image/png', 'image/jpeg']))
|
||||||
|
throw new SimpleException('Invalid thumbnail type "' . $mimeType . '"');
|
||||||
|
|
||||||
|
list ($imageWidth, $imageHeight) = getimagesize($srcPath);
|
||||||
|
if ($imageWidth != self::$config->browsing->thumbWidth)
|
||||||
|
throw new SimpleException('Invalid thumbnail width (should be ' . self::$config->browsing->thumbWidth . ')');
|
||||||
|
if ($imageWidth != self::$config->browsing->thumbHeight)
|
||||||
|
throw new SimpleException('Invalid thumbnail width (should be ' . self::$config->browsing->thumbHeight . ')');
|
||||||
|
|
||||||
|
$dstPath = self::getThumbCustomPath($this->name);
|
||||||
|
|
||||||
|
if (is_uploaded_file($srcPath))
|
||||||
|
move_uploaded_file($srcPath, $dstPath);
|
||||||
|
else
|
||||||
|
rename($srcPath, $dstPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setContentFromPath($srcPath)
|
||||||
|
{
|
||||||
|
$this->mime_type = mime_content_type($srcPath);
|
||||||
|
switch ($this->mime_type)
|
||||||
|
{
|
||||||
|
case 'image/gif':
|
||||||
|
case 'image/png':
|
||||||
|
case 'image/jpeg':
|
||||||
|
list ($imageWidth, $imageHeight) = getimagesize($srcPath);
|
||||||
|
$this->type = PostType::Image;
|
||||||
|
$this->image_width = $imageWidth;
|
||||||
|
$this->image_height = $imageHeight;
|
||||||
|
break;
|
||||||
|
case 'application/x-shockwave-flash':
|
||||||
|
list ($imageWidth, $imageHeight) = getimagesize($srcPath);
|
||||||
|
$this->type = PostType::Flash;
|
||||||
|
$this->image_width = $imageWidth;
|
||||||
|
$this->image_height = $imageHeight;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SimpleException('Invalid file type "' . $this->mime_type . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->orig_name = basename($srcPath);
|
||||||
|
$this->file_size = filesize($srcPath);
|
||||||
|
$this->file_hash = md5_file($srcPath);
|
||||||
|
$duplicatedPost = R::findOne('post', 'file_hash = ?', [$this->file_hash]);
|
||||||
|
if ($duplicatedPost !== null)
|
||||||
|
throw new SimpleException('Duplicate upload: @' . $duplicatedPost->id);
|
||||||
|
|
||||||
|
$dstPath = $this->getFullPath($this->name);
|
||||||
|
|
||||||
|
if (is_uploaded_file($srcPath))
|
||||||
|
move_uploaded_file($srcPath, $dstPath);
|
||||||
|
else
|
||||||
|
rename($srcPath, $dstPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function setContentFromUrl($srcUrl)
|
||||||
|
{
|
||||||
|
$this->orig_name = $srcUrl;
|
||||||
|
|
||||||
|
if (!preg_match('/^https?:\/\//', $srcUrl))
|
||||||
|
throw new SimpleException('Invalid URL "' . $srcUrl . '"');
|
||||||
|
|
||||||
|
if (preg_match('/youtube.com\/watch.*?=([a-zA-Z0-9_-]+)/', $srcUrl, $matches))
|
||||||
|
{
|
||||||
|
$origName = $matches[1];
|
||||||
|
$this->orig_name = $origName;
|
||||||
|
$this->type = PostType::Youtube;
|
||||||
|
$this->mime_type = null;
|
||||||
|
$this->file_size = null;
|
||||||
|
$this->file_hash = null;
|
||||||
|
$this->image_width = null;
|
||||||
|
$this->image_height = null;
|
||||||
|
|
||||||
|
$duplicatedPost = R::findOne('post', 'orig_name = ?', [$origName]);
|
||||||
|
if ($duplicatedPost !== null)
|
||||||
|
throw new SimpleException('Duplicate upload: @' . $duplicatedPost->id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$srcPath = tempnam(sys_get_temp_dir(), 'upload') . '.dat';
|
||||||
|
|
||||||
|
//warning: low level sh*t ahead
|
||||||
|
//download the URL $srcUrl into $srcPath
|
||||||
|
$maxBytes = TextHelper::stripBytesUnits(ini_get('upload_max_filesize'));
|
||||||
|
set_time_limit(0);
|
||||||
|
$urlFP = fopen($srcUrl, 'rb');
|
||||||
|
if (!$urlFP)
|
||||||
|
throw new SimpleException('Cannot open URL for reading');
|
||||||
|
$srcFP = fopen($srcPath, 'w+b');
|
||||||
|
if (!$srcFP)
|
||||||
|
{
|
||||||
|
fclose($urlFP);
|
||||||
|
throw new SimpleException('Cannot open file for writing');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!feof($urlFP))
|
||||||
|
{
|
||||||
|
$buffer = fread($urlFP, 4 * 1024);
|
||||||
|
if (fwrite($srcFP, $buffer) === false)
|
||||||
|
throw new SimpleException('Cannot write into file');
|
||||||
|
fflush($srcFP);
|
||||||
|
if (ftell($srcFP) > $maxBytes)
|
||||||
|
throw new SimpleException('File is too big (maximum allowed size: ' . TextHelper::useBytesUnits($maxBytes) . ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fclose($urlFP);
|
||||||
|
fclose($srcFP);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->setContentFromPath($srcPath);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (file_exists($srcPath))
|
||||||
|
unlink($srcPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function makeThumb($width = null, $height = null)
|
||||||
|
{
|
||||||
|
list ($width, $height) = self::validateThumbSize($width, $height);
|
||||||
|
$dstPath = self::getThumbDefaultPath($this->name, $width, $height);
|
||||||
|
$srcPath = self::getFullPath($this->name);
|
||||||
|
|
||||||
|
if ($this->type == PostType::Youtube)
|
||||||
|
{
|
||||||
|
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
||||||
|
$contents = file_get_contents('http://img.youtube.com/vi/' . $this->orig_name . '/mqdefault.jpg');
|
||||||
|
file_put_contents($tmpPath, $contents);
|
||||||
|
if (file_exists($tmpPath))
|
||||||
|
$srcImage = imagecreatefromjpeg($tmpPath);
|
||||||
|
}
|
||||||
|
else switch ($this->mime_type)
|
||||||
|
{
|
||||||
|
case 'image/jpeg':
|
||||||
|
$srcImage = imagecreatefromjpeg($srcPath);
|
||||||
|
break;
|
||||||
|
case 'image/png':
|
||||||
|
$srcImage = imagecreatefrompng($srcPath);
|
||||||
|
break;
|
||||||
|
case 'image/gif':
|
||||||
|
$srcImage = imagecreatefromgif($srcPath);
|
||||||
|
break;
|
||||||
|
case 'application/x-shockwave-flash':
|
||||||
|
$srcImage = null;
|
||||||
|
exec('which dump-gnash', $tmp, $exitCode);
|
||||||
|
if ($exitCode == 0)
|
||||||
|
{
|
||||||
|
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
||||||
|
exec('dump-gnash --screenshot last --screenshot-file ' . $tmpPath . ' -1 -r1 --max-advances 15 ' . $srcPath);
|
||||||
|
if (file_exists($tmpPath))
|
||||||
|
$srcImage = imagecreatefrompng($tmpPath);
|
||||||
|
}
|
||||||
|
if (!$srcImage)
|
||||||
|
{
|
||||||
|
exec('which swfrender', $tmp, $exitCode);
|
||||||
|
if ($exitCode == 0)
|
||||||
|
{
|
||||||
|
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
||||||
|
exec('swfrender ' . $srcPath . ' -o ' . $tmpPath);
|
||||||
|
if (file_exists($tmpPath))
|
||||||
|
$srcImage = imagecreatefrompng($tmpPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($tmpPath))
|
||||||
|
unlink($tmpPath);
|
||||||
|
|
||||||
|
if (!isset($srcImage))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (self::$config->browsing->thumbStyle)
|
||||||
|
{
|
||||||
|
case 'outside':
|
||||||
|
$dstImage = ThumbnailHelper::cropOutside($srcImage, $width, $height);
|
||||||
|
break;
|
||||||
|
case 'inside':
|
||||||
|
$dstImage = ThumbnailHelper::cropInside($srcImage, $width, $height);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SimpleException('Unknown thumbnail crop style');
|
||||||
|
}
|
||||||
|
|
||||||
|
imagejpeg($dstImage, $dstPath);
|
||||||
|
imagedestroy($srcImage);
|
||||||
|
imagedestroy($dstImage);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Model_Post::initModel();
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
class Model_Tag extends AbstractModel
|
class Model_Tag extends AbstractModel
|
||||||
{
|
{
|
||||||
|
public static function getTableName()
|
||||||
|
{
|
||||||
|
return 'tag';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getQueryBuilder()
|
||||||
|
{
|
||||||
|
return 'Model_Tag_Querybuilder';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function locate($key, $throw = true)
|
public static function locate($key, $throw = true)
|
||||||
{
|
{
|
||||||
$tag = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
$tag = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
||||||
|
@ -13,6 +25,8 @@ class Model_Tag extends AbstractModel
|
||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function removeUnused()
|
public static function removeUnused()
|
||||||
{
|
{
|
||||||
$dbQuery = R::$f
|
$dbQuery = R::$f
|
||||||
|
@ -68,14 +82,6 @@ class Model_Tag extends AbstractModel
|
||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPostCount()
|
|
||||||
{
|
|
||||||
if ($this->bean->getMeta('post_count'))
|
|
||||||
return $this->bean->getMeta('post_count');
|
|
||||||
return $this->bean->countShared('post');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function validateTags($tags)
|
public static function validateTags($tags)
|
||||||
{
|
{
|
||||||
$tags = trim($tags);
|
$tags = trim($tags);
|
||||||
|
@ -92,13 +98,10 @@ class Model_Tag extends AbstractModel
|
||||||
return $tags;
|
return $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTableName()
|
public function getPostCount()
|
||||||
{
|
{
|
||||||
return 'tag';
|
if ($this->bean->getMeta('post_count'))
|
||||||
}
|
return $this->bean->getMeta('post_count');
|
||||||
|
return $this->bean->countShared('post');
|
||||||
public static function getQueryBuilder()
|
|
||||||
{
|
|
||||||
return 'Model_Tag_Querybuilder';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,23 @@
|
||||||
<?php
|
<?php
|
||||||
class Model_User extends AbstractModel
|
class Model_User extends AbstractModel
|
||||||
{
|
{
|
||||||
|
const SETTING_SAFETY = 1;
|
||||||
|
const SETTING_ENDLESS_SCROLLING = 2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static function getTableName()
|
||||||
|
{
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getQueryBuilder()
|
||||||
|
{
|
||||||
|
return 'Model_User_QueryBuilder';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function locate($key, $throw = true)
|
public static function locate($key, $throw = true)
|
||||||
{
|
{
|
||||||
$user = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
$user = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
||||||
|
@ -17,82 +34,44 @@ class Model_User extends AbstractModel
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAvatarUrl($size = 32)
|
public static function create()
|
||||||
{
|
{
|
||||||
$subject = !empty($this->email_confirmed)
|
$user = R::dispense(self::getTableName());
|
||||||
? $this->email_confirmed
|
$user->pass_salt = md5(mt_rand() . uniqid());
|
||||||
: $this->pass_salt . $this->name;
|
return $user;
|
||||||
$hash = md5(strtolower(trim($subject)));
|
|
||||||
$url = 'http://www.gravatar.com/avatar/' . $hash . '?s=' . $size . '&d=retro';
|
|
||||||
return $url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSetting($key)
|
public static function remove($user)
|
||||||
{
|
{
|
||||||
$settings = json_decode($this->settings, true);
|
//remove stuff from auxiliary tables
|
||||||
return isset($settings[$key])
|
R::trashAll(R::find('postscore', 'user_id = ?', [$user->id]));
|
||||||
? $settings[$key]
|
foreach ($user->alias('commenter')->ownComment as $comment)
|
||||||
: null;
|
{
|
||||||
|
$comment->commenter = null;
|
||||||
|
R::store($comment);
|
||||||
|
}
|
||||||
|
foreach ($user->alias('uploader')->ownPost as $post)
|
||||||
|
{
|
||||||
|
$post->uploader = null;
|
||||||
|
R::store($post);
|
||||||
|
}
|
||||||
|
$user->ownFavoritee = [];
|
||||||
|
R::store($user);
|
||||||
|
R::trash($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSetting($key, $value)
|
public static function save($user)
|
||||||
{
|
{
|
||||||
$settings = json_decode($this->settings, true);
|
R::store($user);
|
||||||
$settings[$key] = $value;
|
|
||||||
$settings = json_encode($settings);
|
|
||||||
if (strlen($settings) > 200)
|
|
||||||
throw new SimpleException('Too much data');
|
|
||||||
$this->settings = $settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const SETTING_SAFETY = 1;
|
|
||||||
const SETTING_ENDLESS_SCROLLING = 2;
|
|
||||||
|
|
||||||
public function hasEnabledSafety($safety)
|
public static function getAnonymousName()
|
||||||
{
|
{
|
||||||
$all = $this->getSetting(self::SETTING_SAFETY);
|
return '[Anonymous user]';
|
||||||
if (!$all)
|
|
||||||
return $safety == PostSafety::Safe;
|
|
||||||
return $all & PostSafety::toFlag($safety);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enableSafety($safety, $enabled)
|
|
||||||
{
|
|
||||||
$all = $this->getSetting(self::SETTING_SAFETY);
|
|
||||||
if (!$all)
|
|
||||||
$all = PostSafety::toFlag(PostSafety::Safe);
|
|
||||||
|
|
||||||
$new = $all;
|
|
||||||
if (!$enabled)
|
|
||||||
{
|
|
||||||
$new &= ~PostSafety::toFlag($safety);
|
|
||||||
if (!$new)
|
|
||||||
$new = PostSafety::toFlag(PostSafety::Safe);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$new |= PostSafety::toFlag($safety);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setSetting(self::SETTING_SAFETY, $new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasEnabledEndlessScrolling()
|
|
||||||
{
|
|
||||||
$ret = $this->getSetting(self::SETTING_ENDLESS_SCROLLING);
|
|
||||||
if ($ret === null)
|
|
||||||
$ret = \Chibi\Registry::getConfig()->browsing->endlessScrollingDefault;
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enableEndlessScrolling($enabled)
|
|
||||||
{
|
|
||||||
$this->setSetting(self::SETTING_ENDLESS_SCROLLING, $enabled ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function validateUserName($userName)
|
public static function validateUserName($userName)
|
||||||
{
|
{
|
||||||
$userName = trim($userName);
|
$userName = trim($userName);
|
||||||
|
@ -168,13 +147,126 @@ class Model_User extends AbstractModel
|
||||||
return sha1($salt1 . $salt2 . $pass);
|
return sha1($salt1 . $salt2 . $pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTableName()
|
|
||||||
|
|
||||||
|
public function getAvatarUrl($size = 32)
|
||||||
{
|
{
|
||||||
return 'user';
|
$subject = !empty($this->email_confirmed)
|
||||||
|
? $this->email_confirmed
|
||||||
|
: $this->pass_salt . $this->name;
|
||||||
|
$hash = md5(strtolower(trim($subject)));
|
||||||
|
$url = 'http://www.gravatar.com/avatar/' . $hash . '?s=' . $size . '&d=retro';
|
||||||
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getQueryBuilder()
|
public function getSetting($key)
|
||||||
{
|
{
|
||||||
return 'Model_User_QueryBuilder';
|
$settings = json_decode($this->settings, true);
|
||||||
|
return isset($settings[$key])
|
||||||
|
? $settings[$key]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSetting($key, $value)
|
||||||
|
{
|
||||||
|
$settings = json_decode($this->settings, true);
|
||||||
|
$settings[$key] = $value;
|
||||||
|
$settings = json_encode($settings);
|
||||||
|
if (strlen($settings) > 200)
|
||||||
|
throw new SimpleException('Too much data');
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasEnabledSafety($safety)
|
||||||
|
{
|
||||||
|
$all = $this->getSetting(self::SETTING_SAFETY);
|
||||||
|
if (!$all)
|
||||||
|
return $safety == PostSafety::Safe;
|
||||||
|
return $all & PostSafety::toFlag($safety);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableSafety($safety, $enabled)
|
||||||
|
{
|
||||||
|
$all = $this->getSetting(self::SETTING_SAFETY);
|
||||||
|
if (!$all)
|
||||||
|
$all = PostSafety::toFlag(PostSafety::Safe);
|
||||||
|
|
||||||
|
$new = $all;
|
||||||
|
if (!$enabled)
|
||||||
|
{
|
||||||
|
$new &= ~PostSafety::toFlag($safety);
|
||||||
|
if (!$new)
|
||||||
|
$new = PostSafety::toFlag(PostSafety::Safe);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$new |= PostSafety::toFlag($safety);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setSetting(self::SETTING_SAFETY, $new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasEnabledEndlessScrolling()
|
||||||
|
{
|
||||||
|
$ret = $this->getSetting(self::SETTING_ENDLESS_SCROLLING);
|
||||||
|
if ($ret === null)
|
||||||
|
$ret = \Chibi\Registry::getConfig()->browsing->endlessScrollingDefault;
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableEndlessScrolling($enabled)
|
||||||
|
{
|
||||||
|
$this->setSetting(self::SETTING_ENDLESS_SCROLLING, $enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasFavorited($post)
|
||||||
|
{
|
||||||
|
foreach ($this->bean->ownFavoritee as $fav)
|
||||||
|
if ($fav->post->id == $post->id)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScore($post)
|
||||||
|
{
|
||||||
|
$s = R::findOne('postscore', 'post_id = ? AND user_id = ?', [$post->id, $this->id]);
|
||||||
|
if ($s)
|
||||||
|
return intval($s->score);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addToFavorites($post)
|
||||||
|
{
|
||||||
|
R::preload($this->bean, ['favoritee' => 'post']);
|
||||||
|
foreach ($this->bean->ownFavoritee as $fav)
|
||||||
|
if ($fav->post_id == $post->id)
|
||||||
|
throw new SimpleException('Already in favorites');
|
||||||
|
|
||||||
|
$this->bean->link('favoritee')->post = $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remFromFavorites($post)
|
||||||
|
{
|
||||||
|
$finalKey = null;
|
||||||
|
foreach ($this->bean->ownFavoritee as $key => $fav)
|
||||||
|
if ($fav->post_id == $post->id)
|
||||||
|
$finalKey = $key;
|
||||||
|
|
||||||
|
if ($finalKey === null)
|
||||||
|
throw new SimpleException('Not in favorites');
|
||||||
|
|
||||||
|
unset($this->bean->ownFavoritee[$finalKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function score($post, $score)
|
||||||
|
{
|
||||||
|
R::trashAll(R::find('postscore', 'post_id = ? AND user_id = ?', [$post->id, $this->id]));
|
||||||
|
$score = intval($score);
|
||||||
|
if ($score != 0)
|
||||||
|
{
|
||||||
|
$p = $this->bean->link('postscore');
|
||||||
|
$p->post = $post;
|
||||||
|
$p->score = $score;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ class Privilege extends Enum
|
||||||
const EditPostThumb = 8;
|
const EditPostThumb = 8;
|
||||||
const EditPostSource = 26;
|
const EditPostSource = 26;
|
||||||
const EditPostRelations = 30;
|
const EditPostRelations = 30;
|
||||||
|
const EditPostFile = 36;
|
||||||
const HidePost = 9;
|
const HidePost = 9;
|
||||||
const DeletePost = 10;
|
const DeletePost = 10;
|
||||||
const FeaturePost = 25;
|
const FeaturePost = 25;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<label class="left" for="tags">Tags:</label>
|
<label class="left" for="tags">Tags:</label>
|
||||||
<div class="input-wrapper"><input type="text" name="tags" id="tags" placeholder="enter some tags…" value="<?php echo join(',', array_map(function($tag) { return $tag->name; }, $this->context->transport->post->sharedTag)) ?>"/></div>
|
<div class="input-wrapper"><input type="text" name="tags" id="tags" placeholder="enter some tags…" value="<?php echo join(',', array_map(function($tag) { return $tag->name; }, $this->context->transport->post->sharedTag)) ?>"/></div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="tags-token" id="tags-token" value="<?php echo $this->context->transport->tagsToken ?>"/>
|
<input type="hidden" name="edit-token" id="edit-token" value="<?php echo $this->context->transport->editToken ?>"/>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (PrivilegesHelper::confirm(Privilege::EditPostSource, PrivilegesHelper::getIdentitySubPrivilege($this->context->transport->post->uploader))): ?>
|
<?php if (PrivilegesHelper::confirm(Privilege::EditPostSource, PrivilegesHelper::getIdentitySubPrivilege($this->context->transport->post->uploader))): ?>
|
||||||
|
|
|
@ -11,7 +11,7 @@ setlocale(LC_CTYPE, 'en_US.UTF-8');
|
||||||
ini_set('memory_limit', '128M');
|
ini_set('memory_limit', '128M');
|
||||||
|
|
||||||
//extension sanity checks
|
//extension sanity checks
|
||||||
$requiredExtensions = ['pdo', 'pdo_sqlite', 'gd', 'openssl'];
|
$requiredExtensions = ['pdo', 'pdo_sqlite', 'gd', 'openssl', 'fileinfo'];
|
||||||
foreach ($requiredExtensions as $ext)
|
foreach ($requiredExtensions as $ext)
|
||||||
if (!extension_loaded($ext))
|
if (!extension_loaded($ext))
|
||||||
die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL);
|
die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL);
|
||||||
|
|
Loading…
Reference in a new issue