diff --git a/config.ini b/config.ini index a4f49cd6..d454fd1e 100644 --- a/config.ini +++ b/config.ini @@ -75,6 +75,7 @@ hidePost.all=moderator deletePost.own=moderator deletePost.all=moderator featurePost=moderator +scorePost=registered listUsers=registered viewUser=registered diff --git a/public_html/media/css/post-view.css b/public_html/media/css/post-view.css index 96292564..4a3d68d0 100644 --- a/public_html/media/css/post-view.css +++ b/public_html/media/css/post-view.css @@ -65,6 +65,10 @@ embed { color: #df4b0d; } +#sidebar .score .selected { + font-weight: bold; +} + i.icon-prev { background-position: -12px -1px; diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 69e82bb4..d7f1304b 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -550,6 +550,35 @@ class PostController + /** + * @route /post/{id}/score/{score} + * @validate score -1|0|1 + */ + public function scoreAction($id, $score) + { + $post = Model_Post::locate($id); + PrivilegesHelper::confirmWithException(Privilege::ScorePost); + + if (InputHelper::get('submit')) + { + if (!$this->context->loggedIn) + throw new SimpleException('Not logged in'); + + $p = R::findOne('post_score', 'post_id = ? AND user_id = ?', [$id, $this->context->user->id]); + if (!$p) + { + $p = R::dispense('post_score'); + $p->post = $post; + $p->user = $this->context->user; + } + $p->score = $score; + R::store($p); + $this->context->transport->success = true; + } + } + + + /** * @route /post/{id}/feature */ @@ -612,16 +641,24 @@ class PostController $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('post_score', 'post_id = ? AND user_id = ?', [$post->id, $this->context->user->id]); + if ($s) + $score = intval($s->score); + } + $this->context->stylesheets []= 'post-view.css'; $this->context->stylesheets []= 'comment-small.css'; $this->context->scripts []= 'post-view.js'; $this->context->subTitle = 'showing @' . $post->id . ' – ' . join(', ', array_map(function($x) { return $x['name']; }, $post->sharedTag)); $this->context->favorite = $favorite; + $this->context->score = $score; $this->context->transport->post = $post; $this->context->transport->prevPostId = $prevPost ? $prevPost['id'] : null; $this->context->transport->nextPostId = $nextPost ? $nextPost['id'] : null; diff --git a/src/Models/Model_Post_QueryBuilder.php b/src/Models/Model_Post_QueryBuilder.php index afcec60f..0828fce9 100644 --- a/src/Models/Model_Post_QueryBuilder.php +++ b/src/Models/Model_Post_QueryBuilder.php @@ -91,6 +91,16 @@ class Model_Post_QueryBuilder implements AbstractQueryBuilder $dbQuery->addSql('id <= ?')->put(intval($val)); } + protected static function filterTokenScoreMin($dbQuery, $val) + { + $dbQuery->addSql('score >= ?')->put(intval($val)); + } + + protected static function filterTokenScoreMax($dbQuery, $val) + { + $dbQuery->addSql('score <= ?')->put(intval($val)); + } + protected static function filterTokenTagMin($dbQuery, $val) { $dbQuery->addSql('tag_count >= ?')->put(intval($val)); @@ -273,6 +283,10 @@ class Model_Post_QueryBuilder implements AbstractQueryBuilder case 'favcount': $orderColumn = 'fav_count'; break; + case 'score': + $orderDir *= -1; + $orderColumn = 'score'; + break; case 'tag': case 'tags': case 'tagcount': diff --git a/src/Models/Privilege.php b/src/Models/Privilege.php index e0d38f84..5ebf5461 100644 --- a/src/Models/Privilege.php +++ b/src/Models/Privilege.php @@ -14,6 +14,7 @@ class Privilege extends Enum const HidePost = 9; const DeletePost = 10; const FeaturePost = 25; + const ScorePost = 31; const ListUsers = 11; const ViewUser = 12; diff --git a/src/Upgrades/Upgrade4.sql b/src/Upgrades/Upgrade4.sql new file mode 100644 index 00000000..6d1474d8 --- /dev/null +++ b/src/Upgrades/Upgrade4.sql @@ -0,0 +1,30 @@ +ALTER TABLE post ADD COLUMN score INTEGER NOT NULL DEFAULT 0; + +UPDATE post SET score = 0; + +CREATE TABLE post_score +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + post_id INTEGER, + user_id INTEGER, + score INTEGER, + FOREIGN KEY(post_id) REFERENCES post(id) ON DELETE CASCADE ON UPDATE SET NULL, + FOREIGN KEY(user_id) REFERENCES post(id) ON DELETE CASCADE ON UPDATE SET NULL +); +CREATE INDEX idx_fk_post_score_post_id ON post_score(post_id); +CREATE INDEX idx_fk_post_score_user_id ON post_score(user_id); + +CREATE TRIGGER post_score_update AFTER UPDATE ON post_score FOR EACH ROW +BEGIN + UPDATE post SET score = post.score - old.score + new.score WHERE post.id = new.post_id; +END; + +CREATE TRIGGER post_score_insert AFTER INSERT ON post_score FOR EACH ROW +BEGIN + UPDATE post SET score = post.score + new.score WHERE post.id = new.post_id; +END; + +CREATE TRIGGER post_score_delete BEFORE DELETE ON post_score FOR EACH ROW +BEGIN + UPDATE post SET score = post.score - old.score WHERE post.id = old.post_id; +END; diff --git a/src/Views/index-help.phtml b/src/Views/index-help.phtml index bafe75e4..25cf302a 100644 --- a/src/Views/index-help.phtml +++ b/src/Views/index-help.phtml @@ -22,6 +22,7 @@
  • favorited by David: fav:David
  • favorited by at least four users: favmin:4
  • commented by at least three users: commentmin:3
  • +
  • having minimum score of 4: scoremin:4
  • tagged with at least seven tags: tagmin:7
  • exactly from the specified date: date:2001, date:2012-09-29 (yyyy-mm-dd format)
  • from the specified date onwards: datemin:2001-01-01
  • @@ -40,7 +41,8 @@
  • newest to oldest: order:date (pretty much default browse view)
  • oldest to newest: -order:date
  • most commented first: order:comments
  • -
  • loved by most: order:favs
  • +
  • loved by most: order:favs
  • +
  • highest scored: order:score
  • As shown with -order:date, any of them can be reversed in the same way as negating other tags: by placing a dash before the tag.

    diff --git a/src/Views/post-view.phtml b/src/Views/post-view.phtml index a6eabfac..b5f33d6f 100644 --- a/src/Views/post-view.phtml +++ b/src/Views/post-view.phtml @@ -70,6 +70,36 @@ +
    + Score: + + context->transport->post->score ?> + + +  [ + $this->context->transport->post->id, 'score' => '{score}']) ?> + + context->score === 1): ?> + + + + + up + + +  |  + + context->score === -1): ?> + + + + + down + ] + + +
    +
    Date: diff --git a/src/core.php b/src/core.php index 37cef1d8..f6c86b76 100644 --- a/src/core.php +++ b/src/core.php @@ -48,6 +48,7 @@ $config = configFactory(); R::setup('sqlite:' . $config->main->dbPath); R::freeze(true); R::dependencies(['tag' => ['post'], 'favoritee' => ['post', 'user'], 'comment' => ['post', 'user']]); +R::setStrictTyping(false); //wire models \Chibi\AutoLoader::init([__DIR__ . '/../' . $config->chibi->userCodeDir, __DIR__]); diff --git a/upgrade.php b/upgrade.php index 93f89e17..21617354 100644 --- a/upgrade.php +++ b/upgrade.php @@ -17,6 +17,7 @@ foreach ($upgrades as $upgradePath) { printf('Executing %s...' . PHP_EOL, $upgradePath); $upgradeSql = file_get_contents($upgradePath); + $upgradeSql = preg_replace('/^[ \t]+(.*);/m', '\0--', $upgradeSql); $queries = preg_split('/;\s*[\r\n]+/s', $upgradeSql); $queries = array_map('trim', $queries); foreach ($queries as $query)