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
|
||||
editPostRelations.own=registered
|
||||
editPostRelations.all=moderator
|
||||
editPostFile.all=moderator
|
||||
editPostFile.own=moderator
|
||||
hidePost.own=moderator
|
||||
hidePost.all=moderator
|
||||
deletePost.own=moderator
|
||||
|
|
|
@ -25,7 +25,6 @@ class CommentController
|
|||
$page = max(1, min($pageCount, $page));
|
||||
$comments = Model_Comment::getEntities(null, $commentsPerPage, $page);
|
||||
|
||||
R::preload($comments, ['commenter' => 'user', 'post', 'post.uploader' => 'user']);
|
||||
$this->context->postGroups = true;
|
||||
$this->context->transport->paginator = new StdClass;
|
||||
$this->context->transport->paginator->page = $page;
|
||||
|
@ -55,7 +54,7 @@ class CommentController
|
|||
$text = InputHelper::get('text');
|
||||
$text = Model_Comment::validateText($text);
|
||||
|
||||
$comment = R::dispense('comment');
|
||||
$comment = Model_Comment::create();
|
||||
$comment->post = $post;
|
||||
if ($this->context->loggedIn)
|
||||
$comment->commenter = $this->context->user;
|
||||
|
@ -63,7 +62,7 @@ class CommentController
|
|||
$comment->text = $text;
|
||||
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)]);
|
||||
}
|
||||
$this->context->transport->textPreview = $comment->getText();
|
||||
|
@ -80,10 +79,10 @@ class CommentController
|
|||
public function deleteAction($id)
|
||||
{
|
||||
$comment = Model_Comment::locate($id);
|
||||
R::preload($comment, ['commenter' => 'user']);
|
||||
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)]);
|
||||
R::trash($comment);
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class IndexController
|
|||
{
|
||||
$this->context->subTitle = 'home';
|
||||
$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;
|
||||
|
||||
|
|
|
@ -8,13 +8,18 @@ class PostController
|
|||
$callback();
|
||||
}
|
||||
|
||||
private static function serializeTags($post)
|
||||
private static function serializePost($post)
|
||||
{
|
||||
$x = [];
|
||||
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);
|
||||
$x = join('', $x);
|
||||
$x = join(' ', $x);
|
||||
return md5($x);
|
||||
}
|
||||
|
||||
|
@ -120,7 +125,6 @@ class PostController
|
|||
public function toggleTagAction($id, $tag)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['uploader' => 'user']);
|
||||
$this->context->transport->post = $post;
|
||||
|
||||
$tagRow = Model_Tag::locate($tag, false);
|
||||
|
@ -146,7 +150,7 @@ class PostController
|
|||
$dbTags = Model_Tag::insertOrUpdate($tags);
|
||||
$post->sharedTag = $dbTags;
|
||||
|
||||
R::store($post);
|
||||
Model_Post::save($post);
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
@ -192,173 +196,38 @@ class PostController
|
|||
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
/* file contents */
|
||||
if (isset($_FILES['file']))
|
||||
R::transaction(function()
|
||||
{
|
||||
$suppliedFile = $_FILES['file'];
|
||||
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 . '"');
|
||||
$post = Model_Post::create();
|
||||
|
||||
if (preg_match('/youtube.com\/watch.*?=([a-zA-Z0-9_-]+)/', $url, $matches))
|
||||
{
|
||||
$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 */
|
||||
//basic stuff
|
||||
$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)
|
||||
$dbPost->uploader = $this->context->user;
|
||||
$dbPost->ownFavoritee = [];
|
||||
$dbPost->sharedTag = $dbTags;
|
||||
$post->uploader = $this->context->user;
|
||||
|
||||
if ($sourcePath)
|
||||
{
|
||||
if (is_uploaded_file($sourcePath))
|
||||
move_uploaded_file($sourcePath, $path);
|
||||
else
|
||||
rename($sourcePath, $path);
|
||||
}
|
||||
R::store($dbPost);
|
||||
//store the post to get the ID in the logs
|
||||
Model_Post::save($post);
|
||||
|
||||
//log
|
||||
LogHelper::bufferChanges();
|
||||
$fmt = ($anonymous and !$this->config->misc->logAnonymousUploads)
|
||||
? 'someone'
|
||||
: '{user}';
|
||||
$fmt .= ' added {post} tagged with {tags} marked as {safety}';
|
||||
LogHelper::logEvent('post-new', $fmt, [
|
||||
'post' => TextHelper::reprPost($dbPost),
|
||||
'tags' => join(', ', array_map(['TextHelper', 'reprTag'], $dbTags)),
|
||||
'safety' => PostSafety::toString($dbPost->safety)]);
|
||||
$fmt .= ' added {post}';
|
||||
LogHelper::logEvent('post-new', $fmt, ['post' => TextHelper::reprPost($post)]);
|
||||
|
||||
//after logging basic info, do the editing stuff
|
||||
$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();
|
||||
}
|
||||
|
@ -372,111 +241,21 @@ class PostController
|
|||
public function editAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['uploader' => 'user']);
|
||||
$this->context->transport->post = $post;
|
||||
|
||||
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();
|
||||
$this->doEdit($post, false);
|
||||
LogHelper::flush();
|
||||
|
||||
/* safety */
|
||||
$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_Post::save($post);
|
||||
Model_Tag::removeUnused();
|
||||
|
||||
LogHelper::flush();
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
@ -514,13 +293,12 @@ class PostController
|
|||
public function hideAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['uploader' => 'user']);
|
||||
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
$post->hidden = true;
|
||||
R::store($post);
|
||||
$post->setHidden(true);
|
||||
Model_Post::save($post);
|
||||
|
||||
LogHelper::logEvent('post-hide', '{user} hidden {post}', ['post' => TextHelper::reprPost($post)]);
|
||||
StatusHelper::success();
|
||||
|
@ -535,13 +313,12 @@ class PostController
|
|||
public function unhideAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['uploader' => 'user']);
|
||||
PrivilegesHelper::confirmWithException(Privilege::HidePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
$post->hidden = false;
|
||||
R::store($post);
|
||||
$post->setHidden(false);
|
||||
Model_Post::save($post);
|
||||
|
||||
LogHelper::logEvent('post-unhide', '{user} unhidden {post}', ['post' => TextHelper::reprPost($post)]);
|
||||
StatusHelper::success();
|
||||
|
@ -556,23 +333,11 @@ class PostController
|
|||
public function deleteAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['uploader' => 'user']);
|
||||
PrivilegesHelper::confirmWithException(Privilege::DeletePost, PrivilegesHelper::getIdentitySubPrivilege($post->uploader));
|
||||
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
//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);
|
||||
Model_Post::remove($post);
|
||||
|
||||
LogHelper::logEvent('post-delete', '{user} deleted {post}', ['post' => TextHelper::reprPost($id)]);
|
||||
StatusHelper::success();
|
||||
|
@ -588,7 +353,6 @@ class PostController
|
|||
public function addFavoriteAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['favoritee' => 'user']);
|
||||
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
||||
|
||||
if (InputHelper::get('submit'))
|
||||
|
@ -596,12 +360,8 @@ class PostController
|
|||
if (!$this->context->loggedIn)
|
||||
throw new SimpleException('Not logged in');
|
||||
|
||||
foreach ($post->via('favoritee')->sharedUser as $fav)
|
||||
if ($fav->id == $this->context->user->id)
|
||||
throw new SimpleException('Already in favorites');
|
||||
|
||||
$post->link('favoritee')->user = $this->context->user;
|
||||
R::store($post);
|
||||
$this->context->user->addToFavorites($post);
|
||||
Model_User::save($this->context->user);
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +373,6 @@ class PostController
|
|||
public function remFavoriteAction($id)
|
||||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, ['favoritee' => 'user']);
|
||||
PrivilegesHelper::confirmWithException(Privilege::FavoritePost);
|
||||
|
||||
if (InputHelper::get('submit'))
|
||||
|
@ -621,16 +380,8 @@ class PostController
|
|||
if (!$this->context->loggedIn)
|
||||
throw new SimpleException('Not logged in');
|
||||
|
||||
$finalKey = null;
|
||||
foreach ($post->ownFavoritee as $key => $fav)
|
||||
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);
|
||||
$this->context->user->remFromFavorites($post);
|
||||
Model_User::save($this->context->user);
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
@ -651,15 +402,8 @@ class PostController
|
|||
if (!$this->context->loggedIn)
|
||||
throw new SimpleException('Not logged in');
|
||||
|
||||
$p = R::findOne('postscore', 'post_id = ? AND user_id = ?', [$post->id, $this->context->user->id]);
|
||||
if (!$p)
|
||||
{
|
||||
$p = R::dispense('postscore');
|
||||
$p->post = $post;
|
||||
$p->user = $this->context->user;
|
||||
}
|
||||
$p->score = $score;
|
||||
R::store($p);
|
||||
$this->context->user->score($post, $score);
|
||||
Model_User::save($this->context->user);
|
||||
StatusHelper::success();
|
||||
}
|
||||
}
|
||||
|
@ -690,7 +434,6 @@ class PostController
|
|||
{
|
||||
$post = Model_Post::locate($id);
|
||||
R::preload($post, [
|
||||
'uploader' => 'user',
|
||||
'tag',
|
||||
'comment',
|
||||
'ownComment.commenter' => 'user']);
|
||||
|
@ -728,19 +471,8 @@ class PostController
|
|||
$buildNextPostQuery($nextPostQuery, $id, true);
|
||||
$nextPost = $nextPostQuery->get('row');
|
||||
|
||||
$favorite = false;
|
||||
$score = null;
|
||||
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);
|
||||
}
|
||||
|
||||
$favorite = $this->context->user->hasFavorited($post);
|
||||
$score = $this->context->user->getScore($post);
|
||||
$flagged = in_array(TextHelper::reprPost($post), SessionHelper::get('flagged', []));
|
||||
|
||||
$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->prevPostId = $prevPost ? $prevPost['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)
|
||||
{
|
||||
$dstWidth = $width === null ? $this->config->browsing->thumbWidth : $width;
|
||||
$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';
|
||||
$path = Model_Post::getThumbCustomPath($name, $width, $height);
|
||||
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))
|
||||
{
|
||||
$post = Model_Post::locate($name);
|
||||
|
||||
PrivilegesHelper::confirmWithException(Privilege::ListPosts);
|
||||
PrivilegesHelper::confirmWithException(Privilege::ListPosts, PostSafety::toString($post->safety));
|
||||
$srcPath = $this->config->main->filesPath . DS . $post->name;
|
||||
|
||||
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
|
||||
{
|
||||
$post->makeThumb($width, $height);
|
||||
if (!file_exists($path))
|
||||
$path = $this->config->main->mediaPath . DS . 'img' . DS . 'thumb.jpg';
|
||||
}
|
||||
|
||||
if (isset($tmpPath))
|
||||
unlink($tmpPath);
|
||||
}
|
||||
|
||||
if (!is_readable($path))
|
||||
throw new SimpleException('Thumbnail file is not readable');
|
||||
|
||||
$this->context->layoutName = 'layout-file';
|
||||
$this->context->transport->cacheDaysToLive = 30;
|
||||
$this->context->transport->mimeType = 'image/jpeg';
|
||||
$this->context->transport->fileHash = 'thumb' . md5($name . filemtime($path));
|
||||
|
@ -873,7 +532,6 @@ class PostController
|
|||
{
|
||||
$this->context->layoutName = 'layout-file';
|
||||
$post = Model_Post::locate($name, true);
|
||||
R::preload($post, ['tag']);
|
||||
|
||||
PrivilegesHelper::confirmWithException(Privilege::RetrievePost);
|
||||
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->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);
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
Model_Tag::removeUnused();
|
||||
|
||||
$suppliedSourceTag = InputHelper::get('source-tag');
|
||||
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
||||
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
||||
|
@ -60,9 +62,9 @@ class TagController
|
|||
if ($postTag->id == $sourceTag->id)
|
||||
unset($post->sharedTag[$key]);
|
||||
$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'));
|
||||
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);
|
||||
if (InputHelper::get('submit'))
|
||||
{
|
||||
Model_Tag::removeUnused();
|
||||
|
||||
$suppliedSourceTag = InputHelper::get('source-tag');
|
||||
$suppliedSourceTag = Model_Tag::validateTag($suppliedSourceTag);
|
||||
$sourceTag = Model_Tag::locate($suppliedSourceTag);
|
||||
|
@ -95,7 +99,7 @@ class TagController
|
|||
throw new SimpleException('Target tag already exists');
|
||||
|
||||
$sourceTag->name = $suppliedTargetTag;
|
||||
R::store($sourceTag);
|
||||
Model_Tag::save($sourceTag);
|
||||
|
||||
\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)]);
|
||||
|
|
|
@ -184,7 +184,7 @@ class UserController
|
|||
if (InputHelper::get('submit'))
|
||||
{
|
||||
$user->banned = true;
|
||||
R::store($user);
|
||||
Model_User::save($user);
|
||||
|
||||
LogHelper::logEvent('ban', '{user} banned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||
StatusHelper::success();
|
||||
|
@ -205,7 +205,7 @@ class UserController
|
|||
if (InputHelper::get('submit'))
|
||||
{
|
||||
$user->banned = false;
|
||||
R::store($user);
|
||||
Model_User::save($user);
|
||||
|
||||
LogHelper::logEvent('unban', '{user} unbanned {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||
StatusHelper::success();
|
||||
|
@ -225,7 +225,7 @@ class UserController
|
|||
if (InputHelper::get('submit'))
|
||||
{
|
||||
$user->staff_confirmed = true;
|
||||
R::store($user);
|
||||
Model_User::save($user);
|
||||
LogHelper::logEvent('reg-accept', '{user} confirmed account for {subject}', ['subject' => TextHelper::reprUser($user)]);
|
||||
StatusHelper::success();
|
||||
}
|
||||
|
@ -257,22 +257,11 @@ class UserController
|
|||
if ($suppliedPasswordHash != $user->pass_hash)
|
||||
throw new SimpleException('Must supply valid password');
|
||||
}
|
||||
R::trashAll(R::find('postscore', 'user_id = ?', [$user->id]));
|
||||
foreach ($user->alias('commenter')->ownComment as $comment)
|
||||
{
|
||||
$comment->commenter = null;
|
||||
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)
|
||||
|
||||
$oldId = $user->id;
|
||||
Model_User::remove($user);
|
||||
if ($oldId == $this->context->user->id)
|
||||
AuthController::doLogOut();
|
||||
R::store($user);
|
||||
R::trash($user);
|
||||
|
||||
\Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index'));
|
||||
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'));
|
||||
|
||||
R::store($user);
|
||||
Model_User::save($user);
|
||||
if ($user->id == $this->context->user->id)
|
||||
$this->context->user = $user;
|
||||
AuthController::doReLog();
|
||||
|
@ -394,7 +383,7 @@ class UserController
|
|||
if ($suppliedPasswordHash != $currentPasswordHash)
|
||||
throw new SimpleException('Must supply valid current password');
|
||||
}
|
||||
R::store($user);
|
||||
Model_User::save($user);
|
||||
|
||||
if ($confirmMail)
|
||||
self::sendEmailChangeConfirmation($user);
|
||||
|
@ -478,7 +467,7 @@ class UserController
|
|||
|
||||
AuthController::doReLog();
|
||||
if (!$this->context->user->anonymous)
|
||||
R::store($this->context->user);
|
||||
Model_User::save($this->context->user);
|
||||
|
||||
StatusHelper::success();
|
||||
}
|
||||
|
@ -523,9 +512,8 @@ class UserController
|
|||
throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.');
|
||||
|
||||
//register the user
|
||||
$dbUser = R::dispense('user');
|
||||
$dbUser = Model_User::create();
|
||||
$dbUser->name = $suppliedName;
|
||||
$dbUser->pass_salt = md5(mt_rand() . uniqid());
|
||||
$dbUser->pass_hash = Model_User::hashPassword($suppliedPassword, $dbUser->pass_salt);
|
||||
$dbUser->email_unconfirmed = $suppliedEmail;
|
||||
|
||||
|
@ -546,7 +534,7 @@ class UserController
|
|||
}
|
||||
|
||||
//save the user to db if everything went okay
|
||||
R::store($dbUser);
|
||||
Model_User::save($dbUser);
|
||||
|
||||
if (!empty($dbUser->email_unconfirmed))
|
||||
self::sendEmailChangeConfirmation($dbUser);
|
||||
|
@ -589,7 +577,7 @@ class UserController
|
|||
$dbUser->email_unconfirmed = null;
|
||||
$dbToken->used = true;
|
||||
R::store($dbToken);
|
||||
R::store($dbUser);
|
||||
Model_User::save($dbUser);
|
||||
|
||||
LogHelper::logEvent('user-activation', '{subject} just activated account', ['subject' => TextHelper::reprUser($dbUser)]);
|
||||
$message = 'Activation completed successfully.';
|
||||
|
@ -626,7 +614,7 @@ class UserController
|
|||
$dbUser->pass_hash = Model_User::hashPassword($randomPassword, $dbUser->pass_salt);
|
||||
$dbToken->used = true;
|
||||
R::store($dbToken);
|
||||
R::store($dbUser);
|
||||
Model_User::save($dbUser);
|
||||
|
||||
LogHelper::logEvent('user-pass-reset', '{subject} just reset password', ['subject' => TextHelper::reprUser($dbUser)]);
|
||||
$message = 'Password reset successful. Your new password is **' . $randomPassword . '**.';
|
||||
|
|
|
@ -50,4 +50,34 @@ abstract class AbstractModel extends RedBean_SimpleModel
|
|||
$dbQuery->from($table);
|
||||
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';
|
||||
}
|
||||
|
||||
public function preload()
|
||||
{
|
||||
R::preload($this->bean, ['commenter' => 'user', 'post', 'post.uploader' => 'user']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function locate($key, $throw = true)
|
||||
{
|
||||
$comment = R::findOne(self::getTableName(), 'id = ?', [$key]);
|
||||
|
@ -23,6 +30,8 @@ class Model_Comment extends AbstractModel
|
|||
return $comment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function validateText($text)
|
||||
{
|
||||
$text = trim($text);
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
<?php
|
||||
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)
|
||||
{
|
||||
if (is_numeric($key) and !$disallowNumeric)
|
||||
|
@ -26,6 +50,42 @@ class Model_Post extends AbstractModel
|
|||
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)
|
||||
{
|
||||
$safety = intval($safety);
|
||||
|
@ -47,14 +107,48 @@ class Model_Post extends AbstractModel
|
|||
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)
|
||||
|
@ -65,4 +159,269 @@ class Model_Post extends AbstractModel
|
|||
return true;
|
||||
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
|
||||
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)
|
||||
{
|
||||
$tag = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
||||
|
@ -13,6 +25,8 @@ class Model_Tag extends AbstractModel
|
|||
return $tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function removeUnused()
|
||||
{
|
||||
$dbQuery = R::$f
|
||||
|
@ -68,14 +82,6 @@ class Model_Tag extends AbstractModel
|
|||
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)
|
||||
{
|
||||
$tags = trim($tags);
|
||||
|
@ -92,13 +98,10 @@ class Model_Tag extends AbstractModel
|
|||
return $tags;
|
||||
}
|
||||
|
||||
public static function getTableName()
|
||||
public function getPostCount()
|
||||
{
|
||||
return 'tag';
|
||||
}
|
||||
|
||||
public static function getQueryBuilder()
|
||||
{
|
||||
return 'Model_Tag_Querybuilder';
|
||||
if ($this->bean->getMeta('post_count'))
|
||||
return $this->bean->getMeta('post_count');
|
||||
return $this->bean->countShared('post');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
<?php
|
||||
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)
|
||||
{
|
||||
$user = R::findOne(self::getTableName(), 'LOWER(name) = LOWER(?)', [$key]);
|
||||
|
@ -17,82 +34,44 @@ class Model_User extends AbstractModel
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getAvatarUrl($size = 32)
|
||||
public static function create()
|
||||
{
|
||||
$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;
|
||||
$user = R::dispense(self::getTableName());
|
||||
$user->pass_salt = md5(mt_rand() . uniqid());
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function getSetting($key)
|
||||
public static function remove($user)
|
||||
{
|
||||
$settings = json_decode($this->settings, true);
|
||||
return isset($settings[$key])
|
||||
? $settings[$key]
|
||||
: null;
|
||||
//remove stuff from auxiliary tables
|
||||
R::trashAll(R::find('postscore', 'user_id = ?', [$user->id]));
|
||||
foreach ($user->alias('commenter')->ownComment as $comment)
|
||||
{
|
||||
$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);
|
||||
$settings[$key] = $value;
|
||||
$settings = json_encode($settings);
|
||||
if (strlen($settings) > 200)
|
||||
throw new SimpleException('Too much data');
|
||||
$this->settings = $settings;
|
||||
R::store($user);
|
||||
}
|
||||
|
||||
|
||||
const SETTING_SAFETY = 1;
|
||||
const SETTING_ENDLESS_SCROLLING = 2;
|
||||
|
||||
public function hasEnabledSafety($safety)
|
||||
public static function getAnonymousName()
|
||||
{
|
||||
$all = $this->getSetting(self::SETTING_SAFETY);
|
||||
if (!$all)
|
||||
return $safety == PostSafety::Safe;
|
||||
return $all & PostSafety::toFlag($safety);
|
||||
return '[Anonymous user]';
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$userName = trim($userName);
|
||||
|
@ -168,13 +147,126 @@ class Model_User extends AbstractModel
|
|||
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 EditPostSource = 26;
|
||||
const EditPostRelations = 30;
|
||||
const EditPostFile = 36;
|
||||
const HidePost = 9;
|
||||
const DeletePost = 10;
|
||||
const FeaturePost = 25;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<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>
|
||||
<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 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');
|
||||
|
||||
//extension sanity checks
|
||||
$requiredExtensions = ['pdo', 'pdo_sqlite', 'gd', 'openssl'];
|
||||
$requiredExtensions = ['pdo', 'pdo_sqlite', 'gd', 'openssl', 'fileinfo'];
|
||||
foreach ($requiredExtensions as $ext)
|
||||
if (!extension_loaded($ext))
|
||||
die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL);
|
||||
|
|
Loading…
Reference in a new issue