Skip to content

Commit

Permalink
save annotations relative to fields, not entities; closes #14, contri…
Browse files Browse the repository at this point in the history
…butes to #12

Note that you have to re-install the Drupal annotation module to get the software to work after this change. Like: drush dis annotation && drush pm-uninstall annotation && drush en annotation
  • Loading branch information
tanius committed Jan 26, 2015
1 parent 8c75f22 commit aba5c35
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 58 deletions.
14 changes: 12 additions & 2 deletions drupal_annotation/annotation.admin.inc
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,14 @@ function annotation_admin_content($form, $form_state) {
$header = array(
'id' => array('data' => t('ID'), 'field' => 'a.id', 'sort' => 'asc'),
'type' => array('data' => t('Type'), 'field' => 'a.type'),
'nid' => array('data' => t('Node ID'), 'field' => 'a.nid'),
'entity_type' => array('data' => t('Entity Type'), 'field' => 'a.entity_type'),
'entity_id' => array('data' => t('Entity ID'), 'field' => 'a.entity_id'),
//'field_name' => array('data' => t('Field Name'), 'field' => 'a.field_name'), // Enable on demand.
//'field_delta' => array('data' => t('Field Delta'), 'field' => 'a.field_delta'), // Enable on demand.
//'field_language' => array('data' => t('Field Language'), 'field' => 'a.field_language'), // Enable on demand.
'quote' => array('data' => t('Quote'), 'field' => 'a.quote'),
'text' => array('data' => t('Text'), 'field' => 'a.text'),
//'language' => array('data' => t('Language'), 'field' => 'a.language'), // Language version of annotation itself. Unused so far.
'tid' => array('data' => t('Tag ID'), 'field' => 'a.tid'),
'author' => t('Author'),
'timestamp' => array('data' => t('Updated'), 'field' => 'a.updated'),
Expand Down Expand Up @@ -114,9 +119,14 @@ function annotation_admin_content($form, $form_state) {
$options[$annotation->id] = array(
'id' => $annotation->id,
'type' => $annotation_type->label,
'nid' => $annotation->nid,
'entity_type' => $annotation->entity_type,
'entity_id' => $annotation->entity_id,
//'field_name' => $annotation->field_name, // Enable on demand.
//'field_delta' => $annotation->field_delta, // Enable on demand.
//'field_language' => $annotation->field_language, // Enable on demand.
'quote' => $annotation->quote,
'text' => $annotation->text,
// 'language' => $annotation->language, // Language version of annotation itself. Unused so far.
'tid' => $annotation->tid != NULL ? l($annotation->tid, "taxonomy/term/$annotation->tid") : 'NULL',
'author' => theme('username', array('account' => $accounts[$annotation->uid])),
'timestamp' => format_date($annotation->updated, 'short'),
Expand Down
8 changes: 6 additions & 2 deletions drupal_annotation/annotation.controller.inc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AnnotationAPIController extends EntityAPIController {
'#access' => TRUE,
'#label_display' => 'above',
'#view_mode' => 'full',
// TODO The language of the quote has to be sourced from the annotation.field_language column.
'#language' => LANGUAGE_NONE,
'#field_name' => 'field_fake_quote',
'#field_type' => 'text',
Expand All @@ -44,7 +45,7 @@ class AnnotationAPIController extends EntityAPIController {
),
'#formatter' => 'text_default',
0 => array(
'#markup' => '<blockquote cite="' . url("node/{$entity->nid}") . '">' . check_plain($entity->quote) . '</blockquote>'
'#markup' => '<blockquote cite="' . url("{$entity->entity_type}/{$entity->entity_id}") . '">' . check_plain($entity->quote) . '</blockquote>'
)
);
$content['text'] = array(
Expand All @@ -54,6 +55,7 @@ class AnnotationAPIController extends EntityAPIController {
'#access' => TRUE,
'#label_display' => 'above',
'#view_mode' => 'full',
// TODO The language of the annotation's text has to be sourced from annotation.language column, once in use.
'#language' => LANGUAGE_NONE,
'#field_name' => 'field_fake_text',
'#field_type' => 'text',
Expand All @@ -67,6 +69,8 @@ class AnnotationAPIController extends EntityAPIController {
'#markup' => check_plain($entity->text)
)
);

// TODO Why is this disabled? Enable if adequate.
// $content['author'] = array(
// '#markup' => t('Created by: @author on @date at !uri', array(
// '@author' => $wrapper->user->name->value(array('sanitize' => TRUE)),
Expand All @@ -77,8 +81,8 @@ class AnnotationAPIController extends EntityAPIController {
// $user = $wrapper->user->name->value();
// dsm(get_properties($wrapper));
// dsm($user);
// TODO Add the following only if Annotator's "Tags" plugin is enabled at /admin/config/content/annotator.

// TODO Make the following code conditional of the Annotator's "Tags" plugin being enabled.
if ($entity->tid != NULL) {
$term = taxonomy_term_load($entity->tid);
$term_markup = l($term->name, "taxonomy/term/{$term->tid}") . " (tid: {$term->tid})";
Expand Down
90 changes: 69 additions & 21 deletions drupal_annotation/annotation.install
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,78 @@ function annotation_schema() {
'not null' => TRUE,
'default' => '',
),
'language' => array(
'description' => 'The {languages}.language of this node.',
'entity_type' => array(
'description' => t('entity type of the annotated entity'),
'type' => 'varchar',
'length' => 12,
'length' => 128,
'not null' => TRUE,
'default' => '',
),
'created' => array(
'description' => 'The creation datetime of the annotation.',
// TODO Delete this later after migrating all code from nid to entity_id.
/*
'nid' => array(
'description' => t('id of the annotated entity'),
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'unsigned' => TRUE,
),
'updated' => array(
'description' => 'The updated datetime of the annotation.',
*/
'entity_id' => array(
'description' => t('id of the annotated entity'),
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'unsigned' => TRUE,
),
/* Revision is not implemented as annotations should eventually apply to all revisions, using word IDs.
'revision_id' => array(
'description' => t('id of the annotated entity'),
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
), */
// The field bundle ("node type") does not have to be recorded ina field, because a field name is unique per entity type.
// See for example the body field, which belongs to entity_type node but can be configured per instance (=for each bundle).
'field_name' => array(
'description' => t('field name of the annotated field of the annotated entity'),
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
),
'field_delta' => array(
'description' => t('index of the annotated field value in multi-value fields'),
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
),
'field_language' => array(
'description' => 'The {languages}.language version of the annotated field.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'quote' => array(
'description' => 'The text of this annotation.',
'type' => 'text',
'size' => 'big',
'not null' => TRUE,
),
'text' => array(
'description' => 'content of annotation',
'type' => 'text',
'size' => 'big',
'not null' => FALSE,
),
'quote' => array(
'description' => 'The text of this annotation.',
'type' => 'text',
'size' => 'big',
'language' => array(
'description' => 'The {languages}.language of the annotation text.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'tid' => array(
'description' => t('id of a taxonomy term for this annotation'),
'type' => 'int',
'not null' => FALSE,
),
'ranges' => array(
'description' => 'The ranges of the annotation.',
Expand All @@ -64,25 +106,31 @@ function annotation_schema() {
'not null' => TRUE,
'default' => 0,
),
'nid' => array(
'description' => t('id of the node to which the annotation applies'),
'created' => array(
'description' => 'The creation datetime of the annotation.',
'type' => 'int',
'not null' => FALSE,
'not null' => TRUE,
'default' => 0,
),
'tid' => array(
'description' => t('id of a taxonomy term for this annotation'),
'updated' => array(
'description' => 'The updated datetime of the annotation.',
'type' => 'int',
'not null' => FALSE,
'not null' => TRUE,
'default' => 0,
),
),
'indexes' => array(
'uid' => array('uid'),
'nid' => array('nid'),
'entity_type' => array('entity_type'),
'entity_id' => array('entity_id'),
'field_name' => array('field_name'),
'field_delta' => array('field_delta'),
'tid' => array('tid'),
'annotation_updated' => array('updated'),
'annotation_created' => array('created'),
'annotation_type' => array(array('type', 4)),
),
// TODO List entity_type and field_name as foreign keys if this is usually done in the Drupal database.
'foreign keys' => array(
'uid' => array(
'table' => 'users',
Expand Down
10 changes: 6 additions & 4 deletions drupal_annotation/annotation.module
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,10 @@ function annotation_nids($uid) {
$query = db_select('annotation', 'anno');
$result = $query
->distinct()
->fields('anno', array('nid'))
->fields('anno', array('entity_id'))
->condition('uid', $uid, '=')
->isNotNull('tid')
->orderBy('nid')
->orderBy('entity_id')
->execute()
->fetchCol(0);
return $result;
Expand All @@ -508,6 +508,9 @@ function annotation_tids($uid) {

/**
* Obtain the IDs of all annotations which are created by the specified user.
*
* TODO The current implementation returns all translations / language versions of these annotations.
* There should be an option to filter by language version.
*/
function annotation_ids($uid) {
// Create a subquery to exclude tags not owned by the user.
Expand All @@ -523,7 +526,7 @@ function annotation_ids($uid) {
->fields('anno', array('id'))
->condition('uid', $uid, '=')
->condition('tid', $own_tids , 'IN')
->orderBy('nid') // So when traversing the result, nodes are only reloaded when needed.
->orderBy('entity_id') // So when traversing the result, nodes are only reloaded when needed.
->execute()
->fetchCol(0);

Expand Down Expand Up @@ -880,4 +883,3 @@ function annotation_export_rqda($uid, $filename) {
watchdog('annotation', 'Finished exporting annotations.');
return TRUE;
}

76 changes: 52 additions & 24 deletions drupal_annotation/annotation.store.inc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ function annotation_api_search() {
$total = 0;
$records = array();

// Set the $uid query parameter only if the user has the permission to view others' annotations.
if (user_access('view any annotations') ) {
if (isset($_GET['user'])) {
$username = $_GET['user'];
Expand All @@ -161,48 +162,75 @@ function annotation_api_search() {
}
}

$limit = isset($_GET['limit']) ? $_GET['limit'] : 20;
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$id = isset($_GET['id']) ? $_GET['id'] : NULL;
$created = isset($_GET['created']) ? $_GET['created'] : NULL;
$updated = isset($_GET['updated']) ? $_GET['updated'] : NULL;
$text = isset($_GET['text']) ? $_GET['text'] : NULL;
$quote = isset($_GET['quote']) ? $_GET['quote'] : NULL;
$nid = isset($_GET['nid']) ? $_GET['nid'] : NULL;
// Set all the other query parameters from GET request input.
// TODO Do we need check_plain here? Or does Drupal do it?
// Initiate query
$limit = isset($_GET['limit']) ? $_GET['limit'] : 20;
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;

$id = isset($_GET['id']) ? $_GET['id'] : NULL;
$entity_type = isset($_GET['entity_type']) ? $_GET['entity_type'] : NULL;
$entity_id = isset($_GET['entity_id']) ? $_GET['entity_id'] : NULL;
$field_name = isset($_GET['field_name']) ? $_GET['field_name'] : NULL;
$field_delta = isset($_GET['field_delta']) ? $_GET['field_delta'] : NULL;
$field_language = isset($_GET['field_language']) ? $_GET['field_language'] : NULL;

$quote = isset($_GET['quote']) ? $_GET['quote'] : NULL;
$text = isset($_GET['text']) ? $_GET['text'] : NULL;
$language = isset($_GET['language']) ? $_GET['language'] : NULL;
$created = isset($_GET['created']) ? $_GET['created'] : NULL;
$updated = isset($_GET['updated']) ? $_GET['updated'] : NULL;

// Initiate query.
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'annotation');
$query->entityCondition('entity_type', 'annotation'); // Not related to the annotaton.entity_type field name, but to the Drupal concept.

// Add conditions to query
// Add conditions to query from the allowed and sanitized values determined above.
// TODO Check if the use of LIKE below is correct: do we need it for all text columns?
if ($uid) {
$query->propertyCondition('uid', $uid);
}
if ($id) {
$query->propertyCondition('id', $id);
}
if ($uid) {
// Use uid instead of user
$query->propertyCondition('uid', $uid);
if ($entity_type) {
$query->propertyCondition('entity_type', $entity_type, 'LIKE');
}
if ($created) {
$query->propertyCondition('created', $created);
if ($entity_id) {
$query->propertyCondition('entity_id', $entity_id);
}
if ($updated) {
$query->propertyCondition('updated', $updated);
if ($field_name) {
$query->propertyCondition('field_name', $field_name, 'LIKE');
}
if ($text) {
$query->propertyCondition('text', $text, 'LIKE');
if ($field_delta) {
$query->propertyCondition('field_delta', $field_delta);
}
if ($field_language) {
$query->propertyCondition('field_language', $field_language, 'LIKE');
}
if ($quote) {
$query->propertyCondition('quote', $quote, 'LIKE');
}
if ($nid) {
$query->propertyCondition('nid', $nid);
if ($text) {
$query->propertyCondition('text', $text, 'LIKE');
}
if ($language) {
$query->propertyCondition('language', $language, 'LIKE');
}
if ($created) {
$query->propertyCondition('created', $created);
}
if ($updated) {
$query->propertyCondition('updated', $updated);
}



// Get the total
// Get the total.
$count_query = clone $query;
$total = $count_query->count()->execute();

// Set query range
// Set query range.
if ($limit > 0) {
$query->range($offset, $limit);
}
Expand Down
18 changes: 14 additions & 4 deletions drupal_annotation/js/annotator_store.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(function ($) {
Drupal.behaviors.annotatorStore = {

attach: function (context, settings) {
Drupal.Annotator.annotator('addPlugin', 'Store', {
prefix: settings.annotator_store.prefix,
Expand All @@ -10,11 +10,21 @@
annotationData: {
'type': 'annotator'
},
loadFromSearch: function(that){
loadFromSearch: function (that) {
// TODO Rework this to also properly determine a comment ID.
// TODO Rework this implementation to one where search parameters are handed by Drupal
// rather than being determined from the rendered content. Because that is an
// "unprofessional" dependency on the presentation layer that easily breaks.
// Example code: https://lists.okfn.org/pipermail/annotator-dev/2014-November/001246.html
return {
'nid': jQuery(that.element).parents('.node').attr('id').split('-')[1]
// TODO Determine the proper values for entity_type, field_* to use for this query.
'entity_type' : 'node',
'entity_id' : jQuery(that.element).parents('.node').attr('id').split('-')[1],
//'field_name' : 'body',
//'field_delta' : '0',
//'field_language' : 'en'
};
},
},
});
}

Expand Down
Loading

0 comments on commit aba5c35

Please sign in to comment.