first commit
This commit is contained in:
556
include/annotation_functions.php
Normal file
556
include/annotation_functions.php
Normal file
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Get annotation by ID
|
||||
*
|
||||
* @param integer $ref Annotation ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getAnnotation($ref)
|
||||
{
|
||||
if (0 >= $ref) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$return = ps_query("SELECT " . columns_in("annotation") . " FROM annotation WHERE ref = ?", array("i",$ref));
|
||||
|
||||
if (0 < count($return)) {
|
||||
$return = $return[0];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* General annotations search functionality
|
||||
*
|
||||
* @uses ps_query()
|
||||
*
|
||||
* @param integer $resource
|
||||
* @param integer $resource_type_field
|
||||
* @param integer $user
|
||||
* @param integer $page
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getAnnotations($resource = 0, $resource_type_field = 0, $user = 0, $page = 0)
|
||||
{
|
||||
if (!is_numeric($resource) || !is_numeric($resource_type_field) || !is_numeric($user) || !is_numeric($page)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sql_where_clause = '';
|
||||
$parameters = array();
|
||||
|
||||
if (0 < $resource) {
|
||||
$sql_where_clause = " resource = ?";
|
||||
$parameters = array("i",$resource);
|
||||
}
|
||||
|
||||
if (0 < $resource_type_field) {
|
||||
if ('' != $sql_where_clause) {
|
||||
$sql_where_clause .= ' AND';
|
||||
}
|
||||
|
||||
$sql_where_clause .= " resource_type_field = ?";
|
||||
$parameters = array_merge($parameters, array("i",$resource_type_field));
|
||||
}
|
||||
|
||||
if (0 < $user) {
|
||||
if ('' != $sql_where_clause) {
|
||||
$sql_where_clause .= ' AND';
|
||||
}
|
||||
|
||||
$sql_where_clause .= " user = ?";
|
||||
$parameters = array_merge($parameters, array("i",$user));
|
||||
}
|
||||
|
||||
if (0 < $page) {
|
||||
if ('' != $sql_where_clause) {
|
||||
$sql_where_clause .= ' AND';
|
||||
}
|
||||
|
||||
$sql_where_clause .= " page = ?";
|
||||
$parameters = array_merge($parameters, array("i",$page));
|
||||
}
|
||||
|
||||
if ('' != $sql_where_clause) {
|
||||
$sql_where_clause = "WHERE {$sql_where_clause}";
|
||||
}
|
||||
|
||||
return ps_query("SELECT " . columns_in("annotation") . " FROM annotation {$sql_where_clause}", $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of annotations available for a resource.
|
||||
*
|
||||
* Note: multi page resources will show the total number (ie. all pages)
|
||||
*
|
||||
* @uses ps_value()
|
||||
*
|
||||
* @param integer $resource Resource ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function getResourceAnnotationsCount($resource)
|
||||
{
|
||||
if (!is_numeric($resource) || 0 >= $resource) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) ps_value("SELECT count(ref) AS `value` FROM annotation WHERE resource = ?", array("i",$resource), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get annotations for a specific resource
|
||||
*
|
||||
* @param integer $resource Resource ID
|
||||
* @param integer $page Page number of a document. Non documents will have 0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getResourceAnnotations($resource, $page = 0)
|
||||
{
|
||||
if (0 >= $resource) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$parameters = array("i",$resource);
|
||||
$sql_page_filter = 'AND `page` IS NULL';
|
||||
|
||||
if (0 < $page) {
|
||||
$sql_page_filter = "AND `page` IS NOT NULL AND `page` = ?";
|
||||
$parameters = array_merge($parameters, array("i",$page));
|
||||
}
|
||||
|
||||
return ps_query("SELECT " . columns_in("annotation") . " FROM annotation WHERE resource = ? {$sql_page_filter}", $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of Annotorious annotation objects which can be JSON encoded and passed
|
||||
* directly to Annotorious
|
||||
*
|
||||
* @param integer $resource Resource ID
|
||||
* @param integer $page Page number of a document
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getAnnotoriousResourceAnnotations($resource, $page = 0)
|
||||
{
|
||||
global $baseurl;
|
||||
|
||||
$annotations = array();
|
||||
$can_view_fields = canSeeAnnotationsFields();
|
||||
|
||||
/*
|
||||
Build an annotations array of Annotorious annotation objects
|
||||
|
||||
IMPORTANT: until ResourceSpace will have text fields implemented as nodes, text should be left blank.
|
||||
|
||||
NOTE: src attribute is generated per resource (dummy source) to avoid issues when source is
|
||||
loaded from download.php
|
||||
*/
|
||||
foreach (getResourceAnnotations($resource, $page) as $annotation) {
|
||||
if (in_array($annotation['resource_type_field'], $can_view_fields)) {
|
||||
$annotations[] = array(
|
||||
'src' => "{$baseurl}/annotation/resource/{$resource}",
|
||||
'text' => '',
|
||||
'shapes' => array(
|
||||
array(
|
||||
'type' => 'rect',
|
||||
'geometry' => array(
|
||||
'x' => (float) $annotation['x'],
|
||||
'y' => (float) $annotation['y'],
|
||||
'width' => (float) $annotation['width'],
|
||||
'height' => (float) $annotation['height'],
|
||||
)
|
||||
)
|
||||
),
|
||||
'editable' => annotationEditable($annotation),
|
||||
|
||||
// Custom ResourceSpace properties for Annotation object
|
||||
'ref' => (int) $annotation['ref'],
|
||||
'resource' => (int) $annotation['resource'],
|
||||
'resource_type_field' => (int) $annotation['resource_type_field'],
|
||||
'page' => (int) $annotation['page'],
|
||||
'tags' => getAnnotationTags($annotation),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an annotation can be editable (add/ edit/ remove) by the user
|
||||
*
|
||||
* @uses checkPermission_anonymoususer()
|
||||
*
|
||||
* @param array $annotation
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function annotationEditable(array $annotation)
|
||||
{
|
||||
debug(sprintf('[annotations][fct=annotationEditable] $annotation = %s', json_encode($annotation)));
|
||||
global $userref;
|
||||
|
||||
$add_operation = !isset($annotation['user']);
|
||||
$field_edit_access = metadata_field_edit_access($annotation['resource_type_field']);
|
||||
|
||||
/* Non-admin edit authorisation is valid when:
|
||||
- user is just adding a new annotation
|
||||
- when editing/removing an existing annotation, the annotation was created by the user itself
|
||||
*/
|
||||
$non_admin_athz = ($add_operation || $userref == $annotation['user']);
|
||||
|
||||
// Anonymous users cannot edit by default. They can only edit if they are allowed CRUD operations
|
||||
if (checkPermission_anonymoususer()) {
|
||||
return $non_admin_athz && $field_edit_access;
|
||||
}
|
||||
|
||||
return (checkperm('a') || $non_admin_athz) && $field_edit_access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tags of an annotation. Checks if a tag is attached to the resource,
|
||||
* allowing the user to search by it which is represented by the virtual column
|
||||
* "tag_searchable"
|
||||
*
|
||||
* @param array $annotation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getAnnotationTags(array $annotation)
|
||||
{
|
||||
$resource_ref = $annotation['resource'];
|
||||
$annotation_ref = $annotation['ref'];
|
||||
|
||||
$parameters = array("i", $resource_ref, "i", $annotation_ref);
|
||||
return ps_query("
|
||||
SELECT " . columns_in("node", "n") . ",
|
||||
(SELECT 'yes' FROM resource_node WHERE resource = ? AND node = ref) AS tag_searchable
|
||||
FROM node AS n
|
||||
WHERE ref IN (SELECT node FROM annotation_node WHERE annotation = ?);", $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete annotation
|
||||
*
|
||||
* @see getAnnotation()
|
||||
*
|
||||
* @uses annotationEditable()
|
||||
* @uses getAnnotationTags()
|
||||
* @uses delete_resource_nodes()
|
||||
* @uses db_begin_transaction()
|
||||
* @uses db_end_transaction()
|
||||
*
|
||||
* @param array $annotation Annotation array as returned by getAnnotation()
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function deleteAnnotation(array $annotation)
|
||||
{
|
||||
if (!annotationEditable($annotation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$annotation_ref = $annotation['ref'];
|
||||
$parameters = array("i",$annotation_ref);
|
||||
|
||||
$nodes_to_remove = array();
|
||||
foreach (getAnnotationTags($annotation) as $tag) {
|
||||
$nodes_to_remove[] = $tag['ref'];
|
||||
}
|
||||
|
||||
db_begin_transaction("deleteAnnotation");
|
||||
|
||||
if (0 < count($nodes_to_remove)) {
|
||||
delete_resource_nodes($annotation['resource'], $nodes_to_remove);
|
||||
}
|
||||
|
||||
ps_query("DELETE FROM annotation_node WHERE annotation = ?", $parameters);
|
||||
ps_query("DELETE FROM annotation WHERE ref = ?", $parameters);
|
||||
|
||||
db_end_transaction("deleteAnnotation");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new annotations based on Annotorious annotation
|
||||
*
|
||||
* NOTE: Annotorious annotation shape is an array but at the moment they use only the first shape found
|
||||
*
|
||||
* @param array $annotation
|
||||
*
|
||||
* @return boolean|integer Returns false on failure OR the ref of the newly created annotation
|
||||
*/
|
||||
function createAnnotation(array $annotation)
|
||||
{
|
||||
debug(sprintf('[annotations][fct=createAnnotation] Param $annotation = %s', json_encode($annotation)));
|
||||
global $userref;
|
||||
|
||||
if (!annotationEditable($annotation)) {
|
||||
debug('[annotations][fct=createAnnotation][warn] annotation not editable');
|
||||
return false;
|
||||
}
|
||||
debug('[annotations][fct=createAnnotation] attempting to create annotation...');
|
||||
|
||||
// ResourceSpace specific properties
|
||||
$resource = $annotation['resource'];
|
||||
$resource_type_field = $annotation['resource_type_field'];
|
||||
$page = (isset($annotation['page']) && 0 < $annotation['page'] ? $annotation['page'] : null);
|
||||
$tags = $annotation['tags'] ?? [];
|
||||
|
||||
// Annotorious annotation
|
||||
$x = $annotation['shapes'][0]['geometry']['x'];
|
||||
$y = $annotation['shapes'][0]['geometry']['y'];
|
||||
$width = $annotation['shapes'][0]['geometry']['width'];
|
||||
$height = $annotation['shapes'][0]['geometry']['height'];
|
||||
|
||||
ps_query(
|
||||
'INSERT INTO annotation (resource, resource_type_field, user, x, y, width, height, page) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
'i', $resource,
|
||||
'i', $resource_type_field,
|
||||
'i', $userref,
|
||||
'd', $x,
|
||||
'd', $y,
|
||||
'd', $width,
|
||||
'd', $height,
|
||||
'i', $page,
|
||||
]
|
||||
);
|
||||
$annotation_ref = sql_insert_id();
|
||||
debug('[annotations][fct=createAnnotation] annotation_ref = ' . json_encode($annotation_ref));
|
||||
|
||||
if (0 == $annotation_ref) {
|
||||
debug('[annotations][fct=createAnnotation][warn] Unable to create annotation');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare tags before association by adding new nodes to dynamic keywords list (if permissions allow it)
|
||||
$prepared_tags = prepareTags($tags);
|
||||
|
||||
// Add any tags associated with it
|
||||
if (0 < count($tags)) {
|
||||
addAnnotationNodes($annotation_ref, $prepared_tags);
|
||||
add_resource_nodes($resource, array_column($prepared_tags, 'ref'), false);
|
||||
}
|
||||
|
||||
return $annotation_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update annotation and its tags if needed
|
||||
*
|
||||
* @uses annotationEditable()
|
||||
* @uses getAnnotationTags()
|
||||
* @uses delete_resource_nodes()
|
||||
* @uses addAnnotationNodes()
|
||||
* @uses add_resource_nodes()
|
||||
* @uses db_begin_transaction()
|
||||
* @uses db_end_transaction()
|
||||
*
|
||||
* @param array $annotation
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function updateAnnotation(array $annotation)
|
||||
{
|
||||
debug(sprintf('[annotations][fct=updateAnnotation] Param $annotation = %s', json_encode($annotation)));
|
||||
if (!isset($annotation['ref']) || !annotationEditable($annotation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $userref;
|
||||
|
||||
// ResourceSpace specific properties
|
||||
$annotation_ref = $annotation['ref'];
|
||||
$resource_type_field = $annotation['resource_type_field'];
|
||||
$resource = $annotation['resource'];
|
||||
$page = (isset($annotation['page']) && 0 < $annotation['page'] ? $annotation['page'] : null);
|
||||
$tags = $annotation['tags'] ?? [];
|
||||
|
||||
// Annotorious annotation
|
||||
$x = $annotation['shapes'][0]['geometry']['x'];
|
||||
$y = $annotation['shapes'][0]['geometry']['y'];
|
||||
$width = $annotation['shapes'][0]['geometry']['width'];
|
||||
$height = $annotation['shapes'][0]['geometry']['height'];
|
||||
|
||||
ps_query(
|
||||
'UPDATE annotation SET resource_type_field = ?, user = ?, x = ?, y = ?, width = ?, height = ?, page = ? WHERE ref = ?',
|
||||
[
|
||||
'i', $resource_type_field,
|
||||
'i', $userref,
|
||||
'd', $x,
|
||||
'd', $y,
|
||||
'd', $width,
|
||||
'd', $height,
|
||||
'i', $page,
|
||||
'i', $annotation_ref,
|
||||
]
|
||||
);
|
||||
|
||||
// Delete existing associations
|
||||
$nodes_to_remove = array();
|
||||
foreach (getAnnotationTags($annotation) as $tag) {
|
||||
$nodes_to_remove[] = $tag['ref'];
|
||||
}
|
||||
|
||||
db_begin_transaction("updateAnnotation");
|
||||
|
||||
if (0 < count($nodes_to_remove)) {
|
||||
delete_resource_nodes($resource, $nodes_to_remove);
|
||||
}
|
||||
|
||||
ps_query("DELETE FROM annotation_node WHERE annotation = ?", ['i', $annotation_ref]);
|
||||
|
||||
// Add any tags associated with this annotation
|
||||
if (0 < count($tags)) {
|
||||
// Prepare tags before association by adding new nodes to
|
||||
// dynamic keywords list (if permissions allow it)
|
||||
$prepared_tags = prepareTags($tags);
|
||||
|
||||
// Add new associations
|
||||
addAnnotationNodes($annotation_ref, $prepared_tags);
|
||||
add_resource_nodes($resource, array_column($prepared_tags, 'ref'), false);
|
||||
}
|
||||
|
||||
db_end_transaction("updateAnnotation");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add relations between annotation and nodes
|
||||
*
|
||||
* @param integer $annotation_ref The annotation ID in ResourceSpace
|
||||
* @param array $nodes List of node structures {@see get_nodes()}. Only the "ref" property is required.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function addAnnotationNodes($annotation_ref, array $nodes)
|
||||
{
|
||||
if (0 === count($nodes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query_insert_values = '';
|
||||
$parameters = [];
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$query_insert_values .= ',(?, ?)';
|
||||
$parameters = array_merge($parameters, ['i', $annotation_ref, 'i', $node['ref']]);
|
||||
}
|
||||
$query_insert_values = substr($query_insert_values, 1);
|
||||
|
||||
ps_query("INSERT INTO annotation_node (annotation, node) VALUES {$query_insert_values}", $parameters);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function which allows annotation tags to be prepared (i.e make sure they are all valid nodes)
|
||||
* before creating associations between annotations and tags
|
||||
*
|
||||
* @uses checkperm()
|
||||
* @uses get_resource_type_field()
|
||||
* @uses set_node()
|
||||
* @uses get_node()
|
||||
*
|
||||
* @param array $dirty_tags Original array of tags. These can be (in)valid tags/ new tags.
|
||||
* IMPORTANT: a tag should have the same structure as a node
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function prepareTags(array $dirty_tags)
|
||||
{
|
||||
if (0 === count($dirty_tags)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
global $annotate_fields;
|
||||
|
||||
$clean_tags = array();
|
||||
|
||||
foreach ($dirty_tags as $dirty_tag) {
|
||||
// Check minimum required information for a node
|
||||
if (
|
||||
!isset($dirty_tag['resource_type_field'])
|
||||
|| 0 >= $dirty_tag['resource_type_field']
|
||||
|| !in_array($dirty_tag['resource_type_field'], $annotate_fields)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($dirty_tag['name']) || '' == $dirty_tag['name']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// No access to field? Next...
|
||||
if (!metadata_field_view_access($dirty_tag['resource_type_field'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// New node?
|
||||
if (is_null($dirty_tag['ref']) || (is_string($dirty_tag['ref']) && '' == $dirty_tag['ref'])) {
|
||||
$dirty_field_data = get_resource_type_field($dirty_tag['resource_type_field']);
|
||||
|
||||
// Only dynamic keywords lists are allowed to create new options from annotations if permission allows it
|
||||
if (
|
||||
!(
|
||||
FIELD_TYPE_DYNAMIC_KEYWORDS_LIST == $dirty_field_data['type']
|
||||
&& !checkperm("bdk{$dirty_tag['resource_type_field']}")
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new node but avoid duplicates
|
||||
$new_node_id = set_node(null, $dirty_tag['resource_type_field'], $dirty_tag['name'], null, null);
|
||||
if (false !== $new_node_id && is_numeric($new_node_id)) {
|
||||
$dirty_tag['ref'] = $new_node_id;
|
||||
|
||||
$clean_tags[] = $dirty_tag;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Existing tags
|
||||
$found_node = array();
|
||||
if (
|
||||
get_node((int) $dirty_tag['ref'], $found_node)
|
||||
&& $found_node['resource_type_field'] == $dirty_tag['resource_type_field']
|
||||
) {
|
||||
$clean_tags[] = $found_node;
|
||||
}
|
||||
}
|
||||
|
||||
return $clean_tags;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add annotation count to a search result set
|
||||
*
|
||||
* @param array $items Array of search results returned by do_search()
|
||||
*
|
||||
*/
|
||||
function search_add_annotation_count(&$result)
|
||||
{
|
||||
$annotations = ps_query(
|
||||
"SELECT resource, count(*) as annocount
|
||||
FROM annotation
|
||||
WHERE resource IN (" . ps_param_insert(count($result)) . ")
|
||||
GROUP BY resource",
|
||||
ps_param_fill(array_column($result, "ref"), "i")
|
||||
);
|
||||
$res_annotations = array_column($annotations, "annocount", "resource");
|
||||
foreach ($result as &$resource) {
|
||||
$resource["annotation_count"] = $res_annotations[$resource["ref"]] ?? 0;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user