You are here

class OpenGraphMeta in Open Graph meta tags 7

Same name and namespace in other branches
  1. 6 opengraph_meta.common.inc \OpenGraphMeta
  2. 7.2 opengraph_meta.common.inc \OpenGraphMeta

Hierarchy

Expanded class hierarchy of OpenGraphMeta

File

./opengraph_meta.common.inc, line 17

View source
class OpenGraphMeta {
  const TITLE = 'title';
  const DESCRIPTION = 'description';
  const IMAGE = 'image';
  const SITE_NAME = 'site_name';
  const TYPE = 'type';
  const URL = 'url';

  // Location tags
  const LATITUDE = 'latitude';
  const LONGITUDE = 'longitude';
  const STREET_ADDRESS = 'street-address';
  const LOCALITY = 'locality';
  const REGION = 'region';
  const POST_CODE = 'postal-code';
  const COUNTRY_NAME = 'country-name';

  // Contact tags
  const EMAIL = 'email';
  const PHONE_NUMBER = 'phone_number';
  const FAX_NUMBER = 'fax_number';

  /** Db field name for optional tags (not to be used by external code) */
  const __OPTIONAL_DB_FIELD = 'optional';

  /** Singleton instance. */
  private static $instance = NULL;
  private $settings_obj = NULL;
  private $render_obj = NULL;
  private $data_obj = NULL;

  /**
   * @var array Keep track of which meta tags have already been written to output so that we don't write them again.
   * This is handy when we're outputting multiples nodes to a single page view.
   */
  private $tags_already_output = array();

  /**
   * Constructor
   */
  private function __construct() {
    $this->settings_obj = new OGMDrupalSettings();
    $this->render_obj = new OGMDrupalRender();
    $this->data_obj = new OGMDrupalData();
  }

  /** Get singleton instance. */
  public static function instance() {
    if (empty(self::$instance)) {
      self::$instance = new OpenGraphMeta();
    }
    return self::$instance;
  }

  /**
   * Get default values for all meta tags (including optional ones).
   * @param $node a node object. If provided then defaults will be tailored to this node.
   */
  public function get_og_optional_tag_defaults($node = NULL) {
    static $defaults = array();
    if (empty($defaults)) {
      $defaults = array(
        self::LATITUDE => '',
        self::LONGITUDE => '',
        self::STREET_ADDRESS => '',
        self::LOCALITY => '',
        self::REGION => '',
        self::POST_CODE => '',
        self::COUNTRY_NAME => '',
        self::EMAIL => '',
        self::PHONE_NUMBER => '',
        self::FAX_NUMBER => '',
      );
    }
    if (!empty($node)) {
      $optionals = variable_get(OPENGRAPH_META_VAR_OPTIONAL_TAGS, array());
      foreach ($defaults as $f => &$i) {
        $i = !empty($optionals[$f]) ? $optionals[$f] : $i;
      }
    }
    return $defaults;
  }

  /**
   * Get default values for all meta tags (including optional ones).
   * @param $node a node object. If provided then defaults will be tailored to this node.
   * @param $full_view whether the node is being viewed on its own in full view.
   */
  private function get_og_tag_defaults($node = NULL, $full_view = FALSE) {

    // defaults
    static $defaults = array();
    if (empty($defaults)) {
      $defaults = array(
        self::TITLE => '',
        self::DESCRIPTION => '',
        self::IMAGE => '',
        self::TYPE => '',
        self::URL => '',
      );
    }
    $ret = array_merge($defaults, $this
      ->get_og_optional_tag_defaults($node));
    $ret[self::SITE_NAME] = $this->settings_obj
      ->get(OPENGRAPH_META_VAR_SITE_NAME, $this->settings_obj
      ->get('site_name', 'Drupal'));
    if (!empty($node)) {

      // if node given then override defaults
      $ret[self::TITLE] = $full_view ? drupal_get_title() : $node->title;
      $body = OpenGraphMetaDrupalLayer::get_node_body($node);
      $ret[self::DESCRIPTION] = !empty($body) ? drupal_substr(strip_tags($body), 0, 200) : $node->title;
      $ret[self::TYPE] = $this->settings_obj
        ->get(OPENGRAPH_META_VAR_CONTENT_TYPE_ . $node->type, '');
      $ret[self::IMAGE] = $this->settings_obj
        ->get(OPENGRAPH_META_VAR_FALLBACK_IMG, '');
      $images = $this
        ->harvest_images_from_node($node);
      if (!empty($images)) {
        $i = array_shift($images);
        $ret[self::IMAGE] = $i['url'];
      }
      $ret[self::URL] = url(drupal_get_path_alias('node/' . $node->nid), array(
        'absolute' => TRUE,
      ));
    }
    return $ret;
  }

