diff --git a/flag.inc b/flag.inc index ca27db4..6df0d40 100644 --- a/flag.inc +++ b/flag.inc @@ -16,26 +16,34 @@ * should point to the PHP class implementing this flag. */ function flag_flag_definitions() { - return array( - 'node' => array( - 'title' => t('Nodes'), - 'description' => t("Nodes are a Drupal site's primary content."), - 'handler' => 'flag_node', - ), - 'comment' => array( - 'title' => t('Comments'), - 'description' => t('Comments are responses to node content.'), - 'handler' => 'flag_comment', - ), - 'user' => array( - 'title' => t('Users'), - 'description' => t('Users who have created accounts on your site.'), - 'handler' => 'flag_user', - ), - ); + $flags = array(); + foreach (entity_get_info() as $type => $entity) { + // Only add flag support for non-config entities. + if (!isset($entity['configuration']) || !$entity['configuration']) { + $flags[$type] = array( + 'title' => $entity['label'], + 'description' => t('@entity-type entity', array('@entity-type' => $entity['label'])), + 'handler' => flag_get_handler($type), + ); + } + } + return $flags; } /** + * Returns the flag handler class for an entity type, which is flag_{entity_type}. + * If no entity type specific implementation exists, the default flag_entity + * class is used. + */ +function flag_get_handler($entity_type) { + if(class_exists('flag_' . $entity_type)) { + return 'flag_' . $entity_type; + } + return 'flag_entity'; +} + + +/** * Returns a flag definition. */ function flag_fetch_definition($content_type = NULL) { @@ -1239,16 +1247,175 @@ class flag_flag { } /** + * Base entity flag handler. + */ +class flag_entity extends flag_flag { + var $entity_info; + + /** + * Default contructor that furthermore loads the entity info for the current content_type. + * + * content_type is used as synonym for entity_type in this handler. + */ + function construct() { + parent::construct(); + $this->entity_info = entity_get_info($this->content_type); + } + + /** + * Adds additional options that are common for all entity types. + */ + function options() { + $options = parent::options(); + $options += array( + 'show_on_entity' => TRUE, + 'access_author' => '', + ); + return $options; + } + + /** + * Returns forms for the addional options. + */ + function options_form(&$form) { + $bundles = array(); + foreach ($this->entity_info['bundles'] as $bundle_key => $bundle) { + $bundles[$bundle_key] = check_plain($bundle['label']); + } + $form['access']['types'] = array( + '#type' => 'checkboxes', + '#title' => t('Bundles'), + '#options' => $bundles, + '#description' => t('Check any bundle that this flag may be used on. You must check at least one bundle.'), + '#default_value' => $this->types, + '#required' => TRUE, + ); + $form['access']['access_author'] = array( + '#type' => 'radios', + '#title' => t('Flag access by content authorship'), + '#options' => array( + '' => t('No additional restrictions'), + 'own' => t('Users may only flag content they own'), + 'others' => t('Users may only flag content of others'), + ), + '#default_value' => $this->access_author, + '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."), + ); + $form['display']['show_on_entity'] = array( + '#type' => 'checkbox', + '#title' => t('Display link on entity'), + '#default_value' => isset($this->show_on_entity) ? $this->show_on_entity : TRUE, + '#access' => empty($this->locked['show_on_entity']), + ); + } + + /** + * Loads the entity object. + */ + function _load_content($content_id) { + if(is_numeric($content_id)) { + $entity = entity_load($this->content_type, array($content_id)); + return reset($entity); + } + return NULL; + } + + /** + * Checks whether the flag applies for the current entity type. + */ + function applies_to_content_object($entity) { + if (empty($this->entity_info['entity keys']['bundle']) || in_array($entity->{$this->entity_info['entity keys']['bundle']}, $this->types)) { + return TRUE; + } + return FALSE; + } + + /** + * Returns the entity id, if it already exists. + */ + function get_content_id($entity) { + if ($entity && isset($entity->{$this->entity_info['entity keys']['id']})) { + return $entity->{$this->entity_info['entity keys']['id']}; + } + } + + /** + * Returns TRUE if the link should be displayed. + */ + function uses_hook_link($teaser) { + if ($this->show_on_entity) { + return TRUE; + } + return FALSE; + } + + /** + * Returns token types for the current entity type. + */ + function get_labels_token_types() { + // The token type name might be different to the entity type name. If so, + // an own flag entity handler can be used for overriding this. + return array_merge(array($this->content_type), parent::get_labels_token_types()); + } + + /** + * Replaces tokens. + */ + function replace_tokens($label, $contexts, $options, $content_id) { + if ($content_id && ($entity = $this->fetch_content($content_id))) { + $contexts[$this->content_type] = $entity; + } + return parent::replace_tokens($label, $contexts, $options, $content_id); + } + + /** + * Returns a 'flag action' object. + */ + function get_flag_action($content_id) { + $flag_action = parent::get_flag_action($content_id); + $entity = $this->fetch_content($content_id); + $flag_action->content_title = entity_label($this->content_type, $entity); + $flag_action->content_url = _flag_url($this->content_type . '/' . $this->get_content_id($entity)); + return $flag_action; + } + + /** + * Returns objects the action may possible need. + */ + function get_relevant_action_objects($content_id) { + return array( + $this->content_type => $this->fetch_content($content_id), + ); + } + + /** + * Returns information for the Views integration. + */ + function get_views_info() { + return array( + 'views table' => $this->entity_info['base table'], + 'join field' => $this->entity_info['entity keys']['id'], + 'title field' => isset($this->entity_info['entity keys']['label']) ? $this->entity_info['entity keys']['label'] : '', + 'title' => t('@entity_label flag', array('@entity_label' => $this->entity_info['label'])), + 'help' => t('Limit results to only those entity flagged by a certain flag; Or display information about the flag set on a entity.'), + 'counter title' => t('@entity_label flag counter', array('@entity_label' => $this->entity_info['label'])), + 'counter help' => t('Include this to gain access to the flag counter field.'), + ); + } +} + +/** * Implements a node flag. */ -class flag_node extends flag_flag { +class flag_node extends flag_entity { function options() { $options = parent::options(); + // Use own display settings in the meanwhile. + unset($options['show_on_entity']); $options += array( 'show_on_page' => TRUE, 'show_on_teaser' => TRUE, 'show_on_form' => FALSE, - 'access_author' => '', 'i18n' => 0, ); return $options; @@ -1270,18 +1437,6 @@ class flag_node extends flag_flag { '#weight' => 5, ); - $form['access']['access_author'] = array( - '#type' => 'radios', - '#title' => t('Flag access by content authorship'), - '#options' => array( - '' => t('No additional restrictions'), - 'own' => t('Users may only flag content they own'), - 'others' => t('Users may only flag content of others'), - ), - '#default_value' => $this->access_author, - '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."), - ); - $form['display']['show_on_teaser'] = array( '#type' => 'checkbox', '#title' => t('Display link on node teaser'), @@ -1301,17 +1456,7 @@ class flag_node extends flag_flag { '#description' => t('If you elect to have a checkbox on the node edit form, you may specify its initial state in the settings form for each content type.', array('@content-types-url' => url('admin/structure/types'))), '#access' => empty($this->locked['show_on_form']), ); - } - - function _load_content($content_id) { - return is_numeric($content_id) ? node_load($content_id) : NULL; - } - - function applies_to_content_object($node) { - if ($node && in_array($node->type, $this->types)) { - return TRUE; - } - return FALSE; + unset($form['display']['show_on_entity']); } function type_access_multiple($content_ids, $account = NULL) { @@ -1330,10 +1475,6 @@ class flag_node extends flag_flag { return $access; } - function get_content_id($node) { - return $node->nid; - } - /** * Adjust the Content ID to find the translation parent if i18n-enabled. * @@ -1371,10 +1512,6 @@ class flag_node extends flag_flag { return parent::get_flagging_record($content_id, $uid, $sid); } - function get_labels_token_types() { - return array_merge(array('node'), parent::get_labels_token_types()); - } - function replace_tokens($label, $contexts, $options, $content_id) { if (is_numeric($content_id) && ($node = $this->fetch_content($content_id))) { $contexts['node'] = $node; @@ -1391,50 +1528,16 @@ class flag_node extends flag_flag { } return parent::replace_tokens($label, $contexts, $options, $content_id); } - - function get_flag_action($content_id) { - $flag_action = parent::get_flag_action($content_id); - $node = $this->fetch_content($content_id); - $flag_action->content_title = $node->title; - $flag_action->content_url = _flag_url('node/' . $node->nid); - return $flag_action; - } - - function get_valid_actions() { - $actions = module_invoke_all('action_info'); - foreach ($actions as $callback => $action) { - if ($action['type'] != 'node' && !in_array('any', $action['triggers'])) { - unset($actions[$callback]); - } - } - return $actions; - } - - function get_relevant_action_objects($content_id) { - return array( - 'node' => $this->fetch_content($content_id), - ); - } - - function get_views_info() { - return array( - 'views table' => 'node', - 'join field' => 'nid', - 'title field' => 'title', - 'title' => t('Node flag'), - 'help' => t('Limit results to only those nodes flagged by a certain flag; Or display information about the flag set on a node.'), - 'counter title' => t('Node flag counter'), - 'counter help' => t('Include this to gain access to the flag counter field.'), - ); - } } /** * Implements a comment flag. */ -class flag_comment extends flag_flag { +class flag_comment extends flag_entity { function options() { $options = parent::options(); + // Use own display settings in the meanwhile. + unset($options['show_on_entity']); $options += array( 'access_author' => '', 'show_on_comment' => TRUE, @@ -1465,17 +1568,7 @@ class flag_comment extends flag_flag { '#default_value' => $this->show_on_comment, '#access' => empty($this->locked['show_on_comment']), ); - } - - function _load_content($content_id) { - return comment_load($content_id); - } - - function applies_to_content_object($comment) { - if ($comment && ($node = node_load($comment->nid)) && in_array($node->type, $this->types)) { - return TRUE; - } - return FALSE; + unset($form['display']['show_on_entity']); } function type_access_multiple($content_ids, $account) { @@ -1537,26 +1630,16 @@ class flag_comment extends flag_flag { 'node' => node_load($comment->nid), ); } - - function get_views_info() { - return array( - 'views table' => 'comment', - 'join field' => 'cid', - 'title field' => 'subject', - 'title' => t('Comment flag'), - 'help' => t('Limit results to only those comments flagged by a certain flag; Or display information about the flag set on a comment.'), - 'counter title' => t('Comment flag counter'), - 'counter help' => t('Include this to gain access to the flag counter field.'), - ); - } } /** * Implements a user flag. */ -class flag_user extends flag_flag { +class flag_user extends flag_entity { function options() { $options = parent::options(); + // Use own display settings in the meanwhile. + unset($options['show_on_entity']); $options += array( 'show_on_profile' => TRUE, 'access_uid' => '', @@ -1584,6 +1667,7 @@ class flag_user extends flag_flag { '#default_value' => $this->show_on_profile, '#access' => empty($this->locked['show_on_profile']), ); + unset($form['display']['show_on_entity']); } function form_input($form_values) { @@ -1593,19 +1677,6 @@ class flag_user extends flag_flag { $this->access_uid = empty($form_values['access_uid']) ? 'others' : ''; } - function _load_content($content_id) { - return user_load($content_id); - } - - function applies_to_content_object($user) { - // This user flag doesn't currently support subtypes so we return TRUE for - // any user. - if ($user) { - return TRUE; - } - return FALSE; - } - function type_access($content_id, $action, $account) { // Prevent users from flagging themselves. if ($this->access_uid == 'others' && $content_id == $account->uid) { @@ -1629,10 +1700,6 @@ class flag_user extends flag_flag { return $access; } - function get_content_id($user) { - return $user->uid; - } - function uses_hook_link($teaser) { if ($this->show_on_profile) { return TRUE; @@ -1640,17 +1707,6 @@ class flag_user extends flag_flag { return FALSE; } - function get_labels_token_types() { - return array_merge(array('user'), parent::get_labels_token_types()); - } - - function replace_tokens($label, $contexts, $options, $content_id) { - if ($content_id && ($user = $this->fetch_content($content_id))) { - $contexts['user'] = $user; - } - return parent::replace_tokens($label, $contexts, $options, $content_id); - } - function get_flag_action($content_id) { $flag_action = parent::get_flag_action($content_id); $user = $this->fetch_content($content_id); @@ -1666,15 +1722,9 @@ class flag_user extends flag_flag { } function get_views_info() { - return array( - 'views table' => 'users', - 'join field' => 'uid', - 'title field' => 'name', - 'title' => t('User flag'), - 'help' => t('Limit results to only those users flagged by a certain flag; Or display information about the flag set on a user.'), - 'counter title' => t('User flag counter'), - 'counter help' => t('Include this to gain access to the flag counter field.'), - ); + $views_info = parent::get_views_info(); + $views_info['title field'] = 'name'; + return $views_info; } } diff --git a/flag.module b/flag.module index 957bb12..0017b7d 100644 --- a/flag.module +++ b/flag.module @@ -447,28 +447,17 @@ function flag_form_alter(&$form, &$form_state, $form_id) { } /** - * Implements hook_comment_view(). + * Implements hook_entity_view(). */ -function flag_comment_view($comment) { - $links = flag_link('comment', $comment); - $comment->content['links']['flag'] = array( +function flag_entity_view($entity, $type, $view_mode, $langcode) { + $links = flag_link($type, $entity, $view_mode == 'teaser'); + $entity->content['links']['flag'] = array( '#theme' => 'links', '#links' => $links, '#attributes' => array('class' => array('links', 'inline')), ); } -/** - * Implements hook_node_view(). - */ -function flag_node_view($node, $view_mode) { - $links = flag_link('node', $node, $view_mode == 'teaser'); - $node->content['links']['flag'] = array( - '#theme' => 'links__node__flag', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); -} /** * Implements hook_node_insert(). diff --git a/includes/flag.export.inc b/includes/flag.export.inc index d0ea2ec..fa441b4 100644 --- a/includes/flag.export.inc +++ b/includes/flag.export.inc @@ -51,6 +51,10 @@ function flag_export_flags($flags = array(), $module = '', $indent = '') { unset($new_flag['fid']); // The name is emitted as the key for the array. unset($new_flag['name']); + // Do not export the whole entity info, as it is just used as helper data. + if (isset($new_flag['entity_info'])) { + unset($new_flag['entity_info']); + } $output .= $indent . '// Exported flag: "' . check_plain($flag->get_title()) . '"' . ".\n"; $output .= $indent . '$flags[\'' . $flag->name . '\'] = ' . (function_exists('features_var_export') ? features_var_export($new_flag, $indent) : var_export($new_flag, TRUE)) . ";\n";