social_content.module in Social Content 7
Same filename and directory in other branches
Social Content module.
File
social_content.moduleView source
<?php
/**
* @file
* Social Content module.
*/
/**
* Implements hook_menu().
*/
function social_content_menu() {
$info = array();
$info['admin/config/services/social-content'] = array(
'title' => 'Social content',
'page callback' => 'social_content_overview_types',
'access arguments' => array(
'administer social content',
),
'file' => 'social_content.admin.inc',
);
$info['admin/config/services/social-content/%social_content_type/edit'] = array(
'title' => 'Edit Social content type',
'title callback' => 'social_content_page_title',
'title arguments' => array(
4,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'social_content_form',
4,
),
'access arguments' => array(
'administer social content',
),
'file' => 'social_content.admin.inc',
);
$info['admin/config/services/social-content/%social_content_type/run'] = array(
'title' => 'Run Social content import',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'social_content_import_run_form',
4,
),
'access arguments' => array(
'administer social content',
),
'file' => 'social_content.admin.inc',
);
return $info;
}
/**
* Menu argument loader: loads a social_content_type type by string.
*
* @param string $name
* The machine-readable name of a social_content_type to load.
*
* @return object|FALSE
* A node type object or FALSE if $name does not exist.
*/
function social_content_type_load($name) {
$types = social_content_get_types();
if (isset($types[$name])) {
return $types[$name];
}
else {
return FALSE;
}
}
/**
* Implements hook_permission().
*/
function social_content_permission() {
return array(
'administer social content' => array(
'title' => t('Administer social content'),
'description' => t('Administer social content.'),
),
);
}
/**
* Title callback: Returns the full title for a social content type.
*
* @param object $social_content_type
* The social content type object.
*
* @return string
* An unsanitized string that from the title of the social content type.
*
* @see social_content_menu()
*/
function social_content_page_title($social_content_type) {
return $social_content_type['title'];
}
/**
* Gets all social content types.
*
* Invokes hook_social_content_info and hook_social_content_info_alter.
*/
function social_content_get_types() {
$social_content_types =& drupal_static(__FUNCTION__);
if (!isset($social_content_types)) {
$social_content_types = module_invoke_all('social_content_info');
foreach ($social_content_types as $name => $type) {
$social_content_types[$name]['name'] = $name;
}
drupal_alter('social_content_info', $social_content_types);
}
return $social_content_types;
}
/**
* Get the settings for a social_content_type.
*
* @param object $social_content_type
* The social content type object.
*
* @return array
* An array of settings.
*/
function social_content_get_settings($social_content_type) {
$settings =& drupal_static(__FUNCTION__);
if (!$settings || !isset($settings[$social_content_type['name']])) {
$defaults = array(
'limit' => 200,
'auto_publish' => 1,
'enabled' => 0,
);
if (isset($social_content_type['additional_settings']) && is_array($social_content_type['additional_settings'])) {
$defaults += $social_content_type['additional_settings'];
}
$social_content_settings = variable_get('social_content_' . $social_content_type['name'], array());
$social_content_settings += $defaults;
drupal_alter('social_content_settings', $social_content_settings, $social_content_type);
$settings[$social_content_type['name']] = $social_content_settings;
}
return $settings[$social_content_type['name']];
}
/**
* Implements hook_cron().
*
* Run through the social content types and import the posts.
*/
function social_content_cron() {
$types = social_content_get_types();
foreach ($types as $social_content_type) {
$settings = social_content_get_settings($social_content_type);
if ($settings['enabled']) {
social_content_run_import($social_content_type, $settings);
}
}
}
/**
* Implements hook_cronapi().
*
* Elysia cron hook.
* Split all social content imports into their own jobs for elysia cron.
* Stagger the time by five minutes for each run.
*/
function social_content_cronapi($op, $job = NULL) {
$items = array();
$mins = 0;
foreach (social_content_get_types() as $social_content_type) {
$settings = social_content_get_settings($social_content_type);
if ($settings['enabled']) {
$mins += 5;
if ($mins > 60) {
$mins -= 60;
}
$items['social_content_' . $social_content_type['name']] = array(
'description' => t('Social content import for !title', array(
'!title' => $social_content_type['title'],
)),
'rule' => $mins . ' */2 * * *',
'callback' => 'social_content_run_import',
'arguments' => array(
$social_content_type,
$settings,
),
);
}
}
return $items;
}
/**
* Implements hook_cron_alter().
*
* If we are using elysia cron, then remove the default social_content_cron.
* We don't want it running multiple times.
*/
function social_content_cron_alter(&$data) {
if (isset($data['social_content_cron']) && module_exists('elysia_cron')) {
unset($data['social_content_cron']);
}
}
/**
* Run an import for a particular social content type.
*
* Gathers data from the registered callback (defined in "data_callback").
*
* @param object $social_content_type
* The social content type object.
*
* @param array $settings
* The settings array @see social_content_get_settings()
*
* @return int|bool
* The number of posts imported, or FALSE if it failed.
*/
function social_content_run_import($social_content_type, $settings) {
$data = FALSE;
$langcode = social_content_get_content_type_langcode($social_content_type['content_type']);
$last_id = social_content_get_last_id($social_content_type, $langcode);
$function = $social_content_type['data_callback'];
if (function_exists($function)) {
$data = $function($settings, $last_id);
if (!empty($data)) {
$result = social_content_import_data($data, $social_content_type, $settings, $langcode);
if ($result) {
watchdog('social_content', 'Social content imported !count !type nodes', array(
'!count' => $result,
'!type' => $social_content_type['title'],
), WATCHDOG_INFO);
}
return $result;
}
}
if ($data === FALSE) {
watchdog('social_content', 'Unable to process !social_content_type, unable to fetch feed', array(
'!social_content_type' => $social_content_type['title'],
));
return FALSE;
}
}
/**
* Go through the posts and process each one.
*
* Checks whether the post already exists (if so it's skipped)
* Checks whether we've hit the limit.
*
* @param array $data
* An array of posts, normally retrieved through the "data_callback".
* @param object $social_content_type
* The social content type object.
* @param array $settings
* The settings array @see social_content_get_settings()
* @param settings $langcode
* The language code being used.
*
* @return int|bool
* The number of posts imported, or FALSE if it failed.
*/
function social_content_import_data($data, $social_content_type, $settings, $langcode = LANGUAGE_NONE) {
$count = 0;
foreach ($data as $post) {
$external_id_remote_field = key($social_content_type['external_id_field_mapping']);
if (!isset($post->{$external_id_remote_field})) {
watchdog('social_content', 'Unable to read external ID for !social_content_type', array(
'!social_content_type' => $social_content_type['title'],
), WATCHDOG_WARNING);
return FALSE;
}
$external_id = $post->{$external_id_remote_field};
if (social_content_post_exists($external_id, $social_content_type, $langcode)) {
break;
}
if ($settings['limit'] > 0 && $count > $settings['limit']) {
break;
}
if (social_content_save_post($post, $external_id, $langcode, $social_content_type, $settings)) {
$count++;
}
}
return $count;
}
/**
* Get the langcode to use for the import.
*
* @param string $content_type
* The node content type we are using.
*
* @return string
* Language code
*/
function social_content_get_content_type_langcode($content_type) {
// Check if content can be localised
// TODO: We are assuming that if a content type is translatable
// we should always use the current language.
// Language should come from the default that
// the node would have been created in.
if (module_exists('locale') && locale_multilingual_node_type($content_type)) {
global $language_content;
return $language_content->language;
}
return LANGUAGE_NONE;
}
/**
* Check for existing content.
*
* @return bool
* Existing content was found.
*/
function social_content_post_exists($external_id, $social_content_type, $langcode) {
$field_name = current($social_content_type['external_id_field_mapping']);
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'node')
->propertyCondition('type', $social_content_type['content_type'])
->propertyCondition('language', $langcode)
->fieldCondition($field_name, 'value', $external_id)
->execute();
if (!empty($result)) {
return TRUE;
}
return FALSE;
}
/**
* Get the last external id that was imported.
*
* @param object $social_content_type
* The social content type object.
* @param string $langcode
* The language code string.
*
* @return string|bool
* The latest external id, or NULL if nothing was found.
*/
function social_content_get_last_id($social_content_type, $langcode) {
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'node')
->propertyCondition('type', $social_content_type['content_type'])
->propertyCondition('language', $langcode)
->propertyOrderBy('created', 'DESC')
->range(0, 1)
->execute();
if (!empty($result) && isset($result['node'])) {
$nodes = node_load_multiple(array_keys($result['node']));
$node = array_shift($nodes);
$field_name = current($social_content_type['external_id_field_mapping']);
$field = field_get_items('node', $node, $field_name, $langcode);
if ($field && isset($field[0]['value'])) {
return $field[0]['value'];
}
}
return NULL;
}
/**
* Save the current post.
*
* Creates a entity_metadata_wrapper and sets the the title and external id.
* The wrapper is then passed to the "post_callback" function (defined in
* hook_social_content_info())
*
* @see social_content_import_data()
* @see entity_metadata_wrapper()
*
* @param objet $post
* The post object
* @param string $external_id
* The external ID string
* @param string $langcode
* The language code to be used.
* @param object $social_content_type
* The social content type object.
* @param array $settings
* The settings array @see social_content_get_settings()
*
* @return int|bool
* The created entity id.
*/
function social_content_save_post($post, $external_id, $langcode, $social_content_type, $settings) {
$path = $social_content_type['name'] . '/' . $external_id;
$values = array(
'type' => $social_content_type['content_type'],
'uid' => 1,
'status' => $settings['auto_publish'],
'promote' => 0,
'language' => $langcode,
'created' => time(),
'path' => array(
'alias' => $path,
),
);
$entity = entity_create('node', $values);
$wrapper = entity_metadata_wrapper('node', $entity);
// Set Title.
$wrapper->title
->set($social_content_type['title'] . ' : ' . $external_id);
// Set External ID field.
$extenal_id_field = current($social_content_type['external_id_field_mapping']);
$wrapper->{$extenal_id_field}
->set($external_id);
if (isset($social_content_type['post_callback'])) {
$function = $social_content_type['post_callback'];
if (function_exists($function)) {
$result = $function($wrapper, $post, $external_id, $settings);
if (!$result) {
return FALSE;
}
}
}
// Save the node.
$wrapper
->save();
return $wrapper
->getIdentifier();
}
/**
* Fetches an external image and saves it to the files_managed table.
*
* Helper function, used by modules implementing social content types.
*
* @param string $url
* The url for where to grab the image from.
* @param string $field_name
* Machine name of the field this image will be attached to.
* Required to work out the uri_scheme.
* @param string $min_resolution
* (Optional) The min_resolution to check for.
*
* @return object|bool
* Returns a file object ready to be attached to a field (or FALSE).
*/
function social_content_get_image_file($url, $field_name, $min_resolution = NULL) {
if (!$url) {
return FALSE;
}
$result = drupal_http_request($url);
if ($result->code != 200) {
return FALSE;
}
// Find the image extension
// Facebook has generic /graph/picture urls that redirect.
// So look for a redirect url first in the response.
if (isset($result->redirect_url)) {
$path_info = pathinfo($result->redirect_url);
}
else {
$path_info = pathinfo($url);
}
// TODO: Create a nice filename from the content type.
$filename = $path_info['basename'];
$field_info = field_info_field($field_name);
$field_uri_scheme = $field_info['settings']['uri_scheme'];
$file = file_save_data($result->data, $field_uri_scheme . '://' . $filename, FILE_EXISTS_RENAME);
if (!$file) {
return FALSE;
}
if ($min_resolution) {
$validators = array(
'file_validate_is_image' => array(),
'file_validate_image_resolution' => array(
0,
$min_resolution,
),
);
if (file_validate($file, $validators)) {
return FALSE;
}
}
// Add additional fields (required for adding to a file field).
return array(
'fid' => $file->fid,
'filename' => $file->filename,
'filemime' => $file->filemime,
'uid' => 1,
'uri' => $file->uri,
'status' => 1,
'display' => 1,
);
}
/**
* Strip non utf8 characters that can sometimes come through.
*
* Helper function, used by modules implementing social content types.
*
* @param string $text
* Filtered text.
*/
function social_content_validate_text($text) {
// Solution found here: http://stackoverflow.com/a/4266468
$text = preg_replace('/[^(\\x20-\\x7F)]*/', '', $text);
return $text;
}
Functions
Name![]() |
Description |
---|---|
social_content_cron | Implements hook_cron(). |
social_content_cronapi | Implements hook_cronapi(). |
social_content_cron_alter | Implements hook_cron_alter(). |
social_content_get_content_type_langcode | Get the langcode to use for the import. |
social_content_get_image_file | Fetches an external image and saves it to the files_managed table. |
social_content_get_last_id | Get the last external id that was imported. |
social_content_get_settings | Get the settings for a social_content_type. |
social_content_get_types | Gets all social content types. |
social_content_import_data | Go through the posts and process each one. |
social_content_menu | Implements hook_menu(). |
social_content_page_title | Title callback: Returns the full title for a social content type. |
social_content_permission | Implements hook_permission(). |
social_content_post_exists | Check for existing content. |
social_content_run_import | Run an import for a particular social content type. |
social_content_save_post | Save the current post. |
social_content_type_load | Menu argument loader: loads a social_content_type type by string. |
social_content_validate_text | Strip non utf8 characters that can sometimes come through. |