/** * Set defaults * * @param Vocabulary $vocabulary */ public function setDefaults($vocabulary) { $vocabulary->setBaseDomain($this->getRequest()->getUriPrefix() . '/uri/'); $vocabulary->setLanguage(sfConfig::get('app_default_language')); $vocabulary->setProfileId(sfConfig::get('app_vocabulary_profile_id')); parent::setDefaults($vocabulary); }
/** * Generates and makes a query into a external vocabulary for an exact * match for a particular concept. * @param Vocabulary $exvoc external vocabulary to query * @param string $exuri resource URI * @param string $lang language of label to query for * @return string label, or null if not found in vocabulary */ protected function getExternalLabel($exvoc, $exuri, $lang) { if ($exvoc) { $exsparql = $exvoc->getSparql(); $results = $exsparql->queryLabel($exuri, $lang); return isset($results[$lang]) ? $results[$lang] : null; } else { return null; } }
public function run() { $module = $this->controller->get('m', $this->controller->taxModule); if ($module == '') { $module = $this->controller->taxModule; } $voc = $this->controller->getVocabulary(); if (is_object($voc)) { $v = $voc->id; $vob = $voc; } else { $v = $this->controller->get('v', 0); $vob = Vocabulary::model()->findByPk($v); if (!is_object($vob)) { throw new CHttpException(400, Yii::t('Xpress', 'Invalid Type ID.')); } } $this->controller->voc = $v; $propertyClassName = $vob->propertyClassName; $propertyModule = $vob->propertyModule; $property = ''; if (Yii::app()->request->IsPostRequest) { // save posted data $model = $this->saveData($module, $v, $propertyModule, $propertyClassName); } else { // show edit form $id = $this->controller->get('id', 0); $model = $this->getTerm($id, $v); $property = $this->getProperty($id, $propertyClassName); } $this->controller->render('update', array('model' => $model, 'v' => $v, 'module' => $module, 'property' => $property, 'propertyModule' => $propertyModule, 'propertyClassName' => $propertyClassName)); }
/** * {@inheritdoc} */ public function query() { $query = parent::query(); $query->join('vocabulary_node_types', 'nt', 'v.vid = nt.vid'); $query->fields('nt', array('type')); return $query; }
public function test_term_tree() { // create a vocabulary. if (Vocabulary::get('format_test')) { Vocabulary::get('format_test')->delete(); } $v = Vocabulary::create(array('name' => 'format_test', 'description' => "Vocabulary used for testing Format::term_tree()", 'features' => array('hierarchical'))); // nest some terms. /** * A * | \ * B C * | | \ * D E F // E has no descendants! * | \ | \ * G H I J // G has no descendants! * / \ \ \ * K L M N **/ $a = $v->add_term("A"); $b = $v->add_term("B", $v->get_term($a->id)); $c = $v->add_term("C", $v->get_term($a->id)); $d = $v->add_term("D", $v->get_term($b->id)); $e = $v->add_term("E", $v->get_term($c->id)); $f = $v->add_term("F", $v->get_term($c->id)); $g = $v->add_term("G", $v->get_term($d->id)); $h = $v->add_term("H", $v->get_term($d->id)); $i = $v->add_term("I", $v->get_term($f->id)); $j = $v->add_term("J", $v->get_term($f->id)); $k = $v->add_term("K", $v->get_term($h->id)); $l = $v->add_term("L", $v->get_term($h->id)); $m = $v->add_term("M", $v->get_term($i->id)); $n = $v->add_term("N", $v->get_term($j->id)); $tree_output = Format::term_tree($v->get_tree(), 'test', array('itemstart' => '<li>', 'itemattr' => '', 'liststart' => '<ol>', 'wrapper' => '%s')); $expected_output = <<<END <ol class="tree" id="tree_test"><li>A<ol><li>B<ol><li>D<ol><li>G</li> <li>H<ol><li>K</li> <li>L</li> </ol></li> </ol></li> </ol></li> <li>C<ol><li>E</li> <li>F<ol><li>I<ol><li>M</li> </ol></li> <li>J<ol><li>N</li> </ol></li> </ol></li> </ol></li> </ol></li> </ol> END; $result = $tree_output === $expected_output; if (!$result) { $this->output(sprintf('<strong>Expected:</strong><br><textarea rows="16">%s</textarea><br><strong>Got:</strong><br><textarea rows="16">%s</textarea>', $expected_output, $tree_output)); } $this->assert_true($result, "Output does not match desired HTML"); // clean up $v->delete(); }
/** * Find a model by its primary key. * * @param mixed $id * @param array $columns * @return \Illuminate\Support\Collection|static */ public function find($id) { $model = Vocabulary::find($id); if ($model) { return $model; } throw new ResourceNotFoundException('Vocabulary was not found.'); }
/** * find all vocavularies of current object * * @param int $state vocabulary state * @return array Vocabulary */ public function getVocabularies($state = Vocabulary::STATE_ACTIVE) { $criteria = new CDbCriteria(); $criteria->join = "INNER JOIN " . SITE_ID . '_' . "taxonomy_term tt ON tt.v_id = t.id" . "\n INNER JOIN " . SITE_ID . '_' . "taxonomy_index ti ON ti.term_id = tt.id"; if (is_numeric($this->taxonomy)) { $criteria->compare('t.id', $this->taxonomy); } else { $criteria->compare('t.alias', $this->taxonomy); } $criteria->compare('t.module', $this->module); $criteria->compare('t.state', $state); $objectId = (int) $this->getOwner()->{$this->objectAttribute}; $criteria->compare('ti.object_id', $objectId); return Vocabulary::model()->findAll($criteria); }
/** * get all vocabularies by module * valid current vocabulary * * @param int $type vocabulary * @param string $module module ID * * @return array */ protected function getVocabularies($type, $module) { $criteria = new CDbCriteria(); $criteria->compare('module', $module); $criteria->order = 'name'; $types = Vocabulary::model()->findAll($criteria); if (count($types)) { $types = CHtml::listData($types, 'id', 'name'); } if ($type && !array_key_exists($type, $types)) { $types = array(); //throw new CHttpException(500, 'Invalid Type'); } return $types; }
/** * Hydrates a vocabulary object with the data * * @param array $data * @param Vocabulary $object * * @return Vocabulary */ public function hydrate(array $data, Vocabulary $object) { $object->setName($data['name']); $object->setIsEnabled($data['enabled']); $object->setDescription($data['description']); if (isset($data['id'])) { $object->setId($data['id']); } return $object; }
/** * Sets the Term objects associated to that type of object with that id in this vocabulary * * @param String the name of the object type * @param Integer The id of the object for which you want the terms * @param Array. The names of the terms to associate * * @return boolean. Whether the associations were successful or not **/ public function set_object_terms($object_type, $id, $terms) { // no terms? then let's get out'a'here if (count($terms) == 0) { Plugins::act('term_detach_all_from_object_before', $this->id); $results = $this->get_object_terms($object_type, $id); foreach ($results as $term) { $term->dissociate($object_type, $id); } Plugins::act('term_detach_all_from_object_after', $this->id); return TRUE; } /* * First, let's clean the incoming tag text array, ensuring we have * a unique set of tag texts and slugs. */ $term_ids_to_object = $clean_terms = array(); foreach ((array) $terms as $term) { if (!in_array($term, array_keys($clean_terms))) { if (!in_array($slug = Utils::slugify($term), array_values($clean_terms))) { $clean_terms[$term] = $slug; } } } /* Now, let's insert any *new* term display text or slugs into the terms table */ $placeholders = Utils::placeholder_string(count($clean_terms)); $sql_terms_exist = "SELECT id, term_display, term\n\t\t\tFROM {terms}\n\t\t\tWHERE term_display IN ({$placeholders})\n\t\t\tOR term IN ({$placeholders})\n\t\t\tAND vocabulary_id = ?"; $params = array_merge(array_keys($clean_terms), array_values($clean_terms), (array) $this->id); $existing_terms = DB::get_results($sql_terms_exist, $params, 'Term'); if (count($existing_terms) > 0) { /* Terms exist which match the term text or the term */ foreach ($existing_terms as $existing_term) { /* * Term exists. * Attach object to term, then remove the term from creation list. */ $existing_term->associate($object_type, $id); $term_ids_to_object[] = $existing_term->id; /* * We remove it from the clean_terms collection as we only * want to add to the terms table those terms which don't already exist */ if (in_array($existing_term->term_display, array_keys($clean_terms))) { unset($clean_terms[$existing_term->term_display]); } if (in_array($existing_term->term, array_values($clean_terms))) { foreach ($clean_terms as $text => $slug) { if ($slug == $existing_term->term) { unset($clean_terms[$text]); break; } } } } } /* * $clean_terms now contains an associative array of terms * we need to add to the main terms table, so add them * */ foreach ($clean_terms as $new_term_text => $new_term_slug) { $term = new Term(array('term_display' => $new_term_text, 'term' => $new_term_slug)); $this->add_term($term); $term->associate($object_type, $id); $term_ids_to_object[] = $term->id; } /* * Finally, remove the terms which are no longer associated with the object. */ $repeat_questions = Utils::placeholder_string(count($term_ids_to_object)); $sql_delete = "SELECT term_id FROM {object_terms}\n\t\t\tJOIN {terms} ON term_id = {terms}.id\n\t\t\tWHERE object_id = ? AND term_id NOT IN ({$repeat_questions}) AND object_type_id = ?\n\t\t\tAND {terms}.vocabulary_id = ?"; $params = array_merge((array) $id, array_values($term_ids_to_object), (array) Vocabulary::object_type_id($object_type), (array) $this->id); $result = DB::get_column($sql_delete, $params); foreach ($result as $t) { $term = $this->get_term($t); $term->dissociate($object_type, $id); } return TRUE; }
/** * function __get * Overrides QueryRecord __get to implement custom object properties * @param $name string Name of property to return * @return mixed The requested field value */ public function __get( $name ) { switch ( $name ) { case 'vocabulary': $out = Vocabulary::get_by_id( $this->vocabulary_id ); break; case 'tag_text_searchable': // if it's got spaces, then quote it. if ( strpos( $this->term_display, ' ' ) !== false ) { $out = '\'' . str_replace( "'", "\'", $this->term_display ) . '\''; } else { $out = $this->term_display; } break; case 'count': $out = (int)$this->count(); break; case 'id': return (int)parent::__get( $name ); case 'info': return $this->get_info(); default: $out = parent::__get( $name ); break; } return $out; }
/** * Declares an association between this object and a Vocabulary object. * * @param Vocabulary $v * @return void * @throws PropelException */ public function setVocabulary($v) { if ($v === null) { $this->setSchemeId(NULL); } else { $this->setSchemeId($v->getId()); } $this->aVocabulary = $v; }
/** * Remove a version if it's owner or a privileged person requested to do so **/ public function theme_route_remove_addon_version($theme, $params) { $theme->post = Post::get(array('content_type' => Post::type('addon'), 'slug' => $params['slug'])); // Check if the current user has access to this addon. $theme->permitted_versions = $this->addon_permitted_versions($theme->post); $vocab = Vocabulary::get(self::CATALOG_VOCABULARY); $term = $vocab->get_term($params['version']); if ($term && in_array($term->term, $theme->permitted_versions)) { $term->delete(); } $remaining = $vocab->get_object_terms('addon', $theme->post->id); if (count($remaining) == 0) { // No versions left on this addon, so remove it entirely $theme->post->delete(); // We should propably redirect to the $type overview page instead Utils::redirect(Site::get_url('habari')); } // Redirect so the displayed page uses the updated version list Utils::redirect($theme->post->permalink); }
/** * get the top concepts for a vocabulary * * @return void * @param Vocabulary $vocabulary */ function getTopConcepts($vocabulary, $ts = null) { //get top concepts for vocabulary $c = new Criteria(); $c->add(ConceptPeer::IS_TOP_CONCEPT, 1); $this->topConcepts = $vocabulary->getConcepts($c); return; }
/** * Prepares a message based on parameters; * * This hook is called from MailManagerInterface->mail(). Note that hook_mail(), * unlike hook_mail_alter(), is only called on the $module argument to * MailManagerInterface->mail(), not all modules. * * @param $key * An identifier of the mail. * @param $message * An array to be filled in. Elements in this array include: * - id: An ID to identify the mail sent. Look at module source code or * MailManagerInterface->mail() for possible id values. * - to: The address or addresses the message will be sent to. The * formatting of this string must comply with RFC 2822. * - subject: Subject of the email to be sent. This must not contain any * newline characters, or the mail may not be sent properly. * MailManagerInterface->mail() sets this to an empty * string when the hook is invoked. * - body: An array of lines containing the message to be sent. Drupal will * format the correct line endings for you. MailManagerInterface->mail() * sets this to an empty array when the hook is invoked. The array may * contain either strings or objects implementing * \Drupal\Component\Render\MarkupInterface. * - from: The address the message will be marked as being from, which is * set by MailManagerInterface->mail() to either a custom address or the * site-wide default email address when the hook is invoked. * - headers: Associative array containing mail headers, such as From, * Sender, MIME-Version, Content-Type, etc. * MailManagerInterface->mail() pre-fills several headers in this array. * @param $params * An array of parameters supplied by the caller of * MailManagerInterface->mail(). * * @see \Drupal\Core\Mail\MailManagerInterface::mail() */ function hook_mail($key, &$message, $params) { $account = $params['account']; $context = $params['context']; $variables = array('%site_name' => \Drupal::config('system.site')->get('name'), '%username' => $account->getDisplayName()); if ($context['hook'] == 'taxonomy') { $entity = $params['entity']; $vocabulary = Vocabulary::load($entity->id()); $variables += array('%term_name' => $entity->name, '%term_description' => $entity->description, '%term_id' => $entity->id(), '%vocabulary_name' => $vocabulary->label(), '%vocabulary_description' => $vocabulary->getDescription(), '%vocabulary_id' => $vocabulary->id()); } // Node-based variable translation is only available if we have a node. if (isset($params['node'])) { /** @var \Drupal\node\NodeInterface $node */ $node = $params['node']; $variables += array('%uid' => $node->getOwnerId(), '%url' => $node->url('canonical', array('absolute' => TRUE)), '%node_type' => node_get_type_label($node), '%title' => $node->getTitle(), '%teaser' => $node->teaser, '%body' => $node->body); } $subject = strtr($context['subject'], $variables); $body = strtr($context['message'], $variables); $message['subject'] .= str_replace(array("\r", "\n"), '', $subject); $message['body'][] = MailFormatHelper::htmlToText($body); }
/** * Get posts by vocabulary * - vocabulary => an array describing parameters related to vocabularies attached to posts. This can be one of two forms: * - object-based, in which an array of Term objects are passed * - any => posts associated with any of the terms are returned * - all => posts associated with all of the terms are returned * - not => posts associated with none of the terms are returned * - property-based, in which an array of vocabulary names and associated fields are passed * - vocabulary_name:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, any of which can be associated with the posts * - vocabulary_name:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, any of which can be associated with the posts * - vocabulary_name:not:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, none of which can be associated with the posts * - vocabulary_name:not:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, none of which can be associated with the posts * - vocabulary_name:all:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, all of which must be associated with the posts * - vocabulary_name:all:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, all of which must be associated with the posts */ public function test_get_posts_by_vocabulary() { // setup // create a couple Vocabularies and Terms if (Vocabulary::get("fizz")) { Vocabulary::get("fizz")->delete(); } $fizz = Vocabulary::create(array('name' => 'fizz', 'description' => 'Vocabulary for Posts testing.', 'features' => array('free'))); $fizz_term = new Term(array('term' => 'fizz', 'term_display' => 'Fizz')); $fizz->add_term($fizz_term); $extra_fizz_term = new Term(array('term' => 'extra fizzy', 'term_display' => 'Extra Fizzy')); $fizz->add_term($extra_fizz_term); if (Vocabulary::get("buzz")) { Vocabulary::get("buzz")->delete(); } $buzz = Vocabulary::create(array('name' => 'buzz', 'description' => 'Another Vocabulary for Posts testing.', 'features' => array('free'))); $buzz_term = new Term(array('term' => 'buzz', 'term_display' => 'Buzz')); $buzz->add_term($buzz_term); // create some Posts and associate them with the two Vocabularies for ($i = 1; $i <= 20; $i++) { $post = Post::create(array('title' => "Test Post {$i}", 'content' => 'If this were really a post...', 'user_id' => $this->user->id, 'status' => Post::status('published'), 'content_type' => Post::type('entry'), 'pubdate' => DateTime::date_create(time()))); $post->info->testing_vocab = 1; $post->info->i = $i; $post->info->commit(); if ($i % 3 === 0) { $fizz->set_object_terms('post', $post->id, array($fizz_term->term)); } if ($i % 5 === 0) { $buzz->set_object_terms('post', $post->id, array($buzz_term->term)); } } // Object-based syntax $total_posts = Posts::count_total(); $any_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("any" => array($fizz_term, $buzz_term)), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $all_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("all" => array($fizz_term, $buzz_term)), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $not_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("not" => array($fizz_term, $buzz_term)), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $this->assert_true($any_vocab_posts > $all_vocab_posts, "Any: {$any_vocab_posts} should be greater than All: {$all_vocab_posts}"); $this->assert_true($not_vocab_posts > $all_vocab_posts, "Not: {$not_vocab_posts} should be greater than All: {$all_vocab_posts}"); $this->assert_true($not_vocab_posts < $total_posts, "Not: {$not_vocab_posts} should be less than Total: {$total_posts}"); $this->assert_equal($any_vocab_posts + $not_vocab_posts, $total_posts, "Any: {$any_vocab_posts} plus Not: {$not_vocab_posts} should equal Total: {$total_posts}"); // Property-based syntax $any_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("fizz:term" => "fizz", "buzz:term" => "buzz"), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $all_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("fizz:all:term" => "fizz", "buzz:all:term" => "buzz"), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $not_vocab_posts = Posts::get(array('ignore_permissions' => true, 'vocabulary' => array("fizz:not:term" => "fizz", "buzz:not:term" => "buzz"), 'nolimit' => 1, 'count' => 'DISTINCT {posts}.id')); $this->assert_true($any_vocab_posts > $all_vocab_posts, "Any: {$any_vocab_posts} should be greater than All: {$all_vocab_posts}"); $this->assert_true($not_vocab_posts > $all_vocab_posts, "Not: {$not_vocab_posts} should be greater than All: {$all_vocab_posts}"); $this->assert_true($not_vocab_posts < $total_posts, "Not: {$not_vocab_posts} should be less than Total: {$total_posts}"); $this->assert_equal($any_vocab_posts + $not_vocab_posts, $total_posts, "Any: {$any_vocab_posts} plus Not: {$not_vocab_posts} should equal Total: {$total_posts}"); // teardown Posts::get(array('ignore_permissions' => true, 'has:info' => 'testing_vocab', 'nolimit' => 1))->delete(); $fizz->delete(); $buzz->delete(); }
/** * find terms recursive * * @param Term $parent */ protected function findTermsRecursive($parent, $level = 0, $path = '/', $state = Term::STATE_ACTIVE, $orderBy = 'ordering') { $data = $parent->attributes; $data['level'] = $level++; $vob = Vocabulary::model()->findByPk($parent->v_id); if (is_object($vob)) { $propertyClassName = $vob->propertyClassName; if (!empty($propertyClassName)) { $model = new $propertyClassName(); $property = $model->findByAttributes(array('term_id' => $parent->id, 'status' => $state)); if (is_object($property)) { $properties = $property->attributes; unset($properties['id'], $properties['status'], $properties['creation_datetime'], $properties['last_update'], $properties['created_by'], $properties['updated_by'], $properties['term_id']); $data['properties'] = $properties; } } } $children = $parent->children(array('order' => $orderBy, 'condition' => 'children.state=:state', 'params' => array(':state' => $state))); if (is_array($children) && count($children)) { $data['children'] = array(); foreach ($children as $term) { Yii::trace('findTermsRecursive in ' . $orderBy, 'system.db.abc'); $data['children'][] = $this->findTermsRecursive($term, $level, '/', $state, $orderBy); } } return $data; }
public function __construct(\Vocabulary $vocab) { $this->itemArray["@context"] = "http://rdaregistry.info/Contexts/concepts_langmap.jsonld"; $this->vocab = $vocab; $this->setReleaseFromGithub(); $this->vocabArray["@id"] = $vocab->getUri(); $this->vocabArray["@type"] = "ConceptScheme"; $this->vocabArray['title'] = [$vocab->getLanguage() => $vocab->getName()]; $this->vocabArray['description'] = [$vocab->getLanguage() => $vocab->getNote()]; $this->vocabArray["token"] = $vocab->getToken(); $this->vocabArray["prefix"] = $vocab->getPrefix(); $status = \ConceptPeer::getConceptByUri($vocab->getStatus()->getUri()); $this->vocabArray['status'] = ["@id" => $status->getUri(), "label" => $status->getPrefLabel()]; $this->vocabArray['omr_api'] = "http://api.metadataregistry.org/vocabularies/" . $vocab->getId(); $this->vocabArray['omr_home'] = "http://metadataregistry.org/vocabulary/show/id/" . $vocab->getId() . ".html"; $this->vocabArray['documentation'] = $vocab->getUrl(); $this->vocabArray['tags'] = ["en" => $this->getTags($this->vocab->getCommunity())]; $this->vocabArray["count"] = $vocab->countConcepts(); $this->vocabArray["languages"] = $this->getLanguages($vocab->getLanguages()); $this->vocabArray["dateOfPublication"] = $this->getDateOfPublication(); $this->itemArray["@graph"][] = $this->vocabArray; $concepts = $this->getConcepts(); if ($concepts) { foreach ($concepts as $concept) { $this->itemArray["@graph"][] = $this->getConceptPropertyArray($concept); } } }
/** * Renames terms. * If the master term exists, the terms will be merged with it. * If not, it will be created first. * * @param mixed $master The Term to which they should be renamed, or the slug, text or id of it * @param Array $tags The tag text, slugs or ids to be renamed **/ public function merge($master, $tags, $object_type = 'post') { $type_id = Vocabulary::object_type_id($object_type); $post_ids = array(); $tag_names = array(); // get the master term $master_term = $this->get_term($master); if (!isset($master_term->term)) { // it didn't exist, so we assume it's tag text and create it $master_term = $this->add_term($master); if (!$master_term) { return; } $master_ids = array(); } else { // get the posts the tag is already on so we don't duplicate them $master_ids = $master_term->objects($object_type); } // get array of existing tags first to make sure we don't conflict with a new master tag foreach ($tags as $tag) { // if this is the master tag, there's nothing to do if ($tag == $master) { continue; } $term = $this->get_term($tag); // get all the post ID's tagged with this tag $posts = $term->objects($object_type); $ok_to_delete = true; // if there actually are posts, let's link those up with the new tag now if (count($posts) > 0) { // only try and add the master tag to posts it's not already on $post_ids = array_diff($posts, $master_ids); foreach ($post_ids as $post_id) { $r = $master_term->associate($object_type, $post_id); // if we failed linking this post, we can keep trying others, but don't delete this tag when finished if ($r == false) { $ok_to_delete = false; } else { // otherwise, we did in fact merge a tag - make sure the tag is in the list of ones we merged $tag_names[$tag] = $tag; // and disassociate this post from the existing tag $term->dissociate($object_type, $post_id); } } } // if it's still ok to delete the tag entirely, do so if ($ok_to_delete) { $this->delete_term($term->id); } else { // otherwise, log a special message that we didn't delete it EventLog::log(_t('Not all posts tagged "%1$s" could be reassigned to "%2$s". They have been left alone.', array($tag, $master)), 'err', 'vocabulary', 'habari'); } } EventLog::log(sprintf(_n('Term %1$s in the %2$s vocabulary has been renamed to %3$s.', 'Terms %1$s in the %2$s vocabulary have been renamed to %3$s.', count($tags)), implode(', ', $tag_names), $this->name, $master), 'info', 'vocabulary', 'habari'); }
public function get_menus($as_array = false) { $vocabularies = Vocabulary::get_all(); $outarray = array(); foreach ($vocabularies as $index => $menu) { if (!$menu->term_menu) { // check for the term_menu feature we added. unset($vocabularies[$index]); } else { if ($as_array) { $outarray[$menu->id] = $menu->name; } } } if ($as_array) { return $outarray; } else { return $vocabularies; } }
/** * Returns a post or posts based on supplied parameters. * @todo <b>THIS CLASS SHOULD CACHE QUERY RESULTS!</b> * * @param array $paramarray An associative array of parameters, or a querystring. * The following keys are supported: * - id => a post id or array of post ids * - not:id => a post id or array of post ids to exclude * - slug => a post slug or array of post slugs * - not:slug => a post slug or array of post slugs to exclude * - user_id => an author id or array of author ids * - content_type => a post content type or array post content types * - not:content_type => a post content type or array post content types to exclude * - status => a post status, an array of post statuses, or 'any' for all statuses * - year => a year of post publication * - month => a month of post publication, ignored if year is not specified * - day => a day of post publication, ignored if month and year are not specified * - before => a timestamp to compare post publication dates * - after => a timestamp to compare post publication dates * - month_cts => return the number of posts published in each month * - criteria => a literal search string to match post content * - title => an exact case-insensitive match to a post title * - title_search => a search string that acts only on the post title * - has:info => a post info key or array of post info keys, which should be present * - all:info => a post info key and value pair or array of post info key and value pairs, which should all be present and match * - not:all:info => a post info key and value pair or array of post info key and value pairs, to exclude if all are present and match * - any:info => a post info key and value pair or array of post info key and value pairs, any of which can match * - not:any:info => a post info key and value pair or array of post info key and value pairs, to exclude if any are present and match * - vocabulary => an array describing parameters related to vocabularies attached to posts. This can be one of two forms: * - object-based, in which an array of Term objects are passed * - any => posts associated with any of the terms are returned * - all => posts associated with all of the terms are returned * - not => posts associated with none of the terms are returned * - property-based, in which an array of vocabulary names and associated fields are passed * - vocabulary_name:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, any of which can be associated with the posts * - vocabulary_name:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, any of which can be associated with the posts * - vocabulary_name:not:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, none of which can be associated with the posts * - vocabulary_name:not:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, none of which can be associated with the posts * - vocabulary_name:all:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, all of which must be associated with the posts * - vocabulary_name:all:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, all of which must be associated with the posts * - limit => the maximum number of posts to return, implicitly set for many queries * - nolimit => do not implicitly set limit * - offset => amount by which to offset returned posts, used in conjunction with limit * - page => the 'page' of posts to return when paging, sets the appropriate offset * - count => return the number of posts that would be returned by this request * - orderby => how to order the returned posts * - groupby => columns by which to group the returned posts, for aggregate functions * - having => for selecting posts based on an aggregate function * - where => manipulate the generated WHERE clause. Currently broken, see https://trac.habariproject.org/habari/ticket/1383 * - add_select => an array of clauses to be added to the generated SELECT clause. * - fetch_fn => the function used to fetch data, one of 'get_results', 'get_row', 'get_value', 'get_query' * * Further description of parameters, including usage examples, can be found at * http://wiki.habariproject.org/en/Dev:Retrieving_Posts * * @return array An array of Post objects, or a single post object, depending on request */ public static function get( $paramarray = array() ) { static $presets; // If $paramarray is a string, use it as a Preset if(is_string($paramarray)) { $paramarray = array('preset' => $paramarray); } // If $paramarray is a querystring, convert it to an array $paramarray = Utils::get_params( $paramarray ); // If a preset is defined, get the named array and merge it with the provided parameters, // allowing the additional $paramarray settings to override the preset if(isset($paramarray['preset'])) { if(!isset($presets)) { $presets = Plugins::filter('posts_get_all_presets', $presets, $paramarray['preset']); } if(isset($presets[$paramarray['preset']])) { $preset = Plugins::filter('posts_get_update_preset', $presets[$paramarray['preset']], $paramarray['preset'], $paramarray); $paramarray = array_merge($paramarray, $preset); } } // let plugins alter the param array before we use it. could be useful for modifying search results, etc. $paramarray = Plugins::filter( 'posts_get_paramarray', $paramarray ); $join_params = array(); $params = array(); $fns = array( 'get_results', 'get_row', 'get_value', 'get_query' ); $select_ary = array(); // Default fields to select, everything by default foreach ( Post::default_fields() as $field => $value ) { $select_ary[$field] = "{posts}.$field AS $field"; $select_distinct[$field] = "{posts}.$field"; } // Default parameters $orderby = 'pubdate DESC'; // Define the WHERE sets to process and OR in the final SQL statement if ( isset( $paramarray['where'] ) && is_array( $paramarray['where'] ) ) { $wheresets = $paramarray['where']; } else { $wheresets = array( array() ); } /* Start building the WHERE clauses */ $wheres = array(); $joins = array(); // If the request as a textual WHERE clause, skip the processing of the $wheresets since it's empty if ( isset( $paramarray['where'] ) && is_string( $paramarray['where'] ) ) { $wheres[] = $paramarray['where']; } else { foreach ( $wheresets as $paramset ) { // Safety mechanism to prevent empty queries $where = array(); $paramset = array_merge( (array) $paramarray, (array) $paramset ); // $nots= preg_grep( '%^not:(\w+)$%iu', (array) $paramset ); if ( isset( $paramset['id'] ) ) { if ( is_array( $paramset['id'] ) ) { array_walk( $paramset['id'], create_function( '&$a,$b', '$a = intval( $a );' ) ); $where[] = "{posts}.id IN (" . implode( ',', array_fill( 0, count( $paramset['id'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['id'] ); } else { $where[] = "{posts}.id = ?"; $params[] = (int) $paramset['id']; } } if ( isset( $paramset['not:id'] ) ) { if ( is_array( $paramset['not:id'] ) ) { array_walk( $paramset['not:id'], create_function( '&$a,$b', '$a = intval( $a );' ) ); $where[] = "{posts}.id NOT IN (" . implode( ',', array_fill( 0, count( $paramset['not:id'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['not:id'] ); } else { $where[] = "{posts}.id != ?"; $params[] = (int) $paramset['not:id']; } } if ( isset( $paramset['status'] ) && ( $paramset['status'] != 'any' ) && ( 0 !== $paramset['status'] ) ) { if ( is_array( $paramset['status'] ) ) { // remove 'any' from the list if we have an array $paramset['status'] = array_diff( $paramset['status'], array( 'any' ) ); array_walk( $paramset['status'], create_function( '&$a,$b', '$a = Post::status( $a );' ) ); $where[] = "{posts}.status IN (" . implode( ',', array_fill( 0, count( $paramset['status'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['status'] ); } else { $where[] = "{posts}.status = ?"; $params[] = (int) Post::status( $paramset['status'] ); } } if ( isset( $paramset['content_type'] ) && ( $paramset['content_type'] != 'any' ) && ( 0 !== $paramset['content_type'] ) ) { if ( is_array( $paramset['content_type'] ) ) { // remove 'any' from the list if we have an array $paramset['content_type'] = array_diff( $paramset['content_type'], array( 'any' ) ); array_walk( $paramset['content_type'], create_function( '&$a,$b', '$a = Post::type( $a );' ) ); $where[] = "{posts}.content_type IN (" . implode( ',', array_fill( 0, count( $paramset['content_type'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['content_type'] ); } else { $where[] = "{posts}.content_type = ?"; $params[] = (int) Post::type( $paramset['content_type'] ); } } if ( isset( $paramset['not:content_type'] ) ) { if ( is_array( $paramset['not:content_type'] ) ) { array_walk( $paramset['not:content_type'], create_function( '&$a,$b', '$a = Post::type( $a );' ) ); $where[] = "{posts}.content_type NOT IN (" . implode( ',', array_fill( 0, count( $paramset['not:content_type'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['not:content_type'] ); } else { $where[] = "{posts}.content_type != ?"; $params[] = (int) Post::type( $paramset['not:content_type'] ); } } if ( isset( $paramset['slug'] ) ) { if ( is_array( $paramset['slug'] ) ) { $where[] = "{posts}.slug IN (" . implode( ',', array_fill( 0, count( $paramset['slug'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['slug'] ); } else { $where[] = "{posts}.slug = ?"; $params[] = (string) $paramset['slug']; } } if ( isset( $paramset['not:slug'] ) ) { if ( is_array( $paramset['not:slug'] ) ) { $where[] = "{posts}.slug NOT IN (" . implode( ',', array_fill( 0, count( $paramset['not:slug'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['not:slug'] ); } else { $where[] = "{posts}.slug != ?"; $params[] = (string) $paramset['not:slug']; } } if ( isset( $paramset['user_id'] ) && 0 !== $paramset['user_id'] ) { if ( is_array( $paramset['user_id'] ) ) { array_walk( $paramset['user_id'], create_function( '&$a,$b', '$a = intval( $a );' ) ); $where[] = "{posts}.user_id IN (" . implode( ',', array_fill( 0, count( $paramset['user_id'] ), '?' ) ) . ")"; $params = array_merge( $params, $paramset['user_id'] ); } else { $where[] = "{posts}.user_id = ?"; $params[] = (int) $paramset['user_id']; } } if ( isset( $paramset['vocabulary'] ) ) { if ( is_string( $paramset['vocabulary'] ) ) { $paramset['vocabulary'] = Utils::get_params( $paramset['vocabulary'] ); } // parse out the different formats we accept arguments in into a single mutli-dimensional array of goodness $paramset['vocabulary'] = self::vocabulary_params( $paramset['vocabulary'] ); $object_id = Vocabulary::object_type_id( 'post' ); $all = array(); $any = array(); $not = array(); if ( isset( $paramset['vocabulary']['all'] ) ) { $all = $paramset['vocabulary']['all']; } if ( isset( $paramset['vocabulary']['any'] ) ) { $any = $paramset['vocabulary']['any']; } if ( isset( $paramset['vocabulary']['not'] ) ) { $not = $paramset['vocabulary']['not']; } foreach ( $all as $vocab => $value ) { foreach ( $value as $field => $terms ) { // we only support these fields to search by if ( !in_array( $field, array( 'id', 'term', 'term_display' ) ) ) { continue; } $joins['term2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id'; $joins['terms_term2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id'; $joins['terms_vocabulary'] = ' JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id'; $where[] = '{vocabularies}.name = ? AND {terms}.' . $field . ' IN ( ' . Utils::placeholder_string( $terms ) . ' ) AND {object_terms}.object_type_id = ?'; $params[] = $vocab; $params = array_merge( $params, $terms ); $params[] = $object_id; } // this causes no posts to match if combined with 'any' below and should be re-thought... somehow $groupby = implode( ',', $select_distinct ); $having = 'count(*) = ' . count( $terms ); } foreach ( $any as $vocab => $value ) { foreach ( $value as $field => $terms ) { // we only support these fields to search by if ( !in_array( $field, array( 'id', 'term', 'term_display' ) ) ) { continue; } $joins['term2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id'; $joins['terms_term2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id'; $joins['terms_vocabulary'] = ' JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id'; $where[] = '{vocabularies}.name = ? AND {terms}.' . $field . ' IN ( ' . Utils::placeholder_string( $terms ) . ' ) AND {object_terms}.object_type_id = ?'; $params[] = $vocab; $params = array_merge( $params, $terms ); $params[] = $object_id; } } foreach ( $not as $vocab => $value ) { foreach ( $value as $field => $terms ) { // we only support these fields to search by if ( !in_array( $field, array( 'id', 'term', 'term_display' ) ) ) { continue; } $where[] = 'NOT EXISTS ( SELECT 1 FROM {object_terms} JOIN {terms} ON {terms}.id = {object_terms}.term_id JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id WHERE {terms}.' . $field . ' IN (' . Utils::placeholder_string( $terms ) . ') AND {object_terms}.object_id = {posts}.id AND {object_terms}.object_type_id = ? AND {vocabularies}.name = ? )'; $params = array_merge( $params, array_values( $terms ) ); $params[] = $object_id; $params[] = $vocab; } } } if ( isset( $paramset['criteria'] ) ) { // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes preg_match_all( '/(?<=")([\p{L}\p{N}]+[^"]*)(?=")|([\p{L}\p{N}]+)/u', $paramset['criteria'], $matches ); foreach ( $matches[0] as $word ) { $where[] .= "( LOWER( {posts}.title ) LIKE ? OR LOWER( {posts}.content ) LIKE ?)"; $params[] = '%' . MultiByte::strtolower( $word ) . '%'; $params[] = '%' . MultiByte::strtolower( $word ) . '%'; // Not a typo (there are two ? in the above statement) } } if ( isset( $paramset['title'] ) ) { $where[] .= "LOWER( {posts}.title ) LIKE ?"; $params[] = MultiByte::strtolower( $paramset['title'] ); } if ( isset( $paramset['title_search'] ) ) { // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes preg_match_all( '/(?<=")([\p{L}\p{N}]+[^"]*)(?=")|([\p{L}\p{N}]+)/u', $paramset['title_search'], $matches ); foreach ( $matches[0] as $word ) { $where[] .= " LOWER( {posts}.title ) LIKE ? "; $params[] = '%' . MultiByte::strtolower( $word ) . '%'; } } if ( isset( $paramset['all:info'] ) || isset( $paramset['info'] ) ) { // merge the two possibile calls together $infos = array_merge( isset( $paramset['all:info'] ) ? $paramset['all:info'] : array(), isset( $paramset['info'] ) ? $paramset['info'] : array() ); if ( Utils::is_traversable( $infos ) ) { $pi_count = 0; foreach ( $infos as $info_key => $info_value ) { $pi_count++; $joins['info_' . $info_key] = " LEFT JOIN {postinfo} ipi{$pi_count} ON {posts}.id = ipi{$pi_count}.post_id AND ipi{$pi_count}.name = ? AND ipi{$pi_count}.value = ?"; $join_params[] = $info_key; $join_params[] = $info_value; $where[] = "ipi{$pi_count}.name <> ''"; $select_ary["info_{$info_key}_value"] = "ipi{$pi_count}.value AS info_{$info_key}_value"; $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value"; } } } if ( isset( $paramset['any:info'] ) ) { if ( Utils::is_traversable( $paramset['any:info'] ) ) { $pi_count = 0; $pi_where = array(); foreach ( $paramset['any:info'] as $info_key => $info_value ) { $pi_count++; $join_params[] = $info_key; if ( is_array( $info_value ) ) { $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value IN (" .Utils::placeholder_string( count( $info_value ) ).")"; $join_params = array_merge( $join_params, $info_value ); } else { $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value = ?"; $join_params[] = $info_value; } $pi_where[] = "aipi{$pi_count}.name <> ''"; $select_ary["info_{$info_key}_value"] = "aipi{$pi_count}.value AS info_{$info_key}_value"; $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value"; } $where[] = '(' . implode( ' OR ', $pi_where ) . ')'; } } if ( isset( $paramset['has:info'] ) ) { $the_ins = array(); $has_info = Utils::single_array( $paramset['has:info'] ); $pi_count = 0; $pi_where = array(); foreach ( $has_info as $info_name ) { $pi_count++; $joins['has_info_' . $info_name] = " LEFT JOIN {postinfo} hipi{$pi_count} ON {posts}.id = hipi{$pi_count}.post_id AND hipi{$pi_count}.name = ?"; $join_params[] = $info_name; $pi_where[] = "hipi{$pi_count}.name <> ''"; $select_ary["info_{$info_name}_value"] = "hipi{$pi_count}.value AS info_{$info_name}_value"; $select_distinct["info_{$info_name}_value"] = "info_{$info_name}_value"; } $where[] = '(' . implode( ' OR ', $pi_where ) . ')'; } if ( isset( $paramset['not:all:info'] ) || isset( $paramset['not:info'] ) ) { // merge the two possible calls together $infos = array_merge( isset( $paramset['not:all:info'] ) ? $paramset['not:all:info'] : array(), isset( $paramset['not:info'] ) ? $paramset['not:info'] : array() ); if ( Utils::is_traversable( $infos ) ) { $the_ins = array(); foreach ( $infos as $info_key => $info_value ) { $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) '; $params[] = $info_key; $params[] = $info_value; } $where[] = ' {posts}.id NOT IN ( SELECT post_id FROM {postinfo} WHERE ( ' . implode( ' OR ', $the_ins ) . ' ) GROUP BY post_id HAVING COUNT(*) = ' . count( $infos ) . ' ) '; // see that hard-coded number? sqlite wets itself if we use a bound parameter... don't change that } } if ( isset( $paramset['not:any:info'] ) ) { if ( Utils::is_traversable( $paramset['not:any:info'] ) ) { foreach ( $paramset['not:any:info'] as $info_key => $info_value ) { $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) '; $params[] = $info_key; $params[] = $info_value; } $where[] = ' {posts}.id NOT IN ( SELECT post_id FROM {postinfo} WHERE ( ' . implode( ' OR ', $the_ins ) . ' ) ) '; } } /** * Build the statement needed to filter by pubdate: * If we've got the day, then get the date; * If we've got the month, but no date, get the month; * If we've only got the year, get the whole year. */ if ( isset( $paramset['day'] ) && isset( $paramset['month'] ) && isset( $paramset['year'] ) ) { $where[] = 'pubdate BETWEEN ? AND ?'; $start_date = sprintf( '%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day'] ); $start_date = HabariDateTime::date_create( $start_date ); $params[] = $start_date->sql; $params[] = $start_date->modify( '+1 day -1 second' )->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], $paramset['day'], $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'], $paramset['day'], $paramset['year'] ) ); } elseif ( isset( $paramset['month'] ) && isset( $paramset['year'] ) ) { $where[] = 'pubdate BETWEEN ? AND ?'; $start_date = sprintf( '%d-%02d-%02d', $paramset['year'], $paramset['month'], 1 ); $start_date = HabariDateTime::date_create( $start_date ); $params[] = $start_date->sql; $params[] = $start_date->modify( '+1 month -1 second' )->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], 1, $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'] + 1, 0, $paramset['year'] ) ); } elseif ( isset( $paramset['year'] ) ) { $where[] = 'pubdate BETWEEN ? AND ?'; $start_date = sprintf( '%d-%02d-%02d', $paramset['year'], 1, 1 ); $start_date = HabariDateTime::date_create( $start_date ); $params[] = $start_date->sql; $params[] = $start_date->modify( '+1 year -1 second' )->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, 1, 1, $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, -1, 1, 1, $paramset['year'] + 1 ) ); } if ( isset( $paramset['after'] ) ) { $where[] = 'pubdate > ?'; $params[] = HabariDateTime::date_create( $paramset['after'] )->sql; } if ( isset( $paramset['before'] ) ) { $where[] = 'pubdate < ?'; $params[] = HabariDateTime::date_create( $paramset['before'] )->sql; } // Concatenate the WHERE clauses if ( count( $where ) > 0 ) { $wheres[] = ' (' . implode( ' AND ', $where ) . ') '; } } } // Only show posts to which the current user has permission if ( isset( $paramset['ignore_permissions'] ) ) { $master_perm_where = ''; } else { // This set of wheres will be used to generate a list of post_ids that this user can read $perm_where = array(); $perm_where_denied = array(); $params_where = array(); $where = array(); // Get the tokens that this user is granted or denied access to read $read_tokens = isset( $paramset['read_tokens'] ) ? $paramset['read_tokens'] : ACL::user_tokens( User::identify(), 'read', true ); $deny_tokens = isset( $paramset['deny_tokens'] ) ? $paramset['deny_tokens'] : ACL::user_tokens( User::identify(), 'deny', true ); // If a user can read any post type, let him if ( User::identify()->can( 'post_any', 'read' ) ) { $perm_where = array( 'post_any' => '(1=1)' ); } else { // If a user can read his own posts, let him if ( User::identify()->can( 'own_posts', 'read' ) ) { $perm_where['own_posts_id'] = '{posts}.user_id = ?'; $params_where[] = User::identify()->id; } // If a user can read specific post types, let him $permitted_post_types = array(); foreach ( Post::list_active_post_types() as $name => $posttype ) { if ( User::identify()->can( 'post_' . Utils::slugify( $name ), 'read' ) ) { $permitted_post_types[] = $posttype; } } if ( count( $permitted_post_types ) > 0 ) { $perm_where[] = '{posts}.content_type IN (' . implode( ',', $permitted_post_types ) . ')'; } // If a user can read posts with specific tokens, let him if ( count( $read_tokens ) > 0 ) { $joins['post_tokens__allowed'] = ' LEFT JOIN {post_tokens} pt_allowed ON {posts}.id= pt_allowed.post_id AND pt_allowed.token_id IN ('.implode( ',', $read_tokens ).')'; $perm_where['perms_join_null'] = 'pt_allowed.post_id IS NOT NULL'; } // If a user has access to read other users' unpublished posts, let him if ( User::identify()->can( 'post_unpublished', 'read' ) ) { $perm_where[] = '({posts}.status <> ? AND {posts}.user_id <> ?)'; $params_where[] = Post::status( 'published' ); $params_where[] = User::identify()->id; } } $params_where_denied = array(); // If a user is denied access to all posts, do so if ( User::identify()->cannot( 'post_any' ) ) { $perm_where_denied = array( '(1=0)' ); } else { // If a user is denied read access to specific post types, deny him $denied_post_types = array(); foreach ( Post::list_active_post_types() as $name => $posttype ) { if ( User::identify()->cannot( 'post_' . Utils::slugify( $name ) ) ) { $denied_post_types[] = $posttype; } } if ( count( $denied_post_types ) > 0 ) { $perm_where_denied[] = '{posts}.content_type NOT IN (' . implode( ',', $denied_post_types ) . ')'; } // If a user is denied access to read other users' unpublished posts, deny it if ( User::identify()->cannot( 'post_unpublished' ) ) { $perm_where_denied[] = '({posts}.status = ? OR {posts}.user_id = ?)'; $params_where_denied[] = Post::status( 'published' ); $params_where_denied[] = User::identify()->id; } } // This doesn't work yet because you can't pass these arrays by reference Plugins::act( 'post_get_perm_where', $perm_where, $params_where, $paramarray ); Plugins::act( 'post_get_perm_where_denied', $perm_where_denied, $params_where_denied, $paramarray ); // Set up the merge params $merge_params = array( $join_params, $params ); // If there are granted permissions to check, add them to the where clause if ( count( $perm_where ) == 0 && !isset( $joins['post_tokens__allowed'] ) ) { // You have no grants. You get no posts. $where['perms_granted'] = '(1=0)'; } elseif ( count( $perm_where ) > 0 ) { $where['perms_granted'] = ' (' . implode( ' OR ', $perm_where ) . ') '; $merge_params[] = $params_where; } if ( count( $deny_tokens ) > 0 ) { $joins['post_tokens__denied'] = ' LEFT JOIN {post_tokens} pt_denied ON {posts}.id= pt_denied.post_id AND pt_denied.token_id IN ('.implode( ',', $deny_tokens ).')'; $perm_where_denied['perms_join_null'] = 'pt_denied.post_id IS NULL'; } // If there are denied permissions to check, add them to the where clause if ( count( $perm_where_denied ) > 0 ) { $where['perms_denied'] = ' (' . implode( ' AND ', $perm_where_denied ) . ') '; $merge_params[] = $params_where_denied; } // Merge the params $params = call_user_func_array( 'array_merge', $merge_params ); // AND the separate permission-related WHERE clauses $master_perm_where = implode( ' AND ', $where ); } // Extract the remaining parameters which will be used onwards // For example: page number, fetch function, limit $paramarray = new SuperGlobal( $paramarray ); $extract = $paramarray->filter_keys( 'page', 'fetch_fn', 'count', 'orderby', 'groupby', 'limit', 'offset', 'nolimit', 'having', 'add_select' ); foreach ( $extract as $key => $value ) { $$key = $value; } // Define the LIMIT if it does not exist, unless specific posts are requested or we're getting the monthly counts if ( !isset( $limit ) && !isset( $paramset['id'] ) && !isset( $paramset['slug'] ) && !isset( $paramset['month_cts'] ) ) { $limit = Options::get( 'pagination' ) ? (int) Options::get( 'pagination' ) : 5; } elseif ( !isset( $limit ) ) { $selected_posts = 0; if ( isset( $paramset['id'] ) ) { $selected_posts += count( Utils::single_array( $paramset['id'] ) ); } if ( isset( $paramset['slug'] ) ) { $selected_posts += count( Utils::single_array( $paramset['slug'] ) ); } $limit = $selected_posts > 0 ? $selected_posts : ''; } // Calculate the OFFSET based on the page number if ( isset( $page ) && is_numeric( $page ) && !isset( $paramset['offset'] ) ) { $offset = ( intval( $page ) - 1 ) * intval( $limit ); } /** * Determine which fetch function to use: * If it is specified, make sure it is valid (based on the $fns array defined at the beginning of this function); * Else, use 'get_results' which will return a Posts array of Post objects. */ if ( isset( $fetch_fn ) ) { if ( ! in_array( $fetch_fn, $fns ) ) { $fetch_fn = $fns[0]; } } else { $fetch_fn = $fns[0]; } // If the orderby has a function in it, try to create a select field for it with an alias if ( strpos( $orderby, '(' ) !== false ) { $orders = explode( ',', $orderby ); $ob_index = 0; foreach ( $orders as $key => $order ) { if ( !preg_match( '%(?P<field>.+)\s+(?P<direction>DESC|ASC)%i', $order, $order_matches ) ) { $order_matches = array( 'field' => $order, 'direction' => '', ); } if ( strpos( $order_matches['field'], '(' ) !== false ) { $ob_index++; $field = 'orderby' . $ob_index; $select_ary[$field] = "{$order_matches['field']} AS $field"; $select_distinct[$field] = "{$order_matches['field']} AS $field"; $orders[$key] = $field . ' ' . $order_matches['direction']; } } $orderby = implode( ', ', $orders ); } // Add arbitrary fields to the select clause for sorting and output if ( isset( $add_select ) ) { $select_ary = array_merge( $select_ary, $add_select ); } /** * Turn the requested fields into a comma-separated SELECT field clause */ $select = implode( ', ', $select_ary ); /** * If a count is requested: * Replace the current fields to select with a COUNT(); * Change the fetch function to 'get_value'; * Remove the ORDER BY since it's useless. * Remove the GROUP BY (tag search added it) */ if ( isset( $count ) ) { $select = "COUNT($count)"; $fetch_fn = 'get_value'; $orderby = ''; $groupby = ''; $having = ''; } // If the month counts are requested, replaced the select clause if ( isset( $paramset['month_cts'] ) ) { if ( isset( $paramset['vocabulary'] ) ) { $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(DISTINCT {posts}.id) AS ct'; } else { $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(*) AS ct'; } $groupby = 'year, month'; if ( !isset( $paramarray['orderby'] ) ) { $orderby = 'year, month'; } } // Remove the LIMIT if 'nolimit' // Doing this first should allow OFFSET to work if ( isset( $nolimit ) ) { $limit = ''; } // Define the LIMIT and add the OFFSET if it exists if ( !empty( $limit ) ) { $limit = " LIMIT $limit"; if ( isset( $offset ) ) { $limit .= " OFFSET $offset"; } } else { $limit = ''; } /* All SQL parts are constructed, on to real business! */ /** * Build the final SQL statement */ $query = ' SELECT DISTINCT ' . $select . ' FROM {posts} ' . "\n " . implode( "\n ", $joins ) . "\n"; if ( count( $wheres ) > 0 ) { $query .= ' WHERE (' . implode( " \nOR\n ", $wheres ) . ')'; $query .= ( $master_perm_where == '' ) ? '' : ' AND (' . $master_perm_where . ')'; } elseif ( $master_perm_where != '' ) { $query .= ' WHERE (' . $master_perm_where . ')'; } $query .= ( ! isset( $groupby ) || $groupby == '' ) ? '' : ' GROUP BY ' . $groupby; $query .= ( ! isset( $having ) || $having == '' ) ? '' : ' HAVING ' . $having; $query .= ( ( $orderby == '' ) ? '' : ' ORDER BY ' . $orderby ) . $limit; /** * DEBUG: Uncomment the following line to display everything that happens in this function */ //print_R('<pre>'.$query.'</pre>'); //Utils::debug( $paramarray, $fetch_fn, $query, $params ); //Session::notice($query); if ( 'get_query' == $fetch_fn ) { return array( $query, $params ); } /** * Execute the SQL statement using the PDO extension */ DB::set_fetch_mode( PDO::FETCH_CLASS ); DB::set_fetch_class( 'Post' ); $results = DB::$fetch_fn( $query, $params, 'Post' ); //Utils::debug( $paramarray, $fetch_fn, $query, $params, $results ); //var_dump( $query ); /** * Return the results */ if ( 'get_results' != $fetch_fn ) { // Since a single result was requested, return a single Post object. return $results; } elseif ( is_array( $results ) ) { // With multiple results, return a Posts array of Post objects. $c = __CLASS__; $return_value = new $c( $results ); $return_value->get_param_cache = $paramarray; return $return_value; } }
/** * function __get * Overrides QueryRecord __get to implement custom object properties * @param $name string Name of property to return * @return mixed The requested field value **/ public function __get($name) { switch ($name) { case 'vocabulary': return Vocabulary::get_by_id($this->vocabulary_id); default: return parent::__get($name); } }
/** * Returns an unordered list of all used Tags */ public function theme_show_tags($theme) { $limit = Options::get(__CLASS__ . '__tags_count'); $sql = "\n\t\t\tSELECT t.term AS slug, t.term_display AS text, count(tp.object_id) as ttl\n\t\t\tFROM {terms} t\n\t\t\tINNER JOIN {object_terms} tp\n\t\t\tON t.id=tp.term_id\n\t\t\tINNER JOIN {posts} p\n\t\t\tON p.id=tp.object_id AND p.status = ?\n\t\t\tWHERE t.vocabulary_id = ? AND tp.object_type_id = ?\n\t\t\tGROUP BY t.term, t.term_display\n\t\t\tORDER BY t.term_display\n\t\t\tLIMIT {$limit}\n\t\t"; $tags = DB::get_results($sql, array(Post::status('published'), Tags::vocabulary()->id, Vocabulary::object_type_id('post'))); foreach ($tags as $index => $tag) { $tags[$index]->url = URL::get('display_entries_by_tag', array('tag' => $tag->slug)); } $theme->taglist = $tags; return $theme->fetch('taglist'); }
/** * Returns a post or posts based on supplied parameters. * @todo <b>THIS CLASS SHOULD CACHE QUERY RESULTS!</b> * * @param array $paramarray An associative array of parameters, or a querystring. * The following keys are supported: * - id => a post id or array of post ids * - not:id => a post id or array of post ids to exclude * - slug => a post slug or array of post slugs * - not:slug => a post slug or array of post slugs to exclude * - user_id => an author id or array of author ids * - content_type => a post content type or array post content types * - not:content_type => a post content type or array post content types to exclude * - status => a post status, an array of post statuses, or 'any' for all statuses * - year => a year of post publication * - month => a month of post publication, ignored if year is not specified * - day => a day of post publication, ignored if month and year are not specified * - before => a timestamp to compare post publication dates * - after => a timestamp to compare post publication dates * - month_cts => return the number of posts published in each month * - criteria => a literal search string to match post content or title * - title => an exact case-insensitive match to a post title * - title_search => a search string that acts only on the post title * - has:info => a post info key or array of post info keys, which should be present * - all:info => a post info key and value pair or array of post info key and value pairs, which should all be present and match * - not:all:info => a post info key and value pair or array of post info key and value pairs, to exclude if all are present and match * - any:info => a post info key and value pair or array of post info key and value pairs, any of which can match * - not:any:info => a post info key and value pair or array of post info key and value pairs, to exclude if any are present and match * - vocabulary => an array describing parameters related to vocabularies attached to posts. This can be one of two forms: * - object-based, in which an array of Term objects are passed * - any => posts associated with any of the terms are returned * - all => posts associated with all of the terms are returned * - not => posts associated with none of the terms are returned * - property-based, in which an array of vocabulary names and associated fields are passed * - vocabulary_name:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, any of which can be associated with the posts * - vocabulary_name:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, any of which can be associated with the posts * - vocabulary_name:not:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, none of which can be associated with the posts * - vocabulary_name:not:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, none of which can be associated with the posts * - vocabulary_name:all:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, all of which must be associated with the posts * - vocabulary_name:all:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, all of which must be associated with the posts * - on_query_built => a closure that accepts a Query as a parameter, allowing a plugin to alter the Query for this request directly * - limit => the maximum number of posts to return, implicitly set for many queries * - nolimit => do not implicitly set limit * - offset => amount by which to offset returned posts, used in conjunction with limit * - page => the 'page' of posts to return when paging, sets the appropriate offset * - count => return the number of posts that would be returned by this request * - orderby => how to order the returned posts * - groupby => columns by which to group the returned posts, for aggregate functions * - having => for selecting posts based on an aggregate function * - where => manipulate the generated WHERE clause. Currently broken, see https://trac.habariproject.org/habari/ticket/1383 * - add_select => an array of clauses to be added to the generated SELECT clause. * - fetch_fn => the function used to fetch data, one of 'get_results', 'get_row', 'get_value', 'get_query' * * Further description of parameters, including usage examples, can be found at * http://wiki.habariproject.org/en/Dev:Retrieving_Posts * * @return Posts|Post|string An array of Post objects, or a single post object, depending on request */ public static function get($paramarray = array()) { static $presets; $select_distinct = array(); // If $paramarray is a string, use it as a Preset if (is_string($paramarray)) { $paramarray = array('preset' => $paramarray); } // If $paramarray is a querystring, convert it to an array $paramarray = Utils::get_params($paramarray); if ($paramarray instanceof \ArrayIterator) { $paramarray = $paramarray->getArrayCopy(); } // If a preset is defined, get the named array and merge it with the provided parameters, // allowing the additional $paramarray settings to override the preset if (isset($paramarray['preset'])) { if (!isset($presets)) { $presets = Plugins::filter('posts_get_all_presets', $presets, $paramarray['preset']); } $paramarray = Posts::merge_presets($paramarray, $presets); } // let plugins alter the param array before we use it. could be useful for modifying search results, etc. $paramarray = Plugins::filter('posts_get_paramarray', $paramarray); $join_params = array(); $params = array(); $fns = array('get_results', 'get_row', 'get_value', 'get_query'); $select_ary = array(); // Default fields to select, everything by default $default_fields = Plugins::filter('post_default_fields', Post::default_fields(), $paramarray); if (isset($paramarray['default_fields'])) { $param_defaults = Utils::single_array($paramarray['default_fields']); $default_fields = array_merge($default_fields, $param_defaults); } foreach ($default_fields as $field => $value) { if (preg_match('/(?:(?P<table>[\\w\\{\\}]+)\\.)?(?P<field>\\w+)(?:(?:\\s+as\\s+)(?P<alias>\\w+))?/i', $field, $fielddata)) { if (empty($fielddata['table'])) { $fielddata['table'] = '{posts}'; } if (empty($fielddata['alias'])) { $fielddata['alias'] = $fielddata['field']; } } $select_ary[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']} AS {$fielddata['alias']}"; $select_distinct[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']}"; } // Define the WHERE sets to process and OR in the final SQL statement if (isset($paramarray['where']) && is_array($paramarray['where'])) { $wheresets = $paramarray['where']; } else { $wheresets = array(array()); } /* Start building the WHERE clauses */ $query = Query::create('{posts}'); $query->select($select_ary); // If the request has a textual WHERE clause, add it to the query then continue the processing of the $wheresets if (isset($paramarray['where']) && is_string($paramarray['where'])) { $query->where()->add($paramarray['where']); } foreach ($wheresets as $paramset) { $where = new QueryWhere(); $paramset = array_merge((array) $paramarray, (array) $paramset); if (isset($paramset['id'])) { $where->in('{posts}.id', $paramset['id'], 'posts_id', 'intval'); } if (isset($paramset['not:id'])) { $where->in('{posts}.id', $paramset['not:id'], 'posts_not_id', 'intval', false); } if (isset($paramset['status']) && !self::empty_param($paramset['status'])) { $where->in('{posts}.status', $paramset['status'], 'posts_status', function ($a) { return Post::status($a); }); } if (isset($paramset['not:status']) && !self::empty_param($paramset['not:status'])) { $where->in('{posts}.status', $paramset['not:status'], 'posts_not_status', function ($a) { return Post::status($a); }, null, false); } if (isset($paramset['content_type']) && !self::empty_param($paramset['content_type'])) { $where->in('{posts}.content_type', $paramset['content_type'], 'posts_content_type', function ($a) { return Post::type($a); }); } if (isset($paramset['not:content_type'])) { $where->in('{posts}.content_type', $paramset['not:content_type'], 'posts_not_content_type', function ($a) { return Post::type($a); }, false); } if (isset($paramset['slug'])) { $where->in('{posts}.slug', $paramset['slug'], 'posts_slug'); } if (isset($paramset['not:slug'])) { $where->in('{posts}.slug', $paramset['not:slug'], 'posts_not_slug', null, false); } if (isset($paramset['user_id']) && 0 !== $paramset['user_id']) { $where->in('{posts}.user_id', $paramset['user_id'], 'posts_user_id', 'intval'); } if (isset($paramset['not:user_id']) && 0 !== $paramset['not:user_id']) { $where->in('{posts}.user_id', $paramset['not:user_id'], 'posts_not_user_id', 'intval', false); } if (isset($paramset['vocabulary'])) { if (is_string($paramset['vocabulary'])) { $paramset['vocabulary'] = Utils::get_params($paramset['vocabulary']); } // parse out the different formats we accept arguments in into a single mutli-dimensional array of goodness $paramset['vocabulary'] = self::vocabulary_params($paramset['vocabulary']); $object_id = Vocabulary::object_type_id('post'); if (isset($paramset['vocabulary']['all'])) { $all = $paramset['vocabulary']['all']; foreach ($all as $vocab => $value) { foreach ($value as $field => $terms) { // we only support these fields to search by if (!in_array($field, array('id', 'term', 'term_display'))) { continue; } $join_group = Query::new_param_name('join'); $query->join('JOIN {object_terms} ' . $join_group . '_ot ON {posts}.id = ' . $join_group . '_ot.object_id', array(), 'term2post_posts_' . $join_group); $query->join('JOIN {terms} ' . $join_group . '_t ON ' . $join_group . '_ot.term_id = ' . $join_group . '_t.id', array(), 'terms_term2post_' . $join_group); $query->join('JOIN {vocabularies} ' . $join_group . '_v ON ' . $join_group . '_t.vocabulary_id = ' . $join_group . '_v.id', array(), 'terms_vocabulary_' . $join_group); $where->in($join_group . '_v.name', $vocab); $where->in($join_group . "_t.{$field}", $terms); $where->in($join_group . '_ot.object_type_id', $object_id); } // this causes no posts to match if combined with 'any' below and should be re-thought... somehow $groupby = implode(',', $select_distinct); $having = 'count(*) = ' . count($terms); // @todo this seems like it's in the wrong place } } if (isset($paramset['vocabulary']['any'])) { $any = $paramset['vocabulary']['any']; $orwhere = new QueryWhere('OR'); foreach ($any as $vocab => $value) { foreach ($value as $field => $terms) { $andwhere = new QueryWhere(); // we only support these fields to search by if (!in_array($field, array('id', 'term', 'term_display'))) { continue; } $join_group = Query::new_param_name('join'); $query->join('JOIN {object_terms} ' . $join_group . '_ot ON {posts}.id = ' . $join_group . '_ot.object_id', array(), 'term2post_posts_' . $join_group); $query->join('JOIN {terms} ' . $join_group . '_t ON ' . $join_group . '_ot.term_id = ' . $join_group . '_t.id', array(), 'terms_term2post_' . $join_group); $query->join('JOIN {vocabularies} ' . $join_group . '_v ON ' . $join_group . '_t.vocabulary_id = ' . $join_group . '_v.id', array(), 'terms_vocabulary_' . $join_group); $andwhere->in($join_group . '_v.name', $vocab); $andwhere->in($join_group . "_t.{$field}", $terms); $andwhere->in($join_group . '_ot.object_type_id', $object_id); } $orwhere->add($andwhere); // @todo this seems like it's in the wrong place } $where->add($orwhere); } if (isset($paramset['vocabulary']['not'])) { $not = $paramset['vocabulary']['not']; foreach ($not as $vocab => $value) { foreach ($value as $field => $terms) { // we only support these fields to search by if (!in_array($field, array('id', 'term', 'term_display'))) { continue; } $subquery_alias = Query::new_param_name('subquery'); $subquery = Query::create('{object_terms}')->select('object_id'); $subquery->join('JOIN {terms} ON {terms}.id = {object_terms}.term_id'); $subquery->join('JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id'); $subquery->where()->in("{terms}.{$field}", $terms); $subquery->where()->in('{object_terms}.object_type_id', $object_id); $subquery->where()->in('{vocabularies}.name', $vocab); $query->join('LEFT JOIN (' . $subquery->get() . ') ' . $subquery_alias . ' ON ' . $subquery_alias . '.object_id = {posts}.id', $subquery->params(), $subquery_alias); $where->add('COALESCE(' . $subquery_alias . '.object_id, 0) = 0'); } } } } if (isset($paramset['criteria'])) { // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['criteria'], $matches); foreach ($matches[0] as $word) { $crit_placeholder = $query->new_param_name('criteria'); $where->add("( LOWER( {posts}.title ) LIKE :{$crit_placeholder} OR LOWER( {posts}.content ) LIKE :{$crit_placeholder})", array($crit_placeholder => '%' . MultiByte::strtolower($word) . '%')); } } if (isset($paramset['title'])) { $where->add("LOWER( {posts}.title ) LIKE :title_match", array('title_match' => MultiByte::strtolower($paramset['title']))); } if (isset($paramset['title_search'])) { // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['title_search'], $matches); foreach ($matches[0] as $word) { $crit_placeholder = $query->new_param_name('title_search'); $where->add("LOWER( {posts}.title ) LIKE :{$crit_placeholder}", array($crit_placeholder => '%' . MultiByte::strtolower($word) . '%')); } } // Handle field queries on posts and joined tables foreach ($select_ary as $field => $aliasing) { if (in_array($field, array('id', 'title', 'slug', 'status', 'content_type', 'user_id'))) { // skip fields that we're handling a different way continue; } if (isset($paramset[$field])) { if (is_callable($paramset[$field])) { $paramset[$field]($where, $paramset); } else { $where->in($field, $paramset[$field], 'posts_field_' . $field); } } } //Done if (isset($paramset['all:info']) || isset($paramset['info'])) { // merge the two possibile calls together $infos = array_merge(isset($paramset['all:info']) ? $paramset['all:info'] : array(), isset($paramset['info']) ? $paramset['info'] : array()); if (Utils::is_traversable($infos)) { $pi_count = 0; foreach ($infos as $info_key => $info_value) { $pi_count++; $infokey_field = Query::new_param_name('info_key'); $infovalue_field = Query::new_param_name('info_value'); $query->join("LEFT JOIN {postinfo} ipi{$pi_count} ON {posts}.id = ipi{$pi_count}.post_id AND ipi{$pi_count}.name = :{$infokey_field} AND ipi{$pi_count}.value = :{$infovalue_field}", array($infokey_field => $info_key, $infovalue_field => $info_value), 'all_info_' . $info_key); $where->add("ipi{$pi_count}.name <> ''"); $query->select(array("info_{$info_key}_value" => "ipi{$pi_count}.value AS info_{$info_key}_value")); $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value"; } } } //Done if (isset($paramset['any:info'])) { if (Utils::is_traversable($paramset['any:info'])) { $pi_count = 0; $orwhere = new QueryWhere('OR'); foreach ($paramset['any:info'] as $info_key => $info_value) { $pi_count++; if (is_array($info_value)) { $infokey_field = Query::new_param_name('info_key'); $inwhere = new QueryWhere(''); $inwhere->in("aipi{$pi_count}.value", $info_value); $query->join("LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = :{$infokey_field} AND " . $inwhere->get(), array_merge(array($info_key), $inwhere->params()), 'any_info_' . $info_key); } else { $infokey_field = Query::new_param_name('info_key'); $infovalue_field = Query::new_param_name('info_value'); $query->join("LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = :{$infokey_field} AND aipi{$pi_count}.value = :{$infovalue_field}", array($infokey_field => $info_key, $infovalue_field => $info_value), 'any_info_' . $info_key); } $orwhere->add("aipi{$pi_count}.name <> ''"); $query->select(array("info_{$info_key}_value" => "aipi{$pi_count}.value AS info_{$info_key}_value")); $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value"; } $where->add('(' . $orwhere->get() . ')'); } } // Done if (isset($paramset['has:info'])) { $has_info = Utils::single_array($paramset['has:info']); $pi_count = 0; $orwhere = new QueryWhere('OR'); foreach ($has_info as $info_name) { $infoname_field = Query::new_param_name('info_name'); $pi_count++; $query->join("LEFT JOIN {postinfo} hipi{$pi_count} ON {posts}.id = hipi{$pi_count}.post_id AND hipi{$pi_count}.name = :{$infoname_field}", array($infoname_field => $info_name), 'has_info_' . $info_name); $orwhere->add("hipi{$pi_count}.name <> ''"); $query->select(array("info_{$info_name}_value" => "hipi{$pi_count}.value AS info_{$info_name}_value")); $select_distinct["info_{$info_name}_value"] = "info_{$info_name}_value"; } $where->add('(' . $orwhere->get() . ')'); } //Done if (isset($paramset['not:all:info']) || isset($paramset['not:info'])) { // merge the two possible calls together $infos = array_merge(isset($paramset['not:all:info']) ? $paramset['not:all:info'] : array(), isset($paramset['not:info']) ? $paramset['not:info'] : array()); if (Utils::is_traversable($infos)) { $orwhere = new QueryWhere('OR'); foreach ($infos as $info_key => $info_value) { $andwhere = new QueryWhere(); $andwhere->in('{postinfo}.name', $info_key); $andwhere->in('{postinfo}.value', $info_value); $orwhere->add($andwhere); } // see that hard-coded number in having()? sqlite wets itself if we use a bound parameter... don't change that $subquery = Query::create('{postinfo}')->select('{postinfo}.post_id')->groupby('post_id')->having('COUNT(*) = ' . count($infos)); $subquery->where()->add($orwhere); $where->in('{posts}.id', $subquery, 'posts_not_all_info_query', null, false); } } //Tested. Test fails with original code if (isset($paramset['not:any:info'])) { if (Utils::is_traversable($paramset['not:any:info'])) { $subquery = Query::create('{postinfo}')->select('post_id'); foreach ($paramset['not:any:info'] as $info_key => $info_value) { $infokey_field = $query->new_param_name('info_key'); $infovalue_field = $query->new_param_name('info_value'); // $subquery->where()->add(" ({postinfo}.name = :{$infokey_field} AND {postinfo}.value = :{$infovalue_field} ) ", array($infokey_field => $info_key, $infovalue_field => $info_value)); $subquery->where('OR')->add(" ({postinfo}.name = :{$infokey_field} AND {postinfo}.value = :{$infovalue_field} ) ", array($infokey_field => $info_key, $infovalue_field => $info_value)); } $where->in('{posts}.id', $subquery, 'posts_not_any_info', null, false); } } /** * Build the statement needed to filter by pubdate: * If we've got the day, then get the date; * If we've got the month, but no date, get the month; * If we've only got the year, get the whole year. */ if (isset($paramset['day']) && isset($paramset['month']) && isset($paramset['year'])) { $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']); $start_date = DateTime::create($start_date); $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 day -1 second')->sql)); } elseif (isset($paramset['month']) && isset($paramset['year'])) { $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1); $start_date = DateTime::create($start_date); $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 month -1 second')->sql)); } elseif (isset($paramset['year'])) { $start_date = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1); $start_date = DateTime::create($start_date); $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 year -1 second')->sql)); } if (isset($paramset['after'])) { $where->add('pubdate > :after_date', array('after_date' => DateTime::create($paramset['after'])->sql)); } if (isset($paramset['before'])) { $where->add('pubdate < :before_date', array('before_date' => DateTime::create($paramset['before'])->sql)); } // Concatenate the WHERE clauses $query->where()->add($where); } if (isset($paramset['post_join'])) { $post_joins = Utils::single_array($paramset['post_join']); foreach ($post_joins as $post_join) { if (preg_match('#^(\\S+)(?:\\s+as)?\\s+(\\S+)$#i', $post_join, $matches)) { $query->join("LEFT JOIN {$matches[1]} {$matches[2]} ON {$matches[2]}.post_id = {posts}.id "); } else { $query->join("LEFT JOIN {$post_join} ON {$post_join}.post_id = {posts}.id "); } } } // Only show posts to which the current user has permission if (isset($paramset['ignore_permissions'])) { $master_perm_where = new QueryWhere(); // Set up the merge params $merge_params = array($join_params, $params); $params = call_user_func_array('array_merge', $merge_params); } else { $master_perm_where = new QueryWhere(); // This set of wheres will be used to generate a list of post_ids that this user can read $perm_where = new QueryWhere('OR'); $perm_where_denied = new QueryWhere('AND'); // Get the tokens that this user is granted or denied access to read $read_tokens = isset($paramset['read_tokens']) ? $paramset['read_tokens'] : ACL::user_tokens(User::identify(), 'read', true); $deny_tokens = isset($paramset['deny_tokens']) ? $paramset['deny_tokens'] : ACL::user_tokens(User::identify(), 'deny', true); // If a user can read any post type, let him if (User::identify()->can('post_any', 'read')) { $perm_where->add('(1=1)'); } else { // If a user can read his own posts, let him if (User::identify()->can('own_posts', 'read')) { $perm_where->add('{posts}.user_id = :current_user_id', array('current_user_id' => User::identify()->id)); } // If a user can read specific post types, let him $permitted_post_types = array(); foreach (Post::list_active_post_types() as $name => $posttype) { if (User::identify()->can('post_' . Utils::slugify($name), 'read')) { $permitted_post_types[] = $posttype; } } if (count($permitted_post_types) > 0) { $perm_where->in('{posts}.content_type', $permitted_post_types, 'posts_permitted_types', 'intval'); } // If a user can read posts with specific tokens, let him if (count($read_tokens) > 0) { $query->join('LEFT JOIN {post_tokens} pt_allowed ON {posts}.id= pt_allowed.post_id AND pt_allowed.token_id IN (' . implode(',', $read_tokens) . ')', array(), 'post_tokens__allowed'); $perm_where->add('pt_allowed.post_id IS NOT NULL', array(), 'perms_join_not_null'); } // If a user has access to read other users' unpublished posts, let him if (User::identify()->can('post_unpublished', 'read')) { $perm_where->add('({posts}.status <> :status_published AND {posts}.user_id <> :current_user_id)', array('current_user_id' => User::identify()->id, 'status_published' => Post::status('published'))); } } // If a user is denied access to all posts, do so if (User::identify()->cannot('post_any')) { $perm_where_denied->add('(1=0)'); } else { // If a user is denied read access to specific post types, deny him $denied_post_types = array(); foreach (Post::list_active_post_types() as $name => $posttype) { if (User::identify()->cannot('post_' . Utils::slugify($name))) { $denied_post_types[] = $posttype; } } if (count($denied_post_types) > 0) { $perm_where_denied->in('{posts}.content_type', $denied_post_types, 'posts_denied_types', 'intval', false); } // If a user is denied read access to posts with specific tokens, deny it if (count($deny_tokens) > 0) { $query->join('LEFT JOIN {post_tokens} pt_denied ON {posts}.id= pt_denied.post_id AND pt_denied.token_id IN (' . implode(',', $deny_tokens) . ')', array(), 'post_tokens__denied'); $perm_where_denied->add('pt_denied.post_id IS NULL', array(), 'perms_join_null'); } // If a user is denied access to read other users' unpublished posts, deny it if (User::identify()->cannot('post_unpublished')) { $perm_where_denied->add('({posts}.status = :status_published OR {posts}.user_id = :current_user_id)', array('current_user_id' => User::identify()->id, 'status_published' => Post::status('published'))); } } Plugins::act('post_get_perm_where', $perm_where, $paramarray); Plugins::act('post_get_perm_where_denied', $perm_where_denied, $paramarray); // If there are granted permissions to check, add them to the where clause if ($perm_where->count() == 0 && !$query->joined('post_tokens__allowed')) { $master_perm_where->add('(1=0)', array(), 'perms_granted'); } else { $master_perm_where->add($perm_where, array(), 'perms_granted'); } // If there are denied permissions to check, add them to the where clause if ($perm_where_denied->count() > 0 || $query->joined('post_tokens__denied')) { $master_perm_where->add($perm_where_denied, array(), 'perms_denied'); } } $query->where()->add($master_perm_where, array(), 'master_perm_where'); // Extract the remaining parameters which will be used onwards // For example: page number, fetch function, limit $paramarray = new SuperGlobal($paramarray); $extract = $paramarray->filter_keys('page', 'fetch_fn', 'count', 'orderby', 'groupby', 'limit', 'offset', 'nolimit', 'having', 'add_select'); foreach ($extract as $key => $value) { ${$key} = $value; } // Calculate the OFFSET based on the page number. Requires a limit. if (isset($page) && is_numeric($page) && !isset($paramset['offset']) && isset($limit)) { $offset = (intval($page) - 1) * intval($limit); } /** * Determine which fetch function to use: * If it is specified, make sure it is valid (based on the $fns array defined at the beginning of this function); * Else, use 'get_results' which will return a Posts array of Post objects. */ if (isset($fetch_fn)) { if (!in_array($fetch_fn, $fns)) { $fetch_fn = $fns[0]; } } else { $fetch_fn = $fns[0]; } // Add arbitrary fields to the select clause for sorting and output if (isset($add_select)) { $query->select($add_select); } /** * If a count is requested: * Replace the current fields to select with a COUNT(); * Change the fetch function to 'get_value'; * Remove the ORDER BY since it's useless. * Remove the GROUP BY (tag search added it) */ if (isset($count)) { $query->set_select("COUNT({$count})"); $fetch_fn = isset($paramarray['fetch_fn']) ? $fetch_fn : 'get_value'; $orderby = null; $groupby = null; $having = null; } // If the month counts are requested, replaced the select clause if (isset($paramset['month_cts'])) { if (isset($paramset['vocabulary'])) { $query->set_select('MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(DISTINCT {posts}.id) AS ct'); } else { $query->set_select('MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(*) AS ct'); } $groupby = 'year, month'; if (!isset($paramarray['orderby'])) { $orderby = 'year, month'; } } // Remove the LIMIT if 'nolimit' // Doing this first should allow OFFSET to work if (isset($nolimit)) { $limit = null; } // Define the LIMIT, OFFSET, ORDER BY, GROUP BY if they exist if (isset($limit)) { $query->limit($limit); } if (isset($offset)) { $query->offset($offset); } if (isset($orderby)) { $query->orderby($orderby); } if (isset($groupby)) { $query->groupby($groupby); } if (isset($having)) { $query->having($having); } if (isset($paramarray['on_query_built'])) { foreach (Utils::single_array($paramarray['on_query_built']) as $built) { $built($query); } } Plugins::act('posts_get_query', $query, $paramarray); /* All SQL parts are constructed, on to real business! */ /** * DEBUG: Uncomment the following line to display everything that happens in this function */ //print_R('<pre>'.$query.'</pre>'); //Utils::debug( $paramarray, $fetch_fn, $query, $params ); //Session::notice($query); if ('get_query' == $fetch_fn) { return array($query->get(), $query->params()); } /** * Execute the SQL statement using the PDO extension */ DB::set_fetch_mode(\PDO::FETCH_CLASS); $fetch_class = 'Post'; if (isset($paramarray['fetch_class'])) { $fetch_class = $paramarray['fetch_class']; } DB::set_fetch_class($fetch_class); $results = DB::$fetch_fn($query->get(), $query->params(), $fetch_class); //Utils::debug($results, $query->get(), $query->params()); //Utils::debug( $paramarray, $fetch_fn, $query->get(), $query->params(), $results ); //var_dump( $query ); /** * Return the results */ if ('get_results' != $fetch_fn) { // Since a single result was requested, return a single Post object. return $results; } elseif (is_array($results)) { // With multiple results, return a Posts array of Post objects. $c = __CLASS__; $return_value = new $c($results); $return_value->get_param_cache = $paramarray; return $return_value; } }
/** * Renames a category * If the master category exists, the categories will be merged with it. * If not, it will be created first. * * Adapted from Tags::rename() * * @param mixed tag The category text, slug or id to be renamed * @param mixed master The category to which it should be renamed, or the slug, text or id of it **/ public static function rename($master, $category, $object_type = 'post') { $vocabulary = Vocabulary::get(self::$vocabulary); $type_id = Vocabulary::object_type_id($object_type); // get the term to be renamed $term = $vocabulary->get_term($category); // get the master term $master_term = $vocabulary->get_term($master); // check if it already exists if (!isset($master_term->term)) { // it didn't exist, so we assume it's text and create it $term->term_display = $master; $term->term = $master; $term->update(); // that's it, we're done. EventLog::log(_t('Category %s has been renamed to %s.', array($category, $master), 'simplecategories'), 'info', 'category', 'simplecategories'); } else { if (!$master_term->is_descendant_of($term)) { $posts = array(); $posts = Posts::get(array('vocabulary' => array('any' => array($term), 'not' => array($master_term)), 'nolimit' => true)); // categorize all the $category Posts as $master foreach ($posts as $post) { // $vocabulary->set_object_terms( 'post', $post->id, $master ); $master_term->associate('post', $post->id); } // move the old $term's children over to $master_term foreach ($term->children() as $child) { // is this needed? // $child = $vocabulary->get_term( $child->id ); $vocabulary->move_term($child, $master_term); } // delete the old $term and all its associations self::delete_category($term->id); EventLog::log(_t('Category %s has been merged into %s.', array($category, $master), 'simplecategories'), 'info', 'category', 'simplecategories'); } else { Session::notice(_t('Cannot merge %1$s into %2$s, since %2$s is a descendant of %1$s', array($term, $master), 'shelves')); } } }
/** * Returns the tags vocabulary * * @return Vocabulary The tags vocabulary */ public static function vocabulary() { return Vocabulary::get(self::$vocabulary); }
/** * this is for importing a list of value vocabulary files. * * it's designed specifically to import the list of marc21 VES files, * but may be modified and expanded to support more things * * @param $task * @param $args * * @throws Exception */ function run_import_list($task, $args) { xdebug_break(); //check the argument counts //check the argument counts if (count($args) < 1) { throw new Exception('You must provide a vocabulary type.'); } if (count($args) < 2) { throw new Exception('You must provide a file name.'); } //set the arguments $type = strtolower($args[0]); $filePath = $args[1]; $batchId = $args[3]; //does the file exist? if (! file_exists($filePath)) { throw new Exception('You must supply a valid file to import'); } //is the file a valid type? if (preg_match('/^.+\.([[:alpha:]]{2,4})$/', $filePath, $matches)) { if (! in_array( strtolower($matches[1]), array( "json", "rdf", "csv", "xml" ) ) ) { throw new Exception('You must provide a valid file type based on the extension'); } } else { throw new Exception("File type cannot be determined from the file extension"); } /************************************************************ * Set Defaults Here * *************************************************************/ $fileType = $matches[1]; //todo: need to figure out a way to pass defaults dynamically $importTask = new pakeTask('import-vocabulary'); // parse file to get the fields/columns and data $file = fopen($filePath, "r"); if (! $file) { throw new Exception("Can't read supplied file"); } switch ($fileType) { case "csv": try { $reader = new aCsvReader($filePath); } catch(Exception $e) { throw new Exception("Not a happy CSV file! Error: " . $e); } $uploadPath = $GLOBALS['uploadPath']; ; if ('vocab' == $type) { // Get array of heading names found $headings = $reader->getHeadings(); $fields = VocabularyPeer::getFieldNames(); try { while ($row = $reader->getRow()) { // lookup the URI (or the OMR ID if available) for a match if (empty($row["VES"])) { //skip this one break; } $uri = $baseDomain . $row["VES"] . "#"; $vocab = VocabularyPeer::getVocabularyByUri($uri); $updateTime = time(); if (! $vocab) { // create a new concept or element $vocab = new Vocabulary(); $vocab->setUri($uri); $vocab->setCreatedAt($updateTime); $vocab->setCreatedUserId($userId); $vocab->setAgentId($agentID); $vocab->setBaseDomain($baseDomain); $vocab->setCommunity("Libraries, MARC21"); $vocab->setLanguage("en"); $vocab->setStatusId(1); } else { $vocab->setLastUpdated($updateTime); $vocab->setUpdatedUserId($userId); } $vocab->setName(fixEncoding(rtrim($row['Name']))); $vocab->setNote(fixEncoding(rtrim($row['Note']))); $vocab->setToken($row['VES']); $vocab->save(); //type $args[0] = "vocab"; //vocabid $args[2] = $vocab->getId(); //filepath $args[1] = $GLOBALS['uploadPath'] . $row['VES'] . ".csv"; $args[3] = $batchId; $args[4] = "-d"; run_import_vocabulary($importTask, $args); $foo = $vocab->countConcepts(); } } catch(Exception $e) { throw new Exception($e); } } else //it's a schema { try { while ($row = $reader->getRow()) { //NOTE: this is explicitly tuned to a particular import file //TODO: generalize this import mapping // lookup the URI (or the OMR ID if available) for a match if (empty($row["URI"])) { //skip this one break; } $uri = $row["URI"]; $schema = SchemaPeer::getschemaByUri($uri); $updateTime = time(); if (! $schema) { // create a new vocabulary $schema = new Schema(); $schema->setUri($uri); $schema->setCreatedAt($updateTime); $schema->setCreatedUserId($userId); $schema->setAgentId($agentID); $schema->setBaseDomain($baseDomain); $schema->setProfileId(1); } else { $schema->setUpdatedAt($updateTime); $schema->setUpdatedUserId($userId); } $schema->setCommunity($row['Tags']); $schema->setLanguage($row['Language']); $schema->setNsType("slash"); $schema->setName($row['Label']); $schema->setNote($row['Note']); $schema->setStatusId(1); $schema->setToken($row['Name']); $schema->setUrl($row['URL']); $schema->save(); //todo: create a new import batch here and pass it to the import args //see importVocabulary->saveresults() //$batchId = //type $args[0] = "schema"; //filepath $args[1] = $GLOBALS['uploadPath'] . $row['File Name']; //vocabid $args[2] = $schema->getId(); $args[3] = $batchId; $args[4] = "-d"; run_import_vocabulary($importTask, $args); $foo = $schema->countSchemaPropertys(); } } catch(Exception $e) { throw new Exception($e); } } break; default: } }
private function upgrade_db_post_3749() { $type_id = Vocabulary::object_type_id('post'); $vocabulary = Vocabulary::create(array('name' => 'tags', 'description' => 'Habari\'s tags implementation', 'features' => array('multiple', 'free'))); $new_tag = null; $post_ids = array(); $prefix = Config::get('db_connection')->prefix; $results = DB::get_results("SELECT id, tag_text, tag_slug from {$prefix}tags"); foreach ($results as $tag) { $new_tag = $vocabulary->add_term($tag->tag_text); $post_ids = DB::get_column("SELECT post_id FROM {$prefix}tag2post WHERE tag_id = ?", array($tag->id)); foreach ($post_ids as $id) { DB::insert("{object_terms}", array('term_id' => $new_tag->id, 'object_id' => $id, 'object_type_id' => $type_id)); } } }
/** * Returns a post or posts based on supplied parameters. * <b>THIS CLASS SHOULD CACHE QUERY RESULTS!</b> * * @param array $paramarry An associated array of parameters, or a querystring * @return array An array of Post objects, or a single post object, depending on request */ public static function get($paramarray = array()) { $join_params = array(); $params = array(); $fns = array('get_results', 'get_row', 'get_value'); $select_ary = array(); // Default fields to select, everything by default foreach (Post::default_fields() as $field => $value) { $select_ary[$field] = "{posts}.{$field} AS {$field}"; } // Default parameters $orderby = 'pubdate DESC'; // If $paramarray is a querystring, convert it to an array $paramarray = Utils::get_params($paramarray); // Define the WHERE sets to process and OR in the final SQL statement if (isset($paramarray['where']) && is_array($paramarray['where'])) { $wheresets = $paramarray['where']; } else { $wheresets = array(array()); } /* Start building the WHERE clauses */ $wheres = array(); $joins = array(); // If the request as a textual WHERE clause, skip the processing of the $wheresets since it's empty if (isset($paramarray['where']) && is_string($paramarray['where'])) { $wheres[] = $paramarray['where']; } else { foreach ($wheresets as $paramset) { // Safety mechanism to prevent empty queries $where = array(); $paramset = array_merge((array) $paramarray, (array) $paramset); // $nots= preg_grep( '%^not:(\w+)$%iu', (array) $paramset ); if (isset($paramset['id'])) { if (is_array($paramset['id'])) { array_walk($paramset['id'], create_function('&$a,$b', '$a = intval($a);')); $where[] = "{posts}.id IN (" . implode(',', array_fill(0, count($paramset['id']), '?')) . ")"; $params = array_merge($params, $paramset['id']); } else { $where[] = "{posts}.id = ?"; $params[] = (int) $paramset['id']; } } if (isset($paramset['not:id'])) { if (is_array($paramset['not:id'])) { array_walk($paramset['not:id'], create_function('&$a,$b', '$a = intval($a);')); $where[] = "{posts}.id NOT IN (" . implode(',', array_fill(0, count($paramset['not:id']), '?')) . ")"; $params = array_merge($params, $paramset['not:id']); } else { $where[] = "{posts}.id != ?"; $params[] = (int) $paramset['not:id']; } } if (isset($paramset['status']) && $paramset['status'] != 'any' && 0 !== $paramset['status']) { if (is_array($paramset['status'])) { // remove 'any' from the list if we have an array $paramset['status'] = array_diff($paramset['status'], array('any')); array_walk($paramset['status'], create_function('&$a,$b', '$a = Post::status($a);')); $where[] = "{posts}.status IN (" . implode(',', array_fill(0, count($paramset['status']), '?')) . ")"; $params = array_merge($params, $paramset['status']); } else { $where[] = "{posts}.status = ?"; $params[] = (int) Post::status($paramset['status']); } } if (isset($paramset['content_type']) && $paramset['content_type'] != 'any' && 0 !== $paramset['content_type']) { if (is_array($paramset['content_type'])) { // remove 'any' from the list if we have an array $paramset['content_type'] = array_diff($paramset['content_type'], array('any')); array_walk($paramset['content_type'], create_function('&$a,$b', '$a = Post::type($a);')); $where[] = "{posts}.content_type IN (" . implode(',', array_fill(0, count($paramset['content_type']), '?')) . ")"; $params = array_merge($params, $paramset['content_type']); } else { $where[] = "{posts}.content_type = ?"; $params[] = (int) Post::type($paramset['content_type']); } } if (isset($paramset['not:content_type'])) { if (is_array($paramset['not:content_type'])) { array_walk($paramset['not:content_type'], create_function('&$a,$b', '$a = Post::type($a);')); $where[] = "{posts}.content_type NOT IN (" . implode(',', array_fill(0, count($paramset['not:content_type']), '?')) . ")"; $params = array_merge($params, $paramset['not:content_type']); } else { $where[] = "{posts}.content_type != ?"; $params[] = (int) Post::type($paramset['not:content_type']); } } if (isset($paramset['slug'])) { if (is_array($paramset['slug'])) { $where[] = "{posts}.slug IN (" . implode(',', array_fill(0, count($paramset['slug']), '?')) . ")"; $params = array_merge($params, $paramset['slug']); } else { $where[] = "{posts}.slug = ?"; $params[] = (string) $paramset['slug']; } } if (isset($paramset['user_id']) && 0 !== $paramset['user_id']) { if (is_array($paramset['user_id'])) { array_walk($paramset['user_id'], create_function('&$a,$b', '$a = intval($a);')); $where[] = "{posts}.user_id IN (" . implode(',', array_fill(0, count($paramset['user_id']), '?')) . ")"; $params = array_merge($params, $paramset['user_id']); } else { $where[] = "{posts}.user_id = ?"; $params[] = (int) $paramset['user_id']; } } if (isset($paramset['tag']) || isset($paramset['tag_slug'])) { $joins['tag2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id'; $joins['tags_tag2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id'; if (isset($paramset['tag'])) { if (is_array($paramset['tag'])) { $where[] = "{terms}.term_display IN (" . implode(',', array_fill(0, count($paramset['tag']), '?')) . ")" . ' AND {object_terms}.object_type_id = ?'; $params = array_merge($params, $paramset['tag']); } else { $where[] = '{terms}.term_display = ? AND {object_terms}.object_type_id = ?'; $params[] = (string) $paramset['tag']; } } if (isset($paramset['tag_slug'])) { if (is_array($paramset['tag_slug'])) { $where[] = "{terms}.term IN (" . implode(',', array_fill(0, count($paramset['tag_slug']), '?')) . ")" . ' AND {object_terms}.object_type_id = ?'; $params = array_merge($params, $paramset['tag_slug']); } else { $where[] = '{terms}.term= ? AND {object_terms}.object_type_id = ?'; $params[] = (string) $paramset['tag_slug']; } } $params[] = Vocabulary::object_type_id(Tags::object_type()); } if (isset($paramset['all:tag'])) { $joins['tag2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id'; $joins['tags_tag2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id'; if (is_array($paramset['all:tag'])) { $where[] = '{terms}.term_display IN (' . Utils::placeholder_string($paramset['all:tag']) . ')' . ' AND {object_terms}.object_type_id = ?'; $params = array_merge($params, $paramset['all:tag']); $groupby = '{posts}.id'; $having = 'count(*) = ' . count($paramset['all:tag']); } else { // this is actually the same as plain 'tag' for a single tag search - go with it $where[] = '{terms}.term_display = ? AND {object_terms}.object_type_id = ?'; $params[] = $paramset['all:tag']; } $params[] = Vocabulary::object_type_id(Tags::object_type()); } if (isset($paramset['all:tag_slug'])) { $joins['tag2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id'; $joins['tags_tag2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id'; if (is_array($paramset['all:tag_slug'])) { $where[] = '{terms}.term IN (' . Utils::placeholder_string($paramset['all:tag_slug']) . ')' . ' AND {object_terms}.object_type_id = ?'; $params = array_merge($params, $paramset['all:tag_slug']); $groupby = '{posts}.id'; $having = 'count(*) = ' . count($paramset['all:tag_slug']); } else { // this is actually the same as plain 'tag' for a single tag search - go with it $where[] = '{terms}.term = ? AND {object_terms}.object_type_id = ?'; $params[] = $paramset['all:tag_slug']; } $params[] = Vocabulary::object_type_id(Tags::object_type()); } if (isset($paramset['not:tag'])) { $nottag = Utils::single_array($paramset['not:tag']); $where[] = 'NOT EXISTS (SELECT 1 FROM {object_terms} INNER JOIN {terms} ON {terms}.id = {object_terms}.term_id WHERE {terms}.term_display IN (' . Utils::placeholder_string($nottag) . ') AND {object_terms}.object_id = {posts}.id AND {object_terms}.object_type_id = ?) '; $params = array_merge($params, $nottag); $params[] = Vocabulary::object_type_id(Tags::object_type()); } if (isset($paramset['not:tag_slug'])) { $nottag = Utils::single_array($paramset['not:tag_slug']); $where[] = 'NOT EXISTS (SELECT 1 FROM {object_terms} INNER JOIN {terms} ON {terms}.id = {object_terms}.term_id WHERE {terms}.term_display IN (' . Utils::placeholder_string($nottag) . ') AND {object_terms}.object_id = {posts}.id AND {object_terms}.object_type_id = ?) '; $params = array_merge($params, $nottag); $params[] = Vocabulary::object_type_id(Tags::object_type()); } if (isset($paramset['criteria'])) { preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['criteria'], $matches); foreach ($matches[0] as $word) { $where[] .= "({posts}.title LIKE CONCAT('%',?,'%') OR {posts}.content LIKE CONCAT('%',?,'%'))"; $params[] = $word; $params[] = $word; // Not a typo (there are two ? in the above statement) } } if (isset($paramset['all:info']) || isset($paramset['info'])) { // merge the two possibile calls together $infos = array_merge(isset($paramset['all:info']) ? $paramset['all:info'] : array(), isset($paramset['info']) ? $paramset['info'] : array()); if (Utils::is_traversable($infos)) { $pi_count = 0; foreach ($infos as $info_key => $info_value) { $pi_count++; $joins['info_' . $info_key] = " LEFT JOIN {postinfo} ipi{$pi_count} ON {posts}.id = ipi{$pi_count}.post_id AND ipi{$pi_count}.name = ? AND ipi{$pi_count}.value = ?"; $join_params[] = $info_key; $join_params[] = $info_value; $where[] = "ipi{$pi_count}.name <> ''"; $select_ary["info_{$info_key}_value"] = "ipi{$pi_count}.value AS info_{$info_key}_value"; } } } if (isset($paramset['any:info'])) { if (Utils::is_traversable($paramset['any:info'])) { $pi_count = 0; $pi_where = array(); foreach ($paramset['any:info'] as $info_key => $info_value) { $pi_count++; $join_params[] = $info_key; if (is_array($info_value)) { $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value IN (" . Utils::placeholder_string(count($info_value)) . ")"; $join_params = array_merge($join_params, $info_value); } else { $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value = ?"; $join_params[] = $info_value; } $pi_where[] = "aipi{$pi_count}.name <> ''"; $select_ary["info_{$info_key}_value"] = "aipi{$pi_count}.value AS info_{$info_key}_value"; } $where[] = '(' . implode(' OR ', $pi_where) . ')'; } } if (isset($paramset['has:info'])) { $the_ins = array(); $has_info = Utils::single_array($paramset['has:info']); $pi_count = 0; $pi_where = array(); foreach ($has_info as $info_name) { $pi_count++; $joins['has_info_' . $info_name] = " LEFT JOIN {postinfo} hipi{$pi_count} ON {posts}.id = hipi{$pi_count}.post_id AND hipi{$pi_count}.name = ?"; $join_params[] = $info_name; $pi_where[] = "hipi{$pi_count}.name <> ''"; $select_ary["info_{$info_name}_value"] = "hipi{$pi_count}.value AS info_{$info_name}_value"; } $where[] = '(' . implode(' OR ', $pi_where) . ')'; } if (isset($paramset['not:all:info']) || isset($paramset['not:info'])) { // merge the two possible calls together $infos = array_merge(isset($paramset['not:all:info']) ? $paramset['not:all:info'] : array(), isset($paramset['not:info']) ? $paramset['not:info'] : array()); if (Utils::is_traversable($infos)) { $the_ins = array(); foreach ($infos as $info_key => $info_value) { $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) '; $params[] = $info_key; $params[] = $info_value; } $where[] = ' {posts}.id NOT IN ( SELECT post_id FROM {postinfo} WHERE ( ' . implode(' OR ', $the_ins) . ' ) GROUP BY post_id HAVING COUNT(*) = ' . count($infos) . ' ) '; // see that hard-coded number? sqlite wets itself if we use a bound parameter... don't change that } } if (isset($paramset['not:any:info'])) { if (Utils::is_traversable($paramset['not:any:info'])) { foreach ($paramset['not:any:info'] as $info_key => $info_value) { $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) '; $params[] = $info_key; $params[] = $info_value; } $where[] = ' {posts}.id NOT IN ( SELECT post_id FROM {postinfo} WHERE ( ' . implode(' OR ', $the_ins) . ' ) ) '; } } /** * Build the statement needed to filter by pubdate: * If we've got the day, then get the date; * If we've got the month, but no date, get the month; * If we've only got the year, get the whole year. */ if (isset($paramset['day']) && isset($paramset['month']) && isset($paramset['year'])) { $where[] = 'pubdate BETWEEN ? AND ?'; $startDate = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']); $startDate = HabariDateTime::date_create($startDate); $params[] = $startDate->sql; $params[] = $startDate->modify('+1 day')->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], $paramset['day'], $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'], $paramset['day'], $paramset['year'] ) ); } elseif (isset($paramset['month']) && isset($paramset['year'])) { $where[] = 'pubdate BETWEEN ? AND ?'; $startDate = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1); $startDate = HabariDateTime::date_create($startDate); $params[] = $startDate->sql; $params[] = $startDate->modify('+1 month')->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], 1, $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'] + 1, 0, $paramset['year'] ) ); } elseif (isset($paramset['year'])) { $where[] = 'pubdate BETWEEN ? AND ?'; $startDate = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1); $startDate = HabariDateTime::date_create($startDate); $params[] = $startDate->sql; $params[] = $startDate->modify('+1 year')->sql; //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, 1, 1, $paramset['year'] ) ); //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, -1, 1, 1, $paramset['year'] + 1 ) ); } if (isset($paramset['after'])) { $where[] = 'pubdate > ?'; $params[] = HabariDateTime::date_create($paramset['after'])->sql; } if (isset($paramset['before'])) { $where[] = 'pubdate < ?'; $params[] = HabariDateTime::date_create($paramset['before'])->sql; } // Concatenate the WHERE clauses if (count($where) > 0) { $wheres[] = ' (' . implode(' AND ', $where) . ') '; } } } // Only show posts to which the current user has permission if (isset($paramset['ignore_permissions'])) { $master_perm_where = ''; } else { // This set of wheres will be used to generate a list of post_ids that this user can read $perm_where = array(); $perm_where_denied = array(); $params_where = array(); $where = array(); // Get the tokens that this user is granted or denied access to read $read_tokens = isset($paramset['read_tokens']) ? $paramset['read_tokens'] : ACL::user_tokens(User::identify(), 'read', true); $deny_tokens = isset($paramset['deny_tokens']) ? $paramset['deny_tokens'] : ACL::user_tokens(User::identify(), 'deny', true); // If a user can read his own posts, let him if (User::identify()->can('own_posts', 'read')) { $perm_where['own_posts_id'] = '{posts}.user_id = ?'; $params_where[] = User::identify()->id; } // If a user can read any post type, let him if (User::identify()->can('post_any', 'read')) { $perm_where = array('post_any' => '(1=1)'); $params_where = array(); } else { // If a user can read specific post types, let him $permitted_post_types = array(); foreach (Post::list_active_post_types() as $name => $posttype) { if (User::identify()->can('post_' . Utils::slugify($name), 'read')) { $permitted_post_types[] = $posttype; } } if (count($permitted_post_types) > 0) { $perm_where[] = '{posts}.content_type IN (' . implode(',', $permitted_post_types) . ')'; } // If a user can read posts with specific tokens, let him if (count($read_tokens) > 0) { $joins['post_tokens__allowed'] = ' LEFT JOIN {post_tokens} pt_allowed ON {posts}.id= pt_allowed.post_id AND pt_allowed.token_id IN (' . implode(',', $read_tokens) . ')'; $perm_where['perms_join_null'] = 'pt_allowed.post_id IS NOT NULL'; } } // If a user is denied access to all posts, do so if (User::identify()->cannot('post_any')) { $perm_where_denied = array('(1=0)'); } else { // If a user is denied read access to specific post types, deny him $denied_post_types = array(); foreach (Post::list_active_post_types() as $name => $posttype) { if (User::identify()->cannot('post_' . Utils::slugify($name))) { $denied_post_types[] = $posttype; } } if (count($denied_post_types) > 0) { $perm_where_denied[] = '{posts}.content_type NOT IN (' . implode(',', $denied_post_types) . ')'; } } // If there are granted permissions to check, add them to the where clause if (count($perm_where) == 0 && !isset($joins['post_tokens__allowed'])) { // You have no grants. You get no posts. $where['perms_granted'] = '(1=0)'; } elseif (count($perm_where) > 0) { $where['perms_granted'] = ' (' . implode(' OR ', $perm_where) . ') '; $params = array_merge($join_params, $params, $params_where); } if (count($deny_tokens) > 0) { $joins['post_tokens__denied'] = ' LEFT JOIN {post_tokens} pt_denied ON {posts}.id= pt_denied.post_id AND pt_denied.token_id IN (' . implode(',', $deny_tokens) . ')'; $perm_where_denied['perms_join_null'] = 'pt_denied.post_id IS NULL'; } // If there are denied permissions to check, add them to the where clause if (count($perm_where_denied) > 0) { $where['perms_denied'] = ' (' . implode(' AND ', $perm_where_denied) . ') '; } $master_perm_where = implode(' AND ', $where); } // Extract the remaining parameters which will be used onwards // For example: page number, fetch function, limit $paramarray = new SuperGlobal($paramarray); $extract = $paramarray->filter_keys('page', 'fetch_fn', 'count', 'orderby', 'groupby', 'limit', 'offset', 'nolimit', 'having'); foreach ($extract as $key => $value) { ${$key} = $value; } // Define the LIMIT if it does not exist, unless specific posts are requested if (!isset($limit) && !isset($paramset['id']) && !isset($paramset['slug'])) { $limit = Options::get('pagination') ? (int) Options::get('pagination') : 5; } elseif (!isset($limit)) { $selected_posts = 0; if (isset($paramset['id'])) { $selected_posts += count(Utils::single_array($paramset['id'])); } if (isset($paramset['slug'])) { $selected_posts += count(Utils::single_array($paramset['slug'])); } $limit = $selected_posts > 0 ? $selected_posts : ''; } // Calculate the OFFSET based on the page number if (isset($page) && is_numeric($page) && !isset($paramset['offset'])) { $offset = (intval($page) - 1) * intval($limit); } /** * Determine which fetch function to use: * If it is specified, make sure it is valid (based on the $fns array defined at the beginning of this function); * Else, use 'get_results' which will return a Posts array of Post objects. */ if (isset($fetch_fn)) { if (!in_array($fetch_fn, $fns)) { $fetch_fn = $fns[0]; } } else { $fetch_fn = $fns[0]; } /** * Turn the requested fields into a comma-separated SELECT field clause */ $select = implode(', ', $select_ary); /** * If a count is requested: * Replace the current fields to select with a COUNT(); * Change the fetch function to 'get_value'; * Remove the ORDER BY since it's useless. * Remove the GROUP BY (tag search added it) */ if (isset($count)) { $select = "COUNT({$count})"; $fetch_fn = 'get_value'; $orderby = ''; $groupby = ''; $having = ''; } // If the month counts are requested, replaced the select clause if (isset($paramset['month_cts'])) { if (isset($paramset['tag']) || isset($paramset['tag_slug'])) { $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(DISTINCT {posts}.id) AS ct'; } else { $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(*) AS ct'; } $groupby = 'year, month'; $orderby = 'year, month'; } // Remove the LIMIT if 'nolimit' or 'month_cts' is set // Doing this first should allow OFFSET to work if (isset($nolimit) || isset($paramset['month_cts'])) { $limit = ''; } // Define the LIMIT and add the OFFSET if it exists if (!empty($limit)) { $limit = " LIMIT {$limit}"; if (isset($offset)) { $limit .= " OFFSET {$offset}"; } } else { $limit = ''; } /* All SQL parts are constructed, on to real business! */ /** * Build the final SQL statement */ $query = ' SELECT DISTINCT ' . $select . ' FROM {posts} ' . implode(' ', $joins); if (count($wheres) > 0) { $query .= ' WHERE (' . implode(" \nOR\n ", $wheres) . ')'; $query .= $master_perm_where == '' ? '' : ' AND (' . $master_perm_where . ')'; } elseif ($master_perm_where != '') { $query .= ' WHERE (' . $master_perm_where . ')'; } $query .= !isset($groupby) || $groupby == '' ? '' : ' GROUP BY ' . $groupby; $query .= !isset($having) || $having == '' ? '' : ' HAVING ' . $having; $query .= ($orderby == '' ? '' : ' ORDER BY ' . $orderby) . $limit; /** * DEBUG: Uncomment the following line to display everything that happens in this function */ //print_R('<pre>'.$query.'</pre>'); //Utils::debug( $paramarray, $fetch_fn, $query, $params ); //Session::notice($query); /** * Execute the SQL statement using the PDO extension */ DB::set_fetch_mode(PDO::FETCH_CLASS); DB::set_fetch_class('Post'); $results = DB::$fetch_fn($query, $params, 'Post'); // Utils::debug( $paramarray, $fetch_fn, $query, $params, $results ); // var_dump( $query ); /** * Return the results */ if ('get_results' != $fetch_fn) { // Since a single result was requested, return a single Post object. return $results; } elseif (is_array($results)) { // With multiple results, return a Posts array of Post objects. $c = __CLASS__; $return_value = new $c($results); $return_value->get_param_cache = $paramarray; return $return_value; } }
/** * Method called to associate a Vocabulary object to this object * through the Vocabulary foreign key attribute * * @param Vocabulary $l Vocabulary * @return void * @throws PropelException */ public function addVocabulary(Vocabulary $l) { $this->collVocabularys[] = $l; $l->setAgent($this); }