Sequenzia/app/controllers/PoolController.php
2013-10-26 18:06:58 -05:00

404 lines
15 KiB
PHP
Executable File

<?php
class PoolController extends ApplicationController
{
protected function init()
{
$this->helper('Post');
}
protected function filters()
{
return [
'before' => [
'user_can_see_posts' => ['only' => ['zip']],
'member_only' => ['only' => ['destroy', 'update', 'add_post', 'remove_post', 'import', 'zip']],
'contributor_only' => ['only' => ['copy', 'transfer_metadata']]
]
];
}
public function index()
{
$this->set_title('Pools');
$sql_query = Pool::none()->page($this->page_number())->perPage(CONFIG()->pool_index_default_limit);
$order = $this->params()->order ?: 'id';
$search_tokens = array();
if ($this->params()->query) {
$this->set_title($this->params()->query . " - Pools");
// $query = array_map(function($v){return addslashes($v);}, explode($this->params()->query);
// $query = Tokenize.tokenize_with_quotes($this->params[:query] || "")
$query = explode(' ', addslashes($this->params()->query));
foreach ($query as &$token) {
if (preg_match('/^(order|limit|posts):(.+)$/', $token, $m)) {
if ($m[1] == "order") {
$order = $m[2];
} elseif ($m[1] == "limit") {
$sql_query->perPage(min((int)$m[2], 100));
} elseif ($m[1] == "posts") {
Post::generate_sql_range_helper(Tag::parse_helper($m[2]), "post_count", $sql_query);
}
} else {
// # TODO: removing ^\w- from token.
// $token = preg_replace('~[^\w-]~', '', $token);
$search_tokens[] = $token;
}
}
}
if (!empty($search_tokens)) {
// $value_index_query = QueryParser.escape_for_tsquery($search_tokens);
$value_index_query = implode('_', $search_tokens);
if ($value_index_query) {
# If a search keyword contains spaces, then it was quoted in the search query
# and we should only match adjacent words. tsquery won't do this for us; we need
# to filter results where the words aren't adjacent.
#
# This has a side-effect: any stopwords, stemming, parsing, etc. rules performed
# by to_tsquery won't be done here. We need to perform the same processing as
# is used to generate search_index. We don't perform all of the stemming rules, so
# although "jump" may match "jumping", "jump beans" won't match "jumping beans" because
# we'll filter it out.
#
# This also doesn't perform tokenization, so some obscure cases won't match perfectly;
# for example, "abc def" will match "xxxabc def abc" when it probably shouldn't. Doing
# this more correctly requires Postgresql support that doesn't exist right now.
foreach ($query as $q) {
# Don't do this if there are no spaces in the query, so we don't turn off tsquery
# parsing when we don't need to.
// if (!strstr($q, ' ')) continue;
$sql_query->where("(position(LOWER(?) IN LOWER(REPLACE(name, '_', ' '))) > 0 OR position(LOWER(?) IN LOWER(description)) > 0)",
$q,
$q);
}
}
}
if (empty($order))
$order = empty($search_tokens) ? 'date' : 'name';
switch ($order) {
case "name":
$sql_query->order("name asc");
break;
case "date":
$sql_query->order("created_at desc");
case "updated":
$sql_query->order("updated_at desc");
break;
case "id":
$sql_query->order("id desc");
break;
default:
$sql_query->order("created_at desc");
break;
}
$this->pools = $sql_query->paginate();
$samples = [];
foreach($this->pools as $p) {
if (!$post = $p->get_sample())
continue;
$p_id = (string)$p->id;
$samples[$p_id] = $post;
}
$this->samples = $samples;
$this->respond_to_list('pools');
}
public function show()
{
if (isset($this->params()->samples) && $this->params()->samples == 0)
unset($this->params()->samples);
$this->pool = Pool::find($this->params()->id);
$this->browse_mode = current_user()->pool_browse_mode;
// $q = Tag::parse_query("");
$q = [];
$q['pool'] = (int)$this->params()->id;
$q['show_deleted_only'] = false;
if ($this->browse_mode == 1) {
$q['limit'] = 1000;
$q['order'] = "portrait_pool";
} else {
$q['limit'] = 24;
}
$page = (int)$this->page_number() > 0 ? (int)$this->page_number() : 1;
$offset = ($page-1)*$q['limit'];
list($sql, $params) = Post::generate_sql($q, array('from_api' => true, 'offset' => $offset, 'limit' => $q['limit']));
$posts = Post::findBySql($sql, $params);
$this->posts = new Rails\ActiveRecord\Collection($posts->members(), ['page' => $page, 'perPage' => $q['limit'], 'offset' => $offset, 'totalRows' => $posts->totalRows()]);
$this->set_title($this->pool->pretty_name());
# iTODO:
$this->respondTo([
'html',
// 'xml' => function() {
// $builder = new Builder_XmlMarkup(['indent' => 2]);
// $builder->instruct();
// $xml = $this->pool->to_xml(['builder' => $builder, 'skip_instruct' => true], function() {
// $builder->posts(function() use ($builder) {
// foreach ($this->posts as $post)
// $post->to_xml(['builder' => $builder, 'skip_instruct' => true]);
// })
// });
// $this->render(['xml' => $xml]);
// },
'json' => function() {
$this->render(['json' => $this->pool->toJson()]);
}
]);
}
public function update()
{
$this->pool = Pool::find($this->params()->id);
if (!$this->pool->can_be_updated_by(current_user())) {
$this->access_denied();
return;
}
if ($this->request()->isPost()) {
$this->pool->updateAttributes($this->params()->pool);
$this->respond_to_success("Pool updated", array(array('#show', 'id' => $this->params()->id)));
}
}
public function create()
{
if ($this->request()->isPost()) {
$pool = Pool::create(array_merge($this->params()->pool, array('user_id' => current_user()->id)));
if ($pool->errors()->blank())
$this->respond_to_success("Pool created", array(array('#show', 'id' => $pool->id)));
else
$this->respond_to_error($pool, "#index");
}
}
public function copy()
{
$this->old_pool = Pool::find($this->params()->id);
$name = $this->params()->name ?: $this->old_pool->name . ' (copy)';
$this->new_pool = new Pool(['user_id' => $this->current_user->id, 'name' => $name, 'description' => $this->old_pool->description]);
if ($this->request()->isPost()) {
$this->new_pool->save();
if ($this->new_pool->errors()->any()) {
$this->respond_to_error($this->new_pool, ['#index']);
return;
}
foreach ($this->old_pool->pool_posts as $pp) {
$this->new_pool->add_post($pp->post_id, ['sequence' => $pp->sequence]);
}
$this->respond_to_success("Pool created", ['#show', 'id' => $this->new_pool->id]);
}
}
public function destroy()
{
$this->pool = Pool::find($this->params()->id);
if ($this->request()->isPost()) {
if ($this->pool->can_be_updated_by(current_user())) {
$this->pool->destroy();
$this->respond_to_success("Pool deleted", "#index");
} else
$this->access_denied();
}
}
public function addPost()
{
if ($this->request()->isPost()) {
# iMod
if ($this->request()->format() == 'json') {
try {
$pool = Pool::find($this->params()->pool_id);
} catch (Rails\ActiveRecord\Exception\RecordNotFoundException $e) {
$this->render(['json' => ['reason' => 'Pool not found']]);
return;
}
} else
$pool = Pool::find($this->params()->pool_id);
$this->session()->last_pool_id = $pool->id;
if (isset($this->params()->pool) && !empty($this->params()->pool['sequence']))
$sequence = $this->params()->pool['sequence'];
else
$sequence = null;
try {
$pool->add_post($this->params()->post_id, array('sequence' => $sequence, 'user' => current_user()));
$this->respond_to_success('Post added', array(array('post#show', 'id' => $this->params()->post_id)));
} catch (Pool_PostAlreadyExistsError $e) {
$this->respond_to_error("Post already exists", array('post#show', 'id' => $this->params()->post_id), array('status' => 423));
} catch (Pool_AccessDeniedError $e) {
$this->access_denied();
} catch (Exception $e) {
$this->respond_to_error(get_class($e), array('post#show', 'id' => $this->params()->post_id));
}
} else {
if (current_user()->is_anonymous)
$pools = Pool::where("is_active = TRUE AND is_public = TRUE")->order("name")->take();
else
$pools = Pool::where("is_active = TRUE AND (is_public = TRUE OR user_id = ?)", current_user()->id)->order("name")->take();
$post = Post::find($this->params()->post_id);
}
}
public function removePost()
{
$pool = Pool::find($this->params()->pool_id);
$post = Post::find($this->params()->post_id);
if ($this->request()->isPost()) {
try {
$pool->remove_post($this->params()->post_id, array('user' => current_user()));
} catch (Exception $e) {
if ($e->getMessage() == 'Access Denied')
$this->access_denied();
}
$api_data = Post::batch_api_data(array($post));
$this->response()->headers()->add("X-Post-Id", $this->params()->post_id);
$this->respond_to_success("Post removed", array('post#show', 'id' => $this->params()->post_id), array('api' => $api_data));
}
}
public function order()
{
$this->pool = Pool::find($this->params()->id);
if (!$this->pool->can_be_updated_by(current_user()))
$this->access_denied();
if ($this->request()->isPost()) {
foreach ($this->params()->pool_post_sequence as $i => $seq)
PoolPost::update($i, array('sequence' => $seq));
$this->pool->reload();
$this->pool->update_pool_links();
$this->notice("Ordering updated");
$this->redirectTo(array('#show', 'id' => $this->params()->id));
} else
$this->pool_posts = $this->pool->pool_posts;
}
public function select()
{
if (current_user()->is_anonymous())
$this->pools = Pool::where("is_active = TRUE AND is_public = TRUE")->order("name")->take();
else
$this->pools = Pool::where("is_active = TRUE AND (is_public = TRUE OR user_id = ?)", current_user()->id)->order("name")->take();
$options = array('(000) DO NOT ADD' => 0);
foreach ($this->pools as $p) {
$options[str_replace('_', ' ', $p->name)] = $p->id;
}
$this->options = $options;
$this->last_pool_id = $this->session()->last_pool_id;
$this->setLayout(false);
}
public function zip()
{
if (!CONFIG()->pool_zips)
throw new Rails\ActiveRecord\Exception\RecordNotFoundException();
$pool = Pool::find($this->params()->id);
$files = $pool->get_zip_data($this->params()->all());
$zip = new ZipStream($pool->pretty_name() . '.zip');
foreach ($files as $file) {
list($path, $filename) = $file;
$zip->addLargeFile($path, $filename);
}
$zip->finalize();
$this->render(['nothing' => true]);
}
public function transferMetadata()
{
$this->to = Pool::find($this->params()->to);
if (!$this->params()->from) {
$this->from = null;
return;
}
$this->from = Pool::find($this->params()->from);
$from_posts = $this->from->pool_posts;
$to_posts = $this->to->pool_posts;
if ($from_posts->size() == $to_posts->size()) {
$this->truncated = false;
} else {
$this->truncated = true;
$min_posts = min($from_posts->size(), $to_posts->size());
$from_posts = $from_posts->slice(0, $min_posts);
$to_posts = $to_posts->slice(0, $min_posts);
}
$this->posts = Post::emptyCollection();
foreach ($from_posts as $k => $v) {
$data = [];
$from = $v->post;
$to = $to_posts[$k]->post;
$data['from'] = $from;
$data['to'] = $to;
$from_tags = $from->tags;
$to_tags = $to->tags;
$tags = $from_tags;
if ($from->rating != $to->rating) {
$tags[] = 'rating:' . $to->rating;
}
if ($from->is_shown_in_index != $to->is_shown_in_index) {
$tags[] = $from->is_shown_in_index ? 'show' : 'hide';
}
if ($from->parent_id != $to->id) {
$tags[] = 'child:' . $from->id;
}
$data['tags'] = implode(' ', $tags);
$this->posts[] = $data;
}
}
}