123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 |
- <?php
- /**
- * @file
- * Allows users to add links to their social network profiles.
- */
- define('FOLLOW_NAME', 0);
- define('FOLLOW_ME', 1);
- define('FOLLOW_US', 2);
- /**
- * Implementation of hook_help().
- */
- function follow_help($path, $arg) {
- switch ($path) {
- case 'follow':
- case 'user/%/follow':
- case 'admin/config/services/follow':
- return t('Please copy and paste the url for your public profile or page for each service you would like to display in the block. Links need to match the domain of the service in question.');
- case 'admin/settings/follow':
- return t('Here you can set what the default titles are for the "follow" blocks. If you would like a custom title, edit the individual blocks <a href="!href">here</a>.', array('!href' => url('admin/build/block')));
- }
- }
- /**
- * Implementation of hook_init().
- */
- function follow_init() {
- drupal_add_css(drupal_get_path('module', 'follow') .'/follow.css');
- }
- /**
- * Implementation of hook_menu().
- */
- function follow_menu() {
- $items = array();
- $items['admin/config/services/follow'] = array(
- 'title' => 'Site follow links',
- 'description' => 'Add sitewide follow links',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('follow_links_form'),
- 'access arguments' => array('edit site follow links'),
- );
- $items['user/%/follow'] = array(
- 'title' => 'My follow links',
- 'description' => 'edit follow links',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('follow_links_form', 1),
- 'access callback' => 'follow_links_user_access',
- 'access arguments' => array(1),
- 'type' => MENU_LOCAL_TASK,
- );
- // No settings!
- /* $items['admin/settings/follow'] = array(
- 'title' => 'Follow',
- 'description' => 'Administer the follow module',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('follow_settings_form'),
- 'access arguments' => array('administer follow'),
- ); */
- return $items;
- }
- /**
- * Access callback for user follow links editing.
- */
- function follow_links_user_access($uid) {
- return ((($GLOBALS['user']->uid == $uid) && user_access('edit own follow links')) || user_access('edit any user follow links')) && $uid > 0;
- }
- /**
- * Implementation of hook_permission().
- */
- function follow_permission() {
- return array(
- 'edit own follow links' => array(
- 'title' => t('Edit own follow links'),
- 'description' => t(''),
- ),
- 'edit site follow links' => array(
- 'title' => t('Edit the site-wide follow links'),
- 'description' => t(''),
- ),
- 'edit any user follow links' => array(
- 'title' => t("Edit any user's follow links"),
- 'description' => t(''),
- ),
- 'administer follow' => array(
- 'title' => t('Administer follow'),
- 'description' => t(''),
- ),
- 'change follow link titles' => array(
- 'title' => t('Change link titles'),
- 'description' => t('Allows changing the text that appears in the follow links.'),
- ),
- );
- }
- /**
- * Implementation of hook_theme().
- */
- function follow_theme() {
- $items = array();
- $items['follow_links_form'] = array(
- 'render element' => 'form',
- );
- $items['follow_links'] = array(
- 'variables' => array('links' => array(), 'networks' => array()),
- );
- $items['follow_link'] = array(
- 'variables' => array('link' => NULL, 'title' => NULL),
- );
- return $items;
- }
- /**
- * Implementation of hook_preprocess_page().
- */
- function follow_preprocess_page(&$variables) {
- // Expose the site follow links as a variable to the page template.
- if ($links = follow_links_load()) {
- $args = array(
- 'links' => $links,
- 'networks' => follow_networks_load(),
- );
- $variables['follow_site'] = theme('follow_links', $args);
- }
- }
- /**
- * Implementation of hook_block_info().
- */
- function follow_block_info() {
- $blocks['site'] = array(
- 'info' => t('Follow Site'),
- // We need to cache per role so the edit/configure links display only if user
- // has access.
- 'cache' => DRUPAL_CACHE_PER_ROLE,
- );
- $blocks['user'] = array(
- 'info' => t('Follow User'),
- // Here we need to cache per user so the edit link only shows for the associated
- // account.
- 'cache' => DRUPAL_CACHE_PER_USER,
- );
- return $blocks;
- }
- /**
- * Implementation of hook_block_configure().
- */
- function follow_block_configure($delta = '') {
- switch($delta) {
- case 'site':
- $form['follow_title'] = array(
- '#type' => 'radios',
- '#title' => t('Default block title'),
- '#default_value' => variable_get('follow_site_block_title', FOLLOW_NAME),
- '#options' => array(
- FOLLOW_NAME => t('Follow @name on', array('@name' => variable_get('site_name', 'Drupal'))),
- FOLLOW_ME => t('Follow me on'),
- FOLLOW_US => t('Follow us on'),
- ),
- );
- $form['follow_user'] = array(
- '#type' => 'checkbox',
- '#title' => t('User pages'),
- '#description' => t('Should this block display on user profile pages?'),
- '#default_value' => variable_get('follow_site_block_user', TRUE),
- );
- return $form;
- case 'user':
- $form['follow_title'] = array(
- '#type' => 'radios',
- '#title' => t('Default block title'),
- '#default_value' => variable_get('follow_user_block_title', FOLLOW_NAME),
- '#options' => array(
- FOLLOW_NAME => t('Follow [username] on'),
- FOLLOW_ME => t('Follow me on'),
- ),
- );
- return $form;
- }
- }
- /**
- * Implementation of hook_block_save().
- */
- function follow_block_save($delta = '', $edit = array()) {
- variable_set('follow_'. $delta .'_block_title', $edit['follow_title']);
- if ($delta == 'site') {
- variable_set('follow_site_block_user', $edit['follow_user']);
- }
- }
- /**
- * Implementation of hook_block_view().
- */
- function follow_block_view($delta = '') {
- switch ($delta) {
- case 'site':
- if (($content = _follow_block_content())
- && (variable_get('follow_site_block_user', TRUE) || !(arg(0) == 'user' && is_numeric(arg(1))))) {
- return array(
- 'subject' => _follow_block_subject(),
- 'content' => $content,
- );
- }
- break;
- case 'user':
- $uid = arg(1);
- if (arg(0) == 'user' && is_numeric($uid) && ($content = _follow_block_content($uid))) {
- return array(
- 'subject' => _follow_block_subject($uid),
- 'content' => $content,
- );
- }
- break;
- }
- }
- /**
- * Helper function to build the block title.
- *
- * @param $uid
- * The uid of the user account. Defaults to the site form, $uid = 0.
- */
- function _follow_block_subject($uid = 0) {
- return follow_link_title($uid) .':';
- }
- /**
- * Helper function to create a link or block title.
- *
- * @param $uid
- * The uid of the user account. Defaults to the site form, $uid = 0.
- */
- function follow_link_title($uid = 0) {
- // Check to see if we have a valid username.
- if ($uid) {
- $setting = variable_get('follow_user_block_title', FOLLOW_NAME);
- // Special handling for usernames.
- if ($setting == FOLLOW_NAME) {
- $account = user_load($uid);
- // Set plain to TRUE for realname module support.
- return t('Follow !name on', array('!name' => theme('username', array('account' => $account))));
- }
- return t('Follow me on');
- }
- switch(variable_get('follow_site_block_title', FOLLOW_NAME)) {
- case FOLLOW_NAME:
- return t('Follow @name on', array('@name' => variable_get('site_name', 'Drupal')));
- case FOLLOW_ME:
- return t('Follow me on');
- case FOLLOW_US:
- return t('Follow us on');
- }
- }
- /**
- * Helper function to build the block content display.
- *
- * @param $uid
- * The uid of the user account. Defaults to the site form, $uid = 0.
- */
- function _follow_block_content($uid = 0) {
- $output = '';
- if ($links = follow_links_load($uid)) {
- $output = theme('follow_links', array('links' => $links, 'networks' => follow_networks_load($uid)));
- $output .= _follow_block_config_links($uid);
- }
- return $output;
- }
- /**
- * Theme function to output a list of links.
- *
- * @param $links
- * An array of follow link objects.
- * @param $networks
- * An array of network names, keys are machine names, values are visible titles.
- *
- * @ingroup themeable
- */
- function theme_follow_links($variables) {
- $links = $variables['links'];
- $networks = $variables['networks'];
- $output = '<div class="follow-links clearfix">';
- foreach($links as $link) {
- $title = !empty($link->title) ? $link->title : $networks[$link->name]['title'];
- $output .= theme('follow_link', array('link' => $link, 'title' => $title));
- }
- $output .= '</div>';
- return $output;
- }
- /**
- * Theme function to print an individual link.
- *
- * @param $link
- * A follow link object.
- * @param $title
- * The translated title of the social network.
- *
- * @ingroup themable
- */
- function theme_follow_link($variables) {
- $link = $variables['link'];
- $title = $variables['title'];
- $classes = array();
- $classes[] = 'follow-link';
- $classes[] = "follow-link-{$link->name}";
- $classes[] = $link->uid ? 'follow-link-user' : 'follow-link-site';
- $attributes = array(
- 'class' => $classes,
- 'title' => follow_link_title($link->uid) .' '. $title,
- );
- $link->options['attributes'] = $attributes;
- return l($title, $link->path, $link->options) . "\n";
- }
- /**
- * Outputs a list of configuration links for users with appropriate permissions
- *
- * @param $uid
- * The uid of the user account. Defaults to the site form, $uid = 0.
- * @return
- * A string containing the links, output with theme_links().
- */
- function _follow_block_config_links($uid) {
- $links = array();
- if ($uid == 0 && user_access('edit site follow links')) {
- $links['follow_edit'] = array(
- 'title' => t('Edit'),
- 'href' => 'admin/config/services/follow',
- 'query' => drupal_get_destination(),
- );
- }
- elseif (follow_links_user_access($uid)) {
- $links['follow_edit'] = array(
- 'title' => t('Edit'),
- 'href' => 'user/'. $uid .'/follow',
- 'query' => drupal_get_destination(),
- );
- }
- if (user_access('administer blocks')) {
- $links['follow_configure'] = array(
- 'title' => t('Configure'),
- 'href' => $uid ? 'admin/structure/block/configure/follow/user' : 'admin/structure/block/configure/follow/site',
- 'query' => drupal_get_destination(),
- );
- }
- return theme('links', $links, array('class' => array('links', 'inline')));
- }
- /**
- * The form for editing follow links.
- *
- * @param $form_state
- * A keyed array containing the current state of the form.
- * @param $uid
- * The uid of the user account. Defaults to the site form, $uid = 0.
- *
- * @ingroup forms
- */
- function follow_links_form($form, &$form_state, $uid = 0) {
- $form = array();
- $form['uid'] = array('#type' => 'hidden', '#value' => $uid);
- $form['follow_links']['#tree'] = TRUE;
- $links = follow_links_load($uid);
- $networks = follow_networks_load($uid, TRUE);
- // Put all our existing links at the top, sorted by weight.
- if (is_array($links)) {
- foreach ($links as $name => $link) {
- $title = $networks[$name]['title'];
- $form['follow_links'][$name] = _follow_links_form_link($link, $title, $uid);
- // Unset this specific network so we don't add the same one again below.
- unset($networks[$name]);
- }
- }
- // Now add all the empty ones.
- foreach ($networks as $name => $info) {
- $link = new stdClass();
- $link->name = $name;
- $form['follow_links'][$name] = _follow_links_form_link($link, $info['title'], $uid);
- }
- $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
- return $form;
- }
- /**
- * Helper function to create an individual link form element.
- */
- function _follow_links_form_link($link, $title, $uid) {
- $elements = array();
- $elements['name'] = array(
- '#markup' => $title,
- );
- if (isset($link->lid)) {
- $elements['lid'] = array(
- '#type' => 'hidden',
- '#value' => $link->lid,
- );
- $elements['weight'] = array(
- '#type' => 'weight',
- '#default_value' => $link->weight,
- );
- }
- $elements['url'] = array(
- '#type' => 'textfield',
- '#follow_network' => $link->name,
- '#follow_uid' => $uid,
- '#default_value' => isset($link->url) ? $link->url : '',
- '#element_validate' => array('follow_url_validate'),
- );
- // Provide the title of the link only if the link URL is there and the user
- // has the appropriate access.
- $elements['title'] = array(
- '#type' => 'textfield',
- '#default_value' => isset($link->title) ? $link->title : '',
- '#size' => 15,
- '#access' => user_access('change follow link titles') && !empty($link->url),
- );
- return $elements;
- }
- /**
- * Like drupal_http_build_query() but without urlencodings.
- */
- function follow_build_query(array $query, $parent = '') {
- $params = array();
- foreach ($query as $key => $value) {
- $key = ($parent ? $parent . '[' . $key . ']' : $key);
- // Recurse into children.
- if (is_array($value)) {
- $params[] = follow_build_query($value, $key);
- }
- // If a query parameter value is NULL, only append its key.
- elseif (!isset($value)) {
- $params[] = $key;
- }
- else {
- $params[] = $key . '=' . $value;
- }
- }
- return implode('&', $params);
- }
- /**
- * Build a url for use in the form.
- */
- function follow_build_url($path, $options) {
- $url = $path;
- if (!empty($options['query'])) {
- $url .= (strpos($path, '?') !== FALSE ? '&' : '?') . follow_build_query($options['query']);
- }
- if (!empty($options['fragment'])) {
- $url .= '#' . $options['fragment'];
- }
- return $url;
- }
- /**
- * Split a Drupal path or external link into path and options like a menu link.
- */
- function follow_parse_url($url) {
- $parsed_url = parse_url($url);
- $defaults = array(
- 'scheme' => '',
- 'host' => '',
- 'port' => '',
- 'path' => '/',
- 'query' => '',
- 'fragment' => '',
- );
- $parsed_url += $defaults;
- $options = array('query' => array(), 'fragment' => $parsed_url['fragment']);
- // Parse the query string into an array.
- parse_str($parsed_url['query'], $options['query']);
- if ($parsed_url['scheme']) {
- $parsed_url['scheme'] .= '://';
- }
- // Throw away port for now.
- $path = $parsed_url['scheme'] . $parsed_url['host'] . $parsed_url['path'];
- return array('path' => $path, 'options' => $options);
- }
- /**
- * Validates the url field to verify it's actually a url.
- */
- function follow_url_validate($form) {
- $url = trim($form['#value']);
- $networks = follow_networks_load($form['#follow_uid']);
- $info = $networks[$form['#follow_network']];
- $regex = follow_build_url_regex($info);
- $parsed = follow_parse_url($url);
- if($url && !preg_match($regex, $parsed['path'])) {
- if (!empty($info['domain'])) {
- $message = t('The specified url is invalid for the domain %domain. Make sure you use http://.', array('%domain' => $info['domain']));
- }
- else {
- $message = t('The specified path is invalid. Please enter a path on this site (e.g. rss.xml or taxonomy/term/1/feed).');
- }
- form_error($form, $message);
- }
- }
- /**
- * Build a regex to validate the url based on a known service url.
- */
- function follow_build_url_regex($network_info) {
- if (!empty($network_info['domain'])) {
- // An external link.
- return '@^https?://([a-z0-9\-_.]+\\.|)' . str_replace('.', '\\.', $network_info['domain']) . '/@i';
- }
- // An internal link should not have ':'.
- return '@^[^:]+$@';
- }
- /**
- * Submit handler for the follow_links_form.
- */
- function follow_links_form_submit($form, &$form_state) {
- $values = $form_state['values'];
- $links = $values['follow_links'];
- foreach($links as $name => $link) {
- $link = (object) $link;
- $link->url = trim($link->url);
- // Check to see if there's actually a link
- if (empty($link->url)) {
- // If there's an lid, delete the link.
- if (isset($link->lid)) {
- follow_link_delete($link->lid);
- }
- // Continue to the next link.
- continue;
- }
- // Otherwise save the link.
- else {
- $link->uid = $values['uid'];
- $link->name = $name;
- follow_link_save($link);
- }
- }
- }
- /**
- * Theme the drag-and-drop form.
- *
- * Arranges records in a table, and adds the css and js for draggable sorting.
- *
- * @ingroup themeable
- * @ingroup forms
- */
- function theme_follow_links_form($variables) {
- $form = $variables['form'];
- $rows = array();
- $disabled_rows = array();
- foreach (element_children($form['follow_links']) as $key) {
- $row = array();
- if (isset($form['follow_links'][$key]['weight'])) {
- $row[] = drupal_render($form['follow_links'][$key]['lid']) . drupal_render($form['follow_links'][$key]['name']);
- $row[] = drupal_render($form['follow_links'][$key]['url']);
- if (user_access('change follow link titles')) {
- $row[] = drupal_render($form['follow_links'][$key]['title']);
- }
- // Now, render the weight row.
- $form['follow_links'][$key]['weight']['#attributes']['class'][] = 'follow-links-weight';
- $row[] = drupal_render($form['follow_links'][$key]['weight']);
- // Add the new row to our collection of rows, and give it the 'draggable' class.
- $rows[] = array(
- 'data' => $row,
- 'class' => array('draggable'),
- );
- }
- else {
- $disabled_rows[] = array(drupal_render($form['follow_links'][$key]['name']), drupal_render($form['follow_links'][$key]['url']));
- }
- }
- // Render a list of header titles, and our array of rows, into a table.
- $header = array(t('Name'), t('URL'));
- if (user_access('change follow link titles')) {
- $header[] = t('Customized Name');
- }
- $header[] = t('Weight');
- $disabled_header = array(t('Name'), t('URL'));
- $output = '';
- if (count($rows)) {
- $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'follow-links-weighted-form')));
- }
- if (count($disabled_rows)) {
- $output .= theme('table', array('header' => $disabled_header, 'rows' => $disabled_rows));
- }
- $output .= drupal_render_children($form);
- drupal_add_tabledrag('follow-links-weighted-form', 'order', 'self', 'follow-links-weight');
- return $output;
- }
- /**
- * Follow settings form.
- *
- * @ingroup forms
- */
- function follow_settings_form() {
- $form = array();
- return system_settings_form($form);
- }
- /**
- * Loader function for individual links.
- *
- * @param $uid
- * An int containing the uid of the user. uid 0 pulls the site follow links.
- * @return
- * A single link in array format, or FALSE if none matched the incoming ID.
- */
- function follow_links_load($uid = 0) {
- $links = array();
- $sql = "SELECT * FROM {follow_links} WHERE uid = :uid ORDER BY weight ASC";
- $result = db_query($sql, array(':uid' => $uid));
- foreach ($result as $link) {
- $link->options = unserialize($link->options);
- $link->url = follow_build_url($link->path, $link->options);
- $links[$link->name] = $link;
- }
- return $links;
- }
- /**
- * Inserts a new link, or updates an existing one.
- *
- * @param $link
- * A link object to be saved.
- */
- function follow_link_save($link) {
- $parsed = follow_parse_url($link->url);
- $link->path = $parsed['path'];
- $link->options = $parsed['options'];
- if (isset($link->lid)) {
- drupal_write_record('follow_links', $link, 'lid');
- }
- else {
- drupal_write_record('follow_links', $link);
- }
- return $link;
- }
- /**
- * Deletes a link, given its unique ID.
- *
- * @param $lid
- * An int containing the ID of a link.
- */
- function follow_link_delete($lid) {
- db_delete('follow_links')
- ->condition('lid', $lid)
- ->execute();
- }
- /**
- * Loads all follow networks
- *
- * @param $uid
- * The uid of the user. uid 0 pulls the site follow links.
- * @param $reset
- * Boolean. If TRUE, flushes the follow networks cache.
- *
- * @return
- * An array of network names, keys are machine names, values are visible titles.
- */
- function follow_networks_load($uid = 0, $reset = FALSE) {
- static $networks = array();
- // Clear cache if $reset is TRUE;
- if ($reset) {
- $networks = array();
- }
- // Return presets if the array is populated.
- if (empty($networks[$uid])) {
- // We call hook_follow_networks_alter() to allow other modules to create
- // or alter networks.
- $networks[$uid] = follow_default_networks($uid);
- drupal_alter('follow_networks', $networks, $uid);
- }
- return $networks[$uid];
- }
- /**
- * Retrieves the default networks available.
- *
- * @return
- * An associative array, keyed by the machine name. The values are an array
- * including title of the network, along with the domain to be used for
- * input validation of the network.
- */
- function follow_default_networks($uid) {
- $networks = array(
- 'facebook' => array(
- 'title' => t('Facebook'),
- 'domain' => 'facebook.com',
- ),
- 'virb' => array(
- 'title' => t('Virb'),
- 'domain' => 'virb.com',
- ),
- 'myspace' => array(
- 'title' => t('MySpace'),
- 'domain' => 'myspace.com',
- ),
- 'twitter' => array(
- 'title' => t('Twitter'),
- 'domain' => 'twitter.com',
- ),
- 'picasa' => array(
- 'title' => t('Picasa'),
- 'domain' => 'picasaweb.google.com',
- ),
- 'flickr' => array(
- 'title' => t('Flickr'),
- 'domain' => 'flickr.com',
- ),
- 'youtube' => array(
- 'title' => t('YouTube'),
- 'domain' => 'youtube.com',
- ),
- 'vimeo' => array(
- 'title' => t('Vimeo'),
- 'domain' => 'vimeo.com',
- ),
- 'bliptv' => array(
- 'title' => t('blip.tv'),
- 'domain' => 'blip.tv',
- ),
- 'lastfm' => array(
- 'title' => t('last.fm'),
- 'domain' => 'last.fm',
- ),
- 'linkedin' => array(
- 'title' => t('LinkedIn'),
- 'domain' => 'linkedin.com',
- ),
- 'delicious' => array(
- 'title' => t('Delicious'),
- 'domain' => 'delicious.com',
- ),
- 'tumblr' => array(
- 'title' => t('Tumblr'),
- 'domain' => 'tumblr.com',
- ),
- );
- if ($uid == 0) {
- $networks['this-site'] = array(
- 'title' => t('This site (RSS)'),
- 'domain' => '',
- );
- }
- return $networks;
- }
|