  /**
   * Get all possible values for the og:type meta tags, grouped by category, ready for use as #options for a
   * SELECT form item.
   */
  public function get_all_og_types_for_select_field() {
    static $ret = array();
    if (empty($ret)) {

      // Taken from http://opengraphprotocol.org/ on 18/Nov/2010
      $ogtypes = array(
        t('Activities') => array(
          'activity',
          'sport',
        ),
        t('Businesses') => array(
          'bar',
          'company',
          'cafe',
          'hotel',
          'restaurant',
        ),
        t('Groups') => array(
          'cause',
          'sports_league',
          'sports_team',
        ),
        t('Organizations') => array(
          'band',
          'government',
          'non_profit',
          'school',
          'university',
        ),
        t('People') => array(
          'actor',
          'athlete',
          'author',
          'director',
          'musician',
          'politician',
          'public_figure',
        ),
        t('Places') => array(
          'city',
          'country',
          'landmark',
          'state_province',
        ),
        t('Products and Entertainment') => array(
          'album',
          'book',
          'drink',
          'food',
          'game',
          'movie',
          'product',
          'song',
          'tv_show',
        ),
        t('Websites') => array(
          'article',
          'blog',
          'website',
        ),
      );
      $ret = array(
        '' => '',
      );
      foreach ($ogtypes as $cat => $t) {
        $ret[$cat] = array_combine($t, $t);
      }
    }
    return $ret;
  }

  /**
   * Get whether meta tags are enabled for the given content type.
   * @return TRUE if so; FALSE otherwise.
   */
  public function tags_are_enabled_for_content_type($type) {
    $content_types = $this->settings_obj
      ->get(OPENGRAPH_META_VAR_CONTENT_TYPES_ENABLED, array());
    $content_types = array_filter($content_types);

    // if no content types specifically set OR if this content type is set then tags are enabled
    return empty($content_types) || !empty($content_types[$type]);
  }

  /** Delete FB meta tag data for the given node. */
  public function delete_node_data($nid) {
    $this->data_obj
      ->delete_tags($nid);
  }

  /**
   * Save FB meta tag data for the given node.
   *
   * @param $data key-value pairs
   */
  public function save_node_data($nid, $data) {
    $ret = $this->data_obj
      ->load_tags($nid);
    if (FALSE === $ret) {
      $row = new stdClass();
      $row->nid = $nid;
    }
    else {
      $row = $ret;
    }

    // collapse data tree into 1-D array
    $collapsed_data = new stdClass();

    // needed to work around pass-by-reference deprecation warning for array_walk_recursive
    $collapsed_data->keys = array();
    $collapsed_data->values = array();
    array_walk_recursive($data, create_function('$val, $key, $obj', 'array_push($obj->keys, $key); array_push($obj->values, $val);'), $collapsed_data);
    $collapsed_data = array_combine($collapsed_data->keys, $collapsed_data->values);
    foreach ($this
      ->get_og_tag_defaults() as $field => $_d) {
      $row->{$field} = isset($collapsed_data[$field]) ? $collapsed_data[$field] : '';
    }
    $this->data_obj
      ->update_tags($row, FALSE !== $ret ? 'nid' : array());
  }

  /**
   * Load FB meta tag data for the given node.
   *
   * @return array('title' => ..., 'summary' => ...)
   */
  public function load_node_data($node) {
    $fields = $this
      ->get_og_tag_defaults();
    $row = $this->data_obj
      ->load_tags($node->nid);
    if (FALSE !== $row) {
      foreach ($fields as $field => &$val) {
        if (isset($row->{$field})) {
          $val = $row->{$field};
        }
      }
    }
    return $fields;
  }

  /** Render the meta tag data for the given node. */
  public function render_data(&$node, $opengraph_data) {

    // fallback values in case no values set.
    $allfields = $this
      ->get_og_tag_defaults($node, TRUE);
    foreach ($allfields as $field => $_d) {

      // already written this meta tag to output?
      if (array_key_exists($field, $this->tags_already_output)) {
        $this
          ->warn(t("Already output og:%s", array(
          '%s' => $field,
        )));
        continue;
      }
      $v = !empty($opengraph_data[$field]) ? $opengraph_data[$field] : $_d;
      if (!empty($v)) {
        switch ($field) {
          case self::IMAGE:
            $v = url(ltrim($v, '/'), array(
              'absolute' => TRUE,
            ));
            break;
          case self::TITLE:
          case self::DESCRIPTION:
          case self::STREET_ADDRESS:
          case self::LOCALITY:
          case self::REGION:
          case self::COUNTRY_NAME:
            $v = htmlspecialchars(htmlspecialchars_decode($v));
            break;
          case self::LATITUDE:
          case self::LONGITUDE:
            $v = floatval($v);
            break;
        }

        // allow other people to alter this value
        $v = OpenGraphMetaDrupalLayer::invoke_filter('og_tag_render_alter', $v, array(
          $node,
          $field,
        ));
        $this->render_obj
          ->add_meta('og:' . $field, $v);
        $this->tags_already_output[$field] = true;
      }

      // if value available for field
    }

    // for each field
  }

  /**
   * Harvest all images from the given node.
   *
   * @return array(array('title' => , 'alt' => , 'url' =>))
   */
  public function harvest_images_from_node($node) {

    // extract image fields
    $ret = array();
    OpenGraphMetaDrupalLayer::extract_image_fields((array) $node, $ret);

    // extract all images from body content
    $body = OpenGraphMetaDrupalLayer::get_node_body($node);
    if (!empty($body)) {
      libxml_use_internal_errors(TRUE);

      // turn off libxml errors for now
      $doc = new DOMDocument();
      $doc
        ->loadHTML($body);
      $list = $doc
        ->getElementsByTagName('img');
      for ($i = 0; $list->length > $i; ++$i) {
        $item = $list
          ->item($i);
        if ($item
          ->hasAttribute('src')) {
          $url = $item
            ->getAttribute('src');
          if (!empty($url)) {
            $ret[$url] = array(
              'title' => $url,
              'alt' => $url,
              'url' => $url,
            );
          }
        }
      }
      libxml_use_internal_errors(FALSE);

      // turn libxml errors back on
    }
    return $ret;
  }

  /**
   * Log a warning message.
   * @param $msg the message.
   */
  public function warn($msg) {
    watchdog('opengraph_meta', $msg, array(), WATCHDOG_WARNING);
  }

  /**
   * FOR TESTING PURPOSES ONLY!
   * Replace the internally used data and config instances with the given ones.
   */
  public function __set_objects($data_obj, $settings_obj, $render_obj) {
    $this->data_obj = $data_obj;
    $this->settings_obj = $settings_obj;
    $this->render_obj = $render_obj;
  }

  /**
   * FOR TESTING PURPOSES ONLY!
   * Get the internally used data and config instances with the given ones.
   */
  public function __get_objects() {
    return array(
      $this->data_obj,
      $this->settings_obj,
      $this->render_obj,
    );
  }

  /**
   * FOR TESTING PURPOSES ONLY!
   * Reset array which keeps track of which tags have already been output.
   */
  public function __reset_tags_already_output() {
    $this->tags_already_output = array();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
OpenGraphMeta::$data_obj private property
OpenGraphMeta::$instance private static property Singleton instance.
OpenGraphMeta::$render_obj private property
OpenGraphMeta::$settings_obj private property
OpenGraphMeta::$tags_already_output private property This is handy when we're outputting multiples nodes to a single page view.
OpenGraphMeta::COUNTRY_NAME constant
OpenGraphMeta::delete_node_data public function Delete FB meta tag data for the given node.
OpenGraphMeta::DESCRIPTION constant
OpenGraphMeta::EMAIL constant
OpenGraphMeta::FAX_NUMBER constant
OpenGraphMeta::get_all_og_types_for_select_field public function Get all possible values for the og:type meta tags, grouped by category, ready for use as #options for a SELECT form item.
OpenGraphMeta::get_og_optional_tag_defaults public function Get default values for all meta tags (including optional ones).
OpenGraphMeta::get_og_tag_defaults private function Get default values for all meta tags (including optional ones).
OpenGraphMeta::harvest_images_from_node public function Harvest all images from the given node.
OpenGraphMeta::IMAGE constant
OpenGraphMeta::instance public static function Get singleton instance.
OpenGraphMeta::LATITUDE constant
OpenGraphMeta::load_node_data public function Load FB meta tag data for the given node.
OpenGraphMeta::LOCALITY constant
OpenGraphMeta::LONGITUDE constant
OpenGraphMeta::PHONE_NUMBER constant
OpenGraphMeta::POST_CODE constant
OpenGraphMeta::REGION constant
OpenGraphMeta::render_data public function Render the meta tag data for the given node.
OpenGraphMeta::save_node_data public function Save FB meta tag data for the given node.
OpenGraphMeta::SITE_NAME constant
OpenGraphMeta::STREET_ADDRESS constant
OpenGraphMeta::tags_are_enabled_for_content_type public function Get whether meta tags are enabled for the given content type.
OpenGraphMeta::TITLE constant
OpenGraphMeta::TYPE constant
OpenGraphMeta::URL constant
OpenGraphMeta::warn public function Log a warning message.
OpenGraphMeta::__construct private function Constructor
OpenGraphMeta::__get_objects public function FOR TESTING PURPOSES ONLY! Get the internally used data and config instances with the given ones.
OpenGraphMeta::__OPTIONAL_DB_FIELD constant Db field name for optional tags (not to be used by external code)
OpenGraphMeta::__reset_tags_already_output public function FOR TESTING PURPOSES ONLY! Reset array which keeps track of which tags have already been output.
OpenGraphMeta::__set_objects public function FOR TESTING PURPOSES ONLY! Replace the internally used data and config instances with the given ones.