pax_global_header 0000666 0000000 0000000 00000000064 12501305533 0014507 g ustar 00root root 0000000 0000000 52 comment=b5048b989e4f1389a8190bc8a8976e9999bcdfd7
www-test-master-modules/ 0000775 0000000 0000000 00000000000 12501305533 0015613 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/ 0000775 0000000 0000000 00000000000 12501305533 0017263 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/README.txt 0000664 0000000 0000000 00000000700 12501305533 0020756 0 ustar 00root root 0000000 0000000
This directory is reserved for core module files. Custom or contributed modules
should be placed in their own subdirectory of the sites/all/modules directory.
For multisite installations, they can also be placed in a subdirectory under
/sites/{sitename}/modules/, where {sitename} is the name of your site (e.g.,
www.example.com). This will allow you to more easily update Drupal core files.
For more details, see: http://drupal.org/node/176043
www-test-master-modules/modules/aggregator/ 0000775 0000000 0000000 00000000000 12501305533 0021405 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/aggregator/aggregator-feed-source.tpl.php 0000664 0000000 0000000 00000002121 12501305533 0027231 0 ustar 00root root 0000000 0000000
' . t('Feeds contain published content, and may be grouped in categories, generally by topic. Users view feed content in the main aggregator display, or by their source (usually via an RSS feed reader). The most recent content in a feed or category can be displayed as a block through the Blocks administration page.', array('@aggregator' => url('aggregator'), '@aggregator-sources' => url('aggregator/sources'), '@admin-block' => url('admin/structure/block'))) . '
';
$output .= '
' . t('Adding, editing, and deleting feeds') . '
';
$output .= '
' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the Feed aggregator administration page.', array('@feededit' => url('admin/config/services/aggregator'))) . '
';
$output .= '
' . t('OPML integration') . '
';
$output .= '
' . t('A machine-readable OPML file of all feeds is available. OPML is an XML-based file format used to share outline-structured information such as a list of RSS feeds. Feeds can also be imported via an OPML file.', array('@aggregator-opml' => url('aggregator/opml'), '@import-opml' => url('admin/config/services/aggregator'))) . '
';
$output .= '
' . t('Configuring cron') . '
';
$output .= '
' . t('A correctly configured cron maintenance task is required to update feeds automatically.', array('@cron' => 'http://drupal.org/cron')) . '
' . t('Thousands of sites (particularly news sites and blogs) publish their latest headlines and posts in feeds, using a number of standardized XML-based formats. Formats supported by the aggregator include RSS, RDF, and Atom.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) . '
';
$output .= '
' . t('Current feeds are listed below, and new feeds may be added. For each feed or feed category, the latest items block may be enabled at the blocks administration page.', array('@addfeed' => url('admin/config/services/aggregator/add/feed'), '@block' => url('admin/structure/block'))) . '
';
return $output;
case 'admin/config/services/aggregator/add/feed':
return '
' . t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') . '
';
case 'admin/config/services/aggregator/add/category':
return '
' . t('Categories allow feed items from different feeds to be grouped together. For example, several sport-related feeds may belong to a category named Sports. Feed items may be grouped automatically (by selecting a category when creating or editing a feed) or manually (via the Categorize page available from feed item listings). Each category provides its own feed page and block.') . '
';
case 'admin/config/services/aggregator/add/opml':
return '
' . t('OPML is an XML format used to exchange multiple feeds between aggregators. A single OPML document may contain a collection of many feeds. Drupal can parse such a file and import all feeds at once, saving you the effort of adding them manually. You may either upload a local file from your computer or enter a URL where Drupal can download it.') . '
';
}
}
/**
* Implements hook_theme().
*/
function aggregator_theme() {
return array(
'aggregator_wrapper' => array(
'variables' => array('content' => NULL),
'file' => 'aggregator.pages.inc',
'template' => 'aggregator-wrapper',
),
'aggregator_categorize_items' => array(
'render element' => 'form',
'file' => 'aggregator.pages.inc',
),
'aggregator_feed_source' => array(
'variables' => array('feed' => NULL),
'file' => 'aggregator.pages.inc',
'template' => 'aggregator-feed-source',
),
'aggregator_block_item' => array(
'variables' => array('item' => NULL, 'feed' => 0),
),
'aggregator_summary_items' => array(
'variables' => array('summary_items' => NULL, 'source' => NULL),
'file' => 'aggregator.pages.inc',
'template' => 'aggregator-summary-items',
),
'aggregator_summary_item' => array(
'variables' => array('item' => NULL),
'file' => 'aggregator.pages.inc',
'template' => 'aggregator-summary-item',
),
'aggregator_item' => array(
'variables' => array('item' => NULL),
'file' => 'aggregator.pages.inc',
'template' => 'aggregator-item',
),
'aggregator_page_opml' => array(
'variables' => array('feeds' => NULL),
'file' => 'aggregator.pages.inc',
),
'aggregator_page_rss' => array(
'variables' => array('feeds' => NULL, 'category' => NULL),
'file' => 'aggregator.pages.inc',
),
);
}
/**
* Implements hook_menu().
*/
function aggregator_menu() {
$items['admin/config/services/aggregator'] = array(
'title' => 'Feed aggregator',
'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.",
'page callback' => 'aggregator_admin_overview',
'access arguments' => array('administer news feeds'),
'weight' => 10,
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/add/feed'] = array(
'title' => 'Add feed',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed'),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_ACTION,
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/add/category'] = array(
'title' => 'Add category',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_category'),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_ACTION,
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/add/opml'] = array(
'title' => 'Import OPML',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_opml'),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_ACTION,
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/remove/%aggregator_feed'] = array(
'title' => 'Remove items',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_admin_remove_feed', 5),
'access arguments' => array('administer news feeds'),
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/update/%aggregator_feed'] = array(
'title' => 'Update items',
'page callback' => 'aggregator_admin_refresh_feed',
'page arguments' => array(5),
'access arguments' => array('administer news feeds'),
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/config/services/aggregator/settings'] = array(
'title' => 'Settings',
'description' => 'Configure the behavior of the feed aggregator, including when to discard feed items and how to present feed items and categories.',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_admin_form'),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'file' => 'aggregator.admin.inc',
);
$items['aggregator'] = array(
'title' => 'Feed aggregator',
'page callback' => 'aggregator_page_last',
'access arguments' => array('access news feeds'),
'weight' => 5,
'file' => 'aggregator.pages.inc',
);
$items['aggregator/sources'] = array(
'title' => 'Sources',
'page callback' => 'aggregator_page_sources',
'access arguments' => array('access news feeds'),
'file' => 'aggregator.pages.inc',
);
$items['aggregator/categories'] = array(
'title' => 'Categories',
'page callback' => 'aggregator_page_categories',
'access callback' => '_aggregator_has_categories',
'file' => 'aggregator.pages.inc',
);
$items['aggregator/rss'] = array(
'title' => 'RSS feed',
'page callback' => 'aggregator_page_rss',
'access arguments' => array('access news feeds'),
'type' => MENU_CALLBACK,
'file' => 'aggregator.pages.inc',
);
$items['aggregator/opml'] = array(
'title' => 'OPML feed',
'page callback' => 'aggregator_page_opml',
'access arguments' => array('access news feeds'),
'type' => MENU_CALLBACK,
'file' => 'aggregator.pages.inc',
);
$items['aggregator/categories/%aggregator_category'] = array(
'title callback' => '_aggregator_category_title',
'title arguments' => array(2),
'page callback' => 'aggregator_page_category',
'page arguments' => array(2),
'access arguments' => array('access news feeds'),
'file' => 'aggregator.pages.inc',
);
$items['aggregator/categories/%aggregator_category/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['aggregator/categories/%aggregator_category/categorize'] = array(
'title' => 'Categorize',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_page_category_form', 2),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'file' => 'aggregator.pages.inc',
);
$items['aggregator/categories/%aggregator_category/configure'] = array(
'title' => 'Configure',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_category', 2),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'aggregator.admin.inc',
);
$items['aggregator/sources/%aggregator_feed'] = array(
'page callback' => 'aggregator_page_source',
'page arguments' => array(2),
'access arguments' => array('access news feeds'),
'file' => 'aggregator.pages.inc',
);
$items['aggregator/sources/%aggregator_feed/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['aggregator/sources/%aggregator_feed/categorize'] = array(
'title' => 'Categorize',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_page_source_form', 2),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'file' => 'aggregator.pages.inc',
);
$items['aggregator/sources/%aggregator_feed/configure'] = array(
'title' => 'Configure',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed', 2),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/edit/feed/%aggregator_feed'] = array(
'title' => 'Edit feed',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed', 6),
'access arguments' => array('administer news feeds'),
'file' => 'aggregator.admin.inc',
);
$items['admin/config/services/aggregator/edit/category/%aggregator_category'] = array(
'title' => 'Edit category',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_category', 6),
'access arguments' => array('administer news feeds'),
'file' => 'aggregator.admin.inc',
);
return $items;
}
/**
* Title callback: Returns a title for aggregator category pages.
*
* @param $category
* An aggregator category.
*
* @return
* A string with the aggregator category title.
*/
function _aggregator_category_title($category) {
return $category['title'];
}
/**
* Determines whether there are any aggregator categories.
*
* @return
* TRUE if there is at least one category and the user has access to them;
* FALSE otherwise.
*/
function _aggregator_has_categories() {
return user_access('access news feeds') && (bool) db_query_range('SELECT 1 FROM {aggregator_category}', 0, 1)->fetchField();
}
/**
* Implements hook_permission().
*/
function aggregator_permission() {
return array(
'administer news feeds' => array(
'title' => t('Administer news feeds'),
),
'access news feeds' => array(
'title' => t('View news feeds'),
),
);
}
/**
* Implements hook_cron().
*
* Queues news feeds for updates once their refresh interval has elapsed.
*/
function aggregator_cron() {
$result = db_query('SELECT * FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array(
':time' => REQUEST_TIME,
':never' => AGGREGATOR_CLEAR_NEVER
));
$queue = DrupalQueue::get('aggregator_feeds');
foreach ($result as $feed) {
if ($queue->createItem($feed)) {
// Add timestamp to avoid queueing item more than once.
db_update('aggregator_feed')
->fields(array('queued' => REQUEST_TIME))
->condition('fid', $feed->fid)
->execute();
}
}
// Remove queued timestamp after 6 hours assuming the update has failed.
db_update('aggregator_feed')
->fields(array('queued' => 0))
->condition('queued', REQUEST_TIME - (3600 * 6), '<')
->execute();
}
/**
* Implements hook_cron_queue_info().
*/
function aggregator_cron_queue_info() {
$queues['aggregator_feeds'] = array(
'worker callback' => 'aggregator_refresh',
'time' => 60,
);
return $queues;
}
/**
* Implements hook_block_info().
*/
function aggregator_block_info() {
$blocks = array();
$result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
foreach ($result as $category) {
$blocks['category-' . $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
}
$result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
foreach ($result as $feed) {
$blocks['feed-' . $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
}
return $blocks;
}
/**
* Implements hook_block_configure().
*/
function aggregator_block_configure($delta = '') {
list($type, $id) = explode('-', $delta);
if ($type == 'category') {
$value = db_query('SELECT block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchField();
$form['block'] = array(
'#type' => 'select',
'#title' => t('Number of news items in block'),
'#default_value' => $value,
'#options' => drupal_map_assoc(range(2, 20)),
);
return $form;
}
}
/**
* Implements hook_block_save().
*/
function aggregator_block_save($delta = '', $edit = array()) {
list($type, $id) = explode('-', $delta);
if ($type == 'category') {
db_update('aggregator_category')
->fields(array('block' => $edit['block']))
->condition('cid', $id)
->execute();
}
}
/**
* Implements hook_block_view().
*
* Generates blocks for the latest news items in each category and feed.
*/
function aggregator_block_view($delta = '') {
if (user_access('access news feeds')) {
$block = array();
list($type, $id) = explode('-', $delta);
$result = FALSE;
switch ($type) {
case 'feed':
if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
$block['subject'] = check_plain($feed->title);
$result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id));
$read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
}
break;
case 'category':
if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
$block['subject'] = check_plain($category->title);
$result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $category->block, array(':cid' => $category->cid));
$read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
}
break;
}
$items = array();
if (!empty($result)) {
foreach ($result as $item) {
$items[] = theme('aggregator_block_item', array('item' => $item));
}
}
// Only display the block if there are items to show.
if (count($items) > 0) {
$block['content'] = theme('item_list', array('items' => $items)) . $read_more;
}
return $block;
}
}
/**
* Adds/edits/deletes aggregator categories.
*
* @param $edit
* An associative array describing the category to be added/edited/deleted.
*/
function aggregator_save_category($edit) {
$link_path = 'aggregator/categories/';
if (!empty($edit['cid'])) {
$link_path .= $edit['cid'];
if (!empty($edit['title'])) {
db_merge('aggregator_category')
->key(array('cid' => $edit['cid']))
->fields(array(
'title' => $edit['title'],
'description' => $edit['description'],
))
->execute();
$op = 'update';
}
else {
db_delete('aggregator_category')
->condition('cid', $edit['cid'])
->execute();
// Make sure there is no active block for this category.
if (module_exists('block')) {
db_delete('block')
->condition('module', 'aggregator')
->condition('delta', 'category-' . $edit['cid'])
->execute();
}
$edit['title'] = '';
$op = 'delete';
}
}
elseif (!empty($edit['title'])) {
// A single unique id for bundles and feeds, to use in blocks.
$link_path .= db_insert('aggregator_category')
->fields(array(
'title' => $edit['title'],
'description' => $edit['description'],
'block' => 5,
))
->execute();
$op = 'insert';
}
if (isset($op)) {
menu_link_maintain('aggregator', $op, $link_path, $edit['title']);
}
}
/**
* Add/edit/delete an aggregator feed.
*
* @param $edit
* An associative array describing the feed to be added/edited/deleted.
*/
function aggregator_save_feed($edit) {
if (!empty($edit['fid'])) {
// An existing feed is being modified, delete the category listings.
db_delete('aggregator_category_feed')
->condition('fid', $edit['fid'])
->execute();
}
if (!empty($edit['fid']) && !empty($edit['title'])) {
db_update('aggregator_feed')
->condition('fid', $edit['fid'])
->fields(array(
'title' => $edit['title'],
'url' => $edit['url'],
'refresh' => $edit['refresh'],
'block' => $edit['block'],
))
->execute();
}
elseif (!empty($edit['fid'])) {
$iids = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $edit['fid']))->fetchCol();
if ($iids) {
db_delete('aggregator_category_item')
->condition('iid', $iids, 'IN')
->execute();
}
db_delete('aggregator_feed')->
condition('fid', $edit['fid'])
->execute();
db_delete('aggregator_item')
->condition('fid', $edit['fid'])
->execute();
// Make sure there is no active block for this feed.
if (module_exists('block')) {
db_delete('block')
->condition('module', 'aggregator')
->condition('delta', 'feed-' . $edit['fid'])
->execute();
}
}
elseif (!empty($edit['title'])) {
$edit['fid'] = db_insert('aggregator_feed')
->fields(array(
'title' => $edit['title'],
'url' => $edit['url'],
'refresh' => $edit['refresh'],
'block' => $edit['block'],
'link' => '',
'description' => '',
'image' => '',
))
->execute();
}
if (!empty($edit['title'])) {
// The feed is being saved, save the categories as well.
if (!empty($edit['category'])) {
foreach ($edit['category'] as $cid => $value) {
if ($value) {
db_insert('aggregator_category_feed')
->fields(array(
'fid' => $edit['fid'],
'cid' => $cid,
))
->execute();
}
}
}
}
}
/**
* Removes all items from a feed.
*
* @param $feed
* An object describing the feed to be cleared.
*/
function aggregator_remove($feed) {
_aggregator_get_variables();
// Call hook_aggregator_remove() on all modules.
module_invoke_all('aggregator_remove', $feed);
// Reset feed.
db_update('aggregator_feed')
->condition('fid', $feed->fid)
->fields(array(
'checked' => 0,
'hash' => '',
'etag' => '',
'modified' => 0,
))
->execute();
}
/**
* Gets the fetcher, parser, and processors.
*
* @return
* An array containing the fetcher, parser, and processors.
*/
function _aggregator_get_variables() {
// Fetch the feed.
$fetcher = variable_get('aggregator_fetcher', 'aggregator');
if ($fetcher == 'aggregator') {
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.fetcher.inc';
}
$parser = variable_get('aggregator_parser', 'aggregator');
if ($parser == 'aggregator') {
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.parser.inc';
}
$processors = variable_get('aggregator_processors', array('aggregator'));
if (in_array('aggregator', $processors)) {
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.processor.inc';
}
return array($fetcher, $parser, $processors);
}
/**
* Checks a news feed for new items.
*
* @param $feed
* An object describing the feed to be refreshed.
*/
function aggregator_refresh($feed) {
// Store feed URL to track changes.
$feed_url = $feed->url;
// Fetch the feed.
list($fetcher, $parser, $processors) = _aggregator_get_variables();
$success = module_invoke($fetcher, 'aggregator_fetch', $feed);
// We store the hash of feed data in the database. When refreshing a
// feed we compare stored hash and new hash calculated from downloaded
// data. If both are equal we say that feed is not updated.
$hash = hash('sha256', $feed->source_string);
if ($success && ($feed->hash != $hash)) {
// Parse the feed.
if (module_invoke($parser, 'aggregator_parse', $feed)) {
// Update feed with parsed data.
db_merge('aggregator_feed')
->key(array('fid' => $feed->fid))
->fields(array(
'url' => $feed->url,
'link' => empty($feed->link) ? $feed->url : $feed->link,
'description' => empty($feed->description) ? '' : $feed->description,
'image' => empty($feed->image) ? '' : $feed->image,
'hash' => $hash,
'etag' => empty($feed->etag) ? '' : $feed->etag,
'modified' => empty($feed->modified) ? 0 : $feed->modified,
))
->execute();
// Log if feed URL has changed.
if ($feed->url != $feed_url) {
watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed->title, '%url' => $feed->url));
}
watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed->title));
drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed->title)));
// If there are items on the feed, let all enabled processors do their work on it.
if (@count($feed->items)) {
foreach ($processors as $processor) {
module_invoke($processor, 'aggregator_process', $feed);
}
}
}
}
else {
drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed->title)));
}
// Regardless of successful or not, indicate that this feed has been checked.
db_update('aggregator_feed')
->fields(array('checked' => REQUEST_TIME, 'queued' => 0))
->condition('fid', $feed->fid)
->execute();
// Expire old feed items.
if (function_exists('aggregator_expire')) {
aggregator_expire($feed);
}
}
/**
* Loads an aggregator feed.
*
* @param $fid
* The feed id.
*
* @return
* An object describing the feed.
*/
function aggregator_feed_load($fid) {
$feeds = &drupal_static(__FUNCTION__);
if (!isset($feeds[$fid])) {
$feeds[$fid] = db_query('SELECT * FROM {aggregator_feed} WHERE fid = :fid', array(':fid' => $fid))->fetchObject();
}
return $feeds[$fid];
}
/**
* Loads an aggregator category.
*
* @param $cid
* The category id.
*
* @return
* An associative array describing the category.
*/
function aggregator_category_load($cid) {
$categories = &drupal_static(__FUNCTION__);
if (!isset($categories[$cid])) {
$categories[$cid] = db_query('SELECT * FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $cid))->fetchAssoc();
}
return $categories[$cid];
}
/**
* Returns HTML for an individual feed item for display in the block.
*
* @param $variables
* An associative array containing:
* - item: The item to be displayed.
* - feed: Not used.
*
* @ingroup themeable
*/
function theme_aggregator_block_item($variables) {
// Display the external link to the item.
return '' . check_plain($variables['item']->title) . "\n";
}
/**
* Renders the HTML content safely, as allowed.
*
* @param $value
* The content to be filtered.
*
* @return
* The filtered content.
*/
function aggregator_filter_xss($value) {
return filter_xss($value, preg_split('/\s+|<|>/', variable_get('aggregator_allowed_html_tags', '
www-test-master-modules/modules/block/block.admin.inc 0000664 0000000 0000000 00000060113 12501305533 0023232 0 ustar 00root root 0000000 0000000 'value',
'#value' => $theme,
);
$form['block_regions'] = array(
'#type' => 'value',
// Add a last region for disabled blocks.
'#value' => $block_regions + array(BLOCK_REGION_NONE => BLOCK_REGION_NONE),
);
$form['blocks'] = array();
$form['#tree'] = TRUE;
foreach ($blocks as $i => $block) {
$key = $block['module'] . '_' . $block['delta'];
$form['blocks'][$key]['module'] = array(
'#type' => 'value',
'#value' => $block['module'],
);
$form['blocks'][$key]['delta'] = array(
'#type' => 'value',
'#value' => $block['delta'],
);
$form['blocks'][$key]['info'] = array(
'#markup' => check_plain($block['info']),
);
$form['blocks'][$key]['theme'] = array(
'#type' => 'hidden',
'#value' => $theme,
);
$form['blocks'][$key]['weight'] = array(
'#type' => 'weight',
'#default_value' => $block['weight'],
'#delta' => $weight_delta,
'#title_display' => 'invisible',
'#title' => t('Weight for @block block', array('@block' => $block['info'])),
);
$form['blocks'][$key]['region'] = array(
'#type' => 'select',
'#default_value' => $block['region'] != BLOCK_REGION_NONE ? $block['region'] : NULL,
'#empty_value' => BLOCK_REGION_NONE,
'#title_display' => 'invisible',
'#title' => t('Region for @block block', array('@block' => $block['info'])),
'#options' => $block_regions,
);
$form['blocks'][$key]['configure'] = array(
'#type' => 'link',
'#title' => t('configure'),
'#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure',
);
if ($block['module'] == 'block') {
$form['blocks'][$key]['delete'] = array(
'#type' => 'link',
'#title' => t('delete'),
'#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete',
);
}
}
// Do not allow disabling the main system content block when it is present.
if (isset($form['blocks']['system_main']['region'])) {
$form['blocks']['system_main']['region']['#required'] = TRUE;
}
$form['actions'] = array(
'#tree' => FALSE,
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save blocks'),
);
return $form;
}
/**
* Form submission handler for block_admin_display_form().
*
* @see block_admin_display_form()
*/
function block_admin_display_form_submit($form, &$form_state) {
$transaction = db_transaction();
try {
foreach ($form_state['values']['blocks'] as $block) {
$block['status'] = (int) ($block['region'] != BLOCK_REGION_NONE);
$block['region'] = $block['status'] ? $block['region'] : '';
db_update('block')
->fields(array(
'status' => $block['status'],
'weight' => $block['weight'],
'region' => $block['region'],
))
->condition('module', $block['module'])
->condition('delta', $block['delta'])
->condition('theme', $block['theme'])
->execute();
}
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('block', $e);
throw $e;
}
drupal_set_message(t('The block settings have been updated.'));
cache_clear_all();
}
/**
* Sorts active blocks by region, then by weight; sorts inactive blocks by name.
*
* Callback for usort() in block_admin_display_prepare_blocks().
*/
function _block_compare($a, $b) {
global $theme_key;
// Theme should be set before calling this function, or the current theme
// is being used.
$theme = &drupal_static(__FUNCTION__ . ':theme');
if (!isset($theme)) {
$theme = $theme_key;
}
$regions = &drupal_static(__FUNCTION__ . ':regions');
// We need the region list to correctly order by region.
if (!isset($regions)) {
$regions = array_flip(array_keys(system_region_list($theme)));
$regions[BLOCK_REGION_NONE] = count($regions);
}
// Separate enabled from disabled.
$status = $b['status'] - $a['status'];
if ($status) {
return $status;
}
// Sort by region (in the order defined by theme .info file).
if ((!empty($a['region']) && !empty($b['region'])) && ($place = ($regions[$a['region']] - $regions[$b['region']]))) {
return $place;
}
// Sort by weight, unless disabled.
if ($a['region'] != BLOCK_REGION_NONE) {
$weight = $a['weight'] - $b['weight'];
if ($weight) {
return $weight;
}
}
// Sort by title.
return strcmp($a['info'], $b['info']);
}
/**
* Form constructor for the block configuration form.
*
* Also used by block_add_block_form() for adding a new custom block.
*
* @param $module
* Name of the module that implements the block to be configured.
* @param $delta
* Unique ID of the block within the context of $module.
*
* @see block_admin_configure_validate()
* @see block_admin_configure_submit()
* @ingroup forms
*/
function block_admin_configure($form, &$form_state, $module, $delta) {
$block = block_load($module, $delta);
$form['module'] = array(
'#type' => 'value',
'#value' => $block->module,
);
$form['delta'] = array(
'#type' => 'value',
'#value' => $block->delta,
);
// Get the block subject for the page title.
$info = module_invoke($block->module, 'block_info');
if (isset($info[$block->delta])) {
drupal_set_title(t("'%name' block", array('%name' => $info[$block->delta]['info'])), PASS_THROUGH);
}
$form['settings']['title'] = array(
'#type' => 'textfield',
'#title' => t('Block title'),
'#maxlength' => 255,
'#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use !placeholder to display no title, or leave blank to use the default block title.', array('!placeholder' => '<none>')),
'#default_value' => isset($block->title) ? $block->title : '',
'#weight' => -19,
);
// Module-specific block configuration.
if ($settings = module_invoke($block->module, 'block_configure', $block->delta)) {
foreach ($settings as $k => $v) {
$form['settings'][$k] = $v;
}
}
// Region settings.
$form['regions'] = array(
'#type' => 'fieldset',
'#title' => t('Region settings'),
'#collapsible' => FALSE,
'#description' => t('Specify in which themes and regions this block is displayed.'),
'#tree' => TRUE,
);
$theme_default = variable_get('theme_default', 'bartik');
$admin_theme = variable_get('admin_theme');
foreach (list_themes() as $key => $theme) {
// Only display enabled themes
if ($theme->status) {
$region = db_query("SELECT region FROM {block} WHERE module = :module AND delta = :delta AND theme = :theme", array(
':module' => $block->module,
':delta' => $block->delta,
':theme' => $key,
))->fetchField();
// Use a meaningful title for the main site theme and administrative
// theme.
$theme_title = $theme->info['name'];
if ($key == $theme_default) {
$theme_title = t('!theme (default theme)', array('!theme' => $theme_title));
}
elseif ($admin_theme && $key == $admin_theme) {
$theme_title = t('!theme (administration theme)', array('!theme' => $theme_title));
}
$form['regions'][$key] = array(
'#type' => 'select',
'#title' => $theme_title,
'#default_value' => !empty($region) && $region != -1 ? $region : NULL,
'#empty_value' => BLOCK_REGION_NONE,
'#options' => system_region_list($key, REGIONS_VISIBLE),
'#weight' => ($key == $theme_default ? 9 : 10),
);
}
}
// Visibility settings.
$form['visibility_title'] = array(
'#type' => 'item',
'#title' => t('Visibility settings'),
);
$form['visibility'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(drupal_get_path('module', 'block') . '/block.js'),
),
);
// Per-path visibility.
$form['visibility']['path'] = array(
'#type' => 'fieldset',
'#title' => t('Pages'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'visibility',
'#weight' => 0,
);
$access = user_access('use PHP for settings');
if (isset($block->visibility) && $block->visibility == BLOCK_VISIBILITY_PHP && !$access) {
$form['visibility']['path']['visibility'] = array(
'#type' => 'value',
'#value' => BLOCK_VISIBILITY_PHP,
);
$form['visibility']['path']['pages'] = array(
'#type' => 'value',
'#value' => isset($block->pages) ? $block->pages : '',
);
}
else {
$options = array(
BLOCK_VISIBILITY_NOTLISTED => t('All pages except those listed'),
BLOCK_VISIBILITY_LISTED => t('Only the listed pages'),
);
$description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => ''));
if (module_exists('php') && $access) {
$options += array(BLOCK_VISIBILITY_PHP => t('Pages on which this PHP code returns TRUE (experts only)'));
$title = t('Pages or PHP code');
$description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => ''));
}
else {
$title = t('Pages');
}
$form['visibility']['path']['visibility'] = array(
'#type' => 'radios',
'#title' => t('Show block on specific pages'),
'#options' => $options,
'#default_value' => isset($block->visibility) ? $block->visibility : BLOCK_VISIBILITY_NOTLISTED,
);
$form['visibility']['path']['pages'] = array(
'#type' => 'textarea',
'#title' => '' . $title . '',
'#default_value' => isset($block->pages) ? $block->pages : '',
'#description' => $description,
);
}
// Per-role visibility.
$default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module AND delta = :delta", array(
':module' => $block->module,
':delta' => $block->delta,
))->fetchCol();
$role_options = array_map('check_plain', user_roles());
$form['visibility']['role'] = array(
'#type' => 'fieldset',
'#title' => t('Roles'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'visibility',
'#weight' => 10,
);
$form['visibility']['role']['roles'] = array(
'#type' => 'checkboxes',
'#title' => t('Show block for specific roles'),
'#default_value' => $default_role_options,
'#options' => $role_options,
'#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'),
);
// Per-user visibility.
$form['visibility']['user'] = array(
'#type' => 'fieldset',
'#title' => t('Users'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'visibility',
'#weight' => 20,
);
$form['visibility']['user']['custom'] = array(
'#type' => 'radios',
'#title' => t('Customizable per user'),
'#options' => array(
BLOCK_CUSTOM_FIXED => t('Not customizable'),
BLOCK_CUSTOM_ENABLED => t('Customizable, visible by default'),
BLOCK_CUSTOM_DISABLED => t('Customizable, hidden by default'),
),
'#description' => t('Allow individual users to customize the visibility of this block in their account settings.'),
'#default_value' => isset($block->custom) ? $block->custom : BLOCK_CUSTOM_FIXED,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save block'),
);
return $form;
}
/**
* Form validation handler for block_admin_configure().
*
* @see block_admin_configure()
* @see block_admin_configure_submit()
*/
function block_admin_configure_validate($form, &$form_state) {
if ($form_state['values']['module'] == 'block') {
$custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array(
':bid' => $form_state['values']['delta'],
':info' => $form_state['values']['info'],
))->fetchField();
if (empty($form_state['values']['info']) || $custom_block_exists) {
form_set_error('info', t('Ensure that each block description is unique.'));
}
}
}
/**
* Form submission handler for block_admin_configure().
*
* @see block_admin_configure()
* @see block_admin_configure_validate()
*/
function block_admin_configure_submit($form, &$form_state) {
if (!form_get_errors()) {
$transaction = db_transaction();
try {
db_update('block')
->fields(array(
'visibility' => (int) $form_state['values']['visibility'],
'pages' => trim($form_state['values']['pages']),
'custom' => (int) $form_state['values']['custom'],
'title' => $form_state['values']['title'],
))
->condition('module', $form_state['values']['module'])
->condition('delta', $form_state['values']['delta'])
->execute();
db_delete('block_role')
->condition('module', $form_state['values']['module'])
->condition('delta', $form_state['values']['delta'])
->execute();
$query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
foreach (array_filter($form_state['values']['roles']) as $rid) {
$query->values(array(
'rid' => $rid,
'module' => $form_state['values']['module'],
'delta' => $form_state['values']['delta'],
));
}
$query->execute();
// Store regions per theme for this block
foreach ($form_state['values']['regions'] as $theme => $region) {
db_merge('block')
->key(array('theme' => $theme, 'delta' => $form_state['values']['delta'], 'module' => $form_state['values']['module']))
->fields(array(
'region' => ($region == BLOCK_REGION_NONE ? '' : $region),
'pages' => trim($form_state['values']['pages']),
'status' => (int) ($region != BLOCK_REGION_NONE),
))
->execute();
}
module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']);
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('block', $e);
throw $e;
}
drupal_set_message(t('The block configuration has been saved.'));
cache_clear_all();
$form_state['redirect'] = 'admin/structure/block';
}
}
/**
* Form constructor for the add block form.
*
* @see block_add_block_form_validate()
* @see block_add_block_form_submit()
* @ingroup forms
*/
function block_add_block_form($form, &$form_state) {
return block_admin_configure($form, $form_state, 'block', NULL);
}
/**
* Form validation handler for block_add_block_form().
*
* @see block_add_block_form()
* @see block_add_block_form_submit()
*/
function block_add_block_form_validate($form, &$form_state) {
$custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE info = :info', 0, 1, array(':info' => $form_state['values']['info']))->fetchField();
if (empty($form_state['values']['info']) || $custom_block_exists) {
form_set_error('info', t('Ensure that each block description is unique.'));
}
}
/**
* Form submission handler for block_add_block_form().
*
* Saves the new custom block.
*
* @see block_add_block_form()
* @see block_add_block_form_validate()
*/
function block_add_block_form_submit($form, &$form_state) {
$delta = db_insert('block_custom')
->fields(array(
'body' => $form_state['values']['body']['value'],
'info' => $form_state['values']['info'],
'format' => $form_state['values']['body']['format'],
))
->execute();
// Store block delta to allow other modules to work with new block.
$form_state['values']['delta'] = $delta;
$query = db_insert('block')->fields(array('visibility', 'pages', 'custom', 'title', 'module', 'theme', 'status', 'weight', 'delta', 'cache'));
foreach (list_themes() as $key => $theme) {
if ($theme->status) {
$query->values(array(
'visibility' => (int) $form_state['values']['visibility'],
'pages' => trim($form_state['values']['pages']),
'custom' => (int) $form_state['values']['custom'],
'title' => $form_state['values']['title'],
'module' => $form_state['values']['module'],
'theme' => $theme->name,
'status' => 0,
'weight' => 0,
'delta' => $delta,
'cache' => DRUPAL_NO_CACHE,
));
}
}
$query->execute();
$query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
foreach (array_filter($form_state['values']['roles']) as $rid) {
$query->values(array(
'rid' => $rid,
'module' => $form_state['values']['module'],
'delta' => $delta,
));
}
$query->execute();
// Store regions per theme for this block
foreach ($form_state['values']['regions'] as $theme => $region) {
db_merge('block')
->key(array('theme' => $theme, 'delta' => $delta, 'module' => $form_state['values']['module']))
->fields(array(
'region' => ($region == BLOCK_REGION_NONE ? '' : $region),
'pages' => trim($form_state['values']['pages']),
'status' => (int) ($region != BLOCK_REGION_NONE),
))
->execute();
}
drupal_set_message(t('The block has been created.'));
cache_clear_all();
$form_state['redirect'] = 'admin/structure/block';
}
/**
* Form constructor for the custom block deletion form.
*
* @param $module
* The name of the module that implements the block to be deleted. This should
* always equal 'block' since it only allows custom blocks to be deleted.
* @param $delta
* The unique ID of the block within the context of $module.
*
* @see block_custom_block_delete_submit()
*/
function block_custom_block_delete($form, &$form_state, $module, $delta) {
$block = block_load($module, $delta);
$custom_block = block_custom_block_get($block->delta);
$form['info'] = array('#type' => 'hidden', '#value' => $custom_block['info'] ? $custom_block['info'] : $custom_block['title']);
$form['bid'] = array('#type' => 'hidden', '#value' => $block->delta);
return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $custom_block['info'])), 'admin/structure/block', '', t('Delete'), t('Cancel'));
}
/**
* Form submission handler for block_custom_block_delete().
*
* @see block_custom_block_delete()
*/
function block_custom_block_delete_submit($form, &$form_state) {
db_delete('block_custom')
->condition('bid', $form_state['values']['bid'])
->execute();
db_delete('block')
->condition('module', 'block')
->condition('delta', $form_state['values']['bid'])
->execute();
db_delete('block_role')
->condition('module', 'block')
->condition('delta', $form_state['values']['bid'])
->execute();
drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info'])));
cache_clear_all();
$form_state['redirect'] = 'admin/structure/block';
return;
}
/**
* Processes variables for block-admin-display-form.tpl.php.
*
* The $variables array contains the following arguments:
* - $form
*
* @see block-admin-display.tpl.php
* @see theme_block_admin_display()
*/
function template_preprocess_block_admin_display_form(&$variables) {
$variables['block_regions'] = $variables['form']['block_regions']['#value'];
if (isset($variables['block_regions'][BLOCK_REGION_NONE])) {
$variables['block_regions'][BLOCK_REGION_NONE] = t('Disabled');
}
foreach ($variables['block_regions'] as $key => $value) {
// Initialize an empty array for the region.
$variables['block_listing'][$key] = array();
}
// Initialize disabled blocks array.
$variables['block_listing'][BLOCK_REGION_NONE] = array();
// Add each block in the form to the appropriate place in the block listing.
foreach (element_children($variables['form']['blocks']) as $i) {
$block = &$variables['form']['blocks'][$i];
// Fetch the region for the current block.
$region = (isset($block['region']['#default_value']) ? $block['region']['#default_value'] : BLOCK_REGION_NONE);
// Set special classes needed for table drag and drop.
$block['region']['#attributes']['class'] = array('block-region-select', 'block-region-' . $region);
$block['weight']['#attributes']['class'] = array('block-weight', 'block-weight-' . $region);
$variables['block_listing'][$region][$i] = new stdClass();
$variables['block_listing'][$region][$i]->row_class = !empty($block['#attributes']['class']) ? implode(' ', $block['#attributes']['class']) : '';
$variables['block_listing'][$region][$i]->block_modified = !empty($block['#attributes']['class']) && in_array('block-modified', $block['#attributes']['class']);
$variables['block_listing'][$region][$i]->block_title = drupal_render($block['info']);
$variables['block_listing'][$region][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']);
$variables['block_listing'][$region][$i]->weight_select = drupal_render($block['weight']);
$variables['block_listing'][$region][$i]->configure_link = drupal_render($block['configure']);
$variables['block_listing'][$region][$i]->delete_link = !empty($block['delete']) ? drupal_render($block['delete']) : '';
$variables['block_listing'][$region][$i]->printed = FALSE;
}
$variables['form_submit'] = drupal_render_children($variables['form']);
}
www-test-master-modules/modules/block/block.api.php 0000664 0000000 0000000 00000034653 12501305533 0022743 0 ustar 00root root 0000000 0000000 \nnode/1"), or custom PHP code when 'visibility' is set to
* BLOCK_VISIBILITY_PHP. Paths may use '*' as a wildcard (matching any
* number of characters); '' designates the site's front page. For
* BLOCK_VISIBILITY_PHP, the PHP code's return value should be TRUE if the
* block is to be made visible or FALSE if the block should not be visible.
*
* For a detailed usage example, see block_example.module.
*
* @see hook_block_configure()
* @see hook_block_save()
* @see hook_block_view()
* @see hook_block_info_alter()
*/
function hook_block_info() {
// This example comes from node.module.
$blocks['syndicate'] = array(
'info' => t('Syndicate'),
'cache' => DRUPAL_NO_CACHE
);
$blocks['recent'] = array(
'info' => t('Recent content'),
// DRUPAL_CACHE_PER_ROLE will be assumed.
);
return $blocks;
}
/**
* Change block definition before saving to the database.
*
* @param $blocks
* A multidimensional array of blocks keyed by the defining module and delta;
* the values are blocks returned by hook_block_info(). This hook is fired
* after the blocks are collected from hook_block_info() and the database,
* right before saving back to the database.
* @param $theme
* The theme these blocks belong to.
* @param $code_blocks
* The blocks as defined in hook_block_info() before being overwritten by the
* database data.
*
* @see hook_block_info()
*/
function hook_block_info_alter(&$blocks, $theme, $code_blocks) {
// Disable the login block.
$blocks['user']['login']['status'] = 0;
}
/**
* Define a configuration form for a block.
*
* @param $delta
* Which block is being configured. This is a unique identifier for the block
* within the module, defined in hook_block_info().
*
* @return
* A configuration form, if one is needed for your block beyond the standard
* elements that the block module provides (block title, visibility, etc.).
*
* For a detailed usage example, see block_example.module.
*
* @see hook_block_info()
* @see hook_block_save()
*/
function hook_block_configure($delta = '') {
// This example comes from node.module.
$form = array();
if ($delta == 'recent') {
$form['node_recent_block_count'] = array(
'#type' => 'select',
'#title' => t('Number of recent content items to display'),
'#default_value' => variable_get('node_recent_block_count', 10),
'#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)),
);
}
return $form;
}
/**
* Save the configuration options from hook_block_configure().
*
* This hook allows you to save the block-specific configuration settings
* defined within your hook_block_configure().
*
* @param $delta
* Which block is being configured. This is a unique identifier for the block
* within the module, defined in hook_block_info().
* @param $edit
* The submitted form data from the configuration form.
*
* For a detailed usage example, see block_example.module.
*
* @see hook_block_configure()
* @see hook_block_info()
*/
function hook_block_save($delta = '', $edit = array()) {
// This example comes from node.module.
if ($delta == 'recent') {
variable_set('node_recent_block_count', $edit['node_recent_block_count']);
}
}
/**
* Return a rendered or renderable view of a block.
*
* @param $delta
* Which block to render. This is a unique identifier for the block
* within the module, defined in hook_block_info().
*
* @return
* Either an empty array so the block will not be shown or an array containing
* the following elements:
* - subject: The default localized title of the block. If the block does not
* have a default title, this should be set to NULL.
* - content: The content of the block's body. This may be a renderable array
* (preferable) or a string containing rendered HTML content. If the content
* is empty the block will not be shown.
*
* For a detailed usage example, see block_example.module.
*
* @see hook_block_info()
* @see hook_block_view_alter()
* @see hook_block_view_MODULE_DELTA_alter()
*/
function hook_block_view($delta = '') {
// This example is adapted from node.module.
$block = array();
switch ($delta) {
case 'syndicate':
$block['subject'] = t('Syndicate');
$block['content'] = array(
'#theme' => 'feed_icon',
'#url' => 'rss.xml',
'#title' => t('Syndicate'),
);
break;
case 'recent':
if (user_access('access content')) {
$block['subject'] = t('Recent content');
if ($nodes = node_get_recent(variable_get('node_recent_block_count', 10))) {
$block['content'] = array(
'#theme' => 'node_recent_block',
'#nodes' => $nodes,
);
} else {
$block['content'] = t('No content available.');
}
}
break;
}
return $block;
}
/**
* Perform alterations to the content of a block.
*
* This hook allows you to modify any data returned by hook_block_view().
*
* Note that instead of hook_block_view_alter(), which is called for all
* blocks, you can also use hook_block_view_MODULE_DELTA_alter() to alter a
* specific block.
*
* @param $data
* The data as returned from the hook_block_view() implementation of the
* module that defined the block. This could be an empty array or NULL value
* (if the block is empty) or an array containing:
* - subject: The default localized title of the block.
* - content: Either a string or a renderable array representing the content
* of the block. You should check that the content is an array before trying
* to modify parts of the renderable structure.
* @param $block
* The block object, as loaded from the database, having the main properties:
* - module: The name of the module that defined the block.
* - delta: The unique identifier for the block within that module, as defined
* in hook_block_info().
*
* @see hook_block_view_MODULE_DELTA_alter()
* @see hook_block_view()
*/
function hook_block_view_alter(&$data, $block) {
// Remove the contextual links on all blocks that provide them.
if (is_array($data['content']) && isset($data['content']['#contextual_links'])) {
unset($data['content']['#contextual_links']);
}
// Add a theme wrapper function defined by the current module to all blocks
// provided by the "somemodule" module.
if (is_array($data['content']) && $block->module == 'somemodule') {
$data['content']['#theme_wrappers'][] = 'mymodule_special_block';
}
}
/**
* Perform alterations to a specific block.
*
* Modules can implement hook_block_view_MODULE_DELTA_alter() to modify a
* specific block, rather than implementing hook_block_view_alter().
*
* @param $data
* The data as returned from the hook_block_view() implementation of the
* module that defined the block. This could be an empty array or NULL value
* (if the block is empty) or an array containing:
* - subject: The localized title of the block.
* - content: Either a string or a renderable array representing the content
* of the block. You should check that the content is an array before trying
* to modify parts of the renderable structure.
* @param $block
* The block object, as loaded from the database, having the main properties:
* - module: The name of the module that defined the block.
* - delta: The unique identifier for the block within that module, as defined
* in hook_block_info().
*
* @see hook_block_view_alter()
* @see hook_block_view()
*/
function hook_block_view_MODULE_DELTA_alter(&$data, $block) {
// This code will only run for a specific block. For example, if MODULE_DELTA
// in the function definition above is set to "mymodule_somedelta", the code
// will only run on the "somedelta" block provided by the "mymodule" module.
// Change the title of the "somedelta" block provided by the "mymodule"
// module.
$data['subject'] = t('New title of the block');
}
/**
* Act on blocks prior to rendering.
*
* This hook allows you to add, remove or modify blocks in the block list. The
* block list contains the block definitions, not the rendered blocks. The
* blocks are rendered after the modules have had a chance to manipulate the
* block list.
*
* You can also set $block->content here, which will override the content of the
* block and prevent hook_block_view() from running.
*
* @param $blocks
* An array of $blocks, keyed by the block ID.
*/
function hook_block_list_alter(&$blocks) {
global $language, $theme_key;
// This example shows how to achieve language specific visibility setting for
// blocks.
$result = db_query('SELECT module, delta, language FROM {my_table}');
$block_languages = array();
foreach ($result as $record) {
$block_languages[$record->module][$record->delta][$record->language] = TRUE;
}
foreach ($blocks as $key => $block) {
// Any module using this alter should inspect the data before changing it,
// to ensure it is what they expect.
if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
// This block was added by a contrib module, leave it in the list.
continue;
}
if (!isset($block_languages[$block->module][$block->delta])) {
// No language setting for this block, leave it in the list.
continue;
}
if (!isset($block_languages[$block->module][$block->delta][$language->language])) {
// This block should not be displayed with the active language, remove
// from the list.
unset($blocks[$key]);
}
}
}
/**
* @} End of "addtogroup hooks".
*/
www-test-master-modules/modules/block/block.css 0000664 0000000 0000000 00000001347 12501305533 0022166 0 ustar 00root root 0000000 0000000
#blocks tr.region-title td {
font-weight: bold;
}
#blocks tr.region-message {
font-weight: normal;
color: #999;
}
#blocks tr.region-populated {
display: none;
}
.block-region {
background-color: #ff6;
margin-top: 4px;
margin-bottom: 4px;
padding: 3px;
}
a.block-demo-backlink,
a.block-demo-backlink:link,
a.block-demo-backlink:visited {
background-color: #B4D7F0;
-moz-border-radius: 0 0 10px 10px;
-webkit-border-radius: 0 0 10px 10px;
border-radius: 0 0 10px 10px;
color: #000;
font-family: "Lucida Grande", Verdana, sans-serif;
font-size: small;
line-height: 20px;
left: 20px; /*LTR*/
padding: 5px 10px;
position: fixed;
z-index: 499;
}
a.block-demo-backlink:hover {
text-decoration: underline;
}
www-test-master-modules/modules/block/block.info 0000664 0000000 0000000 00000000613 12501305533 0022324 0 ustar 00root root 0000000 0000000 name = Block
description = Controls the visual building blocks a page is constructed with. Blocks are boxes of content rendered into an area, or region, of a web page.
package = Core
version = VERSION
core = 7.x
files[] = block.test
configure = admin/structure/block
; Information added by Drupal.org packaging script on 2014-11-19
version = "7.34"
project = "drupal"
datestamp = "1416429488"
www-test-master-modules/modules/block/block.install 0000664 0000000 0000000 00000041472 12501305533 0023047 0 ustar 00root root 0000000 0000000 'Stores block settings, such as region and visibility settings.',
'fields' => array(
'bid' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique block ID.',
),
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => "The module from which the block originates; for example, 'user' for the Who's Online block, and 'block' for any custom blocks.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '0',
'description' => 'Unique ID for block within a module.',
),
'theme' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => 'The theme under which the block settings apply.',
),
'status' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => 'Block enabled status. (1 = enabled, 0 = disabled)',
),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Block weight within region.',
),
'region' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => 'Theme region within which the block is set.',
),
'custom' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => 'Flag to indicate how users may control visibility of the block. (0 = Users cannot control, 1 = On by default, but can be hidden, 2 = Hidden by default, but can be shown)',
),
'visibility' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => 'Flag to indicate how to show blocks on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)',
),
'pages' => array(
'type' => 'text',
'not null' => TRUE,
'description' => 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.',
),
'title' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)',
'translatable' => TRUE,
),
'cache' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 1,
'size' => 'tiny',
'description' => 'Binary flag to indicate block cache mode. (-2: Custom cache, -1: Do not cache, 1: Cache per role, 2: Cache per user, 4: Cache per page, 8: Block cache global) See DRUPAL_CACHE_* constants in ../includes/common.inc for more detailed information.',
),
),
'primary key' => array('bid'),
'unique keys' => array(
'tmd' => array('theme', 'module', 'delta'),
),
'indexes' => array(
'list' => array('theme', 'status', 'region', 'weight', 'module'),
),
);
$schema['block_role'] = array(
'description' => 'Sets up access permissions for blocks based on user roles',
'fields' => array(
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'description' => "The block's origin module, from {block}.module.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The block's unique delta within module, from {block}.delta.",
),
'rid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => "The user's role ID from {users_roles}.rid.",
),
),
'primary key' => array('module', 'delta', 'rid'),
'indexes' => array(
'rid' => array('rid'),
),
);
$schema['block_custom'] = array(
'description' => 'Stores contents of custom-made blocks.',
'fields' => array(
'bid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => "The block's {block}.bid.",
),
'body' => array(
'type' => 'text',
'not null' => FALSE,
'size' => 'big',
'description' => 'Block contents.',
'translatable' => TRUE,
),
'info' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'Block description.',
),
'format' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the block body.',
),
),
'unique keys' => array(
'info' => array('info'),
),
'primary key' => array('bid'),
);
$schema['cache_block'] = drupal_get_schema_unprocessed('system', 'cache');
$schema['cache_block']['description'] = 'Cache table for the Block module to store already built blocks, identified by module, delta, and various contexts which may change the block, such as theme, locale, and caching mode defined for the block.';
return $schema;
}
/**
* Implements hook_install().
*/
function block_install() {
// Block should go first so that other modules can alter its output
// during hook_page_alter(). Almost everything on the page is a block,
// so before block module runs, there will not be much to alter.
db_update('system')
->fields(array('weight' => -5))
->condition('name', 'block')
->execute();
}
/**
* Implements hook_update_dependencies().
*/
function block_update_dependencies() {
// block_update_7005() needs to query the {filter_format} table to get a list
// of existing text formats, so it must run after filter_update_7000(), which
// creates that table.
$dependencies['block'][7005] = array(
'filter' => 7000,
);
return $dependencies;
}
/**
* @addtogroup updates-6.x-to-7.x
* @{
*/
/**
* Set system.weight to a low value for block module.
*
* Block should go first so that other modules can alter its output
* during hook_page_alter(). Almost everything on the page is a block,
* so before block module runs, there will not be much to alter.
*/
function block_update_7000() {
db_update('system')
->fields(array('weight' => '-5'))
->condition('name', 'block')
->execute();
}
/**
* Rename {blocks} table to {block}, {blocks_roles} to {block_role} and
* {boxes} to {block_custom}.
*/
function block_update_7002() {
db_drop_index('blocks', 'list');
db_rename_table('blocks', 'block');
db_rename_table('blocks_roles', 'block_role');
db_rename_table('boxes', 'block_custom');
}
/**
* Change the weight column to normal int.
*/
function block_update_7003() {
db_change_field('block', 'weight', 'weight', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Block weight within region.',
), array(
'indexes' => array(
'list' => array('theme', 'status', 'region', 'weight', 'module'),
),
));
}
/**
* Add new blocks to new regions, migrate custom variables to blocks.
*/
function block_update_7004() {
// Collect a list of themes with blocks.
$themes_with_blocks = array();
$result = db_query("SELECT s.name FROM {system} s INNER JOIN {block} b ON s.name = b.theme WHERE s.type = 'theme' GROUP by s.name");
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($result as $theme) {
$themes_with_blocks[] = $theme->name;
// Add new system generated help block.
$insert->values(array(
'module' => 'system',
'delta' => 'help',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => DRUPAL_CACHE_PER_ROLE,
));
// Add new system generated main page content block.
$insert->values(array(
'module' => 'system',
'delta' => 'main',
'theme' => $theme->name,
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => DRUPAL_NO_CACHE,
));
}
$insert->execute();
// Migrate blocks from left/right regions to first/second regions.
db_update('block')
->fields(array('region' => 'sidebar_first'))
->condition('region', 'left')
->execute();
db_update('block')
->fields(array('region' => 'sidebar_second'))
->condition('region', 'right')
->execute();
// Migrate contact form information.
$default_format = variable_get('filter_default_format', 1);
if ($contact_help = variable_get('contact_form_information', '')) {
$bid = db_insert('block_custom')
->fields(array(
'body' => $contact_help,
'info' => 'Contact page help',
'format' => $default_format,
))
->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add contact help block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 5,
'region' => 'help',
'visibility' => BLOCK_VISIBILITY_LISTED,
'pages' => 'contact',
'cache' => DRUPAL_NO_CACHE,
));
}
drupal_set_message('The contact form information setting was migrated to a custom block and set up to only show on the site-wide contact page. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right.');
}
$insert->execute();
// Migrate user help setting.
if ($user_help = variable_get('user_registration_help', '')) {
$bid = db_insert('block_custom')->fields(array('body' => $user_help, 'info' => 'User registration guidelines', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add user registration help block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 5,
'region' => 'help',
'visibility' => BLOCK_VISIBILITY_LISTED,
'pages' => 'user/register',
'cache' => DRUPAL_NO_CACHE,
));
}
drupal_set_message('The user registration guidelines were migrated to a custom block and set up to only show on the user registration page. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right.');
$insert->execute();
}
// Migrate site mission setting.
if ($mission = variable_get('site_mission')) {
$bid = db_insert('block_custom')->fields(array('body' => $mission, 'info' => 'Site mission', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add mission block for themes, which had blocks.
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => 0,
'region' => 'highlighted',
'visibility' => BLOCK_VISIBILITY_LISTED,
'pages' => '',
'cache' => DRUPAL_NO_CACHE,
));
}
drupal_set_message('The site mission was migrated to a custom block and set up to only show on the front page in the highlighted content region. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right. If your theme does not have a highlighted content region, you might need to relocate the block.');
$insert->execute();
// Migrate mission to RSS site description.
variable_set('feed_description', $mission);
}
// Migrate site footer message to a custom block.
if ($footer_message = variable_get('site_footer', '')) {
$bid = db_insert('block_custom')->fields(array('body' => $footer_message, 'info' => 'Footer message', 'format' => $default_format))->execute();
$insert = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($themes_with_blocks as $theme) {
// Add site footer block for themes, which had blocks.
// Set low weight, so the block comes early (it used to be
// before the other blocks).
$insert->values(array(
'module' => 'block',
'delta' => $bid,
'theme' => $theme,
'status' => 1,
'weight' => -10,
'region' => 'footer',
'pages' => '',
'cache' => DRUPAL_NO_CACHE,
));
}
drupal_set_message('The footer message was migrated to a custom block and set up to appear in the footer. The block was set to use the default text format, which might differ from the HTML based format used before. Check the block and ensure that the output is right. If your theme does not have a footer region, you might need to relocate the block.');
$insert->execute();
}
// Remove the variables (even if they were saved empty on the admin interface),
// to avoid keeping clutter in the variables table.
variable_del('contact_form_information');
variable_del('user_registration_help');
variable_del('site_mission');
variable_del('site_footer');
// Rebuild theme data, so the new 'help' region is identified.
system_rebuild_theme_data();
}
/**
* Update the {block_custom}.format column.
*/
function block_update_7005() {
// For an explanation of these updates, see the code comments in
// user_update_7010().
db_change_field('block_custom', 'format', 'format', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the block body.',
));
db_update('block_custom')
->fields(array('format' => NULL))
->condition('body', '')
->condition('format', 0)
->execute();
$existing_formats = db_query("SELECT format FROM {filter_format}")->fetchCol();
$default_format = variable_get('filter_default_format', 1);
db_update('block_custom')
->fields(array('format' => $default_format))
->isNotNull('format')
->condition('format', $existing_formats, 'NOT IN')
->execute();
}
/**
* Recreates cache_block table.
*
* Converts fields that hold serialized variables from text to blob.
* Removes 'headers' column.
*/
function block_update_7006() {
$schema = system_schema_cache_7054();
db_drop_table('cache_block');
db_create_table('cache_block', $schema);
}
/**
* Change {block_custom}.format into varchar.
*/
function block_update_7007() {
db_change_field('block_custom', 'format', 'format', array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The {filter_format}.format of the block body.',
));
}
/**
* @} End of "addtogroup updates-6.x-to-7.x".
*/
/**
* @addtogroup updates-7.x-extra
* @{
*/
/**
* Update database to match Drupal 7 schema.
*/
function block_update_7008() {
db_drop_field('block', 'throttle');
}
/**
* Increase {block}.title length to 255 characters.
*/
function block_update_7009() {
db_change_field('block', 'title', 'title',
array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)',
'translatable' => TRUE,
)
);
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/
www-test-master-modules/modules/block/block.js 0000664 0000000 0000000 00000014121 12501305533 0022004 0 ustar 00root root 0000000 0000000 (function ($) {
/**
* Provide the summary information for the block settings vertical tabs.
*/
Drupal.behaviors.blockSettingsSummary = {
attach: function (context) {
// The drupalSetSummary method required for this behavior is not available
// on the Blocks administration page, so we need to make sure this
// behavior is processed only if drupalSetSummary is defined.
if (typeof jQuery.fn.drupalSetSummary == 'undefined') {
return;
}
$('fieldset#edit-path', context).drupalSetSummary(function (context) {
if (!$('textarea[name="pages"]', context).val()) {
return Drupal.t('Not restricted');
}
else {
return Drupal.t('Restricted to certain pages');
}
});
$('fieldset#edit-node-type', context).drupalSetSummary(function (context) {
var vals = [];
$('input[type="checkbox"]:checked', context).each(function () {
vals.push($.trim($(this).next('label').text()));
});
if (!vals.length) {
vals.push(Drupal.t('Not restricted'));
}
return vals.join(', ');
});
$('fieldset#edit-role', context).drupalSetSummary(function (context) {
var vals = [];
$('input[type="checkbox"]:checked', context).each(function () {
vals.push($.trim($(this).next('label').text()));
});
if (!vals.length) {
vals.push(Drupal.t('Not restricted'));
}
return vals.join(', ');
});
$('fieldset#edit-user', context).drupalSetSummary(function (context) {
var $radio = $('input[name="custom"]:checked', context);
if ($radio.val() == 0) {
return Drupal.t('Not customizable');
}
else {
return $radio.next('label').text();
}
});
}
};
/**
* Move a block in the blocks table from one region to another via select list.
*
* This behavior is dependent on the tableDrag behavior, since it uses the
* objects initialized in that behavior to update the row.
*/
Drupal.behaviors.blockDrag = {
attach: function (context, settings) {
// tableDrag is required and we should be on the blocks admin page.
if (typeof Drupal.tableDrag == 'undefined' || typeof Drupal.tableDrag.blocks == 'undefined') {
return;
}
var table = $('table#blocks');
var tableDrag = Drupal.tableDrag.blocks; // Get the blocks tableDrag object.
// Add a handler for when a row is swapped, update empty regions.
tableDrag.row.prototype.onSwap = function (swappedRow) {
checkEmptyRegions(table, this);
};
// A custom message for the blocks page specifically.
Drupal.theme.tableDragChangedWarning = function () {
return '
' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('The changes to these blocks will not be saved until the Save blocks button is clicked.') + '
';
};
// Add a handler so when a row is dropped, update fields dropped into new regions.
tableDrag.onDrop = function () {
dragObject = this;
// Use "region-message" row instead of "region" row because
// "region-{region_name}-message" is less prone to regexp match errors.
var regionRow = $(dragObject.rowObject.element).prevAll('tr.region-message').get(0);
var regionName = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
var regionField = $('select.block-region-select', dragObject.rowObject.element);
// Check whether the newly picked region is available for this block.
if ($('option[value=' + regionName + ']', regionField).length == 0) {
// If not, alert the user and keep the block in its old region setting.
alert(Drupal.t('The block cannot be placed in this region.'));
// Simulate that there was a selected element change, so the row is put
// back to from where the user tried to drag it.
regionField.change();
}
else if ($(dragObject.rowObject.element).prev('tr').is('.region-message')) {
var weightField = $('select.block-weight', dragObject.rowObject.element);
var oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*block-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
if (!regionField.is('.block-region-' + regionName)) {
regionField.removeClass('block-region-' + oldRegionName).addClass('block-region-' + regionName);
weightField.removeClass('block-weight-' + oldRegionName).addClass('block-weight-' + regionName);
regionField.val(regionName);
}
}
};
// Add the behavior to each region select list.
$('select.block-region-select', context).once('block-region-select', function () {
$(this).change(function (event) {
// Make our new row and select field.
var row = $(this).closest('tr');
var select = $(this);
tableDrag.rowObject = new tableDrag.row(row);
// Find the correct region and insert the row as the last in the region.
table.find('.region-' + select[0].value + '-message').nextUntil('.region-message').last().before(row);
// Modify empty regions with added or removed fields.
checkEmptyRegions(table, row);
// Remove focus from selectbox.
select.get(0).blur();
});
});
var checkEmptyRegions = function (table, rowObject) {
$('tr.region-message', table).each(function () {
// If the dragged row is in this region, but above the message row, swap it down one space.
if ($(this).prev('tr').get(0) == rowObject.element) {
// Prevent a recursion problem when using the keyboard to move rows up.
if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) {
rowObject.swap('after', this);
}
}
// This region has become empty.
if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) {
$(this).removeClass('region-populated').addClass('region-empty');
}
// This region has become populated.
else if ($(this).is('.region-empty')) {
$(this).removeClass('region-empty').addClass('region-populated');
}
});
};
}
};
})(jQuery);
www-test-master-modules/modules/block/block.module 0000664 0000000 0000000 00000115457 12501305533 0022673 0 ustar 00root root 0000000 0000000 ' . t('About') . '';
$output .= '
' . t('The Block module allows you to create boxes of content, which are rendered into an area, or region, of one or more pages of a website. The core Seven administration theme, for example, implements the regions "Content", "Help", "Dashboard main", and "Dashboard sidebar", and a block may appear in any one of these regions. The Blocks administration page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. For more information, see the online handbook entry for Block module.', array('@block' => 'http://drupal.org/documentation/modules/block/', '@blocks' => url('admin/structure/block'))) . '
';
$output .= '
' . t('Uses') . '
';
$output .= '
';
$output .= '
' . t('Positioning content') . '
';
$output .= '
' . t('When working with blocks, remember that all themes do not implement the same regions, or display regions in the same way. Blocks are positioned on a per-theme basis. Users with the Administer blocks permission can disable blocks. Disabled blocks are listed on the Blocks administration page, but are not displayed in any region.', array('@block' => 'http://drupal.org/documentation/modules/block/', '@blocks' => url('admin/structure/block'))) . '
';
$output .= '
' . t('Controlling visibility') . '
';
$output .= '
' . t('Blocks can be configured to be visible only on certain pages, only to users of certain roles, or only on pages displaying certain content types. Administrators can also allow specific blocks to be enabled or disabled by users when they edit their My account page. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.', array('@content-type' => url('admin/structure/types'), '@user' => url('user'))) . '
';
$output .= '
' . t('Creating custom blocks') . '
';
$output .= '
' . t('Users with the Administer blocks permission can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/add'))) . '
';
$output .= '
';
return $output;
case 'admin/structure/block/add':
return '
' . t('Use this page to create a new custom block.') . '
' . t('This page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the Save blocks button at the bottom of the page. Click the configure link next to each block to configure its specific title and visibility settings.') . '
';
return $output;
}
}
/**
* Implements hook_theme().
*/
function block_theme() {
return array(
'block' => array(
'render element' => 'elements',
'template' => 'block',
),
'block_admin_display_form' => array(
'template' => 'block-admin-display-form',
'file' => 'block.admin.inc',
'render element' => 'form',
),
);
}
/**
* Implements hook_permission().
*/
function block_permission() {
return array(
'administer blocks' => array(
'title' => t('Administer blocks'),
),
);
}
/**
* Implements hook_menu().
*/
function block_menu() {
$default_theme = variable_get('theme_default', 'bartik');
$items['admin/structure/block'] = array(
'title' => 'Blocks',
'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
'page callback' => 'block_admin_display',
'page arguments' => array($default_theme),
'access arguments' => array('administer blocks'),
'file' => 'block.admin.inc',
);
$items['admin/structure/block/manage/%/%'] = array(
'title' => 'Configure block',
'page callback' => 'drupal_get_form',
'page arguments' => array('block_admin_configure', 4, 5),
'access arguments' => array('administer blocks'),
'file' => 'block.admin.inc',
);
$items['admin/structure/block/manage/%/%/configure'] = array(
'title' => 'Configure block',
'type' => MENU_DEFAULT_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
);
$items['admin/structure/block/manage/%/%/delete'] = array(
'title' => 'Delete block',
'page callback' => 'drupal_get_form',
'page arguments' => array('block_custom_block_delete', 4, 5),
'access arguments' => array('administer blocks'),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_NONE,
'file' => 'block.admin.inc',
);
$items['admin/structure/block/add'] = array(
'title' => 'Add block',
'page callback' => 'drupal_get_form',
'page arguments' => array('block_add_block_form'),
'access arguments' => array('administer blocks'),
'type' => MENU_LOCAL_ACTION,
'file' => 'block.admin.inc',
);
foreach (list_themes() as $key => $theme) {
$items['admin/structure/block/list/' . $key] = array(
'title' => check_plain($theme->info['name']),
'page arguments' => array($key),
'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
'weight' => $key == $default_theme ? -10 : 0,
'access callback' => '_block_themes_access',
'access arguments' => array($theme),
'file' => 'block.admin.inc',
);
if ($key != $default_theme) {
$items['admin/structure/block/list/' . $key . '/add'] = array(
'title' => 'Add block',
'page callback' => 'drupal_get_form',
'page arguments' => array('block_add_block_form'),
'access arguments' => array('administer blocks'),
'type' => MENU_LOCAL_ACTION,
'file' => 'block.admin.inc',
);
}
$items['admin/structure/block/demo/' . $key] = array(
'title' => check_plain($theme->info['name']),
'page callback' => 'block_admin_demo',
'page arguments' => array($key),
'type' => MENU_CALLBACK,
'access callback' => '_block_themes_access',
'access arguments' => array($theme),
'theme callback' => '_block_custom_theme',
'theme arguments' => array($key),
'file' => 'block.admin.inc',
);
}
return $items;
}
/**
* Menu item access callback - only admin or enabled themes can be accessed.
*/
function _block_themes_access($theme) {
return user_access('administer blocks') && drupal_theme_access($theme);
}
/**
* Theme callback for the block configuration pages.
*
* @param $theme
* The theme whose blocks are being configured. If not set, the default theme
* is assumed.
* @return
* The theme that should be used for the block configuration page, or NULL
* to indicate that the default theme should be used.
*/
function _block_custom_theme($theme = NULL) {
// We return exactly what was passed in, to guarantee that the page will
// always be displayed using the theme whose blocks are being configured.
return $theme;
}
/**
* Implements hook_block_info().
*/
function block_block_info() {
$blocks = array();
$result = db_query('SELECT bid, info FROM {block_custom} ORDER BY info');
foreach ($result as $block) {
$blocks[$block->bid]['info'] = $block->info;
// Not worth caching.
$blocks[$block->bid]['cache'] = DRUPAL_NO_CACHE;
}
return $blocks;
}
/**
* Implements hook_block_configure().
*/
function block_block_configure($delta = 0) {
if ($delta) {
$custom_block = block_custom_block_get($delta);
}
else {
$custom_block = array();
}
return block_custom_block_form($custom_block);
}
/**
* Implements hook_block_save().
*/
function block_block_save($delta = 0, $edit = array()) {
block_custom_block_save($edit, $delta);
}
/**
* Implements hook_block_view().
*
* Generates the administrator-defined blocks for display.
*/
function block_block_view($delta = '') {
$block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject();
$data['subject'] = NULL;
$data['content'] = check_markup($block->body, $block->format, '', TRUE);
return $data;
}
/**
* Implements hook_page_build().
*
* Renders blocks into their regions.
*/
function block_page_build(&$page) {
global $theme;
// The theme system might not yet be initialized. We need $theme.
drupal_theme_initialize();
// Fetch a list of regions for the current theme.
$all_regions = system_region_list($theme);
$item = menu_get_item();
if ($item['path'] != 'admin/structure/block/demo/' . $theme) {
// Load all region content assigned via blocks.
foreach (array_keys($all_regions) as $region) {
// Assign blocks to region.
if ($blocks = block_get_blocks_by_region($region)) {
$page[$region] = $blocks;
}
}
// Once we've finished attaching all blocks to the page, clear the static
// cache to allow modules to alter the block list differently in different
// contexts. For example, any code that triggers hook_page_build() more
// than once in the same page request may need to alter the block list
// differently each time, so that only certain parts of the page are
// actually built. We do not clear the cache any earlier than this, though,
// because it is used each time block_get_blocks_by_region() gets called
// above.
drupal_static_reset('block_list');
}
else {
// Append region description if we are rendering the regions demo page.
$item = menu_get_item();
if ($item['path'] == 'admin/structure/block/demo/' . $theme) {
$visible_regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
foreach ($visible_regions as $region) {
$description = '
' . $all_regions[$region] . '
';
$page[$region]['block_description'] = array(
'#markup' => $description,
'#weight' => 15,
);
}
$page['page_top']['backlink'] = array(
'#type' => 'link',
'#title' => t('Exit block region demonstration'),
'#href' => 'admin/structure/block' . (variable_get('theme_default', 'bartik') == $theme ? '' : '/list/' . $theme),
// Add the "overlay-restore" class to indicate this link should restore
// the context in which the region demonstration page was opened.
'#options' => array('attributes' => array('class' => array('block-demo-backlink', 'overlay-restore'))),
'#weight' => -10,
);
}
}
}
/**
* Gets a renderable array of a region containing all enabled blocks.
*
* @param $region
* The requested region.
*
* @return
* A renderable array of a region containing all enabled blocks.
*/
function block_get_blocks_by_region($region) {
$build = array();
if ($list = block_list($region)) {
$build = _block_get_renderable_array($list);
}
return $build;
}
/**
* Gets an array of blocks suitable for drupal_render().
*
* @param $list
* A list of blocks such as that returned by block_list().
*
* @return
* A renderable array.
*/
function _block_get_renderable_array($list = array()) {
$weight = 0;
$build = array();
foreach ($list as $key => $block) {
$build[$key] = $block->content;
unset($block->content);
// Add contextual links for this block; skip the main content block, since
// contextual links are basically output as tabs/local tasks already. Also
// skip the help block, since we assume that most users do not need or want
// to perform contextual actions on the help block, and the links needlessly
// draw attention on it.
if ($key != 'system_main' && $key != 'system_help') {
$build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta));
}
$build[$key] += array(
'#block' => $block,
'#weight' => ++$weight,
);
$build[$key]['#theme_wrappers'][] ='block';
}
$build['#sorted'] = TRUE;
return $build;
}
/**
* Updates the 'block' DB table with the blocks currently exported by modules.
*
* @param $theme
* The theme to rehash blocks for. If not provided, defaults to the currently
* used theme.
*
* @return
* Blocks currently exported by modules.
*/
function _block_rehash($theme = NULL) {
global $theme_key;
drupal_theme_initialize();
if (!isset($theme)) {
// If theme is not specifically set, rehash for the current theme.
$theme = $theme_key;
}
$regions = system_region_list($theme);
// These are the blocks the function will return.
$blocks = array();
// These are the blocks defined by code and modified by the database.
$current_blocks = array();
// These are {block}.bid values to be kept.
$bids = array();
$or = db_or();
// Gather the blocks defined by modules.
foreach (module_implements('block_info') as $module) {
$module_blocks = module_invoke($module, 'block_info');
foreach ($module_blocks as $delta => $block) {
// Compile a condition to retrieve this block from the database.
$condition = db_and()
->condition('module', $module)
->condition('delta', $delta);
$or->condition($condition);
// Add identifiers.
$block['module'] = $module;
$block['delta'] = $delta;
$block['theme'] = $theme;
$current_blocks[$module][$delta] = $block;
}
}
// Save the blocks defined in code for alter context.
$code_blocks = $current_blocks;
$database_blocks = db_select('block', 'b', array('fetch' => PDO::FETCH_ASSOC))
->fields('b')
->condition($or)
->condition('theme', $theme)
->execute();
$original_database_blocks = array();
foreach ($database_blocks as $block) {
$module = $block['module'];
$delta = $block['delta'];
$original_database_blocks[$module][$delta] = $block;
// The cache mode can only by set from hook_block_info(), so that has
// precedence over the database's value.
if (isset($current_blocks[$module][$delta]['cache'])) {
$block['cache'] = $current_blocks[$module][$delta]['cache'];
}
// Preserve info which is not in the database.
$block['info'] = $current_blocks[$module][$delta]['info'];
// Blocks stored in the database override the blocks defined in code.
$current_blocks[$module][$delta] = $block;
// Preserve this block.
$bids[$block['bid']] = $block['bid'];
}
drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
foreach ($current_blocks as $module => $module_blocks) {
foreach ($module_blocks as $delta => $block) {
if (!isset($block['pages'])) {
// {block}.pages is type 'text', so it cannot have a
// default value, and not null, so we need to provide
// value if the module did not.
$block['pages'] = '';
}
// Make sure weight is set.
if (!isset($block['weight'])) {
$block['weight'] = 0;
}
if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) {
drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block['info'], '%region' => $block['region'])), 'warning');
// Disabled modules are moved into the BLOCK_REGION_NONE later so no
// need to move the block to another region.
$block['status'] = 0;
}
// Set region to none if not enabled and make sure status is set.
if (empty($block['status'])) {
$block['status'] = 0;
$block['region'] = BLOCK_REGION_NONE;
}
// There is no point saving disabled blocks. Still, we need to save them
// because the 'title' attribute is saved to the {blocks} table.
if (isset($block['bid'])) {
// If the block has a bid property, it comes from the database and
// the record needs to be updated, so set the primary key to 'bid'
// before passing to drupal_write_record().
$primary_keys = array('bid');
// Remove a block from the list of blocks to keep if it became disabled.
unset($bids[$block['bid']]);
}
else {
$primary_keys = array();
}
// If the block is new or differs from the original database block, save
// it. To determine whether there was a change it is enough to examine
// the values for the keys in the original database record as that
// contained every database field.
if (!$primary_keys || array_diff_assoc($original_database_blocks[$module][$delta], $block)) {
drupal_write_record('block', $block, $primary_keys);
// Make it possible to test this.
$block['saved'] = TRUE;
}
// Add to the list of blocks we return.
$blocks[] = $block;
}
}
if ($bids) {
// Remove disabled that are no longer defined by the code from the
// database.
db_delete('block')
->condition('bid', $bids, 'NOT IN')
->condition('theme', $theme)
->execute();
}
return $blocks;
}
/**
* Returns information from database about a user-created (custom) block.
*
* @param $bid
* ID of the block to get information for.
*
* @return
* Associative array of information stored in the database for this block.
* Array keys:
* - bid: Block ID.
* - info: Block description.
* - body: Block contents.
* - format: Filter ID of the filter format for the body.
*/
function block_custom_block_get($bid) {
return db_query("SELECT * FROM {block_custom} WHERE bid = :bid", array(':bid' => $bid))->fetchAssoc();
}
/**
* Form constructor for the custom block form.
*
* @param $edit
* (optional) An associative array of information retrieved by
* block_custom_get_block() if an existing block is being edited, or an empty
* array otherwise. Defaults to array().
*
* @ingroup forms
*/
function block_custom_block_form($edit = array()) {
$edit += array(
'info' => '',
'body' => '',
);
$form['info'] = array(
'#type' => 'textfield',
'#title' => t('Block description'),
'#default_value' => $edit['info'],
'#maxlength' => 64,
'#description' => t('A brief description of your block. Used on the Blocks administration page.', array('@overview' => url('admin/structure/block'))),
'#required' => TRUE,
'#weight' => -18,
);
$form['body_field']['#weight'] = -17;
$form['body_field']['body'] = array(
'#type' => 'text_format',
'#title' => t('Block body'),
'#default_value' => $edit['body'],
'#format' => isset($edit['format']) ? $edit['format'] : NULL,
'#rows' => 15,
'#description' => t('The content of the block as shown to the user.'),
'#required' => TRUE,
'#weight' => -17,
);
return $form;
}
/**
* Saves a user-created block in the database.
*
* @param $edit
* Associative array of fields to save. Array keys:
* - info: Block description.
* - body: Associative array of body value and format. Array keys:
* - value: Block contents.
* - format: Filter ID of the filter format for the body.
* @param $delta
* Block ID of the block to save.
*
* @return
* Always returns TRUE.
*/
function block_custom_block_save($edit, $delta) {
db_update('block_custom')
->fields(array(
'body' => $edit['body']['value'],
'info' => $edit['info'],
'format' => $edit['body']['format'],
))
->condition('bid', $delta)
->execute();
return TRUE;
}
/**
* Implements hook_form_FORM_ID_alter() for user_profile_form().
*/
function block_form_user_profile_form_alter(&$form, &$form_state) {
if ($form['#user_category'] == 'account') {
$account = $form['#user'];
$rids = array_keys($account->roles);
$result = db_query("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom <> 0 AND (r.rid IN (:rids) OR r.rid IS NULL) ORDER BY b.weight, b.module", array(':rids' => $rids));
$blocks = array();
foreach ($result as $block) {
$data = module_invoke($block->module, 'block_info');
if ($data[$block->delta]['info']) {
$blocks[$block->module][$block->delta] = array(
'#type' => 'checkbox',
'#title' => check_plain($data[$block->delta]['info']),
'#default_value' => isset($account->data['block'][$block->module][$block->delta]) ? $account->data['block'][$block->module][$block->delta] : ($block->custom == 1),
);
}
}
// Only display the fieldset if there are any personalizable blocks.
if ($blocks) {
$form['block'] = array(
'#type' => 'fieldset',
'#title' => t('Personalize blocks'),
'#description' => t('Blocks consist of content or information that complements the main content of the page. Enable or disable optional blocks using the checkboxes below.'),
'#weight' => 3,
'#collapsible' => TRUE,
'#tree' => TRUE,
);
$form['block'] += $blocks;
}
}
}
/**
* Implements hook_user_presave().
*/
function block_user_presave(&$edit, $account, $category) {
if (isset($edit['block'])) {
$edit['data']['block'] = $edit['block'];
}
}
/**
* Initializes blocks for enabled themes.
*
* @param $theme_list
* An array of theme names.
*/
function block_themes_enabled($theme_list) {
foreach ($theme_list as $theme) {
block_theme_initialize($theme);
}
}
/**
* Assigns an initial, default set of blocks for a theme.
*
* This function is called the first time a new theme is enabled. The new theme
* gets a copy of the default theme's blocks, with the difference that if a
* particular region isn't available in the new theme, the block is assigned
* to the new theme's default region.
*
* @param $theme
* The name of a theme.
*/
function block_theme_initialize($theme) {
// Initialize theme's blocks if none already registered.
$has_blocks = (bool) db_query_range('SELECT 1 FROM {block} WHERE theme = :theme', 0, 1, array(':theme' => $theme))->fetchField();
if (!$has_blocks) {
$default_theme = variable_get('theme_default', 'bartik');
// Apply only to new theme's visible regions.
$regions = system_region_list($theme, REGIONS_VISIBLE);
$result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $default_theme), array('fetch' => PDO::FETCH_ASSOC));
foreach ($result as $block) {
// If the region isn't supported by the theme, assign the block to the theme's default region.
if ($block['status'] && !isset($regions[$block['region']])) {
$block['region'] = system_default_region($theme);
}
$block['theme'] = $theme;
unset($block['bid']);
drupal_write_record('block', $block);
}
}
}
/**
* Returns all blocks in the specified region for the current user.
*
* @param $region
* The name of a region.
*
* @return
* An array of block objects, indexed with the module name and block delta
* concatenated with an underscore, thus: MODULE_DELTA. If you are displaying
* your blocks in one or two sidebars, you may check whether this array is
* empty to see how many columns are going to be displayed.
*
* @todo
* Now that the block table has a primary key, we should use that as the
* array key instead of MODULE_DELTA.
*/
function block_list($region) {
$blocks = &drupal_static(__FUNCTION__);
if (!isset($blocks)) {
$blocks = _block_load_blocks();
}
// Create an empty array if there are no entries.
if (!isset($blocks[$region])) {
$blocks[$region] = array();
}
else {
$blocks[$region] = _block_render_blocks($blocks[$region]);
}
return $blocks[$region];
}
/**
* Loads a block object from the database.
*
* @param $module
* Name of the module that implements the block to load.
* @param $delta
* Unique ID of the block within the context of $module. Pass NULL to return
* an empty block object for $module.
*
* @return
* A block object.
*/
function block_load($module, $delta) {
if (isset($delta)) {
$block = db_query('SELECT * FROM {block} WHERE module = :module AND delta = :delta', array(':module' => $module, ':delta' => $delta))->fetchObject();
}
// If the block does not exist in the database yet return a stub block
// object.
if (empty($block)) {
$block = new stdClass();
$block->module = $module;
$block->delta = $delta;
}
return $block;
}
/**
* Loads blocks' information from the database.
*
* @return
* An array of blocks grouped by region.
*/
function _block_load_blocks() {
global $theme_key;
$query = db_select('block', 'b');
$result = $query
->fields('b')
->condition('b.theme', $theme_key)
->condition('b.status', 1)
->orderBy('b.region')
->orderBy('b.weight')
->orderBy('b.module')
->addTag('block_load')
->addTag('translatable')
->execute();
$block_info = $result->fetchAllAssoc('bid');
// Allow modules to modify the block list.
drupal_alter('block_list', $block_info);
$blocks = array();
foreach ($block_info as $block) {
$blocks[$block->region]["{$block->module}_{$block->delta}"] = $block;
}
return $blocks;
}
/**
* Implements hook_block_list_alter().
*
* Checks the page, user role, and user-specific visibility settings.
* Removes the block if the visibility conditions are not met.
*/
function block_block_list_alter(&$blocks) {
global $user, $theme_key;
// Build an array of roles for each block.
$block_roles = array();
$result = db_query('SELECT module, delta, rid FROM {block_role}');
foreach ($result as $record) {
$block_roles[$record->module][$record->delta][] = $record->rid;
}
foreach ($blocks as $key => $block) {
if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
// This block was added by a contrib module, leave it in the list.
continue;
}
// If a block has no roles associated, it is displayed for every role.
// For blocks with roles associated, if none of the user's roles matches
// the settings from this block, remove it from the block list.
if (isset($block_roles[$block->module][$block->delta]) && !array_intersect($block_roles[$block->module][$block->delta], array_keys($user->roles))) {
// No match.
unset($blocks[$key]);
continue;
}
// Use the user's block visibility setting, if necessary.
if ($block->custom != BLOCK_CUSTOM_FIXED) {
if ($user->uid && isset($user->data['block'][$block->module][$block->delta])) {
$enabled = $user->data['block'][$block->module][$block->delta];
}
else {
$enabled = ($block->custom == BLOCK_CUSTOM_ENABLED);
}
}
else {
$enabled = TRUE;
}
// Limited visibility blocks must list at least one page.
if ($block->visibility == BLOCK_VISIBILITY_LISTED && empty($block->pages)) {
$enabled = FALSE;
}
if (!$enabled) {
unset($blocks[$key]);
continue;
}
// Match path if necessary.
if ($block->pages) {
// Convert path to lowercase. This allows comparison of the same path
// with different case. Ex: /Page, /page, /PAGE.
$pages = drupal_strtolower($block->pages);
if ($block->visibility < BLOCK_VISIBILITY_PHP) {
// Convert the Drupal path to lowercase
$path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
// Compare the lowercase internal and lowercase path alias (if any).
$page_match = drupal_match_path($path, $pages);
if ($path != $_GET['q']) {
$page_match = $page_match || drupal_match_path($_GET['q'], $pages);
}
// When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED),
// the block is displayed on all pages except those listed in $block->pages.
// When set to 1 (BLOCK_VISIBILITY_LISTED), it is displayed only on those
// pages listed in $block->pages.
$page_match = !($block->visibility xor $page_match);
}
elseif (module_exists('php')) {
$page_match = php_eval($block->pages);
}
else {
$page_match = FALSE;
}
}
else {
$page_match = TRUE;
}
if (!$page_match) {
unset($blocks[$key]);
}
}
}
/**
* Render the content and subject for a set of blocks.
*
* @param $region_blocks
* An array of block objects such as returned for one region by _block_load_blocks().
*
* @return
* An array of visible blocks as expected by drupal_render().
*/
function _block_render_blocks($region_blocks) {
$cacheable = TRUE;
// We preserve the submission of forms in blocks, by fetching from cache only
// if the request method is 'GET' (or 'HEAD').
if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') {
$cacheable = FALSE;
}
// Block caching is not usually compatible with node access modules, so by
// default it is disabled when node access modules exist. However, it can be
// allowed by using the variable 'block_cache_bypass_node_grants'.
elseif (!variable_get('block_cache_bypass_node_grants', FALSE) && count(module_implements('node_grants'))) {
$cacheable = FALSE;
}
// Proceed to loop over all blocks in order to compute their respective cache
// identifiers; this allows us to do one single cache_get_multiple() call
// instead of doing one cache_get() call per block.
$cached_blocks = array();
$cids = array();
if ($cacheable) {
foreach ($region_blocks as $key => $block) {
if (!isset($block->content)) {
if (($cid = _block_get_cache_id($block))) {
$cids[$key] = $cid;
}
}
}
if ($cids) {
// We cannot pass $cids in directly because cache_get_multiple() will
// modify it, and we need to use it later on in this function.
$cid_values = array_values($cids);
$cached_blocks = cache_get_multiple($cid_values, 'cache_block');
}
}
foreach ($region_blocks as $key => $block) {
// Render the block content if it has not been created already.
if (!isset($block->content)) {
// Erase the block from the static array - we'll put it back if it has
// content.
unset($region_blocks[$key]);
$cid = empty($cids[$key]) ? NULL : $cids[$key];
// Try fetching the block from the previously loaded cache entries.
if (isset($cached_blocks[$cid])) {
$array = $cached_blocks[$cid]->data;
}
else {
$array = module_invoke($block->module, 'block_view', $block->delta);
// Valid PHP function names cannot contain hyphens.
$delta = str_replace('-', '_', $block->delta);
// Allow modules to modify the block before it is viewed, via either
// hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
drupal_alter(array('block_view', "block_view_{$block->module}_{$delta}"), $array, $block);
if (isset($cid)) {
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
}
}
if (isset($array) && is_array($array)) {
foreach ($array as $k => $v) {
$block->$k = $v;
}
}
if (isset($block->content) && $block->content) {
// Normalize to the drupal_render() structure.
if (is_string($block->content)) {
$block->content = array('#markup' => $block->content);
}
// Override default block title if a custom display title is present.
if ($block->title) {
// Check plain here to allow module generated titles to keep any
// markup.
$block->subject = $block->title == '' ? '' : check_plain($block->title);
}
if (!isset($block->subject)) {
$block->subject = '';
}
$region_blocks["{$block->module}_{$block->delta}"] = $block;
}
}
}
return $region_blocks;
}
/**
* Assemble the cache_id to use for a given block.
*
* The cache_id string reflects the viewing context for the current block
* instance, obtained by concatenating the relevant context information
* (user, page, ...) according to the block's cache settings (BLOCK_CACHE_*
* constants). Two block instances can use the same cached content when
* they share the same cache_id.
*
* Theme and language contexts are automatically differentiated.
*
* @param $block
* @return
* The string used as cache_id for the block.
*/
function _block_get_cache_id($block) {
global $user;
// User 1 being out of the regular 'roles define permissions' schema,
// it brings too many chances of having unwanted output get in the cache
// and later be served to other users. We therefore exclude user 1 from
// block caching.
if (variable_get('block_cache', FALSE) && !in_array($block->cache, array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM)) && $user->uid != 1) {
// Start with common sub-patterns: block identification, theme, language.
$cid_parts[] = $block->module;
$cid_parts[] = $block->delta;
$cid_parts = array_merge($cid_parts, drupal_render_cid_parts($block->cache));
return implode(':', $cid_parts);
}
}
/**
* Implements hook_flush_caches().
*/
function block_flush_caches() {
// Rehash blocks for active themes. We don't use list_themes() here,
// because if MAINTENANCE_MODE is defined it skips reading the database,
// and we can't tell which themes are active.
$themes = db_query("SELECT name FROM {system} WHERE type = 'theme' AND status = 1");
foreach ($themes as $theme) {
_block_rehash($theme->name);
}
return array('cache_block');
}
/**
* Processes variables for block.tpl.php.
*
* Prepares the values passed to the theme_block function to be passed
* into a pluggable template engine. Uses block properties to generate a
* series of template file suggestions. If none are found, the default
* block.tpl.php is used.
*
* Most themes utilize their own copy of block.tpl.php. The default is located
* inside "modules/block/block.tpl.php". Look in there for the full list of
* variables.
*
* The $variables array contains the following arguments:
* - $block
*
* @see block.tpl.php
*/
function template_preprocess_block(&$variables) {
$block_counter = &drupal_static(__FUNCTION__, array());
$variables['block'] = $variables['elements']['#block'];
// All blocks get an independent counter for each region.
if (!isset($block_counter[$variables['block']->region])) {
$block_counter[$variables['block']->region] = 1;
}
// Same with zebra striping.
$variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even';
$variables['block_id'] = $block_counter[$variables['block']->region]++;
// Create the $content variable that templates expect.
$variables['content'] = $variables['elements']['#children'];
$variables['classes_array'][] = drupal_html_class('block-' . $variables['block']->module);
$variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region;
$variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module;
// Hyphens (-) and underscores (_) play a special role in theme suggestions.
// Theme suggestions should only contain underscores, because within
// drupal_find_theme_templates(), underscores are converted to hyphens to
// match template file names, and then converted back to underscores to match
// pre-processing and other function names. So if your theme suggestion
// contains a hyphen, it will end up as an underscore after this conversion,
// and your function names won't be recognized. So, we need to convert
// hyphens to underscores in block deltas for the theme suggestions.
$variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . strtr($variables['block']->delta, '-', '_');
// Create a valid HTML ID and make sure it is unique.
$variables['block_html_id'] = drupal_html_id('block-' . $variables['block']->module . '-' . $variables['block']->delta);
}
/**
* Implements hook_user_role_delete().
*
* Removes deleted role from blocks that use it.
*/
function block_user_role_delete($role) {
db_delete('block_role')
->condition('rid', $role->rid)
->execute();
}
/**
* Implements hook_menu_delete().
*/
function block_menu_delete($menu) {
db_delete('block')
->condition('module', 'menu')
->condition('delta', $menu['menu_name'])
->execute();
db_delete('block_role')
->condition('module', 'menu')
->condition('delta', $menu['menu_name'])
->execute();
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function block_form_system_performance_settings_alter(&$form, &$form_state) {
$disabled = (!variable_get('block_cache_bypass_node_grants', FALSE) && count(module_implements('node_grants')));
$form['caching']['block_cache'] = array(
'#type' => 'checkbox',
'#title' => t('Cache blocks'),
'#default_value' => variable_get('block_cache', FALSE),
'#disabled' => $disabled,
'#description' => $disabled ? t('Block caching is inactive because you have enabled modules defining content access restrictions.') : NULL,
'#weight' => -1,
);
}
/**
* Implements hook_admin_paths().
*/
function block_admin_paths() {
$paths = array(
// Exclude the block demonstration page from admin (overlay) treatment.
// This allows us to present this page in its true form, full page.
'admin/structure/block/demo/*' => FALSE,
);
return $paths;
}
/**
* Implements hook_modules_uninstalled().
*
* Cleans up {block} and {block_role} tables from modules' blocks.
*/
function block_modules_uninstalled($modules) {
db_delete('block')
->condition('module', $modules, 'IN')
->execute();
db_delete('block_role')
->condition('module', $modules, 'IN')
->execute();
}
www-test-master-modules/modules/block/block.test 0000664 0000000 0000000 00000114441 12501305533 0022355 0 ustar 00root root 0000000 0000000 'Block functionality',
'description' => 'Add, edit and delete custom block. Configure and move a module-defined block.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp();
// Create and log in an administrative user having access to the Full HTML
// text format.
$full_html_format = filter_format_load('full_html');
$this->admin_user = $this->drupalCreateUser(array(
'administer blocks',
filter_permission_name($full_html_format),
'access administration pages',
));
$this->drupalLogin($this->admin_user);
// Define the existing regions
$this->regions = array();
$this->regions[] = 'header';
$this->regions[] = 'sidebar_first';
$this->regions[] = 'content';
$this->regions[] = 'sidebar_second';
$this->regions[] = 'footer';
}
/**
* Test creating custom block, moving it to a specific region and then deleting it.
*/
function testCustomBlock() {
// Confirm that the add block link appears on block overview pages.
$this->drupalGet('admin/structure/block');
$this->assertRaw(l('Add block', 'admin/structure/block/add'), 'Add block link is present on block overview page for default theme.');
$this->drupalGet('admin/structure/block/list/seven');
$this->assertRaw(l('Add block', 'admin/structure/block/list/seven/add'), 'Add block link is present on block overview page for non-default theme.');
// Confirm that hidden regions are not shown as options for block placement
// when adding a new block.
theme_enable(array('stark'));
$themes = list_themes();
$this->drupalGet('admin/structure/block/add');
foreach ($themes as $key => $theme) {
if ($theme->status) {
foreach ($theme->info['regions_hidden'] as $hidden_region) {
$elements = $this->xpath('//select[@id=:id]//option[@value=:value]', array(':id' => 'edit-regions-' . $key, ':value' => $hidden_region));
$this->assertFalse(isset($elements[0]), format_string('The hidden region @region is not available for @theme.', array('@region' => $hidden_region, '@theme' => $key)));
}
}
}
// Add a new custom block by filling out the input form on the admin/structure/block/add page.
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $this->randomName(8);
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
// Confirm that the custom block has been created, and then query the created bid.
$this->assertText(t('The block has been created.'), 'Custom block successfully created.');
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
// Check to see if the custom block was created by checking that it's in the database.
$this->assertNotNull($bid, 'Custom block found in database');
// Check that block_block_view() returns the correct title and content.
$data = block_block_view($bid);
$format = db_query("SELECT format FROM {block_custom} WHERE bid = :bid", array(':bid' => $bid))->fetchField();
$this->assertTrue(array_key_exists('subject', $data) && empty($data['subject']), 'block_block_view() provides an empty block subject, since custom blocks do not have default titles.');
$this->assertEqual(check_markup($custom_block['body[value]'], $format), $data['content'], 'block_block_view() provides correct block content.');
// Check whether the block can be moved to all available regions.
$custom_block['module'] = 'block';
$custom_block['delta'] = $bid;
foreach ($this->regions as $region) {
$this->moveBlockToRegion($custom_block, $region);
}
// Verify presence of configure and delete links for custom block.
$this->drupalGet('admin/structure/block');
$this->assertLinkByHref('admin/structure/block/manage/block/' . $bid . '/configure', 0, 'Custom block configure link found.');
$this->assertLinkByHref('admin/structure/block/manage/block/' . $bid . '/delete', 0, 'Custom block delete link found.');
// Set visibility only for authenticated users, to verify delete functionality.
$edit = array();
$edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE;
$this->drupalPost('admin/structure/block/manage/block/' . $bid . '/configure', $edit, t('Save block'));
// Delete the created custom block & verify that it's been deleted and no longer appearing on the page.
$this->clickLink(t('delete'));
$this->drupalPost('admin/structure/block/manage/block/' . $bid . '/delete', array(), t('Delete'));
$this->assertRaw(t('The block %title has been removed.', array('%title' => $custom_block['info'])), 'Custom block successfully deleted.');
$this->assertNoText(t($custom_block['title']), 'Custom block no longer appears on page.');
$count = db_query("SELECT 1 FROM {block_role} WHERE module = :module AND delta = :delta", array(':module' => $custom_block['module'], ':delta' => $custom_block['delta']))->fetchField();
$this->assertFalse($count, 'Table block_role being cleaned.');
}
/**
* Test creating custom block using Full HTML.
*/
function testCustomBlockFormat() {
// Add a new custom block by filling out the input form on the admin/structure/block/add page.
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $this->randomName(8);
$custom_block['body[value]'] = '
Full HTML
';
$full_html_format = filter_format_load('full_html');
$custom_block['body[format]'] = $full_html_format->format;
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
// Set the created custom block to a specific region.
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
$edit = array();
$edit['blocks[block_' . $bid . '][region]'] = $this->regions[1];
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the custom block is being displayed using configured text format.
$this->drupalGet('node');
$this->assertRaw('
Full HTML
', 'Custom block successfully being displayed using Full HTML.');
// Confirm that a user without access to Full HTML can not see the body field,
// but can still submit the form without errors.
$block_admin = $this->drupalCreateUser(array('administer blocks'));
$this->drupalLogin($block_admin);
$this->drupalGet('admin/structure/block/manage/block/' . $bid . '/configure');
$this->assertFieldByXPath("//textarea[@name='body[value]' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Body field contains denied message');
$this->drupalPost('admin/structure/block/manage/block/' . $bid . '/configure', array(), t('Save block'));
$this->assertNoText(t('Ensure that each block description is unique.'));
// Confirm that the custom block is still being displayed using configured text format.
$this->drupalGet('node');
$this->assertRaw('
Full HTML
', 'Custom block successfully being displayed using Full HTML.');
}
/**
* Test block visibility.
*/
function testBlockVisibility() {
$block = array();
// Create a random title for the block
$title = $this->randomName(8);
// Create the custom block
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $title;
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
$block['module'] = 'block';
$block['delta'] = $bid;
$block['title'] = $title;
// Set the block to be hidden on any user path, and to be shown only to
// authenticated users.
$edit = array();
$edit['pages'] = 'user*';
$edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE;
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block'));
// Move block to the first sidebar.
$this->moveBlockToRegion($block, $this->regions[1]);
$this->drupalGet('');
$this->assertText($title, 'Block was displayed on the front page.');
$this->drupalGet('user');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
$this->drupalGet('USER/' . $this->admin_user->uid);
$this->assertNoText($title, 'Block was not displayed according to block visibility rules regardless of path case.');
// Confirm that the block is not displayed to anonymous users.
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($title, 'Block was not displayed to anonymous users.');
}
/**
* Test block visibility when using "pages" restriction but leaving
* "pages" textarea empty
*/
function testBlockVisibilityListedEmpty() {
$block = array();
// Create a random title for the block
$title = $this->randomName(8);
// Create the custom block
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $title;
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
$block['module'] = 'block';
$block['delta'] = $bid;
$block['title'] = $title;
// Move block to the first sidebar.
$this->moveBlockToRegion($block, $this->regions[1]);
// Set the block to be hidden on any user path, and to be shown only to
// authenticated users.
$edit = array();
$edit['visibility'] = BLOCK_VISIBILITY_LISTED;
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block'));
$this->drupalGet('');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
$this->drupalGet('user');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules regardless of path case.');
// Confirm that the block is not displayed to anonymous users.
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($title, 'Block was not displayed to anonymous users.');
}
/**
* Test user customization of block visibility.
*/
function testBlockVisibilityPerUser() {
$block = array();
// Create a random title for the block.
$title = $this->randomName(8);
// Create our custom test block.
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $title;
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
$block['module'] = 'block';
$block['delta'] = $bid;
$block['title'] = $title;
// Move block to the first sidebar.
$this->moveBlockToRegion($block, $this->regions[1]);
// Set the block to be customizable per user, visible by default.
$edit = array();
$edit['custom'] = BLOCK_CUSTOM_ENABLED;
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block'));
// Disable block visibility for the admin user.
$edit = array();
$edit['block[' . $block['module'] . '][' . $block['delta'] . ']'] = FALSE;
$this->drupalPost('user/' . $this->admin_user->uid . '/edit', $edit, t('Save'));
$this->drupalGet('');
$this->assertNoText($block['title'], 'Block was not displayed according to per user block visibility setting.');
// Set the block to be customizable per user, hidden by default.
$edit = array();
$edit['custom'] = BLOCK_CUSTOM_DISABLED;
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block'));
// Enable block visibility for the admin user.
$edit = array();
$edit['block[' . $block['module'] . '][' . $block['delta'] . ']'] = TRUE;
$this->drupalPost('user/' . $this->admin_user->uid . '/edit', $edit, t('Save'));
$this->drupalGet('');
$this->assertText($block['title'], 'Block was displayed according to per user block visibility setting.');
}
/**
* Test configuring and moving a module-define block to specific regions.
*/
function testBlock() {
// Select the Navigation block to be configured and moved.
$block = array();
$block['module'] = 'system';
$block['delta'] = 'management';
$block['title'] = $this->randomName(8);
// Set block title to confirm that interface works and override any custom titles.
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => $block['title']), t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block title set.');
$bid = db_query("SELECT bid FROM {block} WHERE module = :module AND delta = :delta", array(
':module' => $block['module'],
':delta' => $block['delta'],
))->fetchField();
// Check to see if the block was created by checking that it's in the database.
$this->assertNotNull($bid, 'Block found in database');
// Check whether the block can be moved to all available regions.
foreach ($this->regions as $region) {
$this->moveBlockToRegion($block, $region);
}
// Set the block to the disabled region.
$edit = array();
$edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = '-1';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the block was moved to the proper region.
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to disabled region.');
$this->assertNoText(t($block['title']), 'Block no longer appears on page.');
// Confirm that the region's xpath is not available.
$xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-block-' . $bid));
$this->assertNoFieldByXPath($xpath, FALSE, 'Custom block found in no regions.');
// For convenience of developers, put the navigation block back.
$edit = array();
$edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $this->regions[1];
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to first sidebar region.');
$this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', array('title' => 'Navigation'), t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block title set.');
}
function moveBlockToRegion($block, $region) {
// Set the created block to a specific region.
$edit = array();
$edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the block was moved to the proper region.
$this->assertText(t('The block settings have been updated.'), format_string('Block successfully moved to %region_name region.', array( '%region_name' => $region)));
// Confirm that the block is being displayed.
$this->drupalGet('node');
$this->assertText(t($block['title']), 'Block successfully being displayed on the page.');
// Confirm that the custom block was found at the proper region.
$xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
':region-class' => 'region region-' . str_replace('_', '-', $region),
':block-id' => 'block-' . $block['module'] . '-' . $block['delta'],
));
$this->assertFieldByXPath($xpath, NULL, format_string('Custom block found in %region_name region.', array('%region_name' => $region)));
}
/**
* Test _block_rehash().
*/
function testBlockRehash() {
module_enable(array('block_test'));
$this->assertTrue(module_exists('block_test'), 'Test block module enabled.');
// Our new block should be inserted in the database when we visit the
// block management page.
$this->drupalGet('admin/structure/block');
// Our test block's caching should default to DRUPAL_CACHE_PER_ROLE.
$current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
$this->assertEqual($current_caching, DRUPAL_CACHE_PER_ROLE, 'Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.');
// Disable caching for this block.
variable_set('block_test_caching', DRUPAL_NO_CACHE);
// Flushing all caches should call _block_rehash().
drupal_flush_all_caches();
// Verify that the database is updated with the new caching mode.
$current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField();
$this->assertEqual($current_caching, DRUPAL_NO_CACHE, "Test block's database entry updated to DRUPAL_NO_CACHE.");
}
}
class NonDefaultBlockAdmin extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Non default theme admin',
'description' => 'Check the administer page for non default theme.',
'group' => 'Block',
);
}
/**
* Test non-default theme admin.
*/
function testNonDefaultBlockAdmin() {
$admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes'));
$this->drupalLogin($admin_user);
theme_enable(array('stark'));
$this->drupalGet('admin/structure/block/list/stark');
}
}
/**
* Test blocks correctly initialized when picking a new default theme.
*/
class NewDefaultThemeBlocks extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'New default theme blocks',
'description' => 'Checks that the new default theme gets blocks.',
'group' => 'Block',
);
}
/**
* Check the enabled Bartik blocks are correctly copied over.
*/
function testNewDefaultThemeBlocks() {
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer themes'));
$this->drupalLogin($admin_user);
// Ensure no other theme's blocks are in the block table yet.
$themes = array();
$themes['default'] = variable_get('theme_default', 'bartik');
if ($admin_theme = variable_get('admin_theme')) {
$themes['admin'] = $admin_theme;
}
$count = db_query_range('SELECT 1 FROM {block} WHERE theme NOT IN (:themes)', 0, 1, array(':themes' => $themes))->fetchField();
$this->assertFalse($count, 'Only the default theme and the admin theme have blocks.');
// Populate list of all blocks for matching against new theme.
$blocks = array();
$result = db_query('SELECT * FROM {block} WHERE theme = :theme', array(':theme' => $themes['default']));
foreach ($result as $block) {
// $block->theme and $block->bid will not match, so remove them.
unset($block->theme, $block->bid);
$blocks[$block->module][$block->delta] = $block;
}
// Turn on the Stark theme and ensure that it contains all of the blocks
// the default theme had.
theme_enable(array('stark'));
variable_set('theme_default', 'stark');
$result = db_query('SELECT * FROM {block} WHERE theme = :theme', array(':theme' => 'stark'));
foreach ($result as $block) {
unset($block->theme, $block->bid);
$this->assertEqual($blocks[$block->module][$block->delta], $block, format_string('Block %name matched', array('%name' => $block->module . '-' . $block->delta)));
}
}
}
/**
* Test the block system with admin themes.
*/
class BlockAdminThemeTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Admin theme block admin accessibility',
'description' => "Check whether the block administer page for a disabled theme accessible if and only if it's the admin theme.",
'group' => 'Block',
);
}
/**
* Check for the accessibility of the admin theme on the block admin page.
*/
function testAdminTheme() {
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes'));
$this->drupalLogin($admin_user);
// Ensure that access to block admin page is denied when theme is disabled.
$this->drupalGet('admin/structure/block/list/stark');
$this->assertResponse(403, 'The block admin page for a disabled theme can not be accessed');
// Enable admin theme and confirm that tab is accessible.
$edit['admin_theme'] = 'stark';
$this->drupalPost('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin/structure/block/list/stark');
$this->assertResponse(200, 'The block admin page for the admin theme can be accessed');
}
}
/**
* Test block caching.
*/
class BlockCacheTestCase extends DrupalWebTestCase {
protected $admin_user;
protected $normal_user;
protected $normal_user_alt;
public static function getInfo() {
return array(
'name' => 'Block caching',
'description' => 'Test block caching.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp('block_test');
// Create an admin user, log in and enable test blocks.
$this->admin_user = $this->drupalCreateUser(array('administer blocks', 'access administration pages'));
$this->drupalLogin($this->admin_user);
// Create additional users to test caching modes.
$this->normal_user = $this->drupalCreateUser();
$this->normal_user_alt = $this->drupalCreateUser();
// Sync the roles, since drupalCreateUser() creates separate roles for
// the same permission sets.
user_save($this->normal_user_alt, array('roles' => $this->normal_user->roles));
$this->normal_user_alt->roles = $this->normal_user->roles;
// Enable block caching.
variable_set('block_cache', TRUE);
// Enable our test block.
$edit['blocks[block_test_test_cache][region]'] = 'sidebar_first';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
}
/**
* Test DRUPAL_CACHE_PER_ROLE.
*/
function testCachePerRole() {
$this->setCacheMode(DRUPAL_CACHE_PER_ROLE);
// Enable our test block. Set some content for it to display.
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalLogin($this->normal_user);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
// Change the content, but the cached copy should still be served.
$old_content = $current_content;
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from the cache.');
// Clear the cache and verify that the stale data is no longer there.
cache_clear_all();
$this->drupalGet('');
$this->assertNoText($old_content, 'Block cache clear removes stale cache data.');
$this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.');
// Test whether the cached data is served for the correct users.
$old_content = $current_content;
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($old_content, 'Anonymous user does not see content cached per-role for normal user.');
$this->drupalLogin($this->normal_user_alt);
$this->drupalGet('');
$this->assertText($old_content, 'User with the same roles sees per-role cached content.');
$this->drupalLogin($this->admin_user);
$this->drupalGet('');
$this->assertNoText($old_content, 'Admin user does not see content cached per-role for normal user.');
$this->drupalLogin($this->normal_user);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from the per-role cache.');
}
/**
* Test DRUPAL_CACHE_GLOBAL.
*/
function testCacheGlobal() {
$this->setCacheMode(DRUPAL_CACHE_GLOBAL);
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
$old_content = $current_content;
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalLogout();
$this->drupalGet('user');
$this->assertText($old_content, 'Block content served from global cache.');
}
/**
* Test DRUPAL_NO_CACHE.
*/
function testNoCache() {
$this->setCacheMode(DRUPAL_NO_CACHE);
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
// If DRUPAL_NO_CACHE has no effect, the next request would be cached.
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
// A cached copy should not be served.
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('');
$this->assertText($current_content, 'DRUPAL_NO_CACHE prevents blocks from being cached.');
}
/**
* Test DRUPAL_CACHE_PER_USER.
*/
function testCachePerUser() {
$this->setCacheMode(DRUPAL_CACHE_PER_USER);
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalLogin($this->normal_user);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
$old_content = $current_content;
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from per-user cache.');
$this->drupalLogin($this->normal_user_alt);
$this->drupalGet('');
$this->assertText($current_content, 'Per-user block cache is not served for other users.');
$this->drupalLogin($this->normal_user);
$this->drupalGet('');
$this->assertText($old_content, 'Per-user block cache is persistent.');
}
/**
* Test DRUPAL_CACHE_PER_PAGE.
*/
function testCachePerPage() {
$this->setCacheMode(DRUPAL_CACHE_PER_PAGE);
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('node');
$this->assertText($current_content, 'Block content displays on the node page.');
$old_content = $current_content;
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
$this->drupalGet('user');
$this->assertNoText($old_content, 'Block content cached for the node page does not show up for the user page.');
$this->drupalGet('node');
$this->assertText($old_content, 'Block content cached for the node page.');
}
/**
* Private helper method to set the test block's cache mode.
*/
private function setCacheMode($cache_mode) {
db_update('block')
->fields(array('cache' => $cache_mode))
->condition('module', 'block_test')
->execute();
$current_mode = db_query("SELECT cache FROM {block} WHERE module = 'block_test'")->fetchField();
if ($current_mode != $cache_mode) {
$this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $current_mode)));
}
}
}
/**
* Test block HTML id validity.
*/
class BlockHTMLIdTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Block HTML id',
'description' => 'Test block HTML id validity.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp('block_test');
// Create an admin user, log in and enable test blocks.
$this->admin_user = $this->drupalCreateUser(array('administer blocks', 'access administration pages'));
$this->drupalLogin($this->admin_user);
// Enable our test block.
$edit['blocks[block_test_test_html_id][region]'] = 'sidebar_first';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Make sure the block has some content so it will appear
$current_content = $this->randomName();
variable_set('block_test_content', $current_content);
}
/**
* Test valid HTML id.
*/
function testHTMLId() {
$this->drupalGet('');
$this->assertRaw('block-block-test-test-html-id', 'HTML id for test block is valid.');
}
}
/**
* Unit tests for template_preprocess_block().
*/
class BlockTemplateSuggestionsUnitTest extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Block template suggestions',
'description' => 'Test the template_preprocess_block() function.',
'group' => 'Block',
);
}
/**
* Test if template_preprocess_block() handles the suggestions right.
*/
function testBlockThemeHookSuggestions() {
// Define block delta with underscore to be preprocessed
$block1 = new stdClass();
$block1->module = 'block';
$block1->delta = 'underscore_test';
$block1->region = 'footer';
$variables1 = array();
$variables1['elements']['#block'] = $block1;
$variables1['elements']['#children'] = '';
template_preprocess_block($variables1);
$this->assertEqual($variables1['theme_hook_suggestions'], array('block__footer', 'block__block', 'block__block__underscore_test'), 'Found expected block suggestions for delta with underscore');
// Define block delta with hyphens to be preprocessed. Hyphens should be
// replaced with underscores.
$block2 = new stdClass();
$block2->module = 'block';
$block2->delta = 'hyphen-test';
$block2->region = 'footer';
$variables2 = array();
$variables2['elements']['#block'] = $block2;
$variables2['elements']['#children'] = '';
template_preprocess_block($variables2);
$this->assertEqual($variables2['theme_hook_suggestions'], array('block__footer', 'block__block', 'block__block__hyphen_test'), 'Hyphens (-) in block delta were replaced by underscore (_)');
}
}
/**
* Tests for hook_block_view_MODULE_DELTA_alter().
*/
class BlockViewModuleDeltaAlterWebTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Block view module delta alter',
'description' => 'Test the hook_block_view_MODULE_DELTA_alter() hook.',
'group' => 'Block',
);
}
public function setUp() {
parent::setUp(array('block_test'));
}
/**
* Tests that the alter hook is called, even if the delta contains a hyphen.
*/
public function testBlockViewModuleDeltaAlter() {
$block = new stdClass;
$block->module = 'block_test';
$block->delta = 'test_underscore';
$block->title = '';
$render_array = _block_render_blocks(array('region' => $block));
$render = array_pop($render_array);
$test_underscore = $render->content['#markup'];
$this->assertEqual($test_underscore, 'hook_block_view_MODULE_DELTA_alter', 'Found expected altered block content for delta with underscore');
$block = new stdClass;
$block->module = 'block_test';
$block->delta = 'test-hyphen';
$block->title = '';
$render_array = _block_render_blocks(array('region' => $block));
$render = array_pop($render_array);
$test_hyphen = $render->content['#markup'];
$this->assertEqual($test_hyphen, 'hook_block_view_MODULE_DELTA_alter', 'Hyphens (-) in block delta were replaced by underscore (_)');
}
}
/**
* Tests that hidden regions do not inherit blocks when a theme is enabled.
*/
class BlockHiddenRegionTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Blocks not in hidden region',
'description' => 'Checks that a newly enabled theme does not inherit blocks to its hidden regions.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp(array('block_test'));
}
/**
* Tests that hidden regions do not inherit blocks when a theme is enabled.
*/
function testBlockNotInHiddenRegion() {
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes', 'search content'));
$this->drupalLogin($admin_user);
// Enable "block_test_theme" and set it as the default theme.
$theme = 'block_test_theme';
theme_enable(array($theme));
variable_set('theme_default', $theme);
menu_rebuild();
// Ensure that "block_test_theme" is set as the default theme.
$this->drupalGet('admin/structure/block');
$this->assertText('Block test theme(' . t('active tab') . ')', 'Default local task on blocks admin page is the block test theme.');
// Ensure that the search form block is displayed.
$this->drupalGet('');
$this->assertText('Search form', 'Block was displayed on the front page.');
}
}
/**
* Tests that a block assigned to an invalid region triggers the warning.
*/
class BlockInvalidRegionTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Blocks in invalid regions',
'description' => 'Checks that an active block assigned to a non-existing region triggers the warning message and is disabled.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp(array('block', 'block_test'));
// Create an admin user.
$admin_user = $this->drupalCreateUser(array('administer site configuration', 'access administration pages'));
$this->drupalLogin($admin_user);
}
/**
* Tests that blocks assigned to invalid regions work correctly.
*/
function testBlockInInvalidRegion() {
// Enable a test block in the default theme and place it in an invalid region.
db_merge('block')
->key(array(
'module' => 'block_test',
'delta' => 'test_html_id',
'theme' => variable_get('theme_default', 'stark'),
))
->fields(array(
'status' => 1,
'region' => 'invalid_region',
'cache' => DRUPAL_NO_CACHE,
))
->execute();
$warning_message = t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => t('Test block html id'), '%region' => 'invalid_region'));
// Clearing the cache should disable the test block placed in the invalid region.
$this->drupalPost('admin/config/development/performance', array(), 'Clear all caches');
$this->assertRaw($warning_message, 'Enabled block was in the invalid region and has been disabled.');
// Clear the cache to check if the warning message is not triggered.
$this->drupalPost('admin/config/development/performance', array(), 'Clear all caches');
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
// Place disabled test block in the invalid region of the default theme.
db_merge('block')
->key(array(
'module' => 'block_test',
'delta' => 'test_html_id',
'theme' => variable_get('theme_default', 'stark'),
))
->fields(array(
'region' => 'invalid_region',
'cache' => DRUPAL_NO_CACHE,
))
->execute();
// Clear the cache to check if the warning message is not triggered.
$this->drupalPost('admin/config/development/performance', array(), 'Clear all caches');
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
}
}
/**
* Tests that block rehashing works correctly.
*/
class BlockHashTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Block rehash',
'description' => 'Checks _block_rehash() functionality.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp(array('block'));
}
/**
* Tests that block rehashing does not write to the database too often.
*/
function testBlockRehash() {
// No hook_block_info_alter(), no save.
$this->doRehash();
module_enable(array('block_test'), FALSE);
// Save the new blocks, check that the new blocks exist by checking weight.
_block_rehash();
$this->assertWeight(0);
// Now hook_block_info_alter() exists but no blocks are saved on a second
// rehash.
$this->doRehash();
$this->assertWeight(0);
// Now hook_block_info_alter() exists and is changing one block which
// should be saved.
$GLOBALS['conf']['block_test_info_alter'] = 1;
$this->doRehash(TRUE);
$this->assertWeight(10000);
// Now hook_block_info_alter() exists but already changed the block's
// weight before, so it should not be saved again.
$this->doRehash();
$this->assertWeight(10000);
}
/**
* Performs a block rehash and checks several related assertions.
*
* @param $alter_active
* Set to TRUE if the block_test module's hook_block_info_alter()
* implementation is expected to make a change that results in an existing
* block needing to be resaved to the database. Defaults to FALSE.
*/
function doRehash($alter_active = FALSE) {
$saves = 0;
foreach (_block_rehash() as $block) {
$module = $block['module'];
$delta = $block['delta'];
if ($alter_active && $module == 'block_test' && $delta == 'test_html_id') {
$this->assertFalse(empty($block['saved']), "$module $delta saved");
$saves++;
}
else {
$this->assertTrue(empty($block['saved']), "$module $delta not saved");
}
}
$this->assertEqual($alter_active, $saves);
}
/**
* Asserts that the block_test module's block has a given weight.
*
* @param $weight
* The expected weight.
*/
function assertWeight($weight) {
$db_weight = db_query('SELECT weight FROM {block} WHERE module = :module AND delta = :delta', array(':module' => 'block_test', ':delta' => 'test_html_id'))->fetchField();
// By casting to string the assert fails on FALSE.
$this->assertIdentical((string) $db_weight, (string) $weight);
}
}
www-test-master-modules/modules/block/block.tpl.php 0000664 0000000 0000000 00000004631 12501305533 0022762 0 ustar 00root root 0000000 0000000 subject: Block title.
* - $content: Block content.
* - $block->module: Module that generated the block.
* - $block->delta: An ID for the block, unique within each module.
* - $block->region: The block region embedding the current block.
* - $classes: String of classes that can be used to style contextually through
* CSS. It can be manipulated through the variable $classes_array from
* preprocess functions. The default values can be one or more of the
* following:
* - block: The current template type, i.e., "theming hook".
* - block-[module]: The module generating the block. For example, the user
* module is responsible for handling the default user navigation block. In
* that case the class would be 'block-user'.
* - $title_prefix (array): An array containing additional output populated by
* modules, intended to be displayed in front of the main title tag that
* appears in the template.
* - $title_suffix (array): An array containing additional output populated by
* modules, intended to be displayed after the main title tag that appears in
* the template.
*
* Helper variables:
* - $classes_array: Array of html class attribute values. It is flattened
* into a string within the variable $classes.
* - $block_zebra: Outputs 'odd' and 'even' dependent on each block region.
* - $zebra: Same output as $block_zebra but independent of any block region.
* - $block_id: Counter dependent on each block region.
* - $id: Same output as $block_id but independent of any block region.
* - $is_front: Flags true when presented in the front page.
* - $logged_in: Flags true when the current user is a logged-in member.
* - $is_admin: Flags true when the current user is an administrator.
* - $block_html_id: A valid HTML ID and guaranteed unique.
*
* @see template_preprocess()
* @see template_preprocess_block()
* @see template_process()
*
* @ingroup themeable
*/
?>
www-test-master-modules/modules/blog/ 0000775 0000000 0000000 00000000000 12501305533 0020206 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/blog/blog.info 0000664 0000000 0000000 00000000364 12501305533 0022011 0 ustar 00root root 0000000 0000000 name = Blog
description = Enables multi-user blogs.
package = Core
version = VERSION
core = 7.x
files[] = blog.test
; Information added by Drupal.org packaging script on 2014-11-19
version = "7.34"
project = "drupal"
datestamp = "1416429488"
www-test-master-modules/modules/blog/blog.install 0000664 0000000 0000000 00000000624 12501305533 0022523 0 ustar 00root root 0000000 0000000 array(
'name' => t('Blog entry'),
'base' => 'blog',
'description' => t('Use for multi-user blogs. Every user gets a personal blog.'),
)
);
}
/**
* Implements hook_user_view().
*/
function blog_user_view($account) {
if (user_access('create blog content', $account)) {
$account->content['summary']['blog'] = array(
'#type' => 'user_profile_item',
'#title' => t('Blog'),
// l() escapes the attributes, so we should not escape !username here.
'#markup' => l(t('View recent blog entries'), "blog/$account->uid", array('attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($account)))))),
'#attributes' => array('class' => array('blog')),
);
}
}
/**
* Implements hook_help().
*/
function blog_help($path, $arg) {
switch ($path) {
case 'admin/help#blog':
$output = '
' . t('About') . '
';
$output .= '
' . t("The Blog module allows registered users to maintain an online journal, or blog. Blogs are made up of individual blog entries. By default, the blog entries are displayed by creation time in descending order, with comments enabled, and are promoted to the site's front page. For more information, see the online handbook entry for Blog module.", array('@blog' => 'http://drupal.org/documentation/modules/blog/')) . '
';
$output .= '
' . t('Uses') . '
';
$output .= '
';
$output .= '
' . t('Single-user blogs') . '
';
$output .= '
' . t("Each user's blog entries are automatically displayed with a link to the user's main blog page. You can create as many single-user blogs as you have site users with permission to create blog content.") . '
';
$output .= '
' . t('Multi-user blogs') . '
';
$output .= '
' . t("Blog entries from each single-user blog are also aggregated into one central multi-user blog, which displays the blog content of all users in a single listing.") . '
';
$output .= '
' . t('Navigation') . '
';
$output .= '
' . t("There is an optional Blogs menu item added to the Navigation menu, which displays all blogs available on your site, and a My blog item displaying the current user's blog entries.") . '
';
$output .= '
' . t('Blocks') . '
';
$output .= '
' . t('The Blog module also creates a default Recent blog posts block that may be enabled at the blocks administration page.', array('@blocks' => url('admin/structure/block'))) . '
';
$output .= '
';
return $output;
}
}
/**
* Implements hook_form().
*/
function blog_form($node, $form_state) {
return node_content_form($node, $form_state);
}
/**
* Implements hook_view().
*/
function blog_view($node, $view_mode) {
if ($view_mode == 'full' && node_is_page($node)) {
// Breadcrumb navigation. l() escapes title, so we should not escape !name.
drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => format_username($node))), 'blog/' . $node->uid)));
}
return $node;
}
/**
* Implements hook_node_view().
*/
function blog_node_view($node, $view_mode) {
if ($view_mode != 'rss') {
if ($node->type == 'blog' && (arg(0) != 'blog' || arg(1) != $node->uid)) {
// This goes to l(), which escapes !username in both title and attributes.
$links['blog_usernames_blog'] = array(
'title' => t("!username's blog", array('!username' => format_username($node))),
'href' => "blog/$node->uid",
'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => format_username($node)))),
);
$node->content['links']['blog'] = array(
'#theme' => 'links__node__blog',
'#links' => $links,
'#attributes' => array('class' => array('links', 'inline')),
);
}
}
}
/**
* Implements hook_menu().
*/
function blog_menu() {
$items['blog'] = array(
'title' => 'Blogs',
'page callback' => 'blog_page_last',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
'file' => 'blog.pages.inc',
);
$items['blog/%user_uid_optional'] = array(
'title' => 'My blog',
'page callback' => 'blog_page_user',
'page arguments' => array(1),
'access callback' => 'blog_page_user_access',
'access arguments' => array(1),
'file' => 'blog.pages.inc',
);
$items['blog/%user/feed'] = array(
'title' => 'Blogs',
'page callback' => 'blog_feed_user',
'page arguments' => array(1),
'access callback' => 'blog_page_user_access',
'access arguments' => array(1),
'type' => MENU_CALLBACK,
'file' => 'blog.pages.inc',
);
$items['blog/feed'] = array(
'title' => 'Blogs',
'page callback' => 'blog_feed_last',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'blog.pages.inc',
);
return $items;
}
/**
* Implements hook_menu_local_tasks_alter().
*/
function blog_menu_local_tasks_alter(&$data, $router_item, $root_path) {
global $user;
// Add action link to 'node/add/blog' on 'blog' page.
if ($root_path == 'blog') {
$item = menu_get_item('node/add/blog');
if ($item['access']) {
$item['title'] = t('Create new blog entry');
$data['actions']['output'][] = array(
'#theme' => 'menu_local_action',
'#link' => $item,
);
}
}
// Provide a helper action link to the author on the 'blog/%' page.
elseif ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) {
$data['actions']['output']['blog'] = array(
'#theme' => 'menu_local_action',
);
if (user_access('create blog content')) {
$data['actions']['output']['blog']['#link']['title'] = t('Post new blog entry.');
$data['actions']['output']['blog']['#link']['href'] = 'node/add/blog';
}
else {
$data['actions']['output']['blog']['#link']['title'] = t('You are not allowed to post a new blog entry.');
}
}
}
/**
* Access callback for user blog pages.
*/
function blog_page_user_access($account) {
// The visitor must be able to access the site's content.
// For a blog to 'exist' the user must either be able to
// create new blog entries, or it must have existing posts.
return $account->uid && user_access('access content') && (user_access('create blog content', $account) || _blog_post_exists($account));
}
/**
* Helper function to determine if a user has blog posts already.
*/
function _blog_post_exists($account) {
return (bool)db_select('node', 'n')
->fields('n', array('nid'))
->condition('type', 'blog')
->condition('uid', $account->uid)
->condition('status', 1)
->range(0, 1)
->addTag('node_access')
->execute()
->fetchField();
}
/**
* Implements hook_block_info().
*/
function blog_block_info() {
$block['recent']['info'] = t('Recent blog posts');
$block['recent']['properties']['administrative'] = TRUE;
return $block;
}
/**
* Implements hook_block_configure().
*/
function blog_block_configure($delta = '') {
if ($delta == 'recent') {
$form['blog_block_count'] = array(
'#type' => 'select',
'#title' => t('Number of recent blog posts to display'),
'#default_value' => variable_get('blog_block_count', 10),
'#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)),
);
return $form;
}
}
/**
* Implements hook_block_save().
*/
function blog_block_save($delta = '', $edit = array()) {
if ($delta == 'recent') {
variable_set('blog_block_count', $edit['blog_block_count']);
}
}
/**
* Implements hook_block_view().
*
* Displays the most recent 10 blog titles.
*/
function blog_block_view($delta = '') {
global $user;
if (user_access('access content')) {
$result = db_select('node', 'n')
->fields('n', array('nid', 'title', 'created'))
->condition('type', 'blog')
->condition('status', 1)
->orderBy('created', 'DESC')
->range(0, variable_get('blog_block_count', 10))
->addTag('node_access')
->execute();
if ($node_title_list = node_title_list($result)) {
$block['subject'] = t('Recent blog posts');
$block['content']['blog_list'] = $node_title_list;
$block['content']['blog_more'] = array(
'#theme' => 'more_link',
'#url' => 'blog',
'#title' => t('Read the latest blog entries.'),
);
return $block;
}
}
}
/**
* Implements hook_rdf_mapping().
*/
function blog_rdf_mapping() {
return array(
array(
'type' => 'node',
'bundle' => 'blog',
'mapping' => array(
'rdftype' => array('sioc:Post', 'sioct:BlogPost'),
),
),
);
}
www-test-master-modules/modules/blog/blog.pages.inc 0000664 0000000 0000000 00000006646 12501305533 0022736 0 ustar 00root root 0000000 0000000 format_username($account))), PASS_THROUGH);
$build = array();
$query = db_select('node', 'n')->extend('PagerDefault');
$nids = $query
->fields('n', array('nid', 'sticky', 'created'))
->condition('type', 'blog')
->condition('uid', $account->uid)
->condition('status', 1)
->orderBy('sticky', 'DESC')
->orderBy('created', 'DESC')
->limit(variable_get('default_nodes_main', 10))
->addTag('node_access')
->execute()
->fetchCol();
if (!empty($nids)) {
$nodes = node_load_multiple($nids);
$build += node_view_multiple($nodes);
$build['pager'] = array(
'#theme' => 'pager',
'#weight' => 5,
);
}
else {
if ($account->uid == $user->uid) {
drupal_set_message(t('You have not created any blog entries.'));
}
else {
drupal_set_message(t('!author has not created any blog entries.', array('!author' => theme('username', array('account' => $account)))));
}
}
drupal_add_feed('blog/' . $account->uid . '/feed', t('RSS - !title', array('!title' => $title)));
return $build;
}
/**
* Menu callback; displays a Drupal page containing recent blog entries of all users.
*/
function blog_page_last() {
global $user;
$build = array();
$query = db_select('node', 'n')->extend('PagerDefault');
$nids = $query
->fields('n', array('nid', 'sticky', 'created'))
->condition('type', 'blog')
->condition('status', 1)
->orderBy('sticky', 'DESC')
->orderBy('created', 'DESC')
->limit(variable_get('default_nodes_main', 10))
->addTag('node_access')
->execute()
->fetchCol();
if (!empty($nids)) {
$nodes = node_load_multiple($nids);
$build += node_view_multiple($nodes);
$build['pager'] = array(
'#theme' => 'pager',
'#weight' => 5,
);
}
else {
drupal_set_message(t('No blog entries have been created.'));
}
drupal_add_feed('blog/feed', t('RSS - blogs'));
return $build;
}
/**
* Menu callback; displays an RSS feed containing recent blog entries of a given user.
*/
function blog_feed_user($account) {
$nids = db_select('node', 'n')
->fields('n', array('nid', 'created'))
->condition('type', 'blog')
->condition('uid', $account->uid)
->condition('status', 1)
->orderBy('created', 'DESC')
->range(0, variable_get('feed_default_items', 10))
->addTag('node_access')
->execute()
->fetchCol();
$channel['title'] = t("!name's blog", array('!name' => format_username($account)));
$channel['link'] = url('blog/' . $account->uid, array('absolute' => TRUE));
node_feed($nids, $channel);
}
/**
* Menu callback; displays an RSS feed containing recent blog entries of all users.
*/
function blog_feed_last() {
$nids = db_select('node', 'n')
->fields('n', array('nid', 'created'))
->condition('type', 'blog')
->condition('status', 1)
->orderBy('created', 'DESC')
->range(0, variable_get('feed_default_items', 10))
->addTag('node_access')
->execute()
->fetchCol();
$channel['title'] = t('!site_name blogs', array('!site_name' => variable_get('site_name', 'Drupal')));
$channel['link'] = url('blog', array('absolute' => TRUE));
node_feed($nids, $channel);
}
www-test-master-modules/modules/blog/blog.test 0000664 0000000 0000000 00000020446 12501305533 0022040 0 ustar 00root root 0000000 0000000 'Blog functionality',
'description' => 'Create, view, edit, delete, and change blog entries and verify its consistency in the database.',
'group' => 'Blog',
);
}
/**
* Enable modules and create users with specific permissions.
*/
function setUp() {
parent::setUp('blog');
// Create users.
$this->big_user = $this->drupalCreateUser(array('administer blocks'));
$this->own_user = $this->drupalCreateUser(array('create blog content', 'edit own blog content', 'delete own blog content'));
$this->any_user = $this->drupalCreateUser(array('create blog content', 'edit any blog content', 'delete any blog content', 'access administration pages'));
}
/**
* Confirm that the "You are not allowed to post a new blog entry." message
* shows up if a user submitted blog entries, has been denied that
* permission, and goes to the blog page.
*/
function testUnprivilegedUser() {
// Create a blog node for a user with no blog permissions.
$this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->big_user->uid));
$this->drupalLogin($this->big_user);
$this->drupalGet('blog/' . $this->big_user->uid);
$this->assertResponse(200);
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', 'Blog title was displayed');
$this->assertText(t('You are not allowed to post a new blog entry.'), 'No new entries can be posted without the right permission');
}
/**
* View the blog of a user with no blog entries as another user.
*/
function testBlogPageNoEntries() {
$this->drupalLogin($this->big_user);
$this->drupalGet('blog/' . $this->own_user->uid);
$this->assertResponse(200);
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', 'Blog title was displayed');
$this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), 'Users blog displayed with no entries');
}
/**
* Login users, create blog nodes, and test blog functionality through the admin and user interfaces.
*/
function testBlog() {
// Login the admin user.
$this->drupalLogin($this->big_user);
// Enable the recent blog block.
$edit = array();
$edit['blocks[blog_recent][region]'] = 'sidebar_second';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertResponse(200);
// Verify ability to change number of recent blog posts in block.
$edit = array();
$edit['blog_block_count'] = 5;
$this->drupalPost('admin/structure/block/manage/blog/recent/configure', $edit, t('Save block'));
$this->assertEqual(variable_get('blog_block_count', 10), 5, 'Number of recent blog posts changed.');
// Do basic tests for each user.
$this->doBasicTests($this->any_user, TRUE);
$this->doBasicTests($this->own_user, FALSE);
// Create another blog node for the any blog user.
$node = $this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->any_user->uid));
// Verify the own blog user only has access to the blog view node.
$this->verifyBlogs($this->any_user, $node, FALSE, 403);
// Create another blog node for the own blog user.
$node = $this->drupalCreateNode(array('type' => 'blog', 'uid' => $this->own_user->uid));
// Login the any blog user.
$this->drupalLogin($this->any_user);
// Verify the any blog user has access to all the blog nodes.
$this->verifyBlogs($this->own_user, $node, TRUE);
}
/**
* Run basic tests on the indicated user.
*
* @param object $user
* The logged in user.
* @param boolean $admin
* User has 'access administration pages' privilege.
*/
private function doBasicTests($user, $admin) {
// Login the user.
$this->drupalLogin($user);
// Create blog node.
$node = $this->drupalCreateNode(array('type' => 'blog'));
// Verify the user has access to all the blog nodes.
$this->verifyBlogs($user, $node, $admin);
// Create one more node to test the blog page with more than one node
$this->drupalCreateNode(array('type' => 'blog', 'uid' => $user->uid));
// Verify the blog links are displayed.
$this->verifyBlogLinks($user);
}
/**
* Verify the logged in user has the desired access to the various blog nodes.
*
* @param object $node_user
* The user who creates the node.
* @param object $node
* A node object.
* @param boolean $admin
* User has 'access administration pages' privilege.
* @param integer $response
* HTTP response code.
*/
private function verifyBlogs($node_user, $node, $admin, $response = 200) {
$response2 = ($admin) ? 200 : 403;
// View blog help node.
$this->drupalGet('admin/help/blog');
$this->assertResponse($response2);
if ($response2 == 200) {
$this->assertTitle(t('Blog | Drupal'), 'Blog help node was displayed');
$this->assertText(t('Blog'), 'Blog help node was displayed');
}
// Verify the blog block was displayed.
$this->drupalGet('');
$this->assertResponse(200);
$this->assertText(t('Recent blog posts'), 'Blog block was displayed');
// View blog node.
$this->drupalGet('node/' . $node->nid);
$this->assertResponse(200);
$this->assertTitle($node->title . ' | Drupal', 'Blog node was displayed');
$breadcrumb = array(
l(t('Home'), NULL),
l(t('Blogs'), 'blog'),
l(t("!name's blog", array('!name' => format_username($node_user))), 'blog/' . $node_user->uid),
);
$this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed');
// View blog edit node.
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertResponse($response);
if ($response == 200) {
$this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', 'Blog edit node was displayed');
}
if ($response == 200) {
// Edit blog node.
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = 'node/' . $node->nid;
$edit["body[$langcode][0][value]"] = $this->randomName(256);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit["title"])), 'Blog node was edited');
// Delete blog node.
$this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
$this->assertResponse($response);
$this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), 'Blog node was deleted');
}
}
/**
* Verify the blog links are displayed to the logged in user.
*
* @param object $user
* The logged in user.
*/
private function verifyBlogLinks($user) {
// Confirm blog entries link exists on the user page.
$this->drupalGet('user/' . $user->uid);
$this->assertResponse(200);
$this->assertText(t('View recent blog entries'), 'View recent blog entries link was displayed');
// Confirm the recent blog entries link goes to the user's blog page.
$this->clickLink('View recent blog entries');
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'View recent blog entries link target was correct');
// Confirm a blog page was displayed.
$this->drupalGet('blog');
$this->assertResponse(200);
$this->assertTitle('Blogs | Drupal', 'Blog page was displayed');
$this->assertText(t('Home'), 'Breadcrumbs were displayed');
$this->assertLink(t('Create new blog entry'));
// Confirm a blog page was displayed per user.
$this->drupalGet('blog/' . $user->uid);
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'User blog node was displayed');
// Confirm a blog feed was displayed.
$this->drupalGet('blog/feed');
$this->assertTitle(t('Drupal blogs'), 'Blog feed was displayed');
// Confirm a blog feed was displayed per user.
$this->drupalGet('blog/' . $user->uid . '/feed');
$this->assertTitle(t("@name's blog", array('@name' => format_username($user))), 'User blog feed was displayed');
}
}
www-test-master-modules/modules/book/ 0000775 0000000 0000000 00000000000 12501305533 0020215 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/book/book-all-books-block.tpl.php 0000664 0000000 0000000 00000001256 12501305533 0025433 0 ustar 00root root 0000000 0000000
$menu): ?>
www-test-master-modules/modules/book/book-rtl.css 0000664 0000000 0000000 00000000326 12501305533 0022461 0 ustar 00root root 0000000 0000000 /**
* @file
* Right-to-Left styling for the Book module.
*/
.book-navigation .menu {
padding: 1em 3em 0 0;
}
.book-navigation .page-previous {
float: right;
}
.book-navigation .page-up {
float: right;
}
www-test-master-modules/modules/book/book.admin.inc 0000664 0000000 0000000 00000022605 12501305533 0022736 0 ustar 00root root 0000000 0000000 $headers, 'rows' => $rows, 'empty' => t('No books available.')));
}
/**
* Form constructor for the book settings form.
*
* @see book_admin_settings_validate()
*
* @ingroup forms
*/
function book_admin_settings() {
$types = node_type_get_names();
$form['book_allowed_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Content types allowed in book outlines'),
'#default_value' => variable_get('book_allowed_types', array('book')),
'#options' => $types,
'#description' => t('Users with the %outline-perm permission can add all content types.', array('%outline-perm' => t('Administer book outlines'))),
'#required' => TRUE,
);
$form['book_child_type'] = array(
'#type' => 'radios',
'#title' => t('Content type for child pages'),
'#default_value' => variable_get('book_child_type', 'book'),
'#options' => $types,
'#required' => TRUE,
);
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
$form['#validate'][] = 'book_admin_settings_validate';
return system_settings_form($form);
}
/**
* Form validation handler for book_admin_settings().
*
* @see book_admin_settings_submit()
*/
function book_admin_settings_validate($form, &$form_state) {
$child_type = $form_state['values']['book_child_type'];
if (empty($form_state['values']['book_allowed_types'][$child_type])) {
form_set_error('book_child_type', t('The content type for the %add-child link must be one of those selected as an allowed book outline type.', array('%add-child' => t('Add child page'))));
}
}
/**
* Form constructor for administering a single book's hierarchy.
*
* @see book_admin_edit_submit()
*
* @param $node
* The node of the top-level page in the book.
*
* @see book_admin_edit_validate()
* @see book_admin_edit_submit()
* @ingroup forms
*/
function book_admin_edit($form, $form_state, $node) {
drupal_set_title($node->title);
$form['#node'] = $node;
_book_admin_table($node, $form);
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save book pages'),
);
return $form;
}
/**
* Form validation handler for book_admin_edit().
*
* Checks that the book has not been changed while using the form.
*
* @see book_admin_edit_submit()
*/
function book_admin_edit_validate($form, &$form_state) {
if ($form_state['values']['tree_hash'] != $form_state['values']['tree_current_hash']) {
form_set_error('', t('This book has been modified by another user, the changes could not be saved.'));
}
}
/**
* Form submission handler for book_admin_edit().
*
* This function takes care to save parent menu items before their children.
* Saving menu items in the incorrect order can break the menu tree.
*
* @see book_admin_edit_validate()
* @see menu_overview_form_submit()
*/
function book_admin_edit_submit($form, &$form_state) {
// Save elements in the same order as defined in post rather than the form.
// This ensures parents are updated before their children, preventing orphans.
$order = array_flip(array_keys($form_state['input']['table']));
$form['table'] = array_merge($order, $form['table']);
foreach (element_children($form['table']) as $key) {
if ($form['table'][$key]['#item']) {
$row = $form['table'][$key];
$values = $form_state['values']['table'][$key];
// Update menu item if moved.
if ($row['plid']['#default_value'] != $values['plid'] || $row['weight']['#default_value'] != $values['weight']) {
$row['#item']['plid'] = $values['plid'];
$row['#item']['weight'] = $values['weight'];
menu_link_save($row['#item']);
}
// Update the title if changed.
if ($row['title']['#default_value'] != $values['title']) {
$node = node_load($values['nid']);
$langcode = LANGUAGE_NONE;
$node->title = $values['title'];
$node->book['link_title'] = $values['title'];
$node->revision = 1;
$node->log = t('Title changed from %original to %current.', array('%original' => $node->title, '%current' => $values['title']));
node_save($node);
watchdog('content', 'book: updated %title.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid));
}
}
}
drupal_set_message(t('Updated book %title.', array('%title' => $form['#node']->title)));
}
/**
* Builds the table portion of the form for the book administration page.
*
* @param $node
* The node of the top-level page in the book.
* @param $form
* The form that is being modified, passed by reference.
*
* @see book_admin_edit()
*/
function _book_admin_table($node, &$form) {
$form['table'] = array(
'#theme' => 'book_admin_table',
'#tree' => TRUE,
);
$tree = book_menu_subtree_data($node->book);
$tree = array_shift($tree); // Do not include the book item itself.
if ($tree['below']) {
$hash = drupal_hash_base64(serialize($tree['below']));
// Store the hash value as a hidden form element so that we can detect
// if another user changed the book hierarchy.
$form['tree_hash'] = array(
'#type' => 'hidden',
'#default_value' => $hash,
);
$form['tree_current_hash'] = array(
'#type' => 'value',
'#value' => $hash,
);
_book_admin_table_tree($tree['below'], $form['table']);
}
}
/**
* Helps build the main table in the book administration page form.
*
* @param $tree
* A subtree of the book menu hierarchy.
* @param $form
* The form that is being modified, passed by reference.
*
* @return
* The modified form array.
*
* @see book_admin_edit()
*/
function _book_admin_table_tree($tree, &$form) {
// The delta must be big enough to give each node a distinct value.
$count = count($tree);
$delta = ($count < 30) ? 15 : intval($count / 2) + 1;
foreach ($tree as $data) {
$form['book-admin-' . $data['link']['nid']] = array(
'#item' => $data['link'],
'nid' => array('#type' => 'value', '#value' => $data['link']['nid']),
'depth' => array('#type' => 'value', '#value' => $data['link']['depth']),
'href' => array('#type' => 'value', '#value' => $data['link']['href']),
'title' => array(
'#type' => 'textfield',
'#default_value' => $data['link']['link_title'],
'#maxlength' => 255,
'#size' => 40,
),
'weight' => array(
'#type' => 'weight',
'#default_value' => $data['link']['weight'],
'#delta' => max($delta, abs($data['link']['weight'])),
'#title' => t('Weight for @title', array('@title' => $data['link']['title'])),
'#title_display' => 'invisible',
),
'plid' => array(
'#type' => 'hidden',
'#default_value' => $data['link']['plid'],
),
'mlid' => array(
'#type' => 'hidden',
'#default_value' => $data['link']['mlid'],
),
);
if ($data['below']) {
_book_admin_table_tree($data['below'], $form);
}
}
return $form;
}
/**
* Returns HTML for a book administration form.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @see book_admin_table()
* @ingroup themeable
*/
function theme_book_admin_table($variables) {
$form = $variables['form'];
drupal_add_tabledrag('book-outline', 'match', 'parent', 'book-plid', 'book-plid', 'book-mlid', TRUE, MENU_MAX_DEPTH - 2);
drupal_add_tabledrag('book-outline', 'order', 'sibling', 'book-weight');
$header = array(t('Title'), t('Weight'), t('Parent'), array('data' => t('Operations'), 'colspan' => '3'));
$rows = array();
$destination = drupal_get_destination();
$access = user_access('administer nodes');
foreach (element_children($form) as $key) {
$nid = $form[$key]['nid']['#value'];
$href = $form[$key]['href']['#value'];
// Add special classes to be used with tabledrag.js.
$form[$key]['plid']['#attributes']['class'] = array('book-plid');
$form[$key]['mlid']['#attributes']['class'] = array('book-mlid');
$form[$key]['weight']['#attributes']['class'] = array('book-weight');
$data = array(
theme('indentation', array('size' => $form[$key]['depth']['#value'] - 2)) . drupal_render($form[$key]['title']),
drupal_render($form[$key]['weight']),
drupal_render($form[$key]['plid']) . drupal_render($form[$key]['mlid']),
l(t('view'), $href),
$access ? l(t('edit'), 'node/' . $nid . '/edit', array('query' => $destination)) : ' ',
$access ? l(t('delete'), 'node/' . $nid . '/delete', array('query' => $destination) ) : ' ',
);
$row = array('data' => $data);
if (isset($form[$key]['#attributes'])) {
$row = array_merge($row, $form[$key]['#attributes']);
}
$row['class'][] = 'draggable';
$rows[] = $row;
}
return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'book-outline'), 'empty' => t('No book content available.')));
}
www-test-master-modules/modules/book/book.css 0000664 0000000 0000000 00000002014 12501305533 0021656 0 ustar 00root root 0000000 0000000 /**
* @file
* Styling for the Book module.
*/
.book-navigation .menu {
border-top: 1px solid #888;
padding: 1em 0 0 3em; /* LTR */
}
.book-navigation .page-links {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
text-align: center;
padding: 0.5em;
}
.book-navigation .page-previous {
text-align: left;
width: 42%;
display: block;
float: left; /* LTR */
}
.book-navigation .page-up {
margin: 0 5%;
width: 4%;
display: block;
float: left; /* LTR */
}
.book-navigation .page-next {
text-align: right;
width: 42%;
display: block;
float: right;
}
#book-outline {
min-width: 56em;
}
.book-outline-form .form-item {
margin-top: 0;
margin-bottom: 0;
}
html.js #edit-book-pick-book {
display: none;
}
.form-item-book-bid .description {
clear: both;
}
#book-admin-edit select {
margin-right: 24px;
}
#book-admin-edit select.progress-disabled {
margin-right: 0;
}
#book-admin-edit tr.ajax-new-content {
background-color: #ffd;
}
#book-admin-edit .form-item {
float: left;
}
www-test-master-modules/modules/book/book.info 0000664 0000000 0000000 00000000543 12501305533 0022026 0 ustar 00root root 0000000 0000000 name = Book
description = Allows users to create and organize related content in an outline.
package = Core
version = VERSION
core = 7.x
files[] = book.test
configure = admin/content/book/settings
stylesheets[all][] = book.css
; Information added by Drupal.org packaging script on 2014-11-19
version = "7.34"
project = "drupal"
datestamp = "1416429488"
www-test-master-modules/modules/book/book.install 0000664 0000000 0000000 00000004442 12501305533 0022543 0 ustar 00root root 0000000 0000000 condition('module', 'book')
->execute();
menu_cache_clear_all();
}
/**
* Creates the book content type.
*/
function _book_install_type_create() {
// Create an additional node type.
$book_node_type = array(
'type' => 'book',
'name' => t('Book page'),
'base' => 'node_content',
'description' => t('Books have a built-in hierarchical navigation. Use for handbooks or tutorials.'),
'custom' => 1,
'modified' => 1,
'locked' => 0,
);
$book_node_type = node_type_set_defaults($book_node_type);
node_type_save($book_node_type);
node_add_body_field($book_node_type);
// Default to not promoted.
variable_set('node_options_book', array('status'));
// Use this default type for adding content to books.
variable_set('book_allowed_types', array('book'));
variable_set('book_child_type', 'book');
}
/**
* Implements hook_schema().
*/
function book_schema() {
$schema['book'] = array(
'description' => 'Stores book outline information. Uniquely connects each node in the outline to a link in {menu_links}',
'fields' => array(
'mlid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => "The book page's {menu_links}.mlid.",
),
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => "The book page's {node}.nid.",
),
'bid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => "The book ID is the {book}.nid of the top-level page.",
),
),
'primary key' => array('mlid'),
'unique keys' => array(
'nid' => array('nid'),
),
'indexes' => array(
'bid' => array('bid'),
),
);
return $schema;
}
www-test-master-modules/modules/book/book.js 0000664 0000000 0000000 00000001115 12501305533 0021503 0 ustar 00root root 0000000 0000000 /**
* @file
* Javascript behaviors for the Book module.
*/
(function ($) {
Drupal.behaviors.bookFieldsetSummaries = {
attach: function (context) {
$('fieldset.book-outline-form', context).drupalSetSummary(function (context) {
var $select = $('.form-item-book-bid select');
var val = $select.val();
if (val === '0') {
return Drupal.t('Not in book');
}
else if (val === 'new') {
return Drupal.t('New book');
}
else {
return Drupal.checkPlain($select.find(':selected').text());
}
});
}
};
})(jQuery);
www-test-master-modules/modules/book/book.module 0000664 0000000 0000000 00000135507 12501305533 0022371 0 ustar 00root root 0000000 0000000 ' . t('About') . '';
$output .= '
' . t('The Book module is used for creating structured, multi-page content, such as site resource guides, manuals, and wikis. It allows you to create content that has chapters, sections, subsections, or any similarly-tiered structure. For more information, see the online handbook entry for Book module.', array('@book' => 'http://drupal.org/documentation/modules/book/')) . '
';
$output .= '
' . t('Uses') . '
';
$output .= '
';
$output .= '
' . t('Adding and managing book content') . '
';
$output .= '
' . t('You can assign separate permissions for creating, editing, and deleting book content, as well as adding content to books, and creating new books. Users with the Administer book outlines permission can add any type of content to a book by selecting the appropriate book outline while editing the content. They can also view a list of all books, and edit and rearrange section titles on the Book administration page.', array('@admin-book' => url('admin/content/book'))) . '
';
$output .= '
' . t('Book navigation') . '
';
$output .= '
' . t("Book pages have a default book-specific navigation block. This navigation block contains links that lead to the previous and next pages in the book, and to the level above the current page in the book's structure. This block can be enabled on the Blocks administration page. For book pages to show up in the book navigation, they must be added to a book outline.", array('@admin-block' => url('admin/structure/block'))) . '
';
$output .= '
' . t('Collaboration') . '
';
$output .= '
' . t('Books can be created collaboratively, as they allow users with appropriate permissions to add pages into existing books, and add those pages to a custom table of contents menu.') . '
';
$output .= '
' . t('Printing books') . '
';
$output .= '
' . t("Users with the View printer-friendly books permission can select the printer-friendly version link visible at the bottom of a book page's content to generate a printer-friendly display of the page and all of its subsections.") . '
';
$output .= '
';
return $output;
case 'admin/content/book':
return '
' . t('The book module offers a means to organize a collection of related content pages, collectively known as a book. When viewed, this content automatically displays links to adjacent book pages, providing a simple navigation system for creating and reviewing structured content.') . '
';
case 'node/%/outline':
return '
' . t('The outline feature allows you to include pages in the Book hierarchy, as well as move them within the hierarchy or to reorder an entire book.', array('@book' => url('book'), '@book-admin' => url('admin/content/book'))) . '
';
}
}
/**
* Implements hook_theme().
*/
function book_theme() {
return array(
'book_navigation' => array(
'variables' => array('book_link' => NULL),
'template' => 'book-navigation',
),
'book_export_html' => array(
'variables' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL),
'template' => 'book-export-html',
),
'book_admin_table' => array(
'render element' => 'form',
),
'book_title_link' => array(
'variables' => array('link' => NULL),
),
'book_all_books_block' => array(
'render element' => 'book_menus',
'template' => 'book-all-books-block',
),
'book_node_export_html' => array(
'variables' => array('node' => NULL, 'children' => NULL),
'template' => 'book-node-export-html',
),
);
}
/**
* Implements hook_permission().
*/
function book_permission() {
return array(
'administer book outlines' => array(
'title' => t('Administer book outlines'),
),
'create new books' => array(
'title' => t('Create new books'),
),
'add content to books' => array(
'title' => t('Add content and child pages to books'),
),
'access printer-friendly version' => array(
'title' => t('View printer-friendly books'),
'description' => t('View a book page and all of its sub-pages as a single document for ease of printing. Can be performance heavy.'),
),
);
}
/**
* Adds relevant book links to the node's links.
*
* @param $node
* The book page node to add links to.
* @param $view_mode
* The view mode of the node.
*/
function book_node_view_link($node, $view_mode) {
$links = array();
if (isset($node->book['depth'])) {
if ($view_mode == 'full' && node_is_page($node)) {
$child_type = variable_get('book_child_type', 'book');
if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) {
$links['book_add_child'] = array(
'title' => t('Add child page'),
'href' => 'node/add/' . str_replace('_', '-', $child_type),
'query' => array('parent' => $node->book['mlid']),
);
}
if (user_access('access printer-friendly version')) {
$links['book_printer'] = array(
'title' => t('Printer-friendly version'),
'href' => 'book/export/html/' . $node->nid,
'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))
);
}
}
}
if (!empty($links)) {
$node->content['links']['book'] = array(
'#theme' => 'links__node__book',
'#links' => $links,
'#attributes' => array('class' => array('links', 'inline')),
);
}
}
/**
* Implements hook_menu().
*/
function book_menu() {
$items['admin/content/book'] = array(
'title' => 'Books',
'description' => "Manage your site's book outlines.",
'page callback' => 'book_admin_overview',
'access arguments' => array('administer book outlines'),
'type' => MENU_LOCAL_TASK,
'file' => 'book.admin.inc',
);
$items['admin/content/book/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/content/book/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('book_admin_settings'),
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
'weight' => 8,
'file' => 'book.admin.inc',
);
$items['admin/content/book/%node'] = array(
'title' => 'Re-order book pages and change titles',
'page callback' => 'drupal_get_form',
'page arguments' => array('book_admin_edit', 3),
'access callback' => '_book_outline_access',
'access arguments' => array(3),
'type' => MENU_CALLBACK,
'file' => 'book.admin.inc',
);
$items['book'] = array(
'title' => 'Books',
'page callback' => 'book_render',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
'file' => 'book.pages.inc',
);
$items['book/export/%/%'] = array(
'page callback' => 'book_export',
'page arguments' => array(2, 3),
'access arguments' => array('access printer-friendly version'),
'type' => MENU_CALLBACK,
'file' => 'book.pages.inc',
);
$items['node/%node/outline'] = array(
'title' => 'Outline',
'page callback' => 'book_outline',
'page arguments' => array(1),
'access callback' => '_book_outline_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
'file' => 'book.pages.inc',
);
$items['node/%node/outline/remove'] = array(
'title' => 'Remove from outline',
'page callback' => 'drupal_get_form',
'page arguments' => array('book_remove_form', 1),
'access callback' => '_book_outline_remove_access',
'access arguments' => array(1),
'file' => 'book.pages.inc',
);
return $items;
}
/**
* Access callback: Determines if the outline tab is accessible.
*
* @param $node
* The node whose outline tab is to be viewed.
*/
function _book_outline_access($node) {
return user_access('administer book outlines') && node_access('view', $node);
}
/**
* Access callback: Determines if the user can remove nodes from the outline.
*
* @param $node
* The node to remove from the outline.
*
* @see book_menu()
*/
function _book_outline_remove_access($node) {
return _book_node_is_removable($node) && _book_outline_access($node);
}
/**
* Determines if a node can be removed from the book.
*
* A node can be removed from a book if it is actually in a book and it either
* is not a top-level page or is a top-level page with no children.
*
* @param $node
* The node to remove from the outline.
*/
function _book_node_is_removable($node) {
return (!empty($node->book['bid']) && (($node->book['bid'] != $node->nid) || !$node->book['has_children']));
}
/**
* Implements hook_admin_paths().
*/
function book_admin_paths() {
if (variable_get('node_admin_theme')) {
$paths = array(
'node/*/outline' => TRUE,
'node/*/outline/remove' => TRUE,
);
return $paths;
}
}
/**
* Implements hook_entity_info_alter().
*/
function book_entity_info_alter(&$info) {
// Add the 'Print' view mode for nodes.
$info['node']['view modes'] += array(
'print' => array(
'label' => t('Print'),
'custom settings' => FALSE,
),
);
}
/**
* Implements hook_block_info().
*/
function book_block_info() {
$block = array();
$block['navigation']['info'] = t('Book navigation');
$block['navigation']['cache'] = DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;
return $block;
}
/**
* Implements hook_block_view().
*
* Displays the book table of contents in a block when the current page is a
* single-node view of a book node.
*/
function book_block_view($delta = '') {
$block = array();
$current_bid = 0;
if ($node = menu_get_object()) {
$current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
}
if (variable_get('book_block_mode', 'all pages') == 'all pages') {
$block['subject'] = t('Book navigation');
$book_menus = array();
$pseudo_tree = array(0 => array('below' => FALSE));
foreach (book_get_books() as $book_id => $book) {
if ($book['bid'] == $current_bid) {
// If the current page is a node associated with a book, the menu
// needs to be retrieved.
$book_menus[$book_id] = menu_tree_output(menu_tree_all_data($node->book['menu_name'], $node->book));
}
else {
// Since we know we will only display a link to the top node, there
// is no reason to run an additional menu tree query for each book.
$book['in_active_trail'] = FALSE;
// Check whether user can access the book link.
$book_node = node_load($book['nid']);
$book['access'] = node_access('view', $book_node);
$pseudo_tree[0]['link'] = $book;
$book_menus[$book_id] = menu_tree_output($pseudo_tree);
}
}
$book_menus['#theme'] = 'book_all_books_block';
$block['content'] = $book_menus;
}
elseif ($current_bid) {
// Only display this block when the user is browsing a book.
$select = db_select('node', 'n')
->fields('n', array('title'))
->condition('n.nid', $node->book['bid'])
->addTag('node_access');
$title = $select->execute()->fetchField();
// Only show the block if the user has view access for the top-level node.
if ($title) {
$tree = menu_tree_all_data($node->book['menu_name'], $node->book);
// There should only be one element at the top level.
$data = array_shift($tree);
$block['subject'] = theme('book_title_link', array('link' => $data['link']));
$block['content'] = ($data['below']) ? menu_tree_output($data['below']) : '';
}
}
return $block;
}
/**
* Implements hook_block_configure().
*/
function book_block_configure($delta = '') {
$block = array();
$options = array(
'all pages' => t('Show block on all pages'),
'book pages' => t('Show block only on book pages'),
);
$form['book_block_mode'] = array(
'#type' => 'radios',
'#title' => t('Book navigation block display'),
'#options' => $options,
'#default_value' => variable_get('book_block_mode', 'all pages'),
'#description' => t("If Show block on all pages is selected, the block will contain the automatically generated menus for all of the site's books. If Show block only on book pages is selected, the block will contain only the one menu corresponding to the current page's book. In this case, if the current page is not in a book, no block will be displayed. The Page specific visibility settings or other visibility settings can be used in addition to selectively display this block."),
);
return $form;
}
/**
* Implements hook_block_save().
*/
function book_block_save($delta = '', $edit = array()) {
$block = array();
variable_set('book_block_mode', $edit['book_block_mode']);
}
/**
* Returns HTML for a link to a book title when used as a block title.
*
* @param $variables
* An associative array containing:
* - link: An array containing title, href and options for the link.
*
* @ingroup themeable
*/
function theme_book_title_link($variables) {
$link = $variables['link'];
$link['options']['attributes']['class'] = array('book-title');
return l($link['title'], $link['href'], $link['options']);
}
/**
* Returns an array of all books.
*
* This list may be used for generating a list of all the books, or for building
* the options for a form select.
*
* @return
* An array of all books.
*/
function book_get_books() {
$all_books = &drupal_static(__FUNCTION__);
if (!isset($all_books)) {
$all_books = array();
$nids = db_query("SELECT DISTINCT(bid) FROM {book}")->fetchCol();
if ($nids) {
$query = db_select('book', 'b', array('fetch' => PDO::FETCH_ASSOC));
$query->join('node', 'n', 'b.nid = n.nid');
$query->join('menu_links', 'ml', 'b.mlid = ml.mlid');
$query->addField('n', 'type', 'type');
$query->addField('n', 'title', 'title');
$query->fields('b');
$query->fields('ml');
$query->condition('n.nid', $nids, 'IN');
$query->condition('n.status', 1);
$query->orderBy('ml.weight');
$query->orderBy('ml.link_title');
$query->addTag('node_access');
$result2 = $query->execute();
foreach ($result2 as $link) {
$link['href'] = $link['link_path'];
$link['options'] = unserialize($link['options']);
$all_books[$link['bid']] = $link;
}
}
}
return $all_books;
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for node_form().
*
* Adds the book fieldset to the node form.
*
* @see book_pick_book_nojs_submit()
*/
function book_form_node_form_alter(&$form, &$form_state, $form_id) {
$node = $form['#node'];
$access = user_access('administer book outlines');
if (!$access) {
if (user_access('add content to books') && ((!empty($node->book['mlid']) && !empty($node->nid)) || book_type_is_allowed($node->type))) {
// Already in the book hierarchy, or this node type is allowed.
$access = TRUE;
}
}
if ($access) {
_book_add_form_elements($form, $form_state, $node);
// Since the "Book" dropdown can't trigger a form submission when
// JavaScript is disabled, add a submit button to do that. book.css hides
// this button when JavaScript is enabled.
$form['book']['pick-book'] = array(
'#type' => 'submit',
'#value' => t('Change book (update list of parents)'),
'#submit' => array('book_pick_book_nojs_submit'),
'#weight' => 20,
);
}
}
/**
* Form submission handler for node_form().
*
* This handler is run when JavaScript is disabled. It triggers the form to
* rebuild so that the "Parent item" options are changed to reflect the newly
* selected book. When JavaScript is enabled, the submit button that triggers
* this handler is hidden, and the "Book" dropdown directly triggers the
* book_form_update() Ajax callback instead.
*
* @see book_form_update()
* @see book_form_node_form_alter()
*/
function book_pick_book_nojs_submit($form, &$form_state) {
$form_state['node']->book = $form_state['values']['book'];
$form_state['rebuild'] = TRUE;
}
/**
* Builds the parent selection form element for the node form or outline tab.
*
* This function is also called when generating a new set of options during the
* Ajax callback, so an array is returned that can be used to replace an
* existing form element.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* A parent selection form element.
*/
function _book_parent_select($book_link) {
if (variable_get('menu_override_parent_selector', FALSE)) {
return array();
}
// Offer a message or a drop-down to choose a different parent page.
$form = array(
'#type' => 'hidden',
'#value' => -1,
'#prefix' => '
',
'#suffix' => '
',
);
if ($book_link['nid'] === $book_link['bid']) {
// This is a book - at the top level.
if ($book_link['original_bid'] === $book_link['bid']) {
$form['#prefix'] .= '' . t('This is the top-level page in this book.') . '';
}
else {
$form['#prefix'] .= '' . t('This will be the top-level page in this book.') . '';
}
}
elseif (!$book_link['bid']) {
$form['#prefix'] .= '' . t('No book selected.') . '';
}
else {
$form = array(
'#type' => 'select',
'#title' => t('Parent item'),
'#default_value' => $book_link['plid'],
'#description' => t('The parent page in the book. The maximum depth for a book and all child pages is !maxdepth. Some pages in the selected book may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
'#options' => book_toc($book_link['bid'], $book_link['parent_depth_limit'], array($book_link['mlid'])),
'#attributes' => array('class' => array('book-title-select')),
'#prefix' => '
',
'#suffix' => '
',
);
}
return $form;
}
/**
* Builds the common elements of the book form for the node and outline forms.
*
* @param $node
* The node whose form is being viewed.
*/
function _book_add_form_elements(&$form, &$form_state, $node) {
// If the form is being processed during the Ajax callback of our book bid
// dropdown, then $form_state will hold the value that was selected.
if (isset($form_state['values']['book'])) {
$node->book = $form_state['values']['book'];
}
$form['book'] = array(
'#type' => 'fieldset',
'#title' => t('Book outline'),
'#weight' => 10,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('book-outline-form'),
),
'#attached' => array(
'js' => array(drupal_get_path('module', 'book') . '/book.js'),
),
'#tree' => TRUE,
);
foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
$form['book'][$key] = array(
'#type' => 'value',
'#value' => $node->book[$key],
);
}
$form['book']['plid'] = _book_parent_select($node->book);
// @see _book_admin_table_tree(). The weight may be larger than 15.
$form['book']['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => $node->book['weight'],
'#delta' => max(15, abs($node->book['weight'])),
'#weight' => 5,
'#description' => t('Pages at a given level are ordered first by weight and then by title.'),
);
$options = array();
$nid = isset($node->nid) ? $node->nid : 'new';
if (isset($node->nid) && ($nid == $node->book['original_bid']) && ($node->book['parent_depth_limit'] == 0)) {
// This is the top level node in a maximum depth book and thus cannot be moved.
$options[$node->nid] = $node->title;
}
else {
foreach (book_get_books() as $book) {
$options[$book['nid']] = $book['title'];
}
}
if (user_access('create new books') && ($nid == 'new' || ($nid != $node->book['original_bid']))) {
// The node can become a new book, if it is not one already.
$options = array($nid => '<' . t('create a new book') . '>') + $options;
}
if (!$node->book['mlid']) {
// The node is not currently in the hierarchy.
$options = array(0 => '<' . t('none') . '>') + $options;
}
// Add a drop-down to select the destination book.
$form['book']['bid'] = array(
'#type' => 'select',
'#title' => t('Book'),
'#default_value' => $node->book['bid'],
'#options' => $options,
'#access' => (bool) $options,
'#description' => t('Your page will be a part of the selected book.'),
'#weight' => -5,
'#attributes' => array('class' => array('book-title-select')),
'#ajax' => array(
'callback' => 'book_form_update',
'wrapper' => 'edit-book-plid-wrapper',
'effect' => 'fade',
'speed' => 'fast',
),
);
}
/**
* Renders a new parent page select element when the book selection changes.
*
* This function is called via Ajax when the selected book is changed on a node
* or book outline form.
*
* @return
* The rendered parent page select element.
*/
function book_form_update($form, $form_state) {
return $form['book']['plid'];
}
/**
* Handles additions and updates to the book outline.
*
* This common helper function performs all additions and updates to the book
* outline through node addition, node editing, node deletion, or the outline
* tab.
*
* @param $node
* The node that is being saved, added, deleted, or moved.
*
* @return
* TRUE if the menu link was saved; FALSE otherwise.
*/
function _book_update_outline($node) {
if (empty($node->book['bid'])) {
return FALSE;
}
$new = empty($node->book['mlid']);
$node->book['link_path'] = 'node/' . $node->nid;
$node->book['link_title'] = $node->title;
$node->book['parent_mismatch'] = FALSE; // The normal case.
if ($node->book['bid'] == $node->nid) {
$node->book['plid'] = 0;
$node->book['menu_name'] = book_menu_name($node->nid);
}
else {
// Check in case the parent is not is this book; the book takes precedence.
if (!empty($node->book['plid'])) {
$parent = db_query("SELECT * FROM {book} WHERE mlid = :mlid", array(
':mlid' => $node->book['plid'],
))->fetchAssoc();
}
if (empty($node->book['plid']) || !$parent || $parent['bid'] != $node->book['bid']) {
$node->book['plid'] = db_query("SELECT mlid FROM {book} WHERE nid = :nid", array(
':nid' => $node->book['bid'],
))->fetchField();
$node->book['parent_mismatch'] = TRUE; // Likely when JS is disabled.
}
}
if (menu_link_save($node->book)) {
if ($new) {
// Insert new.
db_insert('book')
->fields(array(
'nid' => $node->nid,
'mlid' => $node->book['mlid'],
'bid' => $node->book['bid'],
))
->execute();
// Reset the cache of stored books.
drupal_static_reset('book_get_books');
}
else {
if ($node->book['bid'] != db_query("SELECT bid FROM {book} WHERE nid = :nid", array(
':nid' => $node->nid,
))->fetchField()) {
// Update the bid for this page and all children.
book_update_bid($node->book);
// Reset the cache of stored books.
drupal_static_reset('book_get_books');
}
}
return TRUE;
}
// Failed to save the menu link.
return FALSE;
}
/**
* Updates the book ID of a page and its children when it moves to a new book.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*/
function book_update_bid($book_link) {
$query = db_select('menu_links');
$query->addField('menu_links', 'mlid');
for ($i = 1; $i <= MENU_MAX_DEPTH && $book_link["p$i"]; $i++) {
$query->condition("p$i", $book_link["p$i"]);
}
$mlids = $query->execute()->fetchCol();
if ($mlids) {
db_update('book')
->fields(array('bid' => $book_link['bid']))
->condition('mlid', $mlids, 'IN')
->execute();
}
}
/**
* Gets the book menu tree for a page and returns it as a linear array.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* A linear array of menu links in the order that the links are shown in the
* menu, so the previous and next pages are the elements before and after the
* element corresponding to the current node. The children of the current node
* (if any) will come immediately after it in the array, and links will only
* be fetched as deep as one level deeper than $book_link.
*/
function book_get_flat_menu($book_link) {
$flat = &drupal_static(__FUNCTION__, array());
if (!isset($flat[$book_link['mlid']])) {
// Call menu_tree_all_data() to take advantage of the menu system's caching.
$tree = menu_tree_all_data($book_link['menu_name'], $book_link, $book_link['depth'] + 1);
$flat[$book_link['mlid']] = array();
_book_flatten_menu($tree, $flat[$book_link['mlid']]);
}
return $flat[$book_link['mlid']];
}
/**
* Recursively converts a tree of menu links to a flat array.
*
* @param $tree
* A tree of menu links in an array.
* @param $flat
* A flat array of the menu links from $tree, passed by reference.
*
* @see book_get_flat_menu().
*/
function _book_flatten_menu($tree, &$flat) {
foreach ($tree as $data) {
if (!$data['link']['hidden']) {
$flat[$data['link']['mlid']] = $data['link'];
if ($data['below']) {
_book_flatten_menu($data['below'], $flat);
}
}
}
}
/**
* Fetches the menu link for the previous page of the book.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* A fully loaded menu link for the page before the one represented in
* $book_link.
*/
function book_prev($book_link) {
// If the parent is zero, we are at the start of a book.
if ($book_link['plid'] == 0) {
return NULL;
}
$flat = book_get_flat_menu($book_link);
// Assigning the array to $flat resets the array pointer for use with each().
$curr = NULL;
do {
$prev = $curr;
list($key, $curr) = each($flat);
} while ($key && $key != $book_link['mlid']);
if ($key == $book_link['mlid']) {
// The previous page in the book may be a child of the previous visible link.
if ($prev['depth'] == $book_link['depth'] && $prev['has_children']) {
// The subtree will have only one link at the top level - get its data.
$tree = book_menu_subtree_data($prev);
$data = array_shift($tree);
// The link of interest is the last child - iterate to find the deepest one.
while ($data['below']) {
$data = end($data['below']);
}
return $data['link'];
}
else {
return $prev;
}
}
}
/**
* Fetches the menu link for the next page of the book.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* A fully loaded menu link for the page after the one represented in
* $book_link.
*/
function book_next($book_link) {
$flat = book_get_flat_menu($book_link);
// Assigning the array to $flat resets the array pointer for use with each().
do {
list($key, $curr) = each($flat);
}
while ($key && $key != $book_link['mlid']);
if ($key == $book_link['mlid']) {
return current($flat);
}
}
/**
* Formats the menu links for the child pages of the current page.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* HTML for the links to the child pages of the current page.
*/
function book_children($book_link) {
$flat = book_get_flat_menu($book_link);
$children = array();
if ($book_link['has_children']) {
// Walk through the array until we find the current page.
do {
$link = array_shift($flat);
}
while ($link && ($link['mlid'] != $book_link['mlid']));
// Continue though the array and collect the links whose parent is this page.
while (($link = array_shift($flat)) && $link['plid'] == $book_link['mlid']) {
$data['link'] = $link;
$data['below'] = '';
$children[] = $data;
}
}
if ($children) {
$elements = menu_tree_output($children);
return drupal_render($elements);
}
return '';
}
/**
* Generates the corresponding menu name from a book ID.
*
* @param $bid
* The book ID for which to make a menu name.
*
* @return
* The menu name.
*/
function book_menu_name($bid) {
return 'book-toc-' . $bid;
}
/**
* Implements hook_node_load().
*/
function book_node_load($nodes, $types) {
$result = db_query("SELECT * FROM {book} b INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE b.nid IN (:nids)", array(':nids' => array_keys($nodes)), array('fetch' => PDO::FETCH_ASSOC));
foreach ($result as $record) {
$nodes[$record['nid']]->book = $record;
$nodes[$record['nid']]->book['href'] = $record['link_path'];
$nodes[$record['nid']]->book['title'] = $record['link_title'];
$nodes[$record['nid']]->book['options'] = unserialize($record['options']);
}
}
/**
* Implements hook_node_view().
*/
function book_node_view($node, $view_mode) {
if ($view_mode == 'full') {
if (!empty($node->book['bid']) && empty($node->in_preview)) {
$node->content['book_navigation'] = array(
'#markup' => theme('book_navigation', array('book_link' => $node->book)),
'#weight' => 100,
);
}
}
if ($view_mode != 'rss') {
book_node_view_link($node, $view_mode);
}
}
/**
* Implements hook_page_alter().
*
* Adds the book menu to the list of menus used to build the active trail when
* viewing a book page.
*/
function book_page_alter(&$page) {
if (($node = menu_get_object()) && !empty($node->book['bid'])) {
$active_menus = menu_get_active_menu_names();
$active_menus[] = $node->book['menu_name'];
menu_set_active_menu_names($active_menus);
}
}
/**
* Implements hook_node_presave().
*/
function book_node_presave($node) {
// Always save a revision for non-administrators.
if (!empty($node->book['bid']) && !user_access('administer nodes')) {
$node->revision = 1;
// The database schema requires a log message for every revision.
if (!isset($node->log)) {
$node->log = '';
}
}
// Make sure a new node gets a new menu link.
if (empty($node->nid)) {
$node->book['mlid'] = NULL;
}
}
/**
* Implements hook_node_insert().
*/
function book_node_insert($node) {
if (!empty($node->book['bid'])) {
if ($node->book['bid'] == 'new') {
// New nodes that are their own book.
$node->book['bid'] = $node->nid;
}
$node->book['nid'] = $node->nid;
$node->book['menu_name'] = book_menu_name($node->book['bid']);
_book_update_outline($node);
}
}
/**
* Implements hook_node_update().
*/
function book_node_update($node) {
if (!empty($node->book['bid'])) {
if ($node->book['bid'] == 'new') {
// New nodes that are their own book.
$node->book['bid'] = $node->nid;
}
$node->book['nid'] = $node->nid;
$node->book['menu_name'] = book_menu_name($node->book['bid']);
_book_update_outline($node);
}
}
/**
* Implements hook_node_delete().
*/
function book_node_delete($node) {
if (!empty($node->book['bid'])) {
if ($node->nid == $node->book['bid']) {
// Handle deletion of a top-level post.
$result = db_query("SELECT b.nid FROM {menu_links} ml INNER JOIN {book} b on b.mlid = ml.mlid WHERE ml.plid = :plid", array(
':plid' => $node->book['mlid']
));
foreach ($result as $child) {
$child_node = node_load($child->nid);
$child_node->book['bid'] = $child_node->nid;
_book_update_outline($child_node);
}
}
menu_link_delete($node->book['mlid']);
db_delete('book')
->condition('mlid', $node->book['mlid'])
->execute();
drupal_static_reset('book_get_books');
}
}
/**
* Implements hook_node_prepare().
*/
function book_node_prepare($node) {
// Prepare defaults for the add/edit form.
if (empty($node->book) && (user_access('add content to books') || user_access('administer book outlines'))) {
$node->book = array();
if (empty($node->nid) && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
// Handle "Add child page" links:
$parent = book_link_load($_GET['parent']);
if ($parent && $parent['access']) {
$node->book['bid'] = $parent['bid'];
$node->book['plid'] = $parent['mlid'];
$node->book['menu_name'] = $parent['menu_name'];
}
}
// Set defaults.
$node->book += _book_link_defaults(!empty($node->nid) ? $node->nid : 'new');
}
else {
if (isset($node->book['bid']) && !isset($node->book['original_bid'])) {
$node->book['original_bid'] = $node->book['bid'];
}
}
// Find the depth limit for the parent select.
if (isset($node->book['bid']) && !isset($node->book['parent_depth_limit'])) {
$node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
}
}
/**
* Finds the depth limit for items in the parent select.
*
* @param $book_link
* A fully loaded menu link that is part of the book hierarchy.
*
* @return
* The depth limit for items in the parent select.
*/
function _book_parent_depth_limit($book_link) {
return MENU_MAX_DEPTH - 1 - (($book_link['mlid'] && $book_link['has_children']) ? menu_link_children_relative_depth($book_link) : 0);
}
/**
* Implements hook_form_FORM_ID_alter() for node_delete_confirm().
*
* Alters the confirm form for a single node deletion.
*
* @see node_delete_confirm()
*/
function book_form_node_delete_confirm_alter(&$form, $form_state) {
$node = node_load($form['nid']['#value']);
if (isset($node->book) && $node->book['has_children']) {
$form['book_warning'] = array(
'#markup' => '
' . t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', array('%title' => $node->title)) . '
',
'#weight' => -10,
);
}
}
/**
* Returns an array with default values for a book page's menu link.
*
* @param $nid
* The ID of the node whose menu link is being created.
*
* @return
* The default values for the menu link.
*/
function _book_link_defaults($nid) {
return array('original_bid' => 0, 'menu_name' => '', 'nid' => $nid, 'bid' => 0, 'router_path' => 'node/%', 'plid' => 0, 'mlid' => 0, 'has_children' => 0, 'weight' => 0, 'module' => 'book', 'options' => array());
}
/**
* Processes variables for book-all-books-block.tpl.php.
*
* All non-renderable elements are removed so that the template has full access
* to the structured data but can also simply iterate over all elements and
* render them (as in the default template).
*
* @param $variables
* An associative array containing the following key:
* - book_menus
*
* @see book-all-books-block.tpl.php
*/
function template_preprocess_book_all_books_block(&$variables) {
// Remove all non-renderable elements.
$elements = $variables['book_menus'];
$variables['book_menus'] = array();
foreach (element_children($elements) as $index) {
$variables['book_menus'][$index] = $elements[$index];
}
}
/**
* Processes variables for book-navigation.tpl.php.
*
* @param $variables
* An associative array containing the following key:
* - book_link
*
* @see book-navigation.tpl.php
*/
function template_preprocess_book_navigation(&$variables) {
$book_link = $variables['book_link'];
// Provide extra variables for themers. Not needed by default.
$variables['book_id'] = $book_link['bid'];
$variables['book_title'] = check_plain($book_link['link_title']);
$variables['book_url'] = 'node/' . $book_link['bid'];
$variables['current_depth'] = $book_link['depth'];
$variables['tree'] = '';
if ($book_link['mlid']) {
$variables['tree'] = book_children($book_link);
if ($prev = book_prev($book_link)) {
$prev_href = url($prev['href']);
drupal_add_html_head_link(array('rel' => 'prev', 'href' => $prev_href));
$variables['prev_url'] = $prev_href;
$variables['prev_title'] = check_plain($prev['title']);
}
if ($book_link['plid'] && $parent = book_link_load($book_link['plid'])) {
$parent_href = url($parent['href']);
drupal_add_html_head_link(array('rel' => 'up', 'href' => $parent_href));
$variables['parent_url'] = $parent_href;
$variables['parent_title'] = check_plain($parent['title']);
}
if ($next = book_next($book_link)) {
$next_href = url($next['href']);
drupal_add_html_head_link(array('rel' => 'next', 'href' => $next_href));
$variables['next_url'] = $next_href;
$variables['next_title'] = check_plain($next['title']);
}
}
$variables['has_links'] = FALSE;
// Link variables to filter for values and set state of the flag variable.
$links = array('prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title');
foreach ($links as $link) {
if (isset($variables[$link])) {
// Flag when there is a value.
$variables['has_links'] = TRUE;
}
else {
// Set empty to prevent notices.
$variables[$link] = '';
}
}
}
/**
* Recursively processes and formats menu items for book_toc().
*
* This helper function recursively modifies the table of contents array for
* each item in the menu tree, ignoring items in the exclude array or at a depth
* greater than the limit. Truncates titles over thirty characters and appends
* an indentation string incremented by depth.
*
* @param $tree
* The data structure of the book's menu tree. Includes hidden links.
* @param $indent
* A string appended to each menu item title. Increments by '--' per depth
* level.
* @param $toc
* Reference to the table of contents array. This is modified in place, so the
* function does not have a return value.
* @param $exclude
* (optional) An array of menu link ID values. Any link whose menu link ID is
* in this array will be excluded (along with its children). Defaults to an
* empty array.
* @param $depth_limit
* Any link deeper than this value will be excluded (along with its children).
*/
function _book_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) {
foreach ($tree as $data) {
if ($data['link']['depth'] > $depth_limit) {
// Don't iterate through any links on this level.
break;
}
if (!in_array($data['link']['mlid'], $exclude)) {
$toc[$data['link']['mlid']] = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
if ($data['below']) {
_book_toc_recurse($data['below'], $indent . '--', $toc, $exclude, $depth_limit);
}
}
}
}
/**
* Returns an array of book pages in table of contents order.
*
* @param $bid
* The ID of the book whose pages are to be listed.
* @param $depth_limit
* Any link deeper than this value will be excluded (along with its children).
* @param $exclude
* Optional array of menu link ID values. Any link whose menu link ID is in
* this array will be excluded (along with its children).
*
* @return
* An array of (menu link ID, title) pairs for use as options for selecting a
* book page.
*/
function book_toc($bid, $depth_limit, $exclude = array()) {
$tree = menu_tree_all_data(book_menu_name($bid));
$toc = array();
_book_toc_recurse($tree, '', $toc, $exclude, $depth_limit);
return $toc;
}
/**
* Processes variables for book-export-html.tpl.php.
*
* @param $variables
* An associative array containing the following keys:
* - title
* - contents
* - depth
*
* @see book-export-html.tpl.php
*/
function template_preprocess_book_export_html(&$variables) {
global $base_url, $language;
$variables['title'] = check_plain($variables['title']);
$variables['base_url'] = $base_url;
$variables['language'] = $language;
$variables['language_rtl'] = ($language->direction == LANGUAGE_RTL);
$variables['head'] = drupal_get_html_head();
$variables['dir'] = $language->direction ? 'rtl' : 'ltr';
}
/**
* Traverses the book tree to build printable or exportable output.
*
* During the traversal, the $visit_func() callback is applied to each node and
* is called recursively for each child of the node (in weight, title order).
*
* @param $tree
* A subtree of the book menu hierarchy, rooted at the current page.
* @param $visit_func
* A function callback to be called upon visiting a node in the tree.
*
* @return
* The output generated in visiting each node.
*/
function book_export_traverse($tree, $visit_func) {
$output = '';
foreach ($tree as $data) {
// Note- access checking is already performed when building the tree.
if ($node = node_load($data['link']['nid'], FALSE)) {
$children = '';
if ($data['below']) {
$children = book_export_traverse($data['below'], $visit_func);
}
if (function_exists($visit_func)) {
$output .= call_user_func($visit_func, $node, $children);
}
else {
// Use the default function.
$output .= book_node_export($node, $children);
}
}
}
return $output;
}
/**
* Generates printer-friendly HTML for a node.
*
* @param $node
* The node that will be output.
* @param $children
* (optional) All the rendered child nodes within the current node. Defaults
* to an empty string.
*
* @return
* The HTML generated for the given node.
*
* @see book_export_traverse()
*/
function book_node_export($node, $children = '') {
$build = node_view($node, 'print');
unset($build['#theme']);
// @todo Rendering should happen in the template using render().
$node->rendered = drupal_render($build);
return theme('book_node_export_html', array('node' => $node, 'children' => $children));
}
/**
* Processes variables for book-node-export-html.tpl.php.
*
* @param $variables
* An associative array containing the following keys:
* - node
* - children
*
* @see book-node-export-html.tpl.php
*/
function template_preprocess_book_node_export_html(&$variables) {
$variables['depth'] = $variables['node']->book['depth'];
$variables['title'] = check_plain($variables['node']->title);
$variables['content'] = $variables['node']->rendered;
}
/**
* Determine if a given node type is in the list of types allowed for books.
*
* @param $type
* A node type.
*
* @return
* A Boolean TRUE if the node type can be included in books; otherwise, FALSE.
*/
function book_type_is_allowed($type) {
return in_array($type, variable_get('book_allowed_types', array('book')));
}
/**
* Implements hook_node_type_update().
*
* Updates the Book module's persistent variables if the machine-readable name
* of a node type is changed.
*/
function book_node_type_update($type) {
if (!empty($type->old_type) && $type->old_type != $type->type) {
// Update the list of node types that are allowed to be added to books.
$allowed_types = variable_get('book_allowed_types', array('book'));
$key = array_search($type->old_type, $allowed_types);
if ($key !== FALSE) {
$allowed_types[$type->type] = $allowed_types[$key] ? $type->type : 0;
unset($allowed_types[$key]);
variable_set('book_allowed_types', $allowed_types);
}
// Update the setting for the "Add child page" link.
if (variable_get('book_child_type', 'book') == $type->old_type) {
variable_set('book_child_type', $type->type);
}
}
}
/**
* Gets a book menu link by its menu link ID.
*
* Like menu_link_load(), but adds additional data from the {book} table.
*
* Do not call when loading a node, since this function may call node_load().
*
* @param $mlid
* The menu link ID of the menu item.
*
* @return
* A menu link, with the link translated for rendering and data added from the
* {book} table. FALSE if there is an error.
*/
function book_link_load($mlid) {
if ($item = db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = :mlid", array(
':mlid' => $mlid,
))->fetchAssoc()) {
_menu_link_translate($item);
return $item;
}
return FALSE;
}
/**
* Gets the data representing a subtree of the book hierarchy.
*
* The root of the subtree will be the link passed as a parameter, so the
* returned tree will contain this item and all its descendents in the menu
* tree.
*
* @param $link
* A fully loaded menu link.
*
* @return
* A subtree of menu links in an array, in the order they should be rendered.
*/
function book_menu_subtree_data($link) {
$tree = &drupal_static(__FUNCTION__, array());
// Generate a cache ID (cid) specific for this $menu_name and $link.
$cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid'];
if (!isset($tree[$cid])) {
$cache = cache_get($cid, 'cache_menu');
if ($cache && isset($cache->data)) {
// If the cache entry exists, it will just be the cid for the actual data.
// This avoids duplication of large amounts of data.
$cache = cache_get($cache->data, 'cache_menu');
if ($cache && isset($cache->data)) {
$data = $cache->data;
}
}
// If the subtree data was not in the cache, $data will be NULL.
if (!isset($data)) {
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
$query->join('menu_router', 'm', 'm.path = ml.router_path');
$query->join('book', 'b', 'ml.mlid = b.mlid');
$query->fields('b');
$query->fields('m', array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'delivery_callback', 'title', 'title_callback', 'title_arguments', 'type'));
$query->fields('ml');
$query->condition('menu_name', $link['menu_name']);
for ($i = 1; $i <= MENU_MAX_DEPTH && $link["p$i"]; ++$i) {
$query->condition("p$i", $link["p$i"]);
}
for ($i = 1; $i <= MENU_MAX_DEPTH; ++$i) {
$query->orderBy("p$i");
}
$links = array();
foreach ($query->execute() as $item) {
$links[] = $item;
}
$data['tree'] = menu_tree_data($links, array(), $link['depth']);
$data['node_links'] = array();
menu_tree_collect_node_links($data['tree'], $data['node_links']);
// Compute the real cid for book subtree data.
$tree_cid = 'links:' . $item['menu_name'] . ':subtree-data:' . hash('sha256', serialize($data));
// Cache the data, if it is not already in the cache.
if (!cache_get($tree_cid, 'cache_menu')) {
cache_set($tree_cid, $data, 'cache_menu');
}
// Cache the cid of the (shared) data using the menu and item-specific cid.
cache_set($cid, $tree_cid, 'cache_menu');
}
// Check access for the current user to each item in the tree.
menu_tree_check_access($data['tree'], $data['node_links']);
$tree[$cid] = $data['tree'];
}
return $tree[$cid];
}
www-test-master-modules/modules/book/book.pages.inc 0000664 0000000 0000000 00000016274 12501305533 0022752 0 ustar 00root root 0000000 0000000 $book_list));
}
/**
* Menu callback; Generates representations of a book page and its children.
*
* The function delegates the generation of output to helper functions. The
* function name is derived by prepending 'book_export_' to the given output
* type. So, e.g., a type of 'html' results in a call to the function
* book_export_html().
*
* @param $type
* A string encoding the type of output requested. The following types are
* currently supported in book module:
* - html: Printer-friendly HTML.
* Other types may be supported in contributed modules.
* @param $nid
* An integer representing the node id (nid) of the node to export
*
* @return
* A string representing the node and its children in the book hierarchy in a
* format determined by the $type parameter.
*
* @see book_menu()
*/
function book_export($type, $nid) {
// Check that the node exists and that the current user has access to it.
$node = node_load($nid);
if (!$node) {
return MENU_NOT_FOUND;
}
if (!node_access('view', $node)) {
return MENU_ACCESS_DENIED;
}
$type = drupal_strtolower($type);
$export_function = 'book_export_' . $type;
if (function_exists($export_function)) {
print call_user_func($export_function, $nid);
}
else {
drupal_set_message(t('Unknown export format.'));
drupal_not_found();
}
}
/**
* Generates HTML for export when invoked by book_export().
*
* The given node is embedded to its absolute depth in a top level section. For
* example, a child node with depth 2 in the hierarchy is contained in
* (otherwise empty)
elements corresponding to depth 0 and depth 1.
* This is intended to support WYSIWYG output - e.g., level 3 sections always
* look like level 3 sections, no matter their depth relative to the node
* selected to be exported as printer-friendly HTML.
*
* @param $nid
* An integer representing the node id (nid) of the node to export.
*
* @return
* A string containing HTML representing the node and its children in
* the book hierarchy.
*/
function book_export_html($nid) {
if (user_access('access printer-friendly version')) {
$node = node_load($nid);
if (isset($node->book)) {
$tree = book_menu_subtree_data($node->book);
$contents = book_export_traverse($tree, 'book_node_export');
return theme('book_export_html', array('title' => $node->title, 'contents' => $contents, 'depth' => $node->book['depth']));
}
else {
drupal_not_found();
}
}
else {
drupal_access_denied();
}
}
/**
* Menu callback: Shows the outline form for a single node.
*
* @param $node
* The book node for which to show the outline.
*
* @return string
* A HTML-formatted string with the outline form for a single node.
*
* @see book_menu()
*/
function book_outline($node) {
drupal_set_title($node->title);
return drupal_get_form('book_outline_form', $node);
}
/**
* Form constructor for the book outline form.
*
* Allows handling of all book outline operations via the outline tab.
*
* @param $node
* The book node for which to show the outline.
*
* @see book_outline_form_submit()
* @see book_remove_button_submit()
* @ingroup forms
*/
function book_outline_form($form, &$form_state, $node) {
if (!isset($node->book)) {
// The node is not part of any book yet - set default options.
$node->book = _book_link_defaults($node->nid);
}
else {
$node->book['original_bid'] = $node->book['bid'];
}
// Find the depth limit for the parent select.
if (!isset($node->book['parent_depth_limit'])) {
$node->book['parent_depth_limit'] = _book_parent_depth_limit($node->book);
}
$form['#node'] = $node;
$form['#id'] = 'book-outline';
_book_add_form_elements($form, $form_state, $node);
$form['book']['#collapsible'] = FALSE;
$form['update'] = array(
'#type' => 'submit',
'#value' => $node->book['original_bid'] ? t('Update book outline') : t('Add to book outline'),
'#weight' => 15,
);
$form['remove'] = array(
'#type' => 'submit',
'#value' => t('Remove from book outline'),
'#access' => _book_node_is_removable($node),
'#weight' => 20,
'#submit' => array('book_remove_button_submit'),
);
return $form;
}
/**
* Form submission handler for book_outline_form().
*
* Redirects to removal confirmation form.
*
* @see book_outline_form_submit()
*/
function book_remove_button_submit($form, &$form_state) {
$form_state['redirect'] = 'node/' . $form['#node']->nid . '/outline/remove';
}
/**
* Form submission handler for book_outline_form().
*
* @see book_remove_button_submit()
*/
function book_outline_form_submit($form, &$form_state) {
$node = $form['#node'];
$form_state['redirect'] = "node/" . $node->nid;
$book_link = $form_state['values']['book'];
if (!$book_link['bid']) {
drupal_set_message(t('No changes were made'));
return;
}
$book_link['menu_name'] = book_menu_name($book_link['bid']);
$node->book = $book_link;
if (_book_update_outline($node)) {
if ($node->book['parent_mismatch']) {
// This will usually only happen when JS is disabled.
drupal_set_message(t('The post has been added to the selected book. You may now position it relative to other pages.'));
$form_state['redirect'] = "node/" . $node->nid . "/outline";
}
else {
drupal_set_message(t('The book outline has been updated.'));
}
}
else {
drupal_set_message(t('There was an error adding the post to the book.'), 'error');
}
}
/**
* Form constructor to confirm removal of a node from a book.
*
* @param $node
* The node to delete.
*
* @see book_remove_form_submit()
* @ingroup forms
*/
function book_remove_form($form, &$form_state, $node) {
$form['#node'] = $node;
$title = array('%title' => $node->title);
if ($node->book['has_children']) {
$description = t('%title has associated child pages, which will be relocated automatically to maintain their connection to the book. To recreate the hierarchy (as it was before removing this page), %title may be added again using the Outline tab, and each of its former child pages will need to be relocated manually.', $title);
}
else {
$description = t('%title may be added to hierarchy again using the Outline tab.', $title);
}
return confirm_form($form, t('Are you sure you want to remove %title from the book hierarchy?', $title), 'node/' . $node->nid, $description, t('Remove'));
}
/**
* Form submission handler for book_remove_form().
*/
function book_remove_form_submit($form, &$form_state) {
$node = $form['#node'];
if (_book_node_is_removable($node)) {
menu_link_delete($node->book['mlid']);
db_delete('book')
->condition('nid', $node->nid)
->execute();
drupal_set_message(t('The post has been removed from the book.'));
}
$form_state['redirect'] = 'node/' . $node->nid;
}
www-test-master-modules/modules/book/book.test 0000664 0000000 0000000 00000036165 12501305533 0022063 0 ustar 00root root 0000000 0000000 'Book functionality',
'description' => 'Create a book, add pages, and test book interface.',
'group' => 'Book',
);
}
function setUp() {
parent::setUp(array('book', 'node_access_test'));
// node_access_test requires a node_access_rebuild().
node_access_rebuild();
// Create users.
$this->book_author = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books'));
$this->web_user = $this->drupalCreateUser(array('access printer-friendly version', 'node test view'));
$this->admin_user = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions', 'administer book outlines', 'node test view'));
}
/**
* Creates a new book with a page hierarchy.
*/
function createBook() {
// Create new book.
$this->drupalLogin($this->book_author);
$this->book = $this->createBookNode('new');
$book = $this->book;
/*
* Add page hierarchy to book.
* Book
* |- Node 0
* |- Node 1
* |- Node 2
* |- Node 3
* |- Node 4
*/
$nodes = array();
$nodes[] = $this->createBookNode($book->nid); // Node 0.
$nodes[] = $this->createBookNode($book->nid, $nodes[0]->book['mlid']); // Node 1.
$nodes[] = $this->createBookNode($book->nid, $nodes[0]->book['mlid']); // Node 2.
$nodes[] = $this->createBookNode($book->nid); // Node 3.
$nodes[] = $this->createBookNode($book->nid); // Node 4.
$this->drupalLogout();
return $nodes;
}
/**
* Tests book functionality through node interfaces.
*/
function testBook() {
// Create new book.
$nodes = $this->createBook();
$book = $this->book;
$this->drupalLogin($this->web_user);
// Check that book pages display along with the correct outlines and
// previous/next links.
$this->checkBookNode($book, array($nodes[0], $nodes[3], $nodes[4]), FALSE, FALSE, $nodes[0], array());
$this->checkBookNode($nodes[0], array($nodes[1], $nodes[2]), $book, $book, $nodes[1], array($book));
$this->checkBookNode($nodes[1], NULL, $nodes[0], $nodes[0], $nodes[2], array($book, $nodes[0]));
$this->checkBookNode($nodes[2], NULL, $nodes[1], $nodes[0], $nodes[3], array($book, $nodes[0]));
$this->checkBookNode($nodes[3], NULL, $nodes[2], $book, $nodes[4], array($book));
$this->checkBookNode($nodes[4], NULL, $nodes[3], $book, FALSE, array($book));
$this->drupalLogout();
// Create a second book, and move an existing book page into it.
$this->drupalLogin($this->book_author);
$other_book = $this->createBookNode('new');
$node = $this->createBookNode($book->nid);
$edit = array('book[bid]' => $other_book->nid);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->drupalLogout();
$this->drupalLogin($this->web_user);
// Check that the nodes in the second book are displayed correctly.
// First we must set $this->book to the second book, so that the
// correct regex will be generated for testing the outline.
$this->book = $other_book;
$this->checkBookNode($other_book, array($node), FALSE, FALSE, $node, array());
$this->checkBookNode($node, NULL, $other_book, $other_book, FALSE, array($other_book));
}
/**
* Checks the outline of sub-pages; previous, up, and next.
*
* Also checks the printer friendly version of the outline.
*
* @param $node
* Node to check.
* @param $nodes
* Nodes that should be in outline.
* @param $previous
* (optional) Previous link node. Defaults to FALSE.
* @param $up
* (optional) Up link node. Defaults to FALSE.
* @param $next
* (optional) Next link node. Defaults to FALSE.
* @param $breadcrumb
* The nodes that should be displayed in the breadcrumb.
*/
function checkBookNode($node, $nodes, $previous = FALSE, $up = FALSE, $next = FALSE, array $breadcrumb) {
// $number does not use drupal_static as it should not be reset
// since it uniquely identifies each call to checkBookNode().
static $number = 0;
$this->drupalGet('node/' . $node->nid);
// Check outline structure.
if ($nodes !== NULL) {
$this->assertPattern($this->generateOutlinePattern($nodes), format_string('Node %number outline confirmed.', array('%number' => $number)));
}
else {
$this->pass(format_string('Node %number does not have outline.', array('%number' => $number)));
}
// Check previous, up, and next links.
if ($previous) {
$this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => array('page-previous'), 'title' => t('Go to previous page')))), 'Previous page link found.');
}
if ($up) {
$this->assertRaw(l('up', 'node/' . $up->nid, array('attributes' => array('class' => array('page-up'), 'title' => t('Go to parent page')))), 'Up page link found.');
}
if ($next) {
$this->assertRaw(l($next->title . ' ›', 'node/' . $next->nid, array('attributes' => array('class' => array('page-next'), 'title' => t('Go to next page')))), 'Next page link found.');
}
// Compute the expected breadcrumb.
$expected_breadcrumb = array();
$expected_breadcrumb[] = url('');
foreach ($breadcrumb as $a_node) {
$expected_breadcrumb[] = url('node/' . $a_node->nid);
}
// Fetch links in the current breadcrumb.
$links = $this->xpath('//div[@class="breadcrumb"]/a');
$got_breadcrumb = array();
foreach ($links as $link) {
$got_breadcrumb[] = (string) $link['href'];
}
// Compare expected and got breadcrumbs.
$this->assertIdentical($expected_breadcrumb, $got_breadcrumb, 'The breadcrumb is correctly displayed on the page.');
// Check printer friendly version.
$this->drupalGet('book/export/html/' . $node->nid);
$this->assertText($node->title, 'Printer friendly title found.');
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), 'Printer friendly body found.');
$number++;
}
/**
* Creates a regular expression to check for the sub-nodes in the outline.
*
* @param array $nodes
* An array of nodes to check in outline.
*
* @return
* A regular expression that locates sub-nodes of the outline.
*/
function generateOutlinePattern($nodes) {
$outline = '';
foreach ($nodes as $node) {
$outline .= '(node\/' . $node->nid . ')(.*?)(' . $node->title . ')(.*?)';
}
return '/
/s';
}
/**
* Creates a book node.
*
* @param $book_nid
* A book node ID or set to 'new' to create a new book.
* @param $parent
* (optional) Parent book reference ID. Defaults to NULL.
*/
function createBookNode($book_nid, $parent = NULL) {
// $number does not use drupal_static as it should not be reset
// since it uniquely identifies each call to createBookNode().
static $number = 0; // Used to ensure that when sorted nodes stay in same order.
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $number . ' - SimpleTest test node ' . $this->randomName(10);
$edit["body[$langcode][0][value]"] = 'SimpleTest test body ' . $this->randomName(32) . ' ' . $this->randomName(32);
$edit['book[bid]'] = $book_nid;
if ($parent !== NULL) {
$this->drupalPost('node/add/book', $edit, t('Change book (update list of parents)'));
$edit['book[plid]'] = $parent;
$this->drupalPost(NULL, $edit, t('Save'));
}
else {
$this->drupalPost('node/add/book', $edit, t('Save'));
}
// Check to make sure the book node was created.
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
$number++;
return $node;
}
/**
* Tests book export ("printer-friendly version") functionality.
*/
function testBookExport() {
// Create a book.
$nodes = $this->createBook();
// Login as web user and view printer-friendly version.
$this->drupalLogin($this->web_user);
$this->drupalGet('node/' . $this->book->nid);
$this->clickLink(t('Printer-friendly version'));
// Make sure each part of the book is there.
foreach ($nodes as $node) {
$this->assertText($node->title, 'Node title found in printer friendly version.');
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), 'Node body found in printer friendly version.');
}
// Make sure we can't export an unsupported format.
$this->drupalGet('book/export/foobar/' . $this->book->nid);
$this->assertResponse('404', 'Unsupported export format returned "not found".');
// Make sure we get a 404 on a not existing book node.
$this->drupalGet('book/export/html/123');
$this->assertResponse('404', 'Not existing book node returned "not found".');
// Make sure an anonymous user cannot view printer-friendly version.
$this->drupalLogout();
// Load the book and verify there is no printer-friendly version link.
$this->drupalGet('node/' . $this->book->nid);
$this->assertNoLink(t('Printer-friendly version'), 'Anonymous user is not shown link to printer-friendly version.');
// Try getting the URL directly, and verify it fails.
$this->drupalGet('book/export/html/' . $this->book->nid);
$this->assertResponse('403', 'Anonymous user properly forbidden.');
// Now grant anonymous users permission to view the printer-friendly
// version and verify that node access restrictions still prevent them from
// seeing it.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access printer-friendly version'));
$this->drupalGet('book/export/html/' . $this->book->nid);
$this->assertResponse('403', 'Anonymous user properly forbidden from seeing the printer-friendly version when denied by node access.');
}
/**
* Tests the functionality of the book navigation block.
*/
function testBookNavigationBlock() {
$this->drupalLogin($this->admin_user);
// Set block title to confirm that the interface is available.
$block_title = $this->randomName(16);
$this->drupalPost('admin/structure/block/manage/book/navigation/configure', array('title' => $block_title), t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
// Set the block to a region to confirm block is available.
$edit = array();
$edit['blocks[book_navigation][region]'] = 'footer';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
// Give anonymous users the permission 'node test view'.
$edit = array();
$edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE;
$this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions'));
$this->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
// Test correct display of the block.
$nodes = $this->createBook();
$this->drupalGet('');
$this->assertText($block_title, 'Book navigation block is displayed.');
$this->assertText($this->book->title, format_string('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->title)));
$this->assertNoText($nodes[0]->title, 'No links to individual book pages are displayed.');
}
/**
* Tests the book navigation block when an access module is enabled.
*/
function testNavigationBlockOnAccessModuleEnabled() {
$this->drupalLogin($this->admin_user);
$edit = array();
// Set the block title.
$block_title = $this->randomName(16);
$edit['title'] = $block_title;
// Set block display to 'Show block only on book pages'.
$edit['book_block_mode'] = 'book pages';
$this->drupalPost('admin/structure/block/manage/book/navigation/configure', $edit, t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
// Set the block to a region to confirm block is available.
$edit = array();
$edit['blocks[book_navigation][region]'] = 'footer';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
// Give anonymous users the permission 'node test view'.
$edit = array();
$edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE;
$this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions'));
$this->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
// Create a book.
$this->createBook();
// Test correct display of the block to registered users.
$this->drupalLogin($this->web_user);
$this->drupalGet('node/' . $this->book->nid);
$this->assertText($block_title, 'Book navigation block is displayed to registered users.');
$this->drupalLogout();
// Test correct display of the block to anonymous users.
$this->drupalGet('node/' . $this->book->nid);
$this->assertText($block_title, 'Book navigation block is displayed to anonymous users.');
}
/**
* Tests the access for deleting top-level book nodes.
*/
function testBookDelete() {
$nodes = $this->createBook();
$this->drupalLogin($this->admin_user);
$edit = array();
// Test access to delete top-level and child book nodes.
$this->drupalGet('node/' . $this->book->nid . '/outline/remove');
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
$this->drupalPost('node/' . $nodes[4]->nid . '/outline/remove', $edit, t('Remove'));
$node4 = node_load($nodes[4]->nid, NULL, TRUE);
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
// Delete all child book nodes and retest top-level node deletion.
foreach ($nodes as $node) {
$nids[] = $node->nid;
}
node_delete_multiple($nids);
$this->drupalPost('node/' . $this->book->nid . '/outline/remove', $edit, t('Remove'));
$node = node_load($this->book->nid, NULL, TRUE);
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
}
}
www-test-master-modules/modules/color/ 0000775 0000000 0000000 00000000000 12501305533 0020401 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/color/color-rtl.css 0000664 0000000 0000000 00000001316 12501305533 0023031 0 ustar 00root root 0000000 0000000 /**
* @file
* Right-to-left specific stylesheet for the Color module.
*/
#placeholder {
left: 0;
right: auto;
}
/* Palette */
.color-form .form-item {
padding-left: 0;
padding-right: 1em;
}
.color-form label {
float: right;
clear: right;
}
.color-form .form-text,
.color-form .form-select {
float: right;
}
.color-form .form-text {
margin-right: 0;
margin-left: 5px;
}
#palette .hook {
float: right;
}
#palette .down,
#palette .up,
#palette .both {
background: url(images/hook-rtl.png) no-repeat 0 0;
}
#palette .up {
background-position: 0 -27px;
}
#palette .both {
background-position: 0 -54px;
}
#palette .lock {
float: right;
right: -10px;
}
html.js #preview {
float: right;
}
www-test-master-modules/modules/color/color.css 0000664 0000000 0000000 00000002647 12501305533 0022242 0 ustar 00root root 0000000 0000000 /**
* @file
* Stylesheet for the administration pages of the Color module.
*/
/* Farbtastic placement */
.color-form {
max-width: 50em;
position: relative;
}
#placeholder {
position: absolute;
top: 0;
right: 0; /* LTR */
}
/* Palette */
.color-form .form-item {
height: 2em;
line-height: 2em;
padding-left: 1em; /* LTR */
margin: 0.5em 0;
}
.color-form label {
float: left; /* LTR */
clear: left; /* LTR */
width: 10em;
}
.color-form .form-text,
.color-form .form-select {
float: left; /* LTR */
}
.color-form .form-text {
text-align: center;
margin-right: 5px; /* LTR */
cursor: pointer;
}
#palette .hook {
float: left; /* LTR */
margin-top: 3px;
width: 16px;
height: 16px;
}
#palette .down,
#palette .up,
#palette .both {
background: url(images/hook.png) no-repeat 100% 0; /* LTR */
}
#palette .up {
background-position: 100% -27px; /* LTR */
}
#palette .both {
background-position: 100% -54px; /* LTR */
}
#palette .lock {
float: left; /* LTR */
position: relative;
top: -1.4em;
left: -10px; /* LTR */
width: 20px;
height: 25px;
background: url(images/lock.png) no-repeat 50% 2px;
cursor: pointer;
}
#palette .unlocked {
background-position: 50% -22px;
}
#palette .form-item {
width: 20em;
}
#palette .item-selected {
background: #eee;
}
/* Preview */
#preview {
display: none;
}
html.js #preview {
display: block;
position: relative;
float: left; /* LTR */
}
www-test-master-modules/modules/color/color.info 0000664 0000000 0000000 00000000443 12501305533 0022375 0 ustar 00root root 0000000 0000000 name = Color
description = Allows administrators to change the color scheme of compatible themes.
package = Core
version = VERSION
core = 7.x
files[] = color.test
; Information added by Drupal.org packaging script on 2014-11-19
version = "7.34"
project = "drupal"
datestamp = "1416429488"
www-test-master-modules/modules/color/color.install 0000664 0000000 0000000 00000004347 12501305533 0023117 0 ustar 00root root 0000000 0000000 $info['GD Version'],
);
// Check for PNG support.
if (function_exists('imagecreatefrompng')) {
$requirements['color_gd']['severity'] = REQUIREMENT_OK;
}
else {
$requirements['color_gd']['severity'] = REQUIREMENT_WARNING;
$requirements['color_gd']['description'] = t('The GD library for PHP is enabled, but was compiled without PNG support. Check the PHP image documentation for information on how to correct this.', array('@url' => 'http://www.php.net/manual/ref.image.php'));
}
}
else {
$requirements['color_gd'] = array(
'value' => t('Not installed'),
'severity' => REQUIREMENT_ERROR,
'description' => t('The GD library for PHP is missing or outdated. Check the PHP image documentation for information on how to correct this.', array('@url' => 'http://www.php.net/manual/book.image.php')),
);
}
$requirements['color_gd']['title'] = t('GD library PNG support');
}
return $requirements;
}
/**
* @addtogroup updates-7.x-extra
* @{
*/
/**
* Warn site administrator if unsafe CSS color codes are found in the database.
*/
function color_update_7001() {
$theme_palettes = db_query("SELECT name FROM {variable} WHERE name LIKE 'color_%_palette'")->fetchCol();
foreach ($theme_palettes as $name) {
$palette = variable_get($name, array());
foreach ($palette as $key => $color) {
if (!preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color)) {
drupal_set_message('Some of the custom CSS color codes specified via the color module are invalid. Please examine the themes which are making use of the color module at the Appearance settings page to verify their CSS color values.', 'warning');
}
}
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/
www-test-master-modules/modules/color/color.js 0000664 0000000 0000000 00000016701 12501305533 0022062 0 ustar 00root root 0000000 0000000 /**
* @file
* Attaches the behaviors for the Color module.
*/
(function ($) {
Drupal.behaviors.color = {
attach: function (context, settings) {
var i, j, colors, field_name;
// This behavior attaches by ID, so is only valid once on a page.
var form = $('#system-theme-settings .color-form', context).once('color');
if (form.length == 0) {
return;
}
var inputs = [];
var hooks = [];
var locks = [];
var focused = null;
// Add Farbtastic.
$(form).prepend('').addClass('color-processed');
var farb = $.farbtastic('#placeholder');
// Decode reference colors to HSL.
var reference = settings.color.reference;
for (i in reference) {
reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
}
// Build a preview.
var height = [];
var width = [];
// Loop through all defined gradients.
for (i in settings.gradients) {
// Add element to display the gradient.
$('#preview').once('color').append('');
var gradient = $('#preview #gradient-' + i);
// Add height of current gradient to the list (divided by 10).
height.push(parseInt(gradient.css('height'), 10) / 10);
// Add width of current gradient to the list (divided by 10).
width.push(parseInt(gradient.css('width'), 10) / 10);
// Add rows (or columns for horizontal gradients).
// Each gradient line should have a height (or width for horizontal
// gradients) of 10px (because we divided the height/width by 10 above).
for (j = 0; j < (settings.gradients[i]['direction'] == 'vertical' ? height[i] : width[i]); ++j) {
gradient.append('');
}
}
// Fix preview background in IE6.
if (navigator.appVersion.match(/MSIE [0-6]\./)) {
var e = $('#preview #img')[0];
var image = e.currentStyle.backgroundImage;
e.style.backgroundImage = 'none';
e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')";
}
// Set up colorScheme selector.
$('#edit-scheme', form).change(function () {
var schemes = settings.color.schemes, colorScheme = this.options[this.selectedIndex].value;
if (colorScheme != '' && schemes[colorScheme]) {
// Get colors of active scheme.
colors = schemes[colorScheme];
for (field_name in colors) {
callback($('#edit-palette-' + field_name), colors[field_name], false, true);
}
preview();
}
});
/**
* Renders the preview.
*/
function preview() {
Drupal.color.callback(context, settings, form, farb, height, width);
}
/**
* Shifts a given color, using a reference pair (ref in HSL).
*
* This algorithm ensures relative ordering on the saturation and luminance
* axes is preserved, and performs a simple hue shift.
*
* It is also symmetrical. If: shift_color(c, a, b) == d, then
* shift_color(d, b, a) == c.
*/
function shift_color(given, ref1, ref2) {
// Convert to HSL.
given = farb.RGBToHSL(farb.unpack(given));
// Hue: apply delta.
given[0] += ref2[0] - ref1[0];
// Saturation: interpolate.
if (ref1[1] == 0 || ref2[1] == 0) {
given[1] = ref2[1];
}
else {
var d = ref1[1] / ref2[1];
if (d > 1) {
given[1] /= d;
}
else {
given[1] = 1 - (1 - given[1]) * d;
}
}
// Luminance: interpolate.
if (ref1[2] == 0 || ref2[2] == 0) {
given[2] = ref2[2];
}
else {
var d = ref1[2] / ref2[2];
if (d > 1) {
given[2] /= d;
}
else {
given[2] = 1 - (1 - given[2]) * d;
}
}
return farb.pack(farb.HSLToRGB(given));
}
/**
* Callback for Farbtastic when a new color is chosen.
*/
function callback(input, color, propagate, colorScheme) {
var matched;
// Set background/foreground colors.
$(input).css({
backgroundColor: color,
'color': farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
});
// Change input value.
if ($(input).val() && $(input).val() != color) {
$(input).val(color);
// Update locked values.
if (propagate) {
i = input.i;
for (j = i + 1; ; ++j) {
if (!locks[j - 1] || $(locks[j - 1]).is('.unlocked')) break;
matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
callback(inputs[j], matched, false);
}
for (j = i - 1; ; --j) {
if (!locks[j] || $(locks[j]).is('.unlocked')) break;
matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
callback(inputs[j], matched, false);
}
// Update preview.
preview();
}
// Reset colorScheme selector.
if (!colorScheme) {
resetScheme();
}
}
}
/**
* Resets the color scheme selector.
*/
function resetScheme() {
$('#edit-scheme', form).each(function () {
this.selectedIndex = this.options.length - 1;
});
}
/**
* Focuses Farbtastic on a particular field.
*/
function focus() {
var input = this;
// Remove old bindings.
focused && $(focused).unbind('keyup', farb.updateValue)
.unbind('keyup', preview).unbind('keyup', resetScheme)
.parent().removeClass('item-selected');
// Add new bindings.
focused = this;
farb.linkTo(function (color) { callback(input, color, true, false); });
farb.setColor(this.value);
$(focused).keyup(farb.updateValue).keyup(preview).keyup(resetScheme)
.parent().addClass('item-selected');
}
// Initialize color fields.
$('#palette input.form-text', form)
.each(function () {
// Extract palette field name
this.key = this.id.substring(13);
// Link to color picker temporarily to initialize.
farb.linkTo(function () {}).setColor('#000').linkTo(this);
// Add lock.
var i = inputs.length;
if (inputs.length) {
var lock = $('').toggle(
function () {
$(this).addClass('unlocked');
$(hooks[i - 1]).attr('class',
locks[i - 2] && $(locks[i - 2]).is(':not(.unlocked)') ? 'hook up' : 'hook'
);
$(hooks[i]).attr('class',
locks[i] && $(locks[i]).is(':not(.unlocked)') ? 'hook down' : 'hook'
);
},
function () {
$(this).removeClass('unlocked');
$(hooks[i - 1]).attr('class',
locks[i - 2] && $(locks[i - 2]).is(':not(.unlocked)') ? 'hook both' : 'hook down'
);
$(hooks[i]).attr('class',
locks[i] && $(locks[i]).is(':not(.unlocked)') ? 'hook both' : 'hook up'
);
}
);
$(this).after(lock);
locks.push(lock);
};
// Add hook.
var hook = $('');
$(this).after(hook);
hooks.push(hook);
$(this).parent().find('.lock').click();
this.i = i;
inputs.push(this);
})
.focus(focus);
$('#palette label', form);
// Focus first color.
focus.call(inputs[0]);
// Render preview.
preview();
}
};
})(jQuery);
www-test-master-modules/modules/color/color.module 0000664 0000000 0000000 00000065703 12501305533 0022741 0 ustar 00root root 0000000 0000000 ' . t('About') . '';
$output .= '
' . t('The Color module allows users with the Administer site configuration permission to quickly and easily change the color scheme of themes that have been built to be compatible with it. For more information, see the online handbook entry for Color module.', array('@color' => 'http://drupal.org/documentation/modules/color')) . '
';
$output .= '
' . t('Uses') . '
';
$output .= '
';
$output .= '
' . t('Changing colors') . '
';
$output .= '
' . t("Using the Color module allows you to easily change the color of links, backgrounds, text, and other theme elements. To change the color settings for a compatible theme, select the Settings link for your theme on the Themes administration page. If you don't see a color picker on that page, then your theme is not compatible with the color module. If you are sure that the theme does indeed support the color module, but the color picker does not appear, then follow these troubleshooting procedures.", array('@configure' => url('admin/appearance'), '@troubleshoot' => 'http://drupal.org/node/109457')) . '
';
$output .= '
' . t("The Color module saves a modified copy of the theme's specified stylesheets in the files directory. This means that if you make any manual changes to your theme's stylesheet, you must save your color settings again, even if they haven't changed. This step is required because the module stylesheets (in the files directory) need to be recreated to include your changes.") . '
';
$output .= '
';
return $output;
}
}
/**
* Implements hook_theme().
*/
function color_theme() {
return array(
'color_scheme_form' => array(
'render element' => 'form',
),
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function color_form_system_theme_settings_alter(&$form, &$form_state) {
if (isset($form_state['build_info']['args'][0]) && ($theme = $form_state['build_info']['args'][0]) && color_get_info($theme) && function_exists('gd_info')) {
$form['color'] = array(
'#type' => 'fieldset',
'#title' => t('Color scheme'),
'#weight' => -1,
'#attributes' => array('id' => 'color_scheme_form'),
'#theme' => 'color_scheme_form',
);
$form['color'] += color_scheme_form($form, $form_state, $theme);
$form['#validate'][] = 'color_scheme_form_validate';
$form['#submit'][] = 'color_scheme_form_submit';
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function color_form_system_themes_alter(&$form, &$form_state) {
_color_theme_select_form_alter($form, $form_state);
}
/**
* Helper for hook_form_FORM_ID_alter() implementations.
*/
function _color_theme_select_form_alter(&$form, &$form_state) {
// Use the generated screenshot in the theme list.
$themes = list_themes();
foreach (element_children($form) as $theme) {
if ($screenshot = variable_get('color_' . $theme . '_screenshot')) {
if (isset($form[$theme]['screenshot'])) {
$form[$theme]['screenshot']['#markup'] = theme('image', array('path' => $screenshot, 'title' => '', 'attributes' => array('class' => array('screenshot'))));
}
}
}
}
/**
* Replaces style sheets with color-altered style sheets.
*
* A theme that supports the color module should call this function from its
* THEME_process_html() function, so that the correct style sheets are
* included when html.tpl.php is rendered.
*
* @see theme()
*/
function _color_html_alter(&$vars) {
global $theme_key;
$themes = list_themes();
// Override stylesheets.
$color_paths = variable_get('color_' . $theme_key . '_stylesheets', array());
if (!empty($color_paths)) {
foreach ($themes[$theme_key]->stylesheets['all'] as $base_filename => $old_path) {
// Loop over the path array with recolored CSS files to find matching
// paths which could replace the non-recolored paths.
foreach ($color_paths as $color_path) {
// Color module currently requires unique file names to be used,
// which allows us to compare different file paths.
if (drupal_basename($old_path) == drupal_basename($color_path)) {
// Replace the path to the new css file.
// This keeps the order of the stylesheets intact.
$vars['css'][$old_path]['data'] = $color_path;
}
}
}
$vars['styles'] = drupal_get_css($vars['css']);
}
}
/**
* Replaces the logo with a color-altered logo.
*
* A theme that supports the color module should call this function from its
* THEME_process_page() function, so that the correct logo is included when
* page.tpl.php is rendered.
*
* @see theme()
*/
function _color_page_alter(&$vars) {
global $theme_key;
// Override logo.
$logo = variable_get('color_' . $theme_key . '_logo');
if ($logo && $vars['logo'] && preg_match('!' . $theme_key . '/logo.png$!', $vars['logo'])) {
$vars['logo'] = file_create_url($logo);
}
}
/**
* Retrieves the Color module information for a particular theme.
*/
function color_get_info($theme) {
static $theme_info = array();
if (isset($theme_info[$theme])) {
return $theme_info[$theme];
}
$path = drupal_get_path('theme', $theme);
$file = DRUPAL_ROOT . '/' . $path . '/color/color.inc';
if ($path && file_exists($file)) {
include $file;
$theme_info[$theme] = $info;
return $info;
}
}
/**
* Retrieves the color palette for a particular theme.
*/
function color_get_palette($theme, $default = FALSE) {
// Fetch and expand default palette.
$info = color_get_info($theme);
$palette = $info['schemes']['default']['colors'];
// Load variable.
return $default ? $palette : variable_get('color_' . $theme . '_palette', $palette);
}
/**
* Form constructor for the color configuration form for a particular theme.
*
* @param $theme
* The machine name of the theme whose color settings are being configured.
*
* @see color_scheme_form_validate()
* @see color_scheme_form_submit()
* @ingroup forms
*/
function color_scheme_form($complete_form, &$form_state, $theme) {
$base = drupal_get_path('module', 'color');
$info = color_get_info($theme);
$info['schemes'][''] = array('title' => t('Custom'), 'colors' => array());
$color_sets = array();
$schemes = array();
foreach ($info['schemes'] as $key => $scheme) {
$color_sets[$key] = $scheme['title'];
$schemes[$key] = $scheme['colors'];
$schemes[$key] += $info['schemes']['default']['colors'];
}
// See if we're using a predefined scheme.
// Note: we use the original theme when the default scheme is chosen.
$current_scheme = variable_get('color_' . $theme . '_palette', array());
foreach ($schemes as $key => $scheme) {
if ($current_scheme == $scheme) {
$scheme_name = $key;
break;
}
}
if (empty($scheme_name)) {
if (empty($current_scheme)) {
$scheme_name = 'default';
}
else {
$scheme_name = '';
}
}
// Add scheme selector.
$form['scheme'] = array(
'#type' => 'select',
'#title' => t('Color set'),
'#options' => $color_sets,
'#default_value' => $scheme_name,
'#attached' => array(
// Add Farbtastic color picker.
'library' => array(
array('system', 'farbtastic'),
),
// Add custom CSS.
'css' => array(
$base . '/color.css' => array(),
),
// Add custom JavaScript.
'js' => array(
$base . '/color.js',
array(
'data' => array(
'color' => array(
'reference' => color_get_palette($theme, TRUE),
'schemes' => $schemes,
),
'gradients' => $info['gradients'],
),
'type' => 'setting',
),
),
),
);
// Add palette fields.
$palette = color_get_palette($theme);
$names = $info['fields'];
$form['palette']['#tree'] = TRUE;
foreach ($palette as $name => $value) {
if (isset($names[$name])) {
$form['palette'][$name] = array(
'#type' => 'textfield',
'#title' => check_plain($names[$name]),
'#value_callback' => 'color_palette_color_value',
'#default_value' => $value,
'#size' => 8,
);
}
}
$form['theme'] = array('#type' => 'value', '#value' => $theme);
$form['info'] = array('#type' => 'value', '#value' => $info);
return $form;
}
/**
* Returns HTML for a theme's color form.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @ingroup themeable
*/
function theme_color_scheme_form($variables) {
$form = $variables['form'];
$theme = $form['theme']['#value'];
$info = $form['info']['#value'];
$path = drupal_get_path('theme', $theme) . '/';
drupal_add_css($path . $info['preview_css']);
$preview_js_path = isset($info['preview_js']) ? $path . $info['preview_js'] : drupal_get_path('module', 'color') . '/' . 'preview.js';
// Add the JS at a weight below color.js.
drupal_add_js($preview_js_path, array('weight' => -1));
$output = '';
$output .= '
';
// Attempt to load preview HTML if the theme provides it.
$preview_html_path = DRUPAL_ROOT . '/' . (isset($info['preview_html']) ? drupal_get_path('theme', $theme) . '/' . $info['preview_html'] : drupal_get_path('module', 'color') . '/preview.html');
$output .= file_get_contents($preview_html_path);
// Close the wrapper div.
$output .= '
';
return $output;
}
/**
* Determines the value for a palette color field.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
* @param $form_state
* A keyed array containing the current state of the form.
*
* @return
* The data that will appear in the $form_state['values'] collection for this
* element. Return nothing to use the default.
*/
function color_palette_color_value($element, $input = FALSE, $form_state = array()) {
// If we suspect a possible cross-site request forgery attack, only accept
// hexadecimal CSS color strings from user input, to avoid problems when this
// value is used in the JavaScript preview.
if ($input !== FALSE) {
// Start with the provided value for this textfield, and validate that if
// necessary, falling back on the default value.
$value = form_type_textfield_value($element, $input, $form_state);
if (!$value || !isset($form_state['complete form']['#token']) || color_valid_hexadecimal_string($value) || drupal_valid_token($form_state['values']['form_token'], $form_state['complete form']['#token'])) {
return $value;
}
else {
return $element['#default_value'];
}
}
}
/**
* Determines if a hexadecimal CSS color string is valid.
*
* @param $color
* The string to check.
*
* @return
* TRUE if the string is a valid hexadecimal CSS color string, or FALSE if it
* isn't.
*/
function color_valid_hexadecimal_string($color) {
return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color);
}
/**
* Form validation handler for color_scheme_form().
*
* @see color_scheme_form_submit()
*/
function color_scheme_form_validate($form, &$form_state) {
// Only accept hexadecimal CSS color strings to avoid XSS upon use.
foreach ($form_state['values']['palette'] as $key => $color) {
if (!color_valid_hexadecimal_string($color)) {
form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
}
}
}
/**
* Form submission handler for color_scheme_form().
*
* @see color_scheme_form_validate()
*/
function color_scheme_form_submit($form, &$form_state) {
// Get theme coloring info.
if (!isset($form_state['values']['info'])) {
return;
}
$theme = $form_state['values']['theme'];
$info = $form_state['values']['info'];
// Resolve palette.
$palette = $form_state['values']['palette'];
if ($form_state['values']['scheme'] != '') {
foreach ($palette as $key => $color) {
if (isset($info['schemes'][$form_state['values']['scheme']]['colors'][$key])) {
$palette[$key] = $info['schemes'][$form_state['values']['scheme']]['colors'][$key];
}
}
$palette += $info['schemes']['default']['colors'];
}
// Make sure enough memory is available, if PHP's memory limit is compiled in.
if (function_exists('memory_get_usage')) {
// Fetch source image dimensions.
$source = drupal_get_path('theme', $theme) . '/' . $info['base_image'];
list($width, $height) = getimagesize($source);
// We need at least a copy of the source and a target buffer of the same
// size (both at 32bpp).
$required = $width * $height * 8;
// We intend to prevent color scheme changes if there isn't enough memory
// available. memory_get_usage(TRUE) returns a more accurate number than
// memory_get_usage(), therefore we won't inadvertently reject a color
// scheme change based on a faulty memory calculation.
$usage = memory_get_usage(TRUE);
$memory_limit = ini_get('memory_limit');
$size = parse_size($memory_limit);
if (!drupal_check_memory_limit($usage + $required, $memory_limit)) {
drupal_set_message(t('There is not enough memory available to PHP to change this theme\'s color scheme. You need at least %size more. Check the PHP documentation for more information.', array('%size' => format_size($usage + $required - $size), '@url' => 'http://www.php.net/manual/ini.core.php#ini.sect.resource-limits')), 'error');
return;
}
}
// Delete old files.
foreach (variable_get('color_' . $theme . '_files', array()) as $file) {
@drupal_unlink($file);
}
if (isset($file) && $file = dirname($file)) {
@drupal_rmdir($file);
}
// Don't render the default colorscheme, use the standard theme instead.
if (implode(',', color_get_palette($theme, TRUE)) == implode(',', $palette)) {
variable_del('color_' . $theme . '_palette');
variable_del('color_' . $theme . '_stylesheets');
variable_del('color_' . $theme . '_logo');
variable_del('color_' . $theme . '_files');
variable_del('color_' . $theme . '_screenshot');
return;
}
// Prepare target locations for generated files.
$id = $theme . '-' . substr(hash('sha256', serialize($palette) . microtime()), 0, 8);
$paths['color'] = 'public://color';
$paths['target'] = $paths['color'] . '/' . $id;
foreach ($paths as $path) {
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
}
$paths['target'] = $paths['target'] . '/';
$paths['id'] = $id;
$paths['source'] = drupal_get_path('theme', $theme) . '/';
$paths['files'] = $paths['map'] = array();
// Save palette and logo location.
variable_set('color_' . $theme . '_palette', $palette);
variable_set('color_' . $theme . '_logo', $paths['target'] . 'logo.png');
// Copy over neutral images.
foreach ($info['copy'] as $file) {
$base = drupal_basename($file);
$source = $paths['source'] . $file;
$filepath = file_unmanaged_copy($source, $paths['target'] . $base);
$paths['map'][$file] = $base;
$paths['files'][] = $filepath;
}
// Render new images, if image has been provided.
if ($info['base_image']) {
_color_render_images($theme, $info, $paths, $palette);
}
// Rewrite theme stylesheets.
$css = array();
foreach ($info['css'] as $stylesheet) {
// Build a temporary array with LTR and RTL files.
$files = array();
if (file_exists($paths['source'] . $stylesheet)) {
$files[] = $stylesheet;
$rtl_file = str_replace('.css', '-rtl.css', $stylesheet);
if (file_exists($paths['source'] . $rtl_file)) {
$files[] = $rtl_file;
}
}
foreach ($files as $file) {
// Aggregate @imports recursively for each configured top level CSS file
// without optimization. Aggregation and optimization will be
// handled by drupal_build_css_cache() only.
$style = drupal_load_stylesheet($paths['source'] . $file, FALSE);
// Return the path to where this CSS file originated from, stripping
// off the name of the file at the end of the path.
$base = base_path() . dirname($paths['source'] . $file) . '/';
_drupal_build_css_path(NULL, $base);
// Prefix all paths within this CSS file, ignoring absolute paths.
$style = preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $style);
// Rewrite stylesheet with new colors.
$style = _color_rewrite_stylesheet($theme, $info, $paths, $palette, $style);
$base_file = drupal_basename($file);
$css[] = $paths['target'] . $base_file;
_color_save_stylesheet($paths['target'] . $base_file, $style, $paths);
}
}
// Maintain list of files.
variable_set('color_' . $theme . '_stylesheets', $css);
variable_set('color_' . $theme . '_files', $paths['files']);
}
/**
* Rewrites the stylesheet to match the colors in the palette.
*/
function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) {
$themes = list_themes();
// Prepare color conversion table.
$conversion = $palette;
foreach ($conversion as $k => $v) {
$conversion[$k] = drupal_strtolower($v);
}
$default = color_get_palette($theme, TRUE);
// Split off the "Don't touch" section of the stylesheet.
$split = "Color Module: Don't touch";
if (strpos($style, $split) !== FALSE) {
list($style, $fixed) = explode($split, $style);
}
// Find all colors in the stylesheet and the chunks in between.
$style = preg_split('/(#[0-9a-f]{6}|#[0-9a-f]{3})/i', $style, -1, PREG_SPLIT_DELIM_CAPTURE);
$is_color = FALSE;
$output = '';
$base = 'base';
// Iterate over all the parts.
foreach ($style as $chunk) {
if ($is_color) {
$chunk = drupal_strtolower($chunk);
// Check if this is one of the colors in the default palette.
if ($key = array_search($chunk, $default)) {
$chunk = $conversion[$key];
}
// Not a pre-set color. Extrapolate from the base.
else {
$chunk = _color_shift($palette[$base], $default[$base], $chunk, $info['blend_target']);
}
}
else {
// Determine the most suitable base color for the next color.
// 'a' declarations. Use link.
if (preg_match('@[^a-z0-9_-](a)[^a-z0-9_-][^/{]*{[^{]+$@i', $chunk)) {
$base = 'link';
}
// 'color:' styles. Use text.
elseif (preg_match('/(? $after) {
$before = base_path() . $paths['source'] . $before;
$before = preg_replace('`(^|/)(?!../)([^/]+)/../`', '$1', $before);
$output = str_replace($before, $after, $output);
}
return $output;
}
/**
* Saves the rewritten stylesheet to disk.
*/
function _color_save_stylesheet($file, $style, &$paths) {
$filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE);
$paths['files'][] = $filepath;
// Set standard file permissions for webserver-generated files.
drupal_chmod($file);
}
/**
* Renders images that match a given palette.
*/
function _color_render_images($theme, &$info, &$paths, $palette) {
// Prepare template image.
$source = $paths['source'] . '/' . $info['base_image'];
$source = imagecreatefrompng($source);
$width = imagesx($source);
$height = imagesy($source);
// Prepare target buffer.
$target = imagecreatetruecolor($width, $height);
imagealphablending($target, TRUE);
// Fill regions of solid color.
foreach ($info['fill'] as $color => $fill) {
imagefilledrectangle($target, $fill[0], $fill[1], $fill[0] + $fill[2], $fill[1] + $fill[3], _color_gd($target, $palette[$color]));
}
// Render gradients.
foreach ($info['gradients'] as $gradient) {
// Get direction of the gradient.
if (isset($gradient['direction']) && $gradient['direction'] == 'horizontal') {
// Horizontal gradient.
for ($x = 0; $x < $gradient['dimension'][2]; $x++) {
$color = _color_blend($target, $palette[$gradient['colors'][0]], $palette[$gradient['colors'][1]], $x / ($gradient['dimension'][2] - 1));
imagefilledrectangle($target, ($gradient['dimension'][0] + $x), $gradient['dimension'][1], ($gradient['dimension'][0] + $x + 1), ($gradient['dimension'][1] + $gradient['dimension'][3]), $color);
}
}
else {
// Vertical gradient.
for ($y = 0; $y < $gradient['dimension'][3]; $y++) {
$color = _color_blend($target, $palette[$gradient['colors'][0]], $palette[$gradient['colors'][1]], $y / ($gradient['dimension'][3] - 1));
imagefilledrectangle($target, $gradient['dimension'][0], $gradient['dimension'][1] + $y, $gradient['dimension'][0] + $gradient['dimension'][2], $gradient['dimension'][1] + $y + 1, $color);
}
}
}
// Blend over template.
imagecopy($target, $source, 0, 0, 0, 0, $width, $height);
// Clean up template image.
imagedestroy($source);
// Cut out slices.
foreach ($info['slices'] as $file => $coord) {
list($x, $y, $width, $height) = $coord;
$base = drupal_basename($file);
$image = drupal_realpath($paths['target'] . $base);
// Cut out slice.
if ($file == 'screenshot.png') {
$slice = imagecreatetruecolor(150, 90);
imagecopyresampled($slice, $target, 0, 0, $x, $y, 150, 90, $width, $height);
variable_set('color_' . $theme . '_screenshot', $image);
}
else {
$slice = imagecreatetruecolor($width, $height);
imagecopy($slice, $target, 0, 0, $x, $y, $width, $height);
}
// Save image.
imagepng($slice, $image);
imagedestroy($slice);
$paths['files'][] = $image;
// Set standard file permissions for webserver-generated files
drupal_chmod($image);
// Build before/after map of image paths.
$paths['map'][$file] = $base;
}
// Clean up target buffer.
imagedestroy($target);
}
/**
* Shifts a given color, using a reference pair and a target blend color.
*
* Note: this function is significantly different from the JS version, as it
* is written to match the blended images perfectly.
*
* Constraint: if (ref2 == target + (ref1 - target) * delta) for some fraction
* delta then (return == target + (given - target) * delta).
*
* Loose constraint: Preserve relative positions in saturation and luminance
* space.
*/
function _color_shift($given, $ref1, $ref2, $target) {
// We assume that ref2 is a blend of ref1 and target and find
// delta based on the length of the difference vectors.
// delta = 1 - |ref2 - ref1| / |white - ref1|
$target = _color_unpack($target, TRUE);
$ref1 = _color_unpack($ref1, TRUE);
$ref2 = _color_unpack($ref2, TRUE);
$numerator = 0;
$denominator = 0;
for ($i = 0; $i < 3; ++$i) {
$numerator += ($ref2[$i] - $ref1[$i]) * ($ref2[$i] - $ref1[$i]);
$denominator += ($target[$i] - $ref1[$i]) * ($target[$i] - $ref1[$i]);
}
$delta = ($denominator > 0) ? (1 - sqrt($numerator / $denominator)) : 0;
// Calculate the color that ref2 would be if the assumption was true.
for ($i = 0; $i < 3; ++$i) {
$ref3[$i] = $target[$i] + ($ref1[$i] - $target[$i]) * $delta;
}
// If the assumption is not true, there is a difference between ref2 and ref3.
// We measure this in HSL space. Notation: x' = hsl(x).
$ref2 = _color_rgb2hsl($ref2);
$ref3 = _color_rgb2hsl($ref3);
for ($i = 0; $i < 3; ++$i) {
$shift[$i] = $ref2[$i] - $ref3[$i];
}
// Take the given color, and blend it towards the target.
$given = _color_unpack($given, TRUE);
for ($i = 0; $i < 3; ++$i) {
$result[$i] = $target[$i] + ($given[$i] - $target[$i]) * $delta;
}
// Finally, we apply the extra shift in HSL space.
// Note: if ref2 is a pure blend of ref1 and target, then |shift| = 0.
$result = _color_rgb2hsl($result);
for ($i = 0; $i < 3; ++$i) {
$result[$i] = min(1, max(0, $result[$i] + $shift[$i]));
}
$result = _color_hsl2rgb($result);
// Return hex color.
return _color_pack($result, TRUE);
}
/**
* Converts a hex triplet into a GD color.
*/
function _color_gd($img, $hex) {
$c = array_merge(array($img), _color_unpack($hex));
return call_user_func_array('imagecolorallocate', $c);
}
/**
* Blends two hex colors and returns the GD color.
*/
function _color_blend($img, $hex1, $hex2, $alpha) {
$in1 = _color_unpack($hex1);
$in2 = _color_unpack($hex2);
$out = array($img);
for ($i = 0; $i < 3; ++$i) {
$out[] = $in1[$i] + ($in2[$i] - $in1[$i]) * $alpha;
}
return call_user_func_array('imagecolorallocate', $out);
}
/**
* Converts a hex color into an RGB triplet.
*/
function _color_unpack($hex, $normalize = FALSE) {
if (strlen($hex) == 4) {
$hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
}
$c = hexdec($hex);
for ($i = 16; $i >= 0; $i -= 8) {
$out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1);
}
return $out;
}
/**
* Converts an RGB triplet to a hex color.
*/
function _color_pack($rgb, $normalize = FALSE) {
$out = 0;
foreach ($rgb as $k => $v) {
$out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8));
}
return '#' . str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
}
/**
* Converts an HSL triplet into RGB.
*/
function _color_hsl2rgb($hsl) {
$h = $hsl[0];
$s = $hsl[1];
$l = $hsl[2];
$m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
$m1 = $l * 2 - $m2;
return array(
_color_hue2rgb($m1, $m2, $h + 0.33333),
_color_hue2rgb($m1, $m2, $h),
_color_hue2rgb($m1, $m2, $h - 0.33333),
);
}
/**
* Helper function for _color_hsl2rgb().
*/
function _color_hue2rgb($m1, $m2, $h) {
$h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
if ($h * 2 < 1) return $m2;
if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
return $m1;
}
/**
* Converts an RGB triplet to HSL.
*/
function _color_rgb2hsl($rgb) {
$r = $rgb[0];
$g = $rgb[1];
$b = $rgb[2];
$min = min($r, min($g, $b));
$max = max($r, max($g, $b));
$delta = $max - $min;
$l = ($min + $max) / 2;
$s = 0;
if ($l > 0 && $l < 1) {
$s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
}
$h = 0;
if ($delta > 0) {
if ($max == $r && $max != $g) $h += ($g - $b) / $delta;
if ($max == $g && $max != $b) $h += (2 + ($b - $r) / $delta);
if ($max == $b && $max != $r) $h += (4 + ($r - $g) / $delta);
$h /= 6;
}
return array($h, $s, $l);
}
www-test-master-modules/modules/color/color.test 0000664 0000000 0000000 00000010265 12501305533 0022424 0 ustar 00root root 0000000 0000000 'Color functionality',
'description' => 'Modify the Bartik and Garland theme colors and make sure the changes are reflected on the frontend',
'group' => 'Color',
);
}
function setUp() {
parent::setUp('color');
// Create users.
$this->big_user = $this->drupalCreateUser(array('administer themes'));
// This tests the color module in both Bartik and Garland.
$this->themes = array(
'bartik' => array(
'palette_input' => 'palette[bg]',
'scheme' => 'slate',
'scheme_color' => '#3b3b3b',
),
'garland' => array(
'palette_input' => 'palette[link]',
'scheme' => 'greenbeam',
'scheme_color' => '#0c7a00',
),
);
theme_enable(array_keys($this->themes));
// Array filled with valid and not valid color values
$this->colorTests = array(
'#000' => TRUE,
'#123456' => TRUE,
'#abcdef' => TRUE,
'#0' => FALSE,
'#00' => FALSE,
'#0000' => FALSE,
'#00000' => FALSE,
'123456' => FALSE,
'#00000g' => FALSE,
);
}
/**
* Tests the Color module functionality.
*/
function testColor() {
foreach ($this->themes as $theme => $test_values) {
$this->_testColor($theme, $test_values);
}
}
/**
* Tests the Color module functionality using the given theme.
*/
function _testColor($theme, $test_values) {
variable_set('theme_default', $theme);
$settings_path = 'admin/appearance/settings/' . $theme;
$this->drupalLogin($this->big_user);
$this->drupalGet($settings_path);
$this->assertResponse(200);
$edit['scheme'] = '';
$edit[$test_values['palette_input']] = '#123456';
$this->drupalPost($settings_path, $edit, t('Save configuration'));
$this->drupalGet('');
$stylesheets = variable_get('color_' . $theme . '_stylesheets', array());
$this->assertPattern('|' . file_create_url($stylesheets[0]) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
$stylesheet_content = join("\n", file($stylesheets[0]));
$this->assertTrue(strpos($stylesheet_content, 'color: #123456') !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
$this->drupalGet($settings_path);
$this->assertResponse(200);
$edit['scheme'] = $test_values['scheme'];
$this->drupalPost($settings_path, $edit, t('Save configuration'));
$this->drupalGet('');
$stylesheets = variable_get('color_' . $theme . '_stylesheets', array());
$stylesheet_content = join("\n", file($stylesheets[0]));
$this->assertTrue(strpos($stylesheet_content, 'color: ' . $test_values['scheme_color']) !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
// Test with aggregated CSS turned on.
variable_set('preprocess_css', 1);
$this->drupalGet('');
$stylesheets = variable_get('drupal_css_cache_files', array());
$stylesheet_content = '';
foreach ($stylesheets as $key => $uri) {
$stylesheet_content .= join("\n", file(drupal_realpath($uri)));
}
$this->assertTrue(strpos($stylesheet_content, 'public://') === FALSE, 'Make sure the color paths have been translated to local paths. (' . $theme . ')');
variable_set('preprocess_css', 0);
}
/**
* Tests whether the provided color is valid.
*/
function testValidColor() {
variable_set('theme_default', 'bartik');
$settings_path = 'admin/appearance/settings/bartik';
$this->drupalLogin($this->big_user);
$edit['scheme'] = '';
foreach ($this->colorTests as $color => $is_valid) {
$edit['palette[bg]'] = $color;
$this->drupalPost($settings_path, $edit, t('Save configuration'));
if($is_valid) {
$this->assertText('The configuration options have been saved.');
}
else {
$this->assertText('Main background must be a valid hexadecimal CSS color value.');
}
}
}
}
www-test-master-modules/modules/color/images/ 0000775 0000000 0000000 00000000000 12501305533 0021646 5 ustar 00root root 0000000 0000000 www-test-master-modules/modules/color/images/hook-rtl.png 0000664 0000000 0000000 00000000164 12501305533 0024114 0 ustar 00root root 0000000 0000000 ‰PNG
IHDR Q Õ@u÷ PLTE DDDz˜2P tRNS @æØf IDAT™c`@ÿP .€ªê?Nuøue oÖþ— :Ç IEND®B`‚ www-test-master-modules/modules/color/images/hook.png 0000664 0000000 0000000 00000000164 12501305533 0023315 0 ustar 00root root 0000000 0000000 ‰PNG
IHDR Q Õ@u÷ PLTEÿÿÿDDDÇõùê tRNS @æØf IDAT™c`@ÿQ .€ªê?Nuøue L×?6Z IEND®B`‚ www-test-master-modules/modules/color/images/lock.png 0000664 0000000 0000000 00000000346 12501305533 0023307 0 ustar 00root root 0000000 0000000 ‰PNG
IHDR
2 °¡6 'PLTEé°aaa¹¹¹ñññÂÂÂýýýÝÝÝ xvgÿý¾ÿßúúúµµµ†-)= tRNS @æØf mIDATxÚÅÍÑ€ …á
±ÞÿyCƉÕÚò®ÿÆ}ÈtùJFºª6»êAQñÙ‹$3ìíMl¯#ȶ ?uB-“ÇÿG£…™W6»*AbòÙ«ŠR”*GÕ^%.²-ÈOžÐšÑý¿;;ÂsHÆ IEND®B`‚ www-test-master-modules/modules/color/preview.html 0000664 0000000 0000000 00000001062 12501305533 0022747 0 ustar 00root root 0000000 0000000
Lorem ipsum dolor
Sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.