package.xml 0000664 0001750 0001750 00000046246 12611721515 013460 0 ustar mrubinsk mrubinsk
content
pear.horde.org
Tagging application
This application provides tagging support for the other Horde applications.
Chuck Hagenbuch
chuck
chuck@horde.org
yes
Michael J Rubinsky
mrubinsk
mrubinsk@horde.org
yes
2015-10-21
10:38:05
2.0.5
2.0.0
stable
stable
BSD-2-Clause
* [mjr] Fix bug that was causing tag stats to be incorrectly incremented (Bug #14112).
* [mjr] Add Content_Objects_Manager::delete().
* [jan] Add unit tests for Oracle.
5.3.0
6.0.0alpha1
6.0.0alpha1
1.7.0
Horde_Core
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Date
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Exception
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Db
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Injector
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Rdo
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Util
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
gettext
json
Horde_Argv
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_Controller
pear.horde.org
2.0.0
3.0.0alpha1
3.0.0alpha1
Horde_ElasticSearch
pear.horde.org
1.0.0
2.0.0alpha1
2.0.0alpha1
horde
Role
pear.horde.org
1.0.0alpha1
1.0.0
alpha
alpha
2011-03-09
BSD-2-Clause
* First alpha release for Horde 4.
1.0.0beta1
1.0.0
beta
beta
2011-03-16
BSD-2-Clause
* First beta release for Horde 4.
1.0.0RC1
1.0.0
beta
beta
2011-03-23
BSD-2-Clause
* First release candidate for Horde 4.
1.0.0RC2
1.0.0
beta
beta
2011-03-30
BSD-2-Clause
* Second release candidate for Horde 4.
* [jan] Rename all scripts to be prefixed with content- (Request #9647).
1.0.0
1.0.0
stable
stable
2011-04-06
BSD-2-Clause
* First stable release for Horde 4.
* [jan] Fix case-insensitive filtering of duplicate tags (Bug #9617).
1.0.1
1.0.0
stable
stable
2011-08-05
BSD-2-Clause
* [mjr] Enforce that object and type names are always taken as a string (Bug #10171).
* [mjr] Updated unit tests to new test structure.
* [mjr] Prevent tagging with empty tags.
1.0.2
1.0.0
stable
stable
2011-08-11
BSD-2-Clause
* [mjr] Fix broken tag cloud queries due to missing GROUP BY fields (Bug #10419)
* [mjr] Prevent tagging with empty strings.
1.0.3
1.0.0
stable
stable
2011-10-14
BSD-2-Clause
* [mjr] Honor the limit and radius parameters.
* [mjr] Fix getObjects method when passing object_id (Bug# 10439).
* [mjr] Fix including all non-aggregate fields in GROUP BY clause (Bug# 10419).
* [jan] Fix commandline scripts (Bug #10656).
1.0.4
1.0.0
stable
stable
2011-10-14
BSD-2-Clause
*
2012-08-29
22:36:15
2.0.0beta1
2.0.0beta1
beta
beta
BSD-2-Clause
* First beta release for Horde 5.
2.0.0
2.0.0
stable
stable
2012-10-30
BSD-2-Clause
* First stable release for Horde 5.
2.0.1
2.0.0
stable
stable
2012-11-15
BSD-2-Clause
* [jan] Add missing optional dependency on Horde_ElasticSearch.
* [jan] Catch exceptions from Horde_ElasticSearch.
2.0.2
2.0.0
stable
stable
2013-02-11
BSD-2-Clause
* [mjr] Fix logic that could possibly lead to incorrect content types being applied to objects (Bug #12016).
2.0.3
2.0.0
stable
stable
2013-07-16
BSD-2-Clause
* [mjr] Fix obtaining tag cloud information when filtering by objectIds.
2.0.4
2.0.0
stable
stable
2014-06-02
BSD-2-Clause
* [jan] Fix date format for 'created' column.
2.0.5
2.0.0
stable
stable
2015-10-21
BSD-2-Clause
* [mjr] Fix bug that was causing tag stats to be incorrectly incremented (Bug #14112).
* [mjr] Add Content_Objects_Manager::delete().
* [jan] Add unit tests for Oracle.
content-2.0.5/app/controllers/ApplicationController.php 0000664 0001750 0001750 00000000557 12611721515 023616 0 ustar mrubinsk mrubinsk tagger = $GLOBALS['injector']->getInstance('Content_Tagger');
}
}
content-2.0.5/app/controllers/TagController.php 0000664 0001750 0001750 00000010334 12611721515 022060 0 ustar mrubinsk mrubinsk results = $this->tagger->getTags(array(
'q' => $this->params->q,
'typeId' => $this->params->typeId,
'userId' => $this->params->userId,
'objectId' => $this->params->objectId,
));
$this->_render();
}
/**
*/
public function recentTags()
{
$this->results = $this->tagger->getRecentTags(array(
'limit' => 10,
'typeId' => $this->params->typeId,
'objectId' => $this->params->objectId,
));
$this->_render();
}
public function searchUsers()
{
}
public function recentUsers()
{
}
public function searchObjects()
{
}
public function recentObjects()
{
}
/**
* Add a tag
*/
public function tag()
{
// The route configuration enforces POST or PUT only, but double-check here.
}
/**
* Remove a tag
*/
public function untag()
{
// The route configuration enforces POST or DELETE only, but double-check here.
}
protected function _render()
{
switch ((string)$this->_request->getFormat()) {
case 'html':
$this->render();
break;
case 'atom':
case 'rss':
$method = '_' . $this->_action . 'Feed';
$this->$method();
break;
case 'json':
default:
$this->renderText(json_encode($this->results));
break;
}
}
protected function _recentTagsFeed()
{
$entries = array();
foreach ($this->results as $tag) {
$entries[] = array(
'id' => 'tag/' . $tag['tag_id'], /* @TODO use routes to get the full URI here */
'title' => $tag['tag_name'],
'updated' => $tag['created'],
);
}
$format = $this->_request->getFormat();
$class = 'Horde_Feed_' . ucfirst((string)$this->_request->getFormat());
$feed = new $class(array(
'id' => 'tags/recent', /* @TODO Use routes to get url to this search */
'title' => 'Recent tags',
'updated' => $this->_request->getTimestamp(),
'entry' => $entries,
));
header('Content-type: ' . $format->string);
$this->renderText($feed->saveXml());
}
protected function _recentObjectsFeed()
{
$entries = array();
foreach ($this->results as $object) {
$entries[] = array(
'id' => 'object/' . $object['object_id'], /* @TODO use routes to get the full URI here */
'title' => $object['object_name'],
'updated' => $object['created'],
);
}
$format = $this->_request->getFormat();
$class = 'Horde_Feed_' . ucfirst((string)$this->_request->getFormat());
$feed = new $class(array(
'id' => 'objects/recent', /* @TODO Use routes to get url to this search */
'title' => 'Recent objects',
'updated' => $this->_request->getTimestamp(),
'entry' => $entries,
));
header('Content-type: ' . $format->string);
$this->renderText($feed->saveXml());
}
protected function _recentUsersFeed()
{
$entries = array();
foreach ($this->results as $user) {
$entries[] = array(
'id' => 'user/' . $user['user_id'], /* @TODO use routes to get the full URI here */
'title' => $user['user_name'],
'updated' => $user['created'],
);
}
$format = $this->_request->getFormat();
$class = 'Horde_Feed_' . ucfirst((string)$this->_request->getFormat());
$feed = new $class(array(
'id' => 'users/recent', /* @TODO Use routes to get url to this search */
'title' => 'Recent users',
'updated' => $this->_request->getTimestamp(),
'entry' => $entries,
));
header('Content-type: ' . $format->string);
$this->renderText($feed->saveXml());
}
}
content-2.0.5/app/views/Tag/recentTags.html.php 0000664 0001750 0001750 00000000355 12611721515 021647 0 ustar mrubinsk mrubinsk
results as $tag): ?>
escape($tag['tag_name']) ?>
content-2.0.5/app/views/Tag/searchTags.html.php 0000664 0001750 0001750 00000000270 12611721515 021630 0 ustar mrubinsk mrubinsk
results as $tag_id => $tag_name): ?>
escape($tag_name) ?>
content-2.0.5/bin/content-object-add 0000775 0001750 0001750 00000002463 12611721515 017610 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$options = array(
new Horde_Argv_Option('-i', '--id', array('type' => 'int')),
new Horde_Argv_Option('-t', '--type-id', array('type' => 'int')),
);
$parser = new Horde_Argv_Parser(array('optionList' => $options));
list($opts, $positional) = $parser->parseArgs();
if (!$opts->id || !$opts->type_id) {
throw new InvalidArgumentException('id and type-id are both required');
}
require CONTENT_BASE . '/lib/Objects/Object.php';
require CONTENT_BASE . '/lib/Objects/ObjectMapper.php';
$m = new Content_ObjectMapper($injector->getInstance('Horde_Db_Adapter'));
$i = $m->create(array('object_name' => $opts->id,
'type_id' => $opts->type_id,
));
echo 'Created new object with id ' . $i->object_id . ' for ' . $i->type_id . ':' . $i->object_name . ".\n";
exit(0);
content-2.0.5/bin/content-object-delete 0000775 0001750 0001750 00000002107 12611721515 020315 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$options = array(
new Horde_Argv_Option('-m', '--object-id', array('type' => 'int')),
);
$parser = new Horde_Argv_Parser(array('optionList' => $options));
list($opts, $positional) = $parser->parseArgs();
if (!$opts->object_id) {
throw new InvalidArgumentException('object_id is required');
}
$m = new Content_ObjectMapper($injector->getInstance('Horde_Db_Adapter'));
if ($m->delete($opts->object_id)) {
echo 'Deleted object with id ' . $opts->object_id . ".\n";
exit(0);
} else {
echo 'Object #' . $opts->object_id . " not found.\n";
exit(1);
}
content-2.0.5/bin/content-tag 0000775 0001750 0001750 00000002222 12611721515 016360 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$options = array(
new Horde_Argv_Option('-u', '--user-id', array('type' => 'int')),
new Horde_Argv_Option('-o', '--object-id', array('type' => 'int')),
);
$parser = new Horde_Argv_Parser(array('optionList' => $options));
list($opts, $tags) = $parser->parseArgs();
if (!$opts->user_id || !$opts->object_id) {
throw new InvalidArgumentException('user-id and object-id are both required');
}
if (!count($tags)) {
throw new InvalidArgumentException('List at least one tag to add.');
}
/* @TODO Switch to using the TagController */
$injector->getInstance('Content_Tagger')
->tag($opts->user_id, $opts->object_id, $tags);
exit(0);
content-2.0.5/bin/content-tag-add 0000775 0001750 0001750 00000002022 12611721515 017104 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$parser = new Horde_Argv_Parser();
list($opts, $tags) = $parser->parseArgs();
if (!count($tags)) {
throw new InvalidArgumentException('List at least one tag to add.');
}
require CONTENT_BASE . '/lib/Tags/Tag.php';
require CONTENT_BASE . '/lib/Tags/TagMapper.php';
$m = new Content_TagMapper($injector->getInstance('Horde_Db_Adapter'));
foreach ($tags as $tag) {
$t = $m->create(array('tag_name' => $tag));
echo 'Created new tag with id ' . $t->tag_id . ' and name "' . $t->tag_name . "\".\n";
}
exit(0);
content-2.0.5/bin/content-tag-delete 0000775 0001750 0001750 00000002307 12611721515 017624 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$parser = new Horde_Argv_Parser();
list($opts, $tags) = $parser->parseArgs();
if (!count($tags)) {
throw new InvalidArgumentException('List at least tag to delete.');
}
require CONTENT_BASE . '/lib/Tags/Tag.php';
require CONTENT_BASE . '/lib/Tags/TagMapper.php';
$m = new Content_TagMapper($injector->getInstance('Horde_Db_Adapter'));
foreach ($tags as $tag) {
$t = $m->findOne(array('tag_name' => $tag));
if (!$t) {
echo "$tag doesn't seem to exist, skipping it.\n";
continue;
}
if ($t->delete()) {
echo "Delete tag '$tag' (#".$t->tag_id.")\n";
continue;
} else {
echo "Failed to delete '$tag'\n";
exit(1);
}
}
exit(0);
content-2.0.5/bin/content-untag 0000775 0001750 0001750 00000002227 12611721515 016730 0 ustar mrubinsk mrubinsk #!/usr/bin/env php
get('horde_dir', null, 'pear.horde.org') . '/content/';
}
require_once $baseDir . 'lib/Application.php';
Horde_Registry::appInit('content', array('cli' => true));
$options = array(
new Horde_Argv_Option('-u', '--user-id', array('type' => 'int')),
new Horde_Argv_Option('-i', '--object-id', array('type' => 'int')),
);
$parser = new Horde_Argv_Parser(array('optionList' => $options));
list($opts, $tags) = $parser->parseArgs();
if (!$opts->user_id || !$opts->object_id) {
throw new InvalidArgumentException('user-id and object-id are both required');
}
if (!count($tags)) {
throw new InvalidArgumentException('List at least one tag to remove.');
}
/* @TODO Switch to using the TagController */
$injector->getInstance('Content_Tagger')
->untag($opts->user_id, $opts->object_id, $tags);
exit(0);
content-2.0.5/config/routes.php 0000664 0001750 0001750 00000004376 12611721515 016752 0 ustar mrubinsk mrubinsk connect('tags', array('controller' => 'tag', 'action' => 'searchTags'));
$mapper->connect('tags.:(format)', array('controller' => 'tag', 'action' => 'searchTags'));
// Most recent tags. Available query parameters:
// typeId: restrict matches to tags that have been applied to objects with type $typeId
// userId: restrict matches to tags that have been applied by $userId
$mapper->connect('tags/recent', array('controller' => 'tag', 'action' => 'recentTags'));
$mapper->connect('tags/recent.:(format)', array('controller' => 'tag', 'action' => 'recentTags'));
// List objects. At least a content type, or more specific parameters, are
// required; listing all objects is not allowed.
$mapper->connect('objects', array('controller' => 'tag', 'action' => 'searchObjects'));
$mapper->connect('objects.:(format)', array('controller' => 'tag', 'action' => 'searchObjects'));
// List users. Specific parameters are required as listing all users is not
// allowed.
$mapper->connect('users', array('controller' => 'tag', 'action' => 'searchUsers'));
$mapper->connect('users.:(format)', array('controller' => 'tag', 'action' => 'searchUsers'));
// Tag an object. Required POST parameters are: tags (array or string list) and
// objectId. userId is inferred from the authenticated user:
$mapper->connect('tag', array('controller' => 'tag', 'action' => 'tag',
'conditions' => array('method' => array('POST', 'PUT'))));
// Untag an object. Required POST parameters are: tags (array or string list)
// and objectId. userId is inferred from the authenticated user:
$mapper->connect('untag', array('controller' => 'tag', 'action' => 'untag',
'conditions' => array('method' => array('POST', 'DELETE'))));
content-2.0.5/docs/CHANGES 0000600 0001750 0001750 00000002636 12611721515 015361 0 ustar mrubinsk mrubinsk ------
v2.0.5
------
[mjr] Fix bug that was causing tag stats to be incorrectly incremented (Bug
#14112).
[mjr] Add Content_Objects_Manager::delete().
[jan] Add unit tests for Oracle.
------
v2.0.4
------
[jan] Fix date format for 'created' column.
------
v2.0.3
------
[mjr] Fix obtaining tag cloud information when filtering by objectIds.
------
v2.0.2
------
[mjr] Fix logic that could possibly lead to incorrect content types being
applied to objects (Bug #12016).
------
v2.0.1
------
[jan] Add missing optional dependency on Horde_ElasticSearch.
[jan] Catch exceptions from Horde_ElasticSearch.
------
v2.0.0
------
[jan] Support Horde 5.
------
v1.0.3
------
[mjr] Honor the limit and radius parameters.
[mjr] Fix getObjects method when passing object_id (Bug# 10439).
[mjr] Fix including all non-aggregate fields in GROUP BY clause (Bug# 10419).
[jan] Fix commandline scripts (Bug #10656).
------
v1.0.2
------
[mjr] Fix broken tag cloud queries due to missing GROUP BY fields (Bug #10419)
[mjr] Prevent tagging with empty strings.
------
v1.0.1
------
[mjr] Enforce that object and type names are always taken as a string (Bug
#10171).
[mjr] Updated unit tests to new test structure.
[mjr] Prevent tagging with empty tags.
------
v1.0.0
------
[jan] Fix case-insensitive filtering of duplicate tags (Bug #9617).
[jan] Rename all scripts to be prefixed with content- (Request #9647).
content-2.0.5/docs/RELEASE_NOTES 0000664 0001750 0001750 00000000061 12611721515 016341 0 ustar mrubinsk mrubinsk
* @author Michael Rubinsky
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
/**
* @author Chuck Hagenbuch
* @author Michael Rubinsky
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Objects_Manager
{
/**
* Database adapter
*
* @var Horde_Db_Adapter
*/
protected $_db;
/**
* Tables
*
* @TODO: this should probably be populated by the responsible manager...
* @var array
*/
protected $_tables = array(
'objects' => 'rampage_objects',
);
/**
* Type manager
*
* @var Content_Types_Manager
*/
protected $_typeManager;
/**
* Constructor
*
* @param Horde_Db_Adapter $db The db adapter
* @param Content_Types_Manager $typeManager A content type manager
*
* @return Content_Objects_Manager
*/
public function __construct(Horde_Db_Adapter $db, Content_Types_Manager $typeManager)
{
$this->_db = $db;
$this->_typeManager = $typeManager;
}
/**
* Check for object existence without causing the objects to be created.
* Helps save queries for things like tags when we already know the object
* doesn't yet exist in rampage tables.
*
* @param mixed string|array $objects Either an object identifier or an
* array of them.
* @param mixed $type A type identifier. Either a string
* type name or the integer type_id.
*
* @return mixed Either a hash of object_id => object_names or false if
* the object(s) do not exist.
* @throws InvalidArgumentException, Content_Exception
*/
public function exists($objects, $type)
{
$type = current($this->_typeManager->ensureTypes($type));
if (!is_array($objects)) {
$objects = array($objects);
}
if (!count($objects)) {
return array();
}
// Ensure we take the object as a string indentifier.
foreach ($objects as &$object) {
$object = strval($object);
}
$params = $objects;
$params[] = $type;
try {
$ids = $this->_db->selectAssoc(
'SELECT object_id, object_name FROM ' . $this->_t('objects')
. ' WHERE object_name IN ('
. str_repeat('?,', count($objects) - 1) . '?)'
. ' AND type_id = ?', $params);
if ($ids) {
return $ids;
}
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
return false;
}
/**
* Remove the object.
* NOTE: This does not ensure any references to this object were removed.
* E.g., does not remove any tags etc... That is client code's
* responsibility.
*
* @param array $objects An array of object identifiers to delete.
* @param string $type The type of the objects. All objects must be of
* the same type.
*
* @throws Content_Exception
*/
public function delete(array $objects, $type)
{
$type = current($this->_typeManager->ensureTypes($type));
// Ensure we take the object as a string indentifier.
foreach ($objects as &$object) {
$object = strval($object);
}
$params = $objects;
$params[] = $type;
try {
$this->_db->delete(
'DELETE FROM ' . $this->_t('objects') . ' WHERE object_name IN ('
. str_repeat('?,', count($objects) - 1) . '?)'
. ' AND type_id = ?',
$params
);
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
}
/**
* Ensure that an array of objects exist in storage. Create any that don't,
* return object_ids for all. All objects in the $objects array must be
* of the same content type.
*
* @param mixed $objects An array of objects (or single obejct value).
* Values typed as an integer are assumed to already
* be an object_id.
* @param mixed $type Either a string type_name or integer type_id
*
* @return array An array of object_ids.
*/
public function ensureObjects($objects, $type)
{
if (!is_array($objects)) {
$objects = array($objects);
}
$objectIds = array();
$objectName = array();
$type = current($this->_typeManager->ensureTypes($type));
// Anything already typed as an integer is assumed to be an object id.
foreach ($objects as $objectIndex => $object) {
if (is_int($object)) {
$objectIds[$objectIndex] = $object;
} else {
$objectName[$object] = $objectIndex;
}
}
// Get the ids for any objects that already exist.
try {
if (count($objectName)) {
$rows = $this->_db->selectAll(
'SELECT object_id, object_name FROM ' . $this->_t('objects')
. ' WHERE object_name IN ('
. implode(',', array_map(array($this->_db, 'quoteString'), array_keys($objectName)))
. ') AND type_id = ' . $type);
foreach ($rows as $row) {
$objectIndex = $objectName[$row['object_name']];
unset($objectName[$row['object_name']]);
$objectIds[$objectIndex] = $row['object_id'];
}
}
// Create any objects that didn't already exist
foreach ($objectName as $object => $objectIndex) {
$objectIds[$objectIndex] = $this->_db->insert('INSERT INTO '
. $this->_t('objects') . ' (object_name, type_id) VALUES ('
. $this->_db->quoteString($object) . ', ' . $type . ')');
}
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
return $objectIds;
}
/**
* Shortcut for getting a table name.
*
* @param string $tableType
*
* @return string Configured table name.
*/
protected function _t($tableType)
{
return $this->_db->quoteTableName($this->_tables[$tableType]);
}
}
content-2.0.5/lib/Objects/Object.php 0000664 0001750 0001750 00000000412 12611721515 017514 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Object extends Horde_Rdo_Base
{
}
content-2.0.5/lib/Relationships/Relationship.php 0000664 0001750 0001750 00000000000 12611721515 022173 0 ustar mrubinsk mrubinsk content-2.0.5/lib/Relationships/RelationshipMapper.php 0000664 0001750 0001750 00000000000 12611721515 023340 0 ustar mrubinsk mrubinsk content-2.0.5/lib/Tags/Tag.php 0000664 0001750 0001750 00000000407 12611721515 016332 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Tag extends Horde_Rdo_Base
{
}
content-2.0.5/lib/Tags/TagMapper.php 0000664 0001750 0001750 00000000577 12611721515 017507 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_TagMapper extends Horde_Rdo_Mapper
{
/**
* Inflector doesn't support Horde-style tables yet
*/
protected $_table = 'rampage_tags';
}
content-2.0.5/lib/Types/Manager.php 0000664 0001750 0001750 00000005453 12611721515 017405 0 ustar mrubinsk mrubinsk
* @author Michael Rubinsky
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Types_Manager
{
/**
* Database adapter
* @var Horde_Db_Adapter
*/
protected $_db;
/**
* Tables
* @var array
*/
protected $_tables = array(
'types' => 'rampage_types',
);
public function __construct(Horde_Db_Adapter $db)
{
$this->_db = $db;
}
/**
* Ensure that an array of types exist in storage. Create any that don't,
* return type_ids for all.
*
* @param mixed $types An array of types or single type value. Values typed
* as an integer are assumed to already be an type_id.
*
* @return array An array of type_ids.
* @throws Content_Exception
*/
public function ensureTypes($types)
{
if (!is_array($types)) {
$types = array($types);
}
$typeIds = array();
$typeName = array();
// Anything already typed as an integer is assumed to be a type id.
foreach ($types as $typeIndex => $type) {
if (is_int($type)) {
$typeIds[$typeIndex] = $type;
} else {
$typeName[$type] = $typeIndex;
}
}
try {
// Get the ids for any types that already exist.
if (count($typeName)) {
$rows = $this->_db->selectAssoc('SELECT type_id, type_name FROM '
. $this->_t('types') . ' WHERE type_name IN ('
. implode(',', array_map(array($this->_db, 'quoteString'), array_keys($typeName)))
. ')');
foreach ($rows as $id => $type) {
$typeIndex = $typeName[$type];
unset($typeName[$type]);
$typeIds[$typeIndex] = (int)$id;
}
}
// Create any types that didn't already exist
foreach ($typeName as $type => $typeIndex) {
$typeIds[$typeIndex] = intval($this->_db->insert(
'INSERT INTO ' . $this->_t('types')
. ' (type_name) VALUES ('
. $this->_db->quoteString($type) . ')'));
}
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
return $typeIds;
}
/**
* Shortcut for getting a table name.
*
* @param string $tableType
*
* @return string Configured table name.
*/
protected function _t($tableType)
{
return $this->_db->quoteTableName($this->_tables[$tableType]);
}
}
content-2.0.5/lib/Users/Manager.php 0000664 0001750 0001750 00000005510 12611721515 017374 0 ustar mrubinsk mrubinsk
* @author Michael Rubinsky
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Users_Manager
{
/**
* Database adapter
* @var Horde_Db_Adapter
*/
protected $_db;
/**
* Tables
* @var array
*/
protected $_tables = array(
'users' => 'rampage_users',
);
public function __construct(Horde_Db_Adapter $db)
{
$this->_db = $db;
}
/**
* Ensure that an array of users exist in storage. Create any that don't,
* return user_ids for all.
*
* @param array $users An array of users. Values typed as an integer
* are assumed to already be an user_id.
*
* @return array An array of user_ids.
*/
public function ensureUsers($users)
{
if (!is_array($users)) {
$users = array($users);
}
$userIds = array();
$userName = array();
// Anything already typed as an integer is assumed to be a user id.
foreach ($users as $userIndex => $user) {
if (is_int($user)) {
$userIds[$userIndex] = $user;
} else {
$userName[$user] = $userIndex;
}
}
// Get the ids for any users that already exist.
try {
if (count($userName)) {
$userName;
$sql = 'SELECT user_id, user_name FROM ' . $this->_t('users')
. ' WHERE user_name IN (' . implode(',', array_map(array($this, 'toDriver'), array_keys($userName))) . ')';
foreach ($this->_db->selectAll($sql) as $row) {
$userIndex = $userName[$row['user_name']];
unset($userName[$row['user_name']]);
$userIds[$userIndex] = $row['user_id'];
}
}
// Create any users that didn't already exist
foreach ($userName as $user => $userIndex) {
$userIds[$userIndex] = $this->_db->insert('INSERT INTO ' . $this->_t('users') . ' (user_name) VALUES (' . $this->toDriver($user) . ')');
}
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
return $userIds;
}
/**
* Shortcut for getting a table name.
*
* @param string $tableType
*
* @return string Configured table name.
*/
protected function _t($tableType)
{
return $this->_db->quoteTableName($this->_tables[$tableType]);
}
public function toDriver($value)
{
return $this->_db->quoteString(Horde_String::convertCharset($value, 'UTF-8', $this->_db->getOption('charset')));
}
}
content-2.0.5/lib/Application.php 0000664 0001750 0001750 00000002041 12611721515 017160 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
class Content_Indexer
{
/**
* ElasticSearch client
* @var Horde_ElasticSearch_Client
*/
protected $_es;
/**
* User manager object
* @var Content_Users_Manager
*/
protected $_userManager;
/**
* Type management object
* @var Content_Types_Manager
*/
protected $_typeManager;
/**
* Object manager
* @var Content_Objects_Manager
*/
protected $_objectManager;
/**
* Constructor
*/
public function __construct(Horde_ElasticSearch_Client $es,
Content_Users_Manager $userManager,
Content_Types_Manager $typeManager,
Content_Objects_Manager $objectManager)
{
$this->_es = $es;
$this->_userManager = $userManager;
$this->_typeManager = $typeManager;
$this->_objectManager = $objectManager;
}
public function index($index, $type, $id, $data)
{
try {
$this->_es->add($index, $type, $id, $data);
} catch (Horde_ElasticSearch_Exception $e) {
throw new Content_Exception($e);
}
}
public function search($index, $type, $query)
{
try {
return $this->_es->search($index, $type, $query);
} catch (Horde_ElasticSearch_Exception $e) {
throw new Content_Exception($e);
}
}
/**
* Convenience method - if $object is an array, it is taken as an array of
* 'object' and 'type' to pass to objectManager::ensureObjects() if it's a
* scalar value, it's taken as the object_id and simply returned.
*/
protected function _ensureObject($object)
{
if (is_array($object)) {
$object = current($this->_objectManager->ensureObjects(
$object['object'], (int)current($this->_typeManager->ensureTypes($object['type']))));
}
return (int)$object;
}
}
content-2.0.5/lib/ObjectMapper.php 0000664 0001750 0001750 00000001122 12611721515 017267 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*/
/**
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*
* @todo Change name to Content_Objects_Mapper
*/
class Content_ObjectMapper extends Horde_Rdo_Mapper
{
/**
* Inflector doesn't support Horde-style tables yet
*/
protected $_table = 'rampage_objects';
}
content-2.0.5/lib/Tagger.php 0000664 0001750 0001750 00000116146 12611721515 016142 0 ustar mrubinsk mrubinsk
* @author Michael J. Rubinsky
* @license http://www.horde.org/licenses/bsd BSD
* @category Horde
* @package Horde_Content
*
* References:
* http://forge.mysql.com/wiki/TagSchema
* http://www.slideshare.net/edbond/tagging-and-folksonomy-schema-design-for-scalability-and-performance
* http://blog.thinkphp.de/archives/124-An-alternative-Approach-to-Tagging.html
* http://code.google.com/p/freetag/
*
* @TODO:
* need to add type_id to the rampage_tagged table for performance?
* need stat tables by type_id?
*
* Potential features:
* Infer data from combined tags (capital + washington d.c. - http://www.slideshare.net/kakul/tagging-web-2-expo-2008/)
* Normalize tag text (http://tagsonomy.com/index.php/interview-with-gordon-luk-freetag/)
*/
class Content_Tagger
{
/**
* Database connection
* @var Horde_Db_Adapter
*/
protected $_db;
/**
* Tables
* @var array
*/
protected $_tables = array(
'tags' => 'rampage_tags',
'tagged' => 'rampage_tagged',
'objects' => 'rampage_objects',
'tag_stats' => 'rampage_tag_stats',
'user_tag_stats' => 'rampage_user_tag_stats',
'users' => 'rampage_users',
'types' => 'rampage_types',
);
/**
* User manager object
* @var Content_Users_Manager
*/
protected $_userManager;
/**
* Type management object
* @var Content_Types_Manager
*/
protected $_typeManager;
/**
* Object manager
* @var Content_Objects_Manager
*/
protected $_objectManager;
/**
* Default radius for relationship queries.
* @var integer
*/
protected $_defaultRadius = 10;
/**
* Constructor
*/
public function __construct(Horde_Db_Adapter $db,
Content_Users_Manager $userManager,
Content_Types_Manager $typeManager,
Content_Objects_Manager $objectManager)
{
$this->_db = $db;
$this->_userManager = $userManager;
$this->_typeManager = $typeManager;
$this->_objectManager = $objectManager;
}
/**
* Adds a tag or several tags to an object_id. This method does not
* remove other tags.
*
* @param mixed $userId The user tagging the object.
* @param mixed $objectId The object id to tag or an array containing
* the object_name and type.
* @param array $tags An array of tag name or ids.
* @param Horde_Date $created The datetime of the tagging operation.
*
* @return void
*/
public function tag($userId, $objectId, $tags, Horde_Date $created = null)
{
if (is_null($created)) {
$created = date('Y-m-d H:i:s');
} else {
$created = $created->format('Y-m-d H:i:s');
}
// Make sure the object exists
$objectId = $this->_ensureObject($objectId);
// Validate/ensure the parameters
$userId = current($this->_userManager->ensureUsers($userId));
foreach ($this->ensureTags($tags) as $tagId) {
if (!$this->_db->selectValue('SELECT 1 from ' . $this->_t('tagged') . ' WHERE user_id = ? AND object_id = ? AND tag_id = ?', array((int)$userId, (int)$objectId, (int)$tagId))) {
try {
$this->_db->insert(
'INSERT INTO ' . $this->_t('tagged') . ' (user_id, object_id, tag_id, created) VALUES (?, ?, ?, ?)',
array((int)$userId, (int)$objectId, (int)$tagId, $created));
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
// increment tag stats
if (!$this->_db->update('UPDATE ' . $this->_t('tag_stats') . ' SET count = count + 1 WHERE tag_id = ' . (int)$tagId)) {
$this->_db->insert('INSERT INTO ' . $this->_t('tag_stats') . ' (tag_id, count) VALUES (' . (int)$tagId . ', 1)', null, null, 'tag_id', $tagId);
}
// increment user-tag stats
if (!$this->_db->update('UPDATE ' . $this->_t('user_tag_stats') . ' SET count = count + 1 WHERE user_id = ' . (int)$userId . ' AND tag_id = ' . (int)$tagId)) {
$this->_db->insert('INSERT INTO ' . $this->_t('user_tag_stats') . ' (user_id, tag_id, count) VALUES (' . (int)$userId . ', ' . (int)$tagId . ', 1)');
}
}
}
}
/**
* Undo a user's tagging of an object.
*
* @param mixed $userId The user who tagged the object.
* @param mixed $objectId The object to remove the tag from.
* @param array $tags An array of tag name or ids to remove.
*/
public function untag($userId, $objectId, $tags)
{
// Ensure parameters
$userId = current($this->_userManager->ensureUsers($userId));
$objectId = $this->_ensureObject($objectId);
foreach ($this->ensureTags($tags) as $tagId) {
if ($this->_db->delete('DELETE FROM ' . $this->_t('tagged') . ' WHERE user_id = ? AND object_id = ? AND tag_id = ?', array($userId, $objectId, $tagId))) {
$this->_db->update('UPDATE ' . $this->_t('tag_stats') . ' SET count = count - 1 WHERE tag_id = ?', array($tagId));
$this->_db->update('UPDATE ' . $this->_t('user_tag_stats') . ' SET count = count - 1 WHERE user_id = ? AND tag_id = ?', array($userId, $tagId));
}
}
// Cleanup
$this->_db->delete('DELETE FROM ' . $this->_t('tag_stats') . ' WHERE count = 0');
$this->_db->delete('DELETE FROM ' . $this->_t('user_tag_stats') . ' WHERE count = 0');
}
/**
* Remove all occurrences of a specific tag from an object regardless of
* the username who tagged the object originally.
*
* @param mixed $obejctId The object identifier @see Content_Tagger::tag()
* @param mixed $tags The tags to remove. @see Content_Tagger::tag()
*
* @return void
*/
public function removeTagFromObject($objectId, $tags)
{
$objectId = $this->_ensureObject($objectId);
if (!is_array($tags)) {
$tags = array($tags);
}
foreach ($this->ensureTags($tags) as $tagId) {
// Get the users who have tagged this so we can update the stats
$users = $this->_db->selectValues('SELECT user_id, tag_id FROM ' . $this->_t('tagged') . ' WHERE object_id = ? AND tag_id = ?', array($objectId, $tagId));
// Delete the tags
if ($this->_db->delete('DELETE FROM ' . $this->_t('tagged') . ' WHERE object_id = ? AND tag_id = ?', array($objectId, $tagId))) {
// Update the stats
$this->_db->update('UPDATE ' . $this->_t('tag_stats') . ' SET count = count - ' . count($users) . ' WHERE tag_id = ?', array($tagId));
$this->_db->update('UPDATE ' . $this->_t('user_tag_stats') . ' SET count = count - 1 WHERE user_id IN(' . str_repeat('?, ', count($users) - 1) . '?) AND tag_id = ?', array_merge($users, array($tagId)));
// Housekeeping
$this->_db->delete('DELETE FROM ' . $this->_t('tag_stats') . ' WHERE count = 0');
$this->_db->delete('DELETE FROM ' . $this->_t('user_tag_stats') . ' WHERE count = 0');
}
}
}
/**
* Obtain all the tags for a given set of objects.
*
* @param mixed string|array $objects A local object id or an array of
* local object id strings.
* @param mixed $type Either a string type description, or
* an integer content type_id
*
* @return array An array in the form of:
*
* array('localobjectId' => array('tagone', 'tagtwo'),
* 'anotherobjectid' => array('anothertag', 'yetanother'))
*
*/
public function getTagsByObjects($objects, $type)
{
$object_ids = $this->_objectManager->exists($objects, $type);
$results = array();
if (!$object_ids) {
foreach ($objects as $id) {
$results[$id] = array();
}
} else {
$sql = 'SELECT DISTINCT tag_name, tagged.object_id FROM '
. $this->_t('tags') . ' t INNER JOIN ' . $this->_t('tagged')
. ' tagged ON t.tag_id = tagged.tag_id AND tagged.object_id IN ('
. str_repeat('?,', count($object_ids) - 1) . '?)';
$tags = $this->_db->selectAll($sql, array_keys($object_ids));
foreach ($tags as $tag) {
if (empty($results[$object_ids[$tag['object_id']]])) {
$results[$object_ids[$tag['object_id']]] = array();
}
$results[$object_ids[$tag['object_id']]][] = $tag['tag_name'];
}
}
return $results;
}
/**
* Retrieve tags based on criteria.
*
* @param array $args Search criteria:
* q Starts-with search on tag_name.
* limit Maximum number of tags to return.
* offset Offset the results. Only useful for paginating, and not recommended.
* userId Only return tags that have been applied by a specific user.
* typeId Only return tags that have been applied by a specific object type.
* objectId Only return tags that have been applied to a specific object.
*
* @return array An array of tags, id => name.
*/
public function getTags($args)
{
if (isset($args['objectId'])) {
// Don't create the object just because we're trying to load an
// objects's tags - just check if the object is there. Assume if we
// have an integer, it's a valid object_id.
if (is_array($args['objectId'])) {
$args['objectId'] = $this->_objectManager->exists($args['objectId']['object'], $args['objectId']['type']);
if ($args['objectId']) {
$args['objectId'] = current(array_keys($args['objectId']));
}
}
if (!$args['objectId']) {
return array();
}
$sql = 'SELECT DISTINCT t.tag_id AS tag_id, tag_name FROM ' . $this->_t('tags') . ' t INNER JOIN ' . $this->_t('tagged') . ' tagged ON t.tag_id = tagged.tag_id AND tagged.object_id = ' . (int)$args['objectId'];
} elseif (isset($args['userId']) && isset($args['typeId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$args['typeId'] = current($this->_typeManager->ensureTypes($args['typeId']));
$sql = 'SELECT DISTINCT t.tag_id AS tag_id, tag_name FROM ' . $this->_t('tags') . ' t INNER JOIN ' . $this->_t('tagged') . ' tagged ON t.tag_id = tagged.tag_id AND tagged.user_id = ' . (int)$args['userId'] . ' INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id = ' . (int)$args['typeId'];
} elseif (isset($args['userId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$sql = 'SELECT DISTINCT t.tag_id AS tag_id, tag_name FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id WHERE tagged.user_id = ' . (int)$args['userId'];
$haveWhere = true;
} elseif (isset($args['typeId'])) {
$args['typeId'] = current($this->_typeManager->ensureTypes($args['typeId']));
$sql = 'SELECT DISTINCT t.tag_id AS tag_id, tag_name FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id = ' . (int)$args['typeId'] . ' INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id';
} elseif (isset($args['tagId'])) {
$radius = isset($args['limit']) ? (int)$args['limit'] : $this->_defaultRadius;
unset($args['limit']);
$inner = $this->_db->addLimitOffset('SELECT object_id FROM ' . $this->_t('tagged') . ' WHERE tag_id = ' . (int)$args['tagId'], array('limit' => $radius));
$sql = $this->_db->addLimitOffset('SELECT DISTINCT tagged2.tag_id AS tag_id, tag_name FROM (' . $inner . ') tagged1 INNER JOIN ' . $this->_t('tagged') . ' tagged2 ON tagged1.object_id = tagged2.object_id INNER JOIN ' . $this->_t('tags') . ' t ON tagged2.tag_id = t.tag_id', array('limit' => $args['limit']));
} else {
$sql = 'SELECT DISTINCT t.tag_id, tag_name FROM ' . $this->_t('tags') . ' t JOIN ' . $this->_t('tagged') . ' tagged ON t.tag_id = tagged.tag_id';
}
if (isset($args['q']) && strlen($args['q'])) {
// @TODO tossing a where clause in won't work with all query modes
$sql .= (!empty($haveWhere) ? ' AND' : ' WHERE') . ' tag_name LIKE ' . $this->_db->quoteString($args['q'] . '%');
}
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAssoc($sql);
}
/**
* Generate a tag cloud. Same syntax as getTags, except that fetching a
* cloud for a userId + objectId combination doesn't make sense - the counts
* would all be one. In addition, this method returns counts for each tag.
*
* @param array $args Search criteria:
* - limit: (integer) Maximum number of tags to return.
* - offset: (integet) Offset the results. Only useful for paginating, and
* not recommended.
* - userId: (string) Only return tags that have been applied by a
* specific user.
* - typeId: (array) Only return tags that have been applied by specific
* object types.
* - objectId: (array) Only return tags that have been applied to specific
* objects all objects must be of the same type and
* specified by typeId.
* - tagIds: (array) Only return information on specific tag
* (an array of tag ids).
*
* @return array An array of hashes, each containing tag_id, tag_name, and count.
*/
public function getTagCloud($args = array())
{
if (isset($args['objectId'])) {
if (empty($args['typeId'])) {
throw new Content_Exception('Specified objectId, but failed to specify typeId for getTagCloud call.');
}
$args['objectId'] = $this->_objectManager->ensureObjects($args['objectId'], $args['typeId']);
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id WHERE tagged.object_id IN (' . implode(',', $args['objectId']) . ') GROUP BY t.tag_id, t.tag_name';
} elseif (isset($args['userId']) && isset($args['typeId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$args['typeId'] = $this->_typeManager->ensureTypes($args['typeId']);
// This doesn't use a stat table, so may be slow.
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id IN (' . implode(',', $args['typeId']) . ') INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id WHERE tagged.user_id = ' . (int)$args['userId'] . ' GROUP BY t.tag_id, t.tag_name';
} elseif (isset($args['userId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$sql = 'SELECT t.tag_id AS tag_id, tag_name, count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id INNER JOIN ' . $this->_t('user_tag_stats') . ' uts ON t.tag_id = uts.tag_id AND uts.user_id = ' . (int)$args['userId'] . ' GROUP BY t.tag_id, tag_name, count';
} elseif (isset($args['tagIds']) && isset($args['typeId'])) {
$args['typeId'] = $this->_typeManager->ensureTypes($args['typeId']);
// This doesn't use a stat table, so may be slow.
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id IN(' . implode(',', $args['typeId']) . ') INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id AND t.tag_id IN (' . implode(', ', $args['tagIds']) . ') GROUP BY t.tag_id, t.tag_name';
} elseif (isset($args['typeId'])) {
$args['typeId'] = $this->_typeManager->ensureTypes($args['typeId']);
// This doesn't use a stat table, so may be slow.
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id IN(' . implode(',', $args['typeId']) . ') INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id GROUP BY t.tag_id, t.tag_name';
} elseif (isset($args['tagIds'])) {
$ids = $this->_checkTags($args['tagIds'], false);
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id INNER JOIN ' . $this->_t('tag_stats') . ' ts ON t.tag_id = ts.tag_id WHERE t.tag_id IN (' . implode(', ', $ids) . ') GROUP BY t.tag_id, t.tag_name';
} else {
$sql = 'SELECT t.tag_id AS tag_id, tag_name, COUNT(*) AS count FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id INNER JOIN ' . $this->_t('tag_stats') . ' ts ON t.tag_id = ts.tag_id GROUP BY t.tag_id, t.tag_name';
}
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql . ' ORDER BY count DESC', array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
try {
$rows = $this->_db->selectAll($sql);
$results = array();
foreach ($rows as $row) {
$results[$row['tag_id']] = $row;
}
return $results;
} catch (Exception $e) {
throw new Content_Exception($e);
}
}
/**
* Get the most recently used tags.
*
* @param array $args Search criteria:
* limit Maximum number of tags to return.
* offset Offset the results. Only useful for paginating, and not recommended.
* userId Only return tags that have been used by a specific user.
* typeId Only return tags applied to objects of a specific type.
*
* @return array
*/
public function getRecentTags($args = array())
{
$sql = 'SELECT tagged.tag_id AS tag_id, tag_name, MAX(created) AS created FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('tags') . ' t ON tagged.tag_id = t.tag_id';
if (isset($args['typeId'])) {
$args['typeId'] = current($this->_typeManager->ensureTypes($args['typeId']));
$sql .= ' INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id = ' . (int)$args['typeId'];
}
if (isset($args['userId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$sql .= ' WHERE tagged.user_id = ' . (int)$args['userId'];
}
$sql .= ' GROUP BY tagged.tag_id, tag_name ORDER BY created DESC';
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAll($sql);
}
/**
* Get objects matching search criteria.
*
* @param array $args Search criteria:
* limit Maximum number of objects to return.
* offset Offset the results. Only useful for paginating, and not recommended.
* tagId Return objects related through one or more tags.
* notTagId Don't return objects tagged with one or more tags.
* typeId Only return objects with a specific type.
* objectId Return objects with the same tags as $objectId.
* userId Limit results to objects tagged by a specific user.
* radius Radius setting for relationship queries e.g., objectId
*
* @return array An array of object ids.
*/
public function getObjects($args)
{
if (isset($args['objectId'])) {
if (is_array($args['objectId'])) {
$args['objectId'] = current($this->_objectManager->ensureObjects(
$args['objectId']['object'],
$args['objectId']['type']));
}
$radius = isset($args['radius']) ?
(int)$args['radius'] :
$this->_defaultRadius;
$inner = $this->_db->addLimitOffset(
'SELECT tag_id, object_id FROM ' . $this->_t('tagged')
. ' WHERE object_id = ' . (int)$args['objectId'],
array('limit' => $radius));
$sql = 'SELECT t2.object_id AS object_id, object_name FROM ('
. $inner . ') t1 INNER JOIN ' . $this->_t('tagged')
. ' t2 ON t1.tag_id = t2.tag_id INNER JOIN ' . $this->_t('objects')
. ' objects ON objects.object_id = t2.object_id WHERE t2.object_id != '
. (int)$args['objectId'] . ' GROUP BY t2.object_id, object_name';
if (!empty($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, $args['limit']);
}
} elseif (isset($args['tagId'])) {
$tags = is_array($args['tagId']) ? array_values($args['tagId']) : array($args['tagId']);
$count = count($tags);
if (!$count) {
return array();
}
$notTags = isset($args['notTagId']) ? (is_array($args['notTagId']) ? array_values($args['notTagId']) : array($args['notTagId'])) : array();
$notCount = count($notTags);
$sql = 'SELECT DISTINCT tagged.object_id AS object_id, object_name FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('objects') . ' objects ON objects.object_id = tagged.object_id';
if (!empty($args['typeId'])) {
$args['typeId'] = $this->_typeManager->ensureTypes($args['typeId']);
}
if ($count > 1) {
for ($i = 1; $i < $count; $i++) {
$sql .= ' INNER JOIN ' . $this->_t('tagged') . ' tagged' . $i . ' ON tagged.object_id = tagged' . $i . '.object_id';
}
}
if ($notCount) {
// Left joins for tags we want to exclude.
for ($j = 0; $j < $notCount; $j++) {
$sql .= ' LEFT JOIN ' . $this->_t('tagged') . ' not_tagged' . $j . ' ON tagged.object_id = not_tagged' . $j . '.object_id AND not_tagged' . $j . '.tag_id = ' . (int)$notTags[$j];
}
}
$sql .= ' WHERE tagged.tag_id = ' . (int)$tags[0];
if ($count > 1) {
for ($i = 1; $i < $count; $i++) {
$sql .= ' AND tagged' . $i . '.tag_id = ' . (int)$tags[$i];
}
}
if ($notCount) {
for ($j = 0; $j < $notCount; $j++) {
$sql .= ' AND not_tagged' . $j . '.object_id IS NULL';
}
}
if (!empty($args['typeId']) && count($args['typeId'])) {
$sql .= ' AND objects.type_id IN (' . implode(', ', $args['typeId']) . ')';
}
if (array_key_exists('userId', $args)) {
$args['userId'] = $this->_userManager->ensureUsers($args['userId']);
$sql .= ' AND tagged.user_id IN (' . implode(', ', $args['userId']) . ')';
}
}
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAssoc($sql);
}
/**
* Return objects related to the given object via tags, along with a
* similarity rank.
*
* @param mixed $object_id The object to find relations for.
* @param array $args
* limit Maximum number of objects to return (default 10).
* userId Only return objects that have been tagged by a specific user.
* typeId Only return objects of a specific type.
* threshold Number of tags-in-common objects must have to match (default 1).
*
* @return array An array of (client) object ids => similarity rank
*/
public function getSimilarObjects($object_id, array $args = array())
{
$defaults = array('limit' => 10, 'threshold' => 1);
$args = array_merge($defaults, $args);
if (is_array($object_id)) {
$object_id = $this->_objectManager->exists($object_id['object'], $object_id['type']);
if (!$object_id) {
return array();
}
$object_id = current(array_keys($object_id));
} elseif (!is_int($object_id)) {
throw new InvalidArgumentException(_("Missing or invalid object type parameter."));
}
$threshold = intval($args['threshold']);
$max_objects = intval($args['limit']);
if (!isset($object_id) || !($object_id > 0)) {
return array();
}
if ($threshold <= 0 || $max_objects <= 0) {
return array();
}
/* Get the object's tags */
$tagObjects = $this->getTags(array('objectId' => $object_id));
$tagArray = array_keys($tagObjects);
$numTags = count($tagArray);
if ($numTags == 0) {
return array(); // Return empty set of matches
}
$sql = 'SELECT objects.object_name, COUNT(matches.object_id) AS num_common_tags FROM '
. $this->_t('tagged') . ' matches INNER JOIN '
. $this->_t('tags') . ' tags ON (tags.tag_id = matches.tag_id)';
if (!empty($args['userId'])) {
$sql .= ' INNER JOIN ' . $this->_t('users')
. ' users ON users.user_id = matches.user_id AND users.user_name = '
. $this->_db->quoteString($args['userId']);
}
$sql .= ' INNER JOIN ' . $this->_t('objects')
. ' objects ON objects.object_id = matches.object_id';
if (!empty($args['typeId'])) {
$sql .= ' INNER JOIN ' . $this->_t('types')
. ' types ON types.type_id=objects.type_id AND types.type_name = '
. $this->_db->quoteString($args['typeId']);
}
$sql .= ' WHERE tags.tag_id IN (' . implode(',', $tagArray) . ') AND matches.object_id <> ' . (int)$object_id
.' GROUP BY objects.object_name HAVING COUNT(matches.object_id) >= ' . $threshold
. ' ORDER BY num_common_tags DESC';
$sql = $this->_db->addLimitOffset($sql, array('limit' => $max_objects));
try {
return $this->_db->selectAssoc($sql);
} catch (Horde_Db_Exception $e) {
throw new Content_Exception($e);
}
}
/**
* Get the most recently tagged objects.
*
* @param array $args Search criteria:
* limit Maximum number of objects to return.
* offset Offset the results. Only useful for paginating, and not recommended.
* userId Only return objects that have been tagged by a specific user.
* typeId Only return objects of a specific object type.
*
* @return array
*/
public function getRecentObjects($args = array())
{
$sql = 'SELECT tagged.object_id AS object_id, MAX(created) AS created FROM ' . $this->_t('tagged') . ' tagged';
if (isset($args['typeId'])) {
$args['typeId'] = current($this->_typeManager->ensureTypes($args['typeId']));
$sql .= ' INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id = ' . (int)$args['typeId'];
}
if (isset($args['userId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$sql .= ' WHERE tagged.user_id = ' . (int)$args['userId'];
}
$sql .= ' GROUP BY tagged.object_id ORDER BY created DESC';
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAll($sql);
}
/**
* Find users through objects, tags, or other users.
*/
public function getUsers($args)
{
if (isset($args['objectId'])) {
$args['objectId'] = $this->_ensureObject($args['objectId']);
$sql = 'SELECT t.user_id, user_name FROM ' . $this->_t('tagged') . ' t INNER JOIN ' . $this->_t('users') . ' u ON t.user_id = u.user_id WHERE object_id = ' . (int)$args['objectId'];
} elseif (isset($args['userId'])) {
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$radius = isset($args['radius']) ? (int)$args['radius'] : $this->_defaultRadius;
$sql = 'SELECT others.user_id, user_name FROM ' . $this->_t('tagged') . ' others INNER JOIN ' . $this->_t('users') . ' u ON u.user_id = others.user_id INNER JOIN (SELECT tag_id FROM ' . $this->_t('tagged') . ' WHERE user_id = ' . (int)$args['userId'] . ' GROUP BY tag_id HAVING COUNT(tag_id) >= ' . $radius . ') self ON others.tag_id = self.tag_id GROUP BY others.user_id';
} elseif (isset($args['tagId'])) {
$tags = $this->ensureTags($args['tagId']);
//$tags = is_array($args['tagId']) ? array_values($args['tagId']) : array($args['tagId']);
$count = count($tags);
if (!$count) {
return array();
}
$notTags = isset($args['notTagId']) ? (is_array($args['notTagId']) ? array_values($args['notTagId']) : array($args['notTagId'])) : array();
$notCount = count($notTags);
$sql = 'SELECT DISTINCT tagged.user_id, user_name FROM ' . $this->_t('tagged') . ' tagged INNER JOIN ' . $this->_t('users') . ' u ON u.user_id = tagged.user_id ';
if ($count > 1) {
for ($i = 1; $i < $count; $i++) {
$sql .= ' INNER JOIN ' . $this->_t('tagged') . ' tagged' . $i . ' ON tagged.user_id = tagged' . $i . '.user_id';
}
}
if ($notCount) {
// Left joins for tags we want to exclude.
for ($j = 0; $j < $notCount; $j++) {
$sql .= ' LEFT JOIN ' . $this->_t('tagged') . ' not_tagged' . $j . ' ON tagged.user_id = not_tagged' . $j . '.user_id AND not_tagged' . $j . '.tag_id = ' . (int)$notTags[$j];
}
}
$sql .= ' WHERE tagged.tag_id = ' . (int)$tags[0];
if ($count > 1) {
for ($i = 1; $i < $count; $i++) {
$sql .= ' AND tagged' . $i . '.tag_id = ' . (int)$tags[$i];
}
}
if ($notCount) {
for ($j = 0; $j < $notCount; $j++) {
$sql .= ' AND not_tagged' . $j . '.user_id IS NULL';
}
}
}
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAssoc($sql);
}
/**
* Get the users who have most recently tagged objects.
*
* @param array $args Search criteria:
* limit Maximum number of users to return.
* offset Offset the results. Only useful for paginating, and not recommended.
* typeId Only return users who have tagged objects of a specific object type.
*
* @return array
*/
public function getRecentUsers($args = array())
{
$sql = 'SELECT tagged.user_id AS user_id, MAX(created) AS created FROM ' . $this->_t('tagged') . ' tagged';
if (isset($args['typeId'])) {
$args['typeId'] = current($this->_typeManager->ensureTypes($args['typeId']));
$sql .= ' INNER JOIN ' . $this->_t('objects') . ' objects ON tagged.object_id = objects.object_id AND objects.type_id = ' . (int)$args['typeId'];
}
$sql .= ' GROUP BY tagged.user_id ORDER BY created DESC';
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit'], 'offset' => isset($args['offset']) ? $args['offset'] : 0));
}
return $this->_db->selectAll($sql);
}
/**
* Return users related to a given user along with a similarity rank.
*/
public function getSimilarUsers($args)
{
$args['userId'] = current($this->_userManager->ensureUsers($args['userId']));
$radius = isset($args['radius']) ? (int)$args['radius'] : $this->_defaultRadius;
$sql = 'SELECT others.user_id, (others.count - self.count) AS rank FROM ' . $this->_t('user_tag_stats') . ' others INNER JOIN (SELECT tag_id, count FROM ' . $this->_t('user_tag_stats') . ' WHERE user_id = ' . (int)$args['userId'] . ' AND count >= ' . $radius . ') self ON others.tag_id = self.tag_id ORDER BY rank DESC';
if (isset($args['limit'])) {
$sql = $this->_db->addLimitOffset($sql, array('limit' => $args['limit']));
}
return $this->_db->selectAssoc($sql);
}
/**
* Check if tags exists, optionally create them if they don't and return ids
* for all that exist (including those that are optionally created).
*
* @param string|array $tags The tag names to check.
* @param boolean $create If true, create the tag in the tags table.
*
* @return array A hash of tag_name => tag_id values.
*/
protected function _checkTags($tags, $create = true)
{
if (empty($tags)) {
return array();
}
if (!is_array($tags)) {
$tags = is_int($tags) ? array($tags) : $this->splitTags($tags);
}
$tagIds = array();
// Anything already typed as an integer is assumed to be a tag id.
foreach ($tags as $tag) {
if (is_int($tag)) {
$tagIds[$tag] = $tag;
continue;
}
// Don't attempt to tag with an empty value
if (!strlen(trim($tag))) {
continue;
}
// Get the ids for any tags that already exist.
$sql = 'SELECT tag_id FROM ' . $this->_t('tags')
. ' WHERE LOWER(tag_name) = LOWER('
. $this->toDriver($tag) . ')';
if ($id = $this->_db->selectValue($sql)) {
$tagIds[$tag] = (int)$id;
} elseif ($create) {
// Create any tags that didn't already exist
$tagIds[$tag] = (int)$this->_db->insert('INSERT INTO ' . $this->_t('tags') . ' (tag_name) VALUES (' . $this->toDriver($tag) . ')');
}
}
return $tagIds;
}
/**
* Ensure that an array of tags exist, create any that don't, and
* return ids for all of them.
*
* @param array $tags Array of tag names or ids.
*
* @return array Hash of tag_name => tag_id values.
*/
public function ensureTags($tags)
{
return $this->_checkTags($tags);
}
/**
*
*/
public function getTagIds($tags)
{
return $this->_checkTags($tags, false);
}
/**
* Split a string into an array of tag names, respecting tags with spaces
* and ones that are quoted in some way. For example:
* this, "somecompany, llc", "and ""this"" w,o.rks", foo bar
*
* Would parse to:
* array('this', 'somecompany, llc', 'and "this" w,o.rks', 'foo bar')
*
* @param string $text String to split into 1 or more tags.
*
* @return array Split tag array.
*/
public function splitTags($text)
{
// From http://drupal.org/project/community_tags
$regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
preg_match_all($regexp, $text, $matches);
$tags = array();
foreach (array_unique($matches[1]) as $tag) {
// Remove escape codes
$tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag)));
if (strlen($tag)) {
$tags[] = $tag;
}
}
return $tags;
}
/**
* Retrieve a set of tags with relationships to the specified set
* of tags.
*
* @param array $ids An array of tag_ids.
* @param integer $object The object type to limit to.
* @param string $user The user to limit to.
*
* @return array A hash of tag_id -> tag_name
*/
public function browseTags($ids, $object_type, $user)
{
if (!count($ids)) {
return array();
}
$sql = 'SELECT DISTINCT t.tag_id, t.tag_name FROM ' . $this->_t('tagged') . ' as r, ' . $this->_t('objects') . ' as i, ' . $this->_t('tags') . ' as t';
for ($i = 0; $i < count($ids); $i++) {
$sql .= ',' . $this->_t('tagged') . ' as r' . $i;
}
$sql .= ' WHERE r.tag_id = t.tag_id AND r.object_id = i.object_id';
for ($i = 0; $i < count($ids); $i++) {
$sql .= ' AND r' . $i . '.object_id = r.object_id AND r.tag_id != ' . (int)$ids[$i] . ' AND r' . $i . '.tag_id = ' . (int)$ids[$i];
}
/* Note that we don't convertCharset here, it's done in listTagInfo */
$tags = $this->_db->selectAssoc($sql);
foreach ($tags as $key => &$value) {
$value = Horde_String::convertCharset($value, $this->_db->getOption('charset'), 'UTF-8');
}
return $tags;
}
/**
* Convenience method - if $object is an array, it is taken as an array of
* 'object' and 'type' to pass to objectManager::ensureObjects() if it's a
* scalar value, it's taken as the object_id and simply returned.
*/
protected function _ensureObject($object)
{
if (is_array($object)) {
$object = current($this->_objectManager->ensureObjects(
$object['object'], (int)current($this->_typeManager->ensureTypes($object['type']))));
}
return (int)$object;
}
/**
* Shortcut for getting a table name.
*
* @param string $tableType
*
* @return string Configured table name.
*/
protected function _t($tableType)
{
return $this->_db->quoteTableName($this->_tables[$tableType]);
}
public function toDriver($value)
{
return $this->_db->quoteString(Horde_String::convertCharset($value, 'UTF-8', $this->_db->getOption('charset')));
}
}
content-2.0.5/migration/1_rampage_base_tables.php 0000664 0001750 0001750 00000003271 12611721515 022326 0 ustar mrubinsk mrubinsk tables();
if (!in_array('rampage_types', $tableList)) {
// rampage_types
$t = $this->createTable('rampage_types', array('autoincrementKey' => 'type_id'));
$t->column('type_name', 'string', array('limit' => 255, 'null' => false));
$t->end();
$this->addIndex('rampage_types', array('type_name'), array('name' => 'rampage_objects_type_name', 'unique' => true));
}
if (!in_array('rampage_objects', $tableList)) {
// rampage_objects
$t = $this->createTable('rampage_objects', array('autoincrementKey' => 'object_id'));
$t->column('object_name', 'string', array('limit' => 255, 'null' => false));
$t->column('type_id', 'integer', array('null' => false, 'unsigned' => true));
$t->end();
$this->addIndex('rampage_objects', array('type_id', 'object_name'), array('name' => 'rampage_objects_type_object_name', 'unique' => true));
}
if (!in_array('rampage_users', $tableList)) {
// rampage_users
$t = $this->createTable('rampage_users', array('autoincrementKey' => 'user_id'));
$t->column('user_name', 'string', array('limit' => 255, 'null' => false));
$t->end();
$this->addIndex('rampage_users', array('user_name'), array('name' => 'rampage_users_user_name', 'unique' => true));
}
}
public function down()
{
$this->dropTable('rampage_types');
$this->dropTable('rampage_objects');
$this->dropTable('rampage_users');
}
}
content-2.0.5/migration/2_rampage_tag_tables.php 0000664 0001750 0001750 00000005361 12611721515 022172 0 ustar mrubinsk mrubinsk tables();
if (!in_array('rampage_tags', $tableList)) {
// rampage_tags
$t = $this->createTable('rampage_tags', array('autoincrementKey' => 'tag_id'));
$t->column('tag_name', 'string', array('limit' => 255, 'null' => false));
$t->end();
$this->addIndex('rampage_tags', array('tag_name'), array('name' => 'rampage_tags_tag_name', 'unique' => true));
}
if (!in_array('rampage_tagged', $tableList)) {
// rampage_tagged
$t = $this->createTable('rampage_tagged', array('autoincrementKey' => false));
$t->column('user_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('object_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('tag_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('created', 'datetime');
$t->primaryKey(array('user_id', 'object_id', 'tag_id'));
$t->end();
$this->addIndex('rampage_tagged', array('object_id'), array('name' => 'rampage_tagged_object_id'));
$this->addIndex('rampage_tagged', array('tag_id'), array('name' => 'rampage_tagged_tag_id'));
$this->addIndex('rampage_tagged', array('created'), array('name' => 'rampage_tagged_created'));
}
if (!in_array('rampage_tag_stats', $tableList)) {
// rampage_tag_stats
$t = $this->createTable('rampage_tag_stats', array('autoincrementKey' => false));
$t->column('tag_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('count', 'integer', array('unsigned' => true));
$t->primaryKey(array('tag_id'));
$t->end();
}
if (!in_array('rampage_user_tag_stats', $tableList)) {
// rampage_user_tag_stats
$t = $this->createTable('rampage_user_tag_stats', array('autoincrementKey' => false));
$t->column('user_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('tag_id', 'integer', array('null' => false, 'unsigned' => true));
$t->column('count', 'integer', array('unsigned' => true));
$t->primaryKey(array('user_id', 'tag_id'));
$t->end();
$this->addIndex('rampage_user_tag_stats', array('tag_id'), array('name' => 'rampage_user_tag_stats_tag_id'));
}
}
public function down()
{
$this->dropTable('rampage_tags');
$this->dropTable('rampage_tagged');
$this->dropTable('rampage_tag_stats');
$this->dropTable('rampage_user_tag_stats');
}
}
content-2.0.5/test/Content/Sql/Pdo/MysqlTest.php 0000664 0001750 0001750 00000001363 12611721515 021774 0 ustar mrubinsk mrubinsk
* @category Horde
* @package Content
* @subpackage UnitTests
* @license http://www.horde.org/licenses/bsd BSD
*/
class Content_Sql_Pdo_SqliteTest extends Content_Test_Sql_Base
{
public static function setUpBeforeClass()
{
$factory_db = new Horde_Test_Factory_Db();
try {
self::$db = $factory_db->create();
parent::setUpBeforeClass();
} catch (Horde_Test_Exception $e) {
self::$reason = 'Sqlite not available';
}
}
} content-2.0.5/test/Content/Sql/Base.php 0000664 0001750 0001750 00000010416 12611721515 020156 0 ustar mrubinsk mrubinsk _create();
$objects = self::$db->selectAll('SELECT * FROM rampage_objects');
$this->assertEquals(4, count($objects));
// If these aren't strings, then ids were taken as names.
foreach ($objects as $object) {
$this->assertInternalType('string', $object['object_name']);
}
$types = self::$db->selectAll('SELECT * FROM rampage_types');
$this->assertEquals(2, count($types));
foreach ($types as $type) {
$this->assertInternalType('string', $type['type_name']);
}
}
/**
* @depends testCreate
*/
public function testEnsureTags()
{
$this->_testEnsureTags();
}
public function testEnsureTypes()
{
$this->_testEnsureTypes();
}
/**
* @depends testCreate
*/
public function testFullTagCloudSimple()
{
$this->_testFullTagCloudSimple();
}
/**
* @depends testCreate
*/
public function testTagCloudByType()
{
$this->_testTagCloudByType();
}
/**
* @depends testCreate
*/
public function testTagCloudByUser()
{
$this->_testTagCloudByUser();
}
/**
* @depends testCreate
*/
public function testTagCloudByUserType()
{
$this->_testTagCloudByUserType();
}
/**
* @depends testCreate
*/
public function testTagCloudByTagType()
{
$this->_testTagCloudByTagType();
}
/**
* @depends testCreate
*/
public function testTagCloudByTagIds()
{
$this->_testTagCloudByTagIds();
}
/**
* @depends testCreate
*/
public function testGetRecentTags()
{
$this->_testGetRecentTags();
}
/**
* @depends testCreate
*/
public function testGetRecentTagsByUser()
{
$this->_testGetRecentTagsByUser();
}
/**
* @depends testCreate
*/
public function testGetRecentObjects()
{
$this->_testGetRecentObjects();
}
/**
* @depends testCreate
*/
public function testGetRecentTagsByType()
{
$this->_testGetRecentTagsByType();
}
/**
* @depends testCreate
*/
public function testGetRecentObjectsByUser()
{
$this->_testGetRecentObjectsByUser();
}
/**
* @depends testCreate
*/
public function testGetRecentObjectsByType()
{
$this->_testGetRecentObjectsByType();
}
/**
*
* @depends testCreate
*/
public function testGetRecentUsers()
{
$this->_testGetRecentUsers();
}
/**
* @depends testCreate
*/
public function testGetRecentUsersByType()
{
$this->_testGetRecentUsersByType();
}
/**
* @depends testCreate
*/
public function testUntag()
{
$this->_testUntag();
}
public static function setUpBeforeClass()
{
self::$injector = new Horde_Injector(new Horde_Injector_TopLevel());
self::$injector->setInstance('Horde_Db_Adapter', self::$db);
// FIXME: get migration directory if not running from Git checkout.
self::$migrator = new Horde_Db_Migration_Migrator(
self::$db,
null, //$logger,
array('migrationsPath' => __DIR__ . '/../../../migration',
'schemaTableName' => 'content_test_schema'));
self::$migrator->up();
self::$tagger = self::$injector->getInstance('Content_Tagger');
self::$type_mgr = self::$injector->createInstance('Content_Types_Manager');
}
public static function tearDownAfterClass()
{
if (self::$migrator) {
self::$migrator->down();
}
self::$db = null;
parent::tearDownAfterClass();
}
public function setUp()
{
if (!self::$db) {
$this->markTestSkipped(self::$reason);
}
}
} content-2.0.5/test/Content/Sql/MysqliTest.php 0000664 0001750 0001750 00000001600 12611721515 021415 0 ustar mrubinsk mrubinsk
* @category Horde
* @package Content
* @subpackage UnitTests
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
class Content_Sql_MysqliTest extends Content_Test_Sql_Base
{
public static function setUpBeforeClass()
{
if (!extension_loaded('mysqli')) {
self::$reason = 'No mysqli extension';
return;
}
$config = self::getConfig('CONTENT_SQL_MYSQLI_TEST_CONFIG',
__DIR__ . '/..');
if ($config && !empty($config['content']['sql']['mysqli'])) {
self::$db = new Horde_Db_Adapter_Mysqli($config['content']['sql']['mysqli']);
parent::setUpBeforeClass();
}
}
}
content-2.0.5/test/Content/Sql/MysqlTest.php 0000664 0001750 0001750 00000001571 12611721515 021253 0 ustar mrubinsk mrubinsk
* @category Horde
* @package Content
* @subpackage UnitTests
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
class Content_Sql_MysqlTest extends Content_Test_Sql_Base
{
public static function setUpBeforeClass()
{
if (!extension_loaded('mysql')) {
self::$reason = 'No mysql extension';
return;
}
$config = self::getConfig('CONTENT_SQL_MYSQL_TEST_CONFIG',
__DIR__ . '/..');
if ($config && !empty($config['content']['sql']['mysql'])) {
self::$db = new Horde_Db_Adapter_Mysql($config['content']['sql']['mysql']);
parent::setUpBeforeClass();
}
}
}
content-2.0.5/test/Content/Sql/Oci8Test.php 0000664 0001750 0001750 00000001562 12611721515 020750 0 ustar mrubinsk mrubinsk
* @category Horde
* @package Content
* @subpackage UnitTests
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
class Content_Sql_Oci8Test extends Content_Test_Sql_Base
{
public static function setUpBeforeClass()
{
if (!extension_loaded('oci8')) {
self::$reason = 'No oci8 extension';
return;
}
$config = self::getConfig('CONTENT_SQL_OCI8_TEST_CONFIG',
__DIR__ . '/..');
if ($config && !empty($config['content']['sql']['oci8'])) {
self::$db = new Horde_Db_Adapter_Oci8($config['content']['sql']['oci8']);
parent::setUpBeforeClass();
}
}
}
content-2.0.5/test/Content/AllTests.php 0000664 0001750 0001750 00000000132 12611721515 020272 0 ustar mrubinsk mrubinsk run();
content-2.0.5/test/Content/Autoload.php 0000664 0001750 0001750 00000001124 12611721515 020311 0 ustar mrubinsk mrubinsk
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @link http://pear.horde.org/index.php?package=Content
*/
Horde_Test_Autoload::addPrefix('Content', __DIR__ . '/../../lib');
content-2.0.5/test/Content/Base.php 0000664 0001750 0001750 00000033434 12611721515 017424 0 ustar mrubinsk mrubinsk
* @author Chuck Hagenbuch
* @category Horde
* @package Content
* @subpackage UnitTests
*/
class Content_Test_Base extends Horde_Test_Case
{
/**
* @static Content_Tagger
*/
static $tagger;
static $type_mgr;
/**
* Primes the fixture, and tests basic tagging functionality where all
* bits of data are new (user, type, object, tag)..
*
*/
protected function _create()
{
$this->_testEmpty();
// user alice tags an event named 'party' with the tag 'personal' and
// an event named 'anniversary' with the tag 'personal'
self::$tagger->tag('alice', array('type' => 'event', 'object' => 'party'), 'play', new Horde_Date('2008-01-01T00:10:00'));
// user alice tags an event named 'office hours' with the tag 'work'
self::$tagger->tag('alice', array('type' => 'event', 'object' => 'office hours'), 'work', new Horde_Date('2008-01-01T00:05:00'));
// user bob tags a blog named 'daring fireball' with the tag 'apple'
self::$tagger->tag('bob', array('type' => 'blog', 'object' => 'daring fireball'), 'apple', new Horde_Date('2008-01-01T00:20:00'));
// Two users have tagged the same object, with the same tag
self::$tagger->tag('alice', array('type' => 'event', 'object' => 'anniversary'), 'personal', new Horde_Date('2009-01-01T00:05:00'));
self::$tagger->tag('bob', array('type' => 'event', 'object' => 'anniversary'), 'personal', new Horde_Date('2009-01-01T00:06:00'));
}
protected function _testEmpty()
{
// Basic check that no data exists.
$this->assertEmpty(self::$tagger->getTags(array()));
$this->assertEmpty(self::$tagger->getRecentTags());
$this->assertEmpty(self::$tagger->getRecentObjects());
}
/**
* Test types
*
*/
protected function _testEnsureTypes()
{
$this->assertEquals(array(0 => 1, 1 => 2), self::$type_mgr->ensureTypes(array('event', 'blog')));
$this->assertEquals(array(0 => 2, 1 => 1), self::$type_mgr->ensureTypes(array('blog', 'event')));
$this->assertEquals(array(0 => 3, 1 => 2), self::$type_mgr->ensureTypes(array('foo', 'blog')));
}
/**
* Test ensureTags.
*
* 1 => play
* 2 => work
* 3 => apple
* 4 => personal
*/
protected function _testEnsureTags()
{
// Test passing tag_ids to ensureTags
$this->assertEquals(array(1 => 1), self::$tagger->ensureTags(1));
$this->assertEquals(array(1 => 1), self::$tagger->ensureTags(array(1)));
$this->assertEquals(array(1 => 1, 2 => 2), self::$tagger->ensureTags(array(1, 2)));
// Test passing tag names
$this->assertEquals(array('work' => 2), self::$tagger->ensureTags('work'));
$this->assertEquals(array('work' => 2), self::$tagger->ensureTags(array('work')));
$this->assertEquals(array('work' => 2, 'play' => 1), self::$tagger->ensureTags(array('work', 'play')));
// Test mixed
$this->assertEquals(array(1 => 1, 'play' => 1), self::$tagger->ensureTags(array(1, 'play')));
$this->assertEquals(array(2 => 2, 'work' => 2), self::$tagger->ensureTags(array('work', 2)));
$this->assertEquals(array(1 => 1, 'work' => 2), self::$tagger->ensureTags(array(1, 'work')));
}
protected function _testFullTagCloudSimple()
{
$expected = array(
'1' => array(
'tag_id' => 1,
'tag_name' => 'play',
'count' => 1
),
'2' => array(
'tag_id' => 2,
'tag_name' => 'work',
'count' => 1
),
'3' => array(
'tag_id' => 3,
'tag_name' => 'apple',
'count' => 1
),
'4' => array(
'tag_id' => 4,
'tag_name' => 'personal',
'count' => 2
)
);
$cloud = self::$tagger->getTagCloud();
$this->assertEquals($expected, $cloud);
}
protected function _testTagCloudByType()
{
$expected = array(
'3' => array(
'tag_id' => 3,
'tag_name' => 'apple',
'count' => 1
)
);
$cloud = self::$tagger->getTagCloud(array('typeId' => 'blog'));
$this->assertEquals($expected, $cloud);
}
protected function _testTagCloudByUser()
{
$expected = array(
'3' => array(
'tag_id' => 3,
'tag_name' => 'apple',
'count' => 1
),
'4' => array(
'tag_id' => 4,
'tag_name' => 'personal',
'count' => 1
)
);
$cloud = self::$tagger->getTagCloud(array('userId' => 'bob'));
$this->assertEquals($expected, $cloud);
}
protected function _testTagCloudByUserType()
{
$expected = array(
'1' => array(
'tag_id' => 1,
'tag_name' => 'play',
'count' => 1
),
'2' => array(
'tag_id' => 2,
'tag_name' => 'work',
'count' => 1
),
'4' => array(
'tag_id' => 4,
'tag_name' => 'personal',
'count' => 1
)
);
$cloud = self::$tagger->getTagCloud(array('userId' => 'alice', 'typeId' => 'event'));
$this->assertEquals($expected, $cloud);
}
protected function _testTagCloudByTagType()
{
$expected = array(
'2' => array(
'tag_id' => 2,
'tag_name' => 'work',
'count' => 1
)
);
$cloud = self::$tagger->getTagCloud(array('tagIds' => array(2), 'typeId' => 'event'));
$this->assertEquals($expected, $cloud);
}
protected function _testTagCloudByTagIds()
{
$expected = array(
'2' => array(
'tag_id' => 2,
'tag_name' => 'work',
'count' => 1
),
'4' => array(
'tag_id' => 4,
'tag_name' => 'personal',
'count' => 2
)
);
$cloud = self::$tagger->getTagCloud(array('tagIds' => array(2, 4)));
$this->assertEquals($expected, $cloud);
}
protected function _testGetRecentTags()
{
$recent = self::$tagger->getRecentTags();
$this->assertEquals(4, count($recent));
$this->assertEquals(4, $recent[0]['tag_id']);
$this->assertEquals('personal', $recent[0]['tag_name']);
$date = new Horde_Date($recent[0]['created']);
$this->assertEquals(1230764760, $date->timestamp());
}
protected function _testGetRecentTagsByUser()
{
$recent = self::$tagger->getRecentTags(array('userId' => 1));
$this->assertEquals(3, count($recent));
$recent = self::$tagger->getRecentTags(array('userId' => 2));
$this->assertEquals(2, count($recent));
$recent = self::$tagger->getRecentTags(array('userId' => 'alice'));
$this->assertEquals(3, count($recent));
}
protected function _testGetRecentTagsByType()
{
$recent = self::$tagger->getRecentTags(array('typeId' => 'event'));
$this->assertEquals(3, count($recent));
}
protected function _testGetRecentObjects()
{
$recent = self::$tagger->getRecentObjects();
$this->assertEquals(4, count($recent));
$this->assertEquals(4, $recent[0]['object_id']);
$date = new Horde_Date($recent[0]['created'], 'UTC');
$this->assertEquals(1230764760, $date->timestamp());
}
protected function _testUntag()
{
self::$tagger->untag('alice', array('type' => 'event', 'object' => 'party'), 'play');
$count = self::$tagger->getRecentTags();
$this->assertEquals(3, count($count));
//readd
self::$tagger->tag('alice', array('type' => 'event', 'object' => 'party'), 'play', new Horde_Date('2008-01-01T00:10:00'));
$count = self::$tagger->getRecentTags();
$this->assertEquals(4, count($count));
}
/**
* @TODO: SHould validate the values too, not just the count.
*/
protected function _testGetRecentObjectsByUser()
{
// alice has 3 recent objects
$recent = self::$tagger->getRecentObjects(array('userId' => 'alice'));
$this->assertEquals(3, count($recent));
// bob has 2
$recent = self::$tagger->getRecentObjects(array('userId' => 'bob'));
$this->assertEquals(2, count($recent));
// just for kicks, test using the user id, not name.
$recent = self::$tagger->getRecentObjects(array('userId' => 1));
$this->assertEquals(3, count($recent));
}
protected function _testGetRecentObjectsByType()
{
$recent = self::$tagger->getRecentObjects(array('typeId' => 1));
$this->assertEquals(3, count($recent));
$recent = self::$tagger->getRecentObjects(array('typeId' => 2));
$this->assertEquals(1, count($recent));
}
protected function _testGetRecentUsers()
{
$recent = self::$tagger->getRecentUsers();
$this->assertEquals(2, count($recent));
}
protected function _testGetRecentUsersByType()
{
$recent = self::$tagger->getRecentUsers(array('typeId' => 1));
$this->assertEquals(2, count($recent));
$recent = self::$tagger->getRecentUsers(array('typeId' => 2));
$this->assertEquals(1, count($recent));
}
/**
* Test obtaining objects that are tagged with the same tags as the provided
* object.
*
* See Bug: 10439
*/
public function testGetObjectsByObjectId()
{
self::$tagger->tag('mike', array('type' => 'event', 'object' => 'irene'), 'hurricane', new Horde_Date('2011-08-28T00:01:00'));
self::$tagger->tag('mike', array('type' => 'event', 'object' => 'floyd'), 'hurricane', new Horde_Date('1999-09-07T00:02:00'));
$object = self::$tagger->getObjects(array('objectId' => array('type' => 'event', 'object' => 'irene')));
$this->assertEquals('floyd', current($object));
}
public function testDuplicateTagsByCase()
{
// These tests don't work at the moment, because SQLite sucks at
// non-ascii comparing.
/*
self::$tagger->tag('mike', 1, 'TYÖ');
self::$tagger->tag('mike', 1, 'TYÖ');
self::$tagger->tag('mike', 1, 'työ');
self::$tagger->tag('mike', 1, 'työ');
*/
// Use older timestamps to avoid interfering with the later tests
self::$tagger->tag('mike', array('type' => 'foo', 'object' => 'xyz'), 'foo', new Horde_Date('2008-01-01T00:05:00'));
self::$tagger->tag('alice', array('type' => 'foo', 'object' => 'xyz'), 'FOO', new Horde_Date('2008-01-01T00:05:00'));
self::$tagger->tag('alice', array('type' => 'foo', 'object' => 'xyz'), array('test', 'TEST'), new Horde_Date('2008-01-01T00:05:00'));
$this->assertEquals(2, count(self::$tagger->getTags(array('objectId' => array('type' => 'foo', 'object' => 'xyz')))));
}
public function testGetRecentTagsLimit()
{
// Create 100 tags on 100 tag_ids, with tag_id = t1 being applied
// most recently, and so on. Prepend "t" to each tag to force the
// creation of tags that don't yet exist in the test database.
for ($i = 1; $i <= 100; $i++) {
self::$tagger->tag(1, 1, "t$i", new Horde_Date(strtotime('now - ' . $i . ' minutes')));
}
$recentLimit = self::$tagger->getRecentTags(array('limit' => 25));
$this->assertEquals(25, count($recentLimit));
$this->assertEquals('t1', $recentLimit[0]['tag_name']);
}
/**
* @depends testGetRecentTagsLimit
*/
public function testGetRecentTagsOffset()
{
$recentOffset = self::$tagger->getRecentTags(array('limit' => 25, 'offset' => 25));
$this->assertEquals(25, count($recentOffset));
$this->assertEquals('t26', $recentOffset[0]['tag_name']);
}
public function testGetRecentObjectsLimit()
{
// Create 100 tags on 100 object_ids, with object_id = 1 being tagged
// most recently, and so on.
for ($i = 1; $i <= 100; $i++) {
self::$tagger->tag(1, $i, 1, new Horde_Date(strtotime('now - ' . $i . ' minutes')));
}
$recentLimit = self::$tagger->getRecentObjects(array('limit' => 25));
$this->assertEquals(25, count($recentLimit));
$this->assertEquals(1, $recentLimit[0]['object_id']);
}
/**
* @depend testGetRecentTagsOffset
*/
public function testGetRecentObjectsOffset()
{
$recentOffset = self::$tagger->getRecentObjects(array('limit' => 25, 'offset' => 25));
$this->assertEquals(25, count($recentOffset));
$this->assertEquals(26, $recentOffset[0]['object_id']);
}
public function testGetRecentUsersLimit()
{
// Create 100 tags by 100 user_ids, with user_id = 1 tagging
// most recently, and so on.
for ($i = 1; $i <= 100; $i++) {
self::$tagger->tag($i, 1, 1, new Horde_Date(strtotime('now - ' . $i . ' minutes')));
}
$recentLimit = self::$tagger->getRecentUsers(array('limit' => 25));
$this->assertEquals(25, count($recentLimit));
$this->assertEquals(1, $recentLimit[0]['user_id']);
}
/**
* @depend testGetRecentUsersLimit
*/
public function testGetRecentUsersOffset()
{
$recentOffset = self::$tagger->getRecentUsers(array('limit' => 25, 'offset' => 25));
$this->assertEquals(25, count($recentOffset));
$this->assertEquals(26, $recentOffset[0]['user_id']);
}
}
content-2.0.5/test/Content/bootstrap.php 0000664 0001750 0001750 00000000143 12611721515 020556 0 ustar mrubinsk mrubinsk
content-2.0.5/.htaccess 0000664 0001750 0001750 00000000271 12611721515 015237 0 ustar mrubinsk mrubinsk
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]