summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Great Git Migration2006-03-31 06:43:47 +0000
committerThe Great Git Migration2006-03-31 06:43:47 +0000
commit1e65cfca48330d542cb70ebbc45dea77e5ff8474 (patch)
treed4d0af1b64336550010b58617ce2964c04125490
parent603a6618ee17ad8d49cc1c4929133644da243f55 (diff)
This commit was manufactured as part of Drupal's Great Git Migration to4.7.0-rc-1
create tag 'DRUPAL-4-7-0-RC-1'. Sprout from master 2006-03-31 06:43:46 UTC Gerhard Killesreiter <killes_www_drop_org@227.no-reply.drupal.org> '#28625, Forum vocabulary does not handle standard vocabulary features correctly, removed these features by means of formapi, patch by profix898, with some love by dopry and chx' Delete: modules/aggregator/aggregator.module modules/archive/archive.module modules/block/block.module modules/blog/blog.module modules/blogapi/blogapi.module modules/book/book.module modules/comment/comment.module modules/contact/contact.module modules/drupal/drupal.module modules/filter/filter.module modules/forum/forum.module modules/help/help.module modules/legacy/legacy.module modules/locale/locale.module modules/menu/menu.module modules/node/node.module modules/page/page.module modules/path/path.module modules/ping/ping.module modules/poll/poll.module modules/profile/profile.module modules/search/search.module modules/statistics/statistics.module modules/story/story.module modules/system/system.module modules/taxonomy/taxonomy.module modules/throttle/throttle.module modules/tracker/tracker.module modules/upload/upload.module modules/user/user.module modules/watchdog/watchdog.module
-rw-r--r--modules/aggregator/aggregator.module1361
-rw-r--r--modules/archive/archive.module291
-rw-r--r--modules/block/block.module633
-rw-r--r--modules/blog/blog.module305
-rw-r--r--modules/blogapi/blogapi.module737
-rw-r--r--modules/book/book.module1045
-rw-r--r--modules/comment/comment.module1725
-rw-r--r--modules/contact/contact.module551
-rw-r--r--modules/drupal/drupal.module364
-rw-r--r--modules/filter/filter.module1347
-rw-r--r--modules/forum/forum.module1120
-rw-r--r--modules/help/help.module135
-rw-r--r--modules/legacy/legacy.module202
-rw-r--r--modules/locale/locale.module417
-rw-r--r--modules/menu/menu.module763
-rw-r--r--modules/node/node.module2421
-rw-r--r--modules/page/page.module100
-rw-r--r--modules/path/path.module351
-rw-r--r--modules/ping/ping.module68
-rw-r--r--modules/poll/poll.module503
-rw-r--r--modules/profile/profile.module776
-rw-r--r--modules/search/search.module1228
-rw-r--r--modules/statistics/statistics.module493
-rw-r--r--modules/story/story.module86
-rw-r--r--modules/system/system.module1256
-rw-r--r--modules/taxonomy/taxonomy.module1366
-rw-r--r--modules/throttle/throttle.module158
-rw-r--r--modules/tracker/tracker.module129
-rw-r--r--modules/upload/upload.module649
-rw-r--r--modules/user/user.module2088
-rw-r--r--modules/watchdog/watchdog.module194
31 files changed, 0 insertions, 22862 deletions
diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module
deleted file mode 100644
index f34c401..0000000
--- a/modules/aggregator/aggregator.module
+++ /dev/null
@@ -1,1361 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Used to aggregate syndicated content (RSS and RDF).
- */
-
-/**
- * Implementation of hook_help().
- */
-function aggregator_help($section) {
- switch ($section) {
- case 'admin/help#aggregator':
- $output = '<p>'. t('The news aggregator is a powerful on-site RSS syndicator/news reader that can gather fresh content from news sites and weblogs around the web.') .'</p>';
- $output .= '<p>'. t('Users can view the latest news chronologically in the <a href="%aggregator">main news aggregator display</a> or by <a href="%aggregator-sources">source</a>. Administrators can add, edit and delete feeds and choose how often to check for newly updated news for each individual feed. Administrators can also tag individual feeds with categories, offering selective grouping of some feeds into separate displays. Listings of the latest news for individual sources or categorized sources can be enabled as blocks for display in the sidebar through the <a href="%admin-block">block administration page</a>. The news aggregator requires cron to check for the latest news from the sites to which you have subscribed. Drupal also provides a <a href="%aggregator-opml">machine-readable OPML file</a> of all of your subscribed feeds.', array('%aggregator' => url('aggregator'), '%aggregator-sources' => url('aggregator/sources'), '%admin-block' => url('admin/block'), '%aggregator-opml' => url('aggregator/opml'))) .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>administer your list of news feeds <a href="%admin-aggregator">administer &gt;&gt; aggregator</a>.</li>
-<li>add a new feed <a href="%admin-aggregator-add-feed">administer &gt;&gt; aggregator &gt;&gt; add feed</a>.</li>
-<li>add a new category <a href="%admin-aggregator-add-category">administer &gt;&gt; aggregator &gt;&gt; add category</a>.</li>
-<li>configure global settings for the news aggregator <a href="%admin-settings-aggregator">administer &gt;&gt; settings &gt;&gt; aggregator</a>.</li>
-<li>control access to the aggregator module through access permissions <a href="%admin-access">administer &gt;&gt; access control &gt;&gt; permissions</a>.</li>
-<li>set permissions to access new feeds for user roles such as anonymous users at <a href="%admin-access">administer &gt;&gt; access control</a>.</li>
-<li>view the <a href="%aggregator">aggregator page</a>.</li>
-</ul>
-', array('%admin-aggregator' => url('admin/aggregator'), '%admin-aggregator-add-feed' => url('admin/aggregator/add/feed'), '%admin-aggregator-add-category' => url('admin/aggregator/add/category'), '%admin-settings-aggregator' => url('admin/settings/aggregator'), '%admin-access' => url('admin/access'), '%aggregator' => url('aggregator')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%aggregator">Aggregator page</a>.', array('%aggregator' => 'http://drupal.org/handbook/modules/aggregator/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Aggregates syndicated content (RSS and RDF feeds).');
- case 'admin/aggregator':
- return t('<p>Thousands of sites (particularly news sites and weblogs) publish their latest headlines and/or stories in a machine-readable format so that other sites can easily link to them. This content is usually in the form of an <a href="http://blogs.law.harvard.edu/tech/rss">RSS</a> feed (which is an XML-based syndication standard). To display the feed or category in a block you must decide how many items to show by editing the feed or block and turning on the <a href="%block">feed\'s block</a>.</p>', array('%block' => url('admin/block')));
- case 'admin/aggregator/add/feed':
- return t('<p>Add a site that has an RSS/RDF feed. The URL is the full path to the RSS feed file. For the feed to update automatically you must run "cron.php" on a regular basis. If you already have a feed with the URL you are planning to use, the system will not accept another feed with the same URL.</p>');
- case 'admin/aggregator/add/category':
- return t('<p>Categories provide a way to group items from different news feeds together. Each news category has its own feed page and block. For example, you could tag various sport-related feeds as belonging to a category called <em>Sports</em>. News items can be added to a category automatically by setting a feed to automatically place its item into that category, or by using the categorize items link in any listing of news items.</p>');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function aggregator_menu($may_cache) {
- $items = array();
- $edit = user_access('administer news feeds');
- $view = user_access('access news feeds');
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/aggregator',
- 'title' => t('aggregator'),
- 'callback' => 'aggregator_admin_overview',
- 'access' => $edit);
- $items[] = array('path' => 'admin/aggregator/add/feed',
- 'title' => t('add feed'),
- 'callback' => 'aggregator_form_feed',
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/aggregator/add/category',
- 'title' => t('add category'),
- 'callback' => 'aggregator_form_category',
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/aggregator/remove',
- 'title' => t('remove items'),
- 'callback' => 'aggregator_admin_remove_feed',
- 'access' => $edit,
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/aggregator/update',
- 'title' => t('update items'),
- 'callback' => 'aggregator_admin_refresh_feed',
- 'access' => $edit,
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/aggregator/list',
- 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10);
- $items[] = array('path' => 'aggregator',
- 'title' => t('news aggregator'),
- 'callback' => 'aggregator_page_last',
- 'access' => $view,
- 'weight' => 5);
- $items[] = array('path' => 'aggregator/sources',
- 'title' => t('sources'),
- 'callback' => 'aggregator_page_sources',
- 'access' => $view);
- $items[] = array('path' => 'aggregator/categories',
- 'title' => t('categories'),
- 'callback' => 'aggregator_page_categories',
- 'access' => $view,
- 'type' => MENU_ITEM_GROUPING);
- $items[] = array('path' => 'aggregator/rss',
- 'title' => t('RSS feed'),
- 'callback' => 'aggregator_page_rss',
- 'access' => $view,
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'aggregator/opml',
- 'title' => t('OPML feed'),
- 'callback' => 'aggregator_page_opml',
- 'access' => $view,
- 'type' => MENU_CALLBACK);
- }
- else {
- if (arg(0) == 'aggregator' && is_numeric(arg(2))) {
- if (arg(1) == 'sources') {
- $feed = aggregator_get_feed(arg(2));
- if ($feed) {
- $items[] = array('path' => 'aggregator/sources/'. $feed['fid'],
- 'title' => $feed->title,
- 'callback' => 'aggregator_page_source',
- 'access' => $view);
- $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/view',
- 'title' => t('view'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10);
- $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/categorize',
- 'title' => t('categorize'),
- 'callback' => 'aggregator_page_source',
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/configure',
- 'title' => t('configure'),
- 'callback' => 'aggregator_form_feed',
- 'callback arguments' => array($feed),
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1);
- }
- }
- else if (arg(1) == 'categories') {
- $category = aggregator_get_category(arg(2));
- if ($category) {
- $items[] = array('path' => 'aggregator/categories/'. $category['cid'],
- 'title' => $category->title,
- 'callback' => 'aggregator_page_category',
- 'access' => $view);
- $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/view',
- 'title' => t('view'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10);
- $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/categorize',
- 'title' => t('categorize'),
- 'callback' => 'aggregator_page_category',
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/configure',
- 'title' => t('configure'),
- 'callback' => 'aggregator_form_category',
- 'callback arguments' => array($category),
- 'access' => $edit,
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1);
- }
- }
- }
- else if (arg(1) == 'aggregator' && is_numeric(arg(4))) {
- if (arg(3) == 'feed') {
- $feed = aggregator_get_feed(arg(4));
- if ($feed) {
- $items[] = array('path' => 'admin/aggregator/edit/feed/'. $feed['fid'],
- 'title' => t('edit feed'),
- 'callback' => 'aggregator_form_feed',
- 'callback arguments' => array($feed),
- 'access' => $edit,
- 'type' => MENU_CALLBACK);
- }
- }
- else {
- $category = aggregator_get_category(arg(4));
- if ($category) {
- $items[] = array('path' => 'admin/aggregator/edit/category/'. $category['cid'],
- 'title' => t('edit category'),
- 'callback' => 'aggregator_form_category',
- 'callback arguments' => array($category),
- 'access' => $edit,
- 'type' => MENU_CALLBACK);
- }
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_settings().
- */
-function aggregator_settings() {
- $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
- $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
-
- $form['aggregator_allowed_html_tags'] = array(
- '#type' => 'textfield', '#title' => t('Allowed HTML tags'), '#size' => 80, '#maxlength' => 255,
- '#default_value' => variable_get('aggregator_allowed_html_tags', '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'),
- '#description' => t('The list of tags which are allowed in feeds, i.e., which will not be removed by Drupal.')
- );
-
- $form['aggregator_summary_items'] = array(
- '#type' => 'select', '#title' => t('Items shown in sources and categories pages') ,
- '#default_value' => variable_get('aggregator_summary_items', 3), '#options' => $items,
- '#description' => t('The number of items which will be shown with each feed or category in the feed and category summary pages.')
- );
-
- $form['aggregator_clear'] = array(
- '#type' => 'select', '#title' => t('Discard news items older than'),
- '#default_value' => variable_get('aggregator_clear', 9676800), '#options' => $period,
- '#description' => t('Older news items will be automatically discarded. Requires crontab.')
- );
-
- $form['aggregator_category_selector'] = array(
- '#type' => 'radios', '#title' => t('Category selection type'), '#default_value' => variable_get('aggregator_category_selector', 'check'),
- '#options' => array('checkboxes' => t('checkboxes'), 'select' => t('multiple selector')),
- '#description' => t('The type of category selection widget which is shown on categorization pages. Checkboxes are easier to use; a multiple selector is good for working with large numbers of categories.')
- );
- return $form;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function aggregator_perm() {
- return array('administer news feeds', 'access news feeds');
-}
-
-/**
- * Implementation of hook_cron().
- *
- * Checks news feeds for updates once their refresh interval has elapsed.
- */
-function aggregator_cron() {
- $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < %d', time());
- while ($feed = db_fetch_array($result)) {
- aggregator_refresh($feed);
- }
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates blocks for the latest news items in each category and feed.
- */
-function aggregator_block($op, $delta = 0, $edit = array()) {
- if (user_access('access news feeds')) {
- if ($op == 'list') {
- $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
- while ($category = db_fetch_object($result)) {
- $block['category-'. $category->cid]['info'] = t('%title category latest items', array('%title' => theme('placeholder', $category->title)));
- }
- $result = db_query('SELECT fid, title FROM {aggregator_feed} ORDER BY fid');
- while ($feed = db_fetch_object($result)) {
- $block['feed-'. $feed->fid]['info'] = t('%title feed latest items', array('%title' => theme('placeholder', $feed->title)));
- }
- }
- else if ($op == 'configure') {
- list($type, $id) = explode('-', $delta);
- if ($type == 'category') {
- $value = db_result(db_query('SELECT block FROM {aggregator_category} WHERE cid = %d', $id));
- }
- else {
- $value = db_result(db_query('SELECT block FROM {aggregator_feed} WHERE fid = %d', $id));
- }
- $form['block'] = array('#type' => 'select', '#title' => t('Number of news items in block'), '#default_value' => $value, '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
- return $form;
- }
- else if ($op == 'save') {
- list($type, $id) = explode('-', $delta);
- if ($type == 'category') {
- $value = db_query('UPDATE {aggregator_category} SET block = %d WHERE cid = %d', $edit['block'], $id);
- }
- else {
- $value = db_query('UPDATE {aggregator_feed} SET block = %d WHERE fid = %d', $edit['block'], $id);
- }
- }
- else if ($op == 'view') {
- list($type, $id) = explode('-', $delta);
- switch ($type) {
- case 'feed':
- if ($feed = db_fetch_object(db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE fid = %d', $id))) {
- $block['subject'] = check_plain($feed->title);
- $result = db_query_range('SELECT * FROM {aggregator_item} WHERE fid = %d ORDER BY timestamp DESC, iid DESC', $feed->fid, 0, $feed->block);
- $block['content'] = '<div class="more-link">'. l(t('more'), 'aggregator/sources/'. $feed->fid, array('title' => t('View this feed\'s recent news.'))) .'</div>';
- }
- break;
-
- case 'category':
- if ($category = db_fetch_object(db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = %d', $id))) {
- $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 = %d ORDER BY i.timestamp DESC, i.iid DESC', $category->cid, 0, $category->block);
- $block['content'] = '<div class="more-link">'. l(t('more'), 'aggregator/categories/'. $category->cid, array('title' => t('View this category\'s recent news.'))) .'</div>';
- }
- break;
- }
- $items = array();
- while ($item = db_fetch_object($result)) {
- $items[] = theme('aggregator_block_item', $item);
- }
- $block['content'] = theme('item_list', $items) . $block['content'];
- }
- return $block;
- }
-}
-
-/**
- * Generate a form to add/edit/delete aggregator categories.
- */
- function aggregator_form_category($edit = array()) {
- $form['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $edit['title'],
- '#maxlength' => 64,
- '#required' => TRUE,
- );
- $form['description'] = array('#type' => 'textarea',
- '#title' => t('Description'),
- '#default_value' => $edit['description'],
- );
- $form['submit'] = array('#type' => 'submit', '#value' =>t('Submit'));
-
- if ($edit['cid']) {
- $form['delete'] = array('#type' => 'submit', '#value' =>t('Delete'));
- $form['cid'] = array('#type' => 'hidden', '#value' => $edit['cid']);
- }
-
- return drupal_get_form('aggregator_form_category', $form);
-}
-
-/**
- * Validate aggregator_form_feed form submissions.
- */
-function aggregator_form_category_validate($form_id, $form_values) {
- if ($_POST['op'] == t('Submit')) {
- // Check for duplicate titles
- if (isset($form_values['cid'])) {
- $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s' AND cid != %d", $form_values['title'], $form_values['cid']));
- }
- else {
- $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s'", $form_values['title']));
- }
- if ($category) {
- form_set_error('title', t('A category named %category already exists. Please enter a unique title.', array('%category' => theme('placeholder', $form_values['title']))));
- }
- }
-}
-
-/**
- * Process aggregator_form_category form submissions.
- * @todo Add delete confirmation dialog.
- */
-function aggregator_form_category_submit($form_id, $form_values) {
- if ($_POST['op'] == t('Delete')) {
- $title = $form_values['title'];
- // Unset the title:
- unset($form_values['title']);
- }
- aggregator_save_category($form_values);
- menu_rebuild();
- if (isset($form_values['cid'])) {
- if (isset($form_values['title'])) {
- drupal_set_message(t('The category %category has been updated.', array('%category' => theme('placeholder', $form_values['title']))));
- if (arg(0) == 'admin') {
- return 'admin/aggregator/';
- }
- else {
- return 'aggregator/categories/'. $form_values['cid'];
- }
- }
- else {
- watchdog('aggregator', t('Category %category deleted.', array('%category' => theme('placeholder', $title))));
- drupal_set_message(t('The category %category has been deleted.', array('%category' => theme('placeholder', $title))));
- if (arg(0) == 'admin') {
- return 'admin/aggregator/';
- }
- else {
- return 'aggregator/categories/';
- }
- }
- }
- else {
- watchdog('aggregator', t('Category %category added.', array('%category' => theme('placeholder', $form_values['title']))), WATCHDOG_NOTICE, l(t('view'), 'admin/aggregator'));
- drupal_set_message(t('The category %category has been added.', array('%category' => theme('placeholder', $form_values['title']))));
- }
-}
-
-/**
- * Add/edit/delete aggregator categories.
- */
-function aggregator_save_category($edit) {
- if ($edit['cid'] && $edit['title']) {
- db_query("UPDATE {aggregator_category} SET title = '%s', description = '%s' WHERE cid = %d", $edit['title'], $edit['description'], $edit['cid']);
- }
- else if ($edit['cid']) {
- db_query('DELETE FROM {aggregator_category} WHERE cid = %d', $edit['cid']);
- }
- else if ($edit['title']) {
- // A single unique id for bundles and feeds, to use in blocks
- $next_id = db_next_id('{aggregator_category}_cid');
- db_query("INSERT INTO {aggregator_category} (cid, title, description, block) VALUES (%d, '%s', '%s', 5)", $next_id, $edit['title'], $edit['description']);
- }
-}
-
-/**
- * Generate a form to add/edit feed sources.
- */
-function aggregator_form_feed($edit = array()) {
- $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
-
- if ($edit['refresh'] == '') {
- $edit['refresh'] = 3600;
- }
-
- $form['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $edit['title'],
- '#maxlength' => 64,
- '#description' => t('The name of the feed; typically the name of the web site you syndicate content from.'),
- '#required' => TRUE,
- );
- $form['url'] = array('#type' => 'textfield',
- '#title' => t('URL'),
- '#default_value' => $edit['url'],
- '#maxlength' => 255,
- '#description' => t('The fully-qualified URL of the feed.'),
- '#required' => TRUE,
- );
- $form['refresh'] = array('#type' => 'select',
- '#title' => t('Update interval'),
- '#default_value' => $edit['refresh'],
- '#options' => $period,
- '#description' => t('The refresh interval indicating how often you want to update this feed. Requires crontab.'),
- );
-
- // Handling of categories:
- $options = array();
- $values = array();
- $categories = db_query('SELECT c.cid, c.title, f.fid FROM {aggregator_category} c LEFT JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = %d ORDER BY title', $edit['fid']);
- while ($category = db_fetch_object($categories)) {
- $options[$category->cid] = $category->title;
- if ($category->fid) $values[] = check_plain($category->cid);
- }
- if ($options) {
- $form['category'] = array('#type' => 'checkboxes',
- '#title' => t('Categorize news items'),
- '#default_value' => $values,
- '#options' => $options,
- '#description' => t('New items in this feed will be automatically filed in the checked categories as they are received.'),
- );
- }
- $form['submit'] = array('#type' => 'submit', '#value' =>t('Submit'));
-
- if ($edit['fid']) {
- $form['delete'] = array('#type' => 'submit', '#value' =>t('Delete'));
- $form['fid'] = array('#type' => 'hidden', '#value' => $edit['fid']);
- }
-
- return drupal_get_form('aggregator_form_feed', $form);
-}
-
-/**
- * Validate aggregator_form_feed form submissions.
- */
-function aggregator_form_feed_validate($form_id, $form_values) {
- if ($_POST['op'] == t('Submit')) {
- // Check for duplicate titles
- if (isset($form_values['fid'])) {
- $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE (title = '%s' OR url='%s') AND fid != %d", $form_values['title'], $form_values['url'], $form_values['fid']);
- }
- else {
- $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = '%s' OR url='%s'", $form_values['title'], $form_values['url']);
- }
- while ($feed = db_fetch_object($result)) {
- if (strcasecmp($feed->title, $form_values['title']) == 0) {
- form_set_error('title', t('A feed named %feed already exists. Please enter a unique title.', array('%feed' => theme('placeholder', $form_values['title']))));
- }
- }
- }
-}
-
-/**
- * Process aggregator_form_feed form submissions.
- * @todo Add delete confirmation dialog.
- */
-function aggregator_form_feed_submit($form_id, $form_values) {
- if ($_POST['op'] == t('Delete')) {
- $title = $form_values['title'];
- // Unset the title:
- unset($form_values['title']);
- }
- aggregator_save_feed($form_values);
- menu_rebuild();
- if (isset($form_values['fid'])) {
- if (isset($form_values['title'])) {
- drupal_set_message(t('The feed %feed has been updated.', array('%feed' => theme('placeholder', $form_values['title']))));
- if (arg(0) == 'admin') {
- return 'admin/aggregator/';
- }
- else {
- return 'aggregator/sources/'. $form_values['fid'];
- }
- }
- else {
- watchdog('aggregator', t('Feed %feed deleted.', array('%feed' => theme('placeholder', $title))));
- drupal_set_message(t('The feed %feed has been deleted.', array('%feed' => theme('placeholder', $title))));
- if (arg(0) == 'admin') {
- return 'admin/aggregator/';
- }
- else {
- return 'aggregator/sources/';
- }
- }
- }
- else {
- watchdog('aggregator', t('Feed %feed added.', array('%feed' => theme('placeholder', $form_values['title']))), WATCHDOG_NOTICE, l(t('view'), 'admin/aggregator'));
- drupal_set_message(t('The feed %feed has been added.', array('%feed' => theme('placeholder', $form_values['title']))));
- }
-}
-
-/**
- * Add/edit/delete an aggregator feed.
- */
-function aggregator_save_feed($edit) {
- if ($edit['fid']) {
- // An existing feed is being modified, delete the category listings.
- db_query('DELETE FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']);
- }
- if ($edit['fid'] && $edit['title']) {
- db_query("UPDATE {aggregator_feed} SET title = '%s', url = '%s', refresh = %d WHERE fid = %d", $edit['title'], $edit['url'], $edit['refresh'], $edit['fid']);
- }
- else if ($edit['fid']) {
- $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $edit['fid']);
- while ($item = db_fetch_object($result)) {
- $items[] = "iid = $item->iid";
- }
- if ($items) {
- db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items));
- }
- db_query('DELETE FROM {aggregator_feed} WHERE fid = %d', $edit['fid']);
- db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $edit['fid']);
- }
- else if ($edit['title']) {
- // A single unique id for bundles and feeds, to use in blocks.
- $edit['fid'] = db_next_id('{aggregator_feed}_fid');
- db_query("INSERT INTO {aggregator_feed} (fid, title, url, refresh, block) VALUES (%d, '%s', '%s', %d, 5)", $edit['fid'], $edit['title'], $edit['url'], $edit['refresh']);
- }
- if ($edit['title']) {
- // The feed is being saved, save the categories as well.
- if ($edit['category']) {
- foreach ($edit['category'] as $cid => $value) {
- if ($value) {
- db_query('INSERT INTO {aggregator_category_feed} (fid, cid) VALUES (%d, %d)', $edit['fid'], $cid);
- }
- }
- }
- }
-}
-
-function aggregator_remove($feed) {
- $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $feed['fid']);
- while ($item = db_fetch_object($result)) {
- $items[] = "iid = $item->iid";
- }
- if ($items) {
- db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items));
- }
- db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $feed['fid']);
- db_query("UPDATE {aggregator_feed} SET checked = 0, etag = '', modified = 0 WHERE fid = %d", $feed['fid']);
- drupal_set_message(t('The news items from %site have been removed.', array('%site' => theme('placeholder', $feed['title']))));
-}
-
-/**
- * Call-back function used by the XML parser.
- */
-function aggregator_element_start($parser, $name, $attributes) {
- global $item, $element, $tag, $items, $channel;
-
- switch ($name) {
- case 'IMAGE':
- case 'TEXTINPUT':
- case 'CONTENT':
- case 'SUMMARY':
- case 'TAGLINE':
- case 'SUBTITLE':
- case 'LOGO':
- case 'INFO':
- $element = $name;
- break;
- case 'ID':
- if ($element != 'ITEM') {
- $element = $name;
- }
- case 'LINK':
- if ($attributes['REL'] == 'alternate') {
- if ($element == 'ITEM') {
- $items[$item]['LINK'] = $attributes['HREF'];
- }
- else {
- $channel['LINK'] = $attributes['HREF'];
- }
- }
- break;
- case 'ITEM':
- $element = $name;
- $item += 1;
- break;
- case 'ENTRY':
- $element = 'ITEM';
- $item += 1;
- break;
- }
-
- $tag = $name;
-}
-
-/**
- * Call-back function used by the XML parser.
- */
-function aggregator_element_end($parser, $name) {
- global $element;
-
- switch ($name) {
- case 'IMAGE':
- case 'TEXTINPUT':
- case 'ITEM':
- case 'ENTRY':
- case 'CONTENT':
- case 'INFO':
- $element = '';
- break;
- case 'ID':
- if ($element == 'ID') {
- $element = '';
- }
- }
-}
-
-/**
- * Call-back function used by the XML parser.
- */
-function aggregator_element_data($parser, $data) {
- global $channel, $element, $items, $item, $image, $tag;
-
- switch ($element) {
- case 'ITEM':
- $items[$item][$tag] .= $data;
- break;
- case 'IMAGE':
- case 'LOGO':
- $image[$tag] .= $data;
- break;
- case 'LINK':
- if ($data) {
- $items[$item][$tag] .= $data;
- }
- break;
- case 'CONTENT':
- $items[$item]['CONTENT'] .= $data;
- break;
- case 'SUMMARY':
- $items[$item]['SUMMARY'] .= $data;
- break;
- case 'TAGLINE':
- case 'SUBTITLE':
- $channel['DESCRIPTION'] .= $data;
- break;
- case 'INFO':
- case 'ID':
- case 'TEXTINPUT':
- // The sub-element is not supported. However, we must recognize
- // it or its contents will end up in the item array.
- break;
- default:
- $channel[$tag] .= $data;
- }
-}
-
-/**
- * Checks a news feed for new items.
- */
-function aggregator_refresh($feed) {
- global $channel, $image;
-
- // Generate conditional GET headers.
- $headers = array();
- if ($feed['etag']) {
- $headers['If-None-Match'] = $feed['etag'];
- }
- if ($feed['modified']) {
- $headers['If-Modified-Since'] = gmdate('D, d M Y H:i:s', $feed['modified']) .' GMT';
- }
-
- // Request feed.
- $result = drupal_http_request($feed['url'], $headers);
-
- // Process HTTP response code.
- switch ($result->code) {
- case 304:
- db_query('UPDATE {aggregator_feed} SET checked = %d WHERE fid = %d', time(), $feed['fid']);
- drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => theme('placeholder', $feed['title']))));
- break;
- case 301:
- $feed['url'] = $result->redirect_url;
- watchdog('aggregator', t('Updated URL for feed %title to %url.', array('%title' => theme('placeholder', $feed['title']), '%url' => theme('placeholder', $feed['url']))));
- break;
-
- case 200:
- case 302:
- case 307:
- // Filter the input data:
- if (aggregator_parse_feed($result->data, $feed)) {
-
- if ($result->headers['Last-Modified']) {
- $modified = strtotime($result->headers['Last-Modified']);
- }
-
- /*
- ** Prepare the channel data:
- */
-
- foreach ($channel as $key => $value) {
- $channel[$key] = trim($value);
- }
-
- /*
- ** Prepare the image data (if any):
- */
-
- foreach ($image as $key => $value) {
- $image[$key] = trim($value);
- }
-
- if ($image['LINK'] && $image['URL'] && $image['TITLE']) {
- // Note, we should really use theme_image() here but that only works with local images it won't work with images fetched with a URL unless PHP version > 5
- $image = '<a href="'. check_url($image['LINK']) .'" class="feed-image"><img src="'. check_url($image['URL']) .'" alt="'. check_plain($image['TITLE']) .'" /></a>';
- }
- else {
- $image = NULL;
- }
-
- /*
- ** Update the feed data:
- */
-
- db_query("UPDATE {aggregator_feed} SET url = '%s', checked = %d, link = '%s', description = '%s', image = '%s', etag = '%s', modified = %d WHERE fid = %d", $feed['url'], time(), $channel['LINK'], $channel['DESCRIPTION'], $image, $result->headers['ETag'], $modified, $feed['fid']);
-
- /*
- ** Clear the cache:
- */
-
- cache_clear_all();
-
- watchdog('aggregator', t('There is new syndicated content from %site.', array('%site' => theme('placeholder', $feed['title']))));
- drupal_set_message(t('There is new syndicated content from %site.', array('%site' => theme('placeholder', $feed['title']))));
- }
- break;
- default:
- watchdog('aggregator', t('The RSS-feed from %site seems to be broken, due to "%error".', array('%site' => theme('placeholder', $feed['title']), '%error' => theme('placeholder', $result->code .' '. $result->error))), WATCHDOG_WARNING);
- drupal_set_message(t('The RSS-feed from %site seems to be broken, because of error "%error".', array('%site' => theme('placeholder', $feed['title']), '%error' => theme('placeholder', $result->code .' '. $result->error))));
- }
-}
-
-/**
- * Parse the W3C date/time format, a subset of ISO 8601. PHP date parsing
- * functions do not handle this format.
- * See http://www.w3.org/TR/NOTE-datetime for more information.
- * Originally from MagpieRSS (http://magpierss.sourceforge.net/).
- *
- * @param $date_str A string with a potentially W3C DTF date.
- * @return A timestamp if parsed successfully or -1 if not.
- */
-function aggregator_parse_w3cdtf($date_str) {
- if (preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/', $date_str, $match)) {
- list($year, $month, $day, $hours, $minutes, $seconds) = array($match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
- // calc epoch for current date assuming GMT
- $epoch = gmmktime($hours, $minutes, $seconds, $month, $day, $year);
- if ($match[10] != 'Z') { // Z is zulu time, aka GMT
- list($tz_mod, $tz_hour, $tz_min) = array($match[8], $match[9], $match[10]);
- // zero out the variables
- if (!$tz_hour) {
- $tz_hour = 0;
- }
- if (!$tz_min) {
- $tz_min = 0;
- }
- $offset_secs = (($tz_hour * 60) + $tz_min) * 60;
- // is timezone ahead of GMT? then subtract offset
- if ($tz_mod == '+') {
- $offset_secs *= -1;
- }
- $epoch += $offset_secs;
- }
- return $epoch;
- }
- else {
- return FALSE;
- }
-}
-
-function aggregator_parse_feed(&$data, $feed) {
- global $items, $image, $channel;
-
- // Unset the global variables before we use them:
- unset($GLOBALS['element'], $GLOBALS['item'], $GLOBALS['tag']);
- $items = array();
- $image = array();
- $channel = array();
-
- // parse the data:
- $xml_parser = drupal_xml_parser_create($data);
- xml_set_element_handler($xml_parser, 'aggregator_element_start', 'aggregator_element_end');
- xml_set_character_data_handler($xml_parser, 'aggregator_element_data');
-
- if (!xml_parse($xml_parser, $data, 1)) {
- watchdog('aggregator', t('The RSS-feed from %site seems to be broken, due to an error "%error" on line %line.', array('%site' => theme('placeholder', $feed['title']), '%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser))), WATCHDOG_WARNING);
- drupal_set_message(t('The RSS-feed from %site seems to be broken, because of error "%error" on line %line.', array('%site' => theme('placeholder', $feed['title']), '%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser))), 'error');
- return 0;
- }
- xml_parser_free($xml_parser);
-
- /*
- ** We reverse the array such that we store the first item last,
- ** and the last item first. In the database, the newest item
- ** should be at the top.
- */
-
- $items = array_reverse($items);
-
- foreach ($items as $item) {
- unset($title, $link, $author, $description);
-
- // Prepare the item:
- foreach ($item as $key => $value) {
- $item[$key] = trim($value);
- }
-
- /*
- ** Resolve the item's title. If no title is found, we use
- ** up to 40 characters of the description ending at a word
- ** boundary but not splitting potential entities.
- */
-
- if ($item['TITLE']) {
- $title = $item['TITLE'];
- }
- else {
- $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['DESCRIPTION'], 40));
- }
-
- /*
- ** Resolve the items link.
- */
-
- if ($item['LINK']) {
- $link = $item['LINK'];
- }
- elseif ($item['GUID'] && (strncmp($item['GUID'], 'http://', 7) == 0)) {
- $link = $item['GUID'];
- }
- else {
- $link = $feed['link'];
- }
-
- /**
- * Atom feeds have a CONTENT and/or SUMMARY tag instead of a DESCRIPTION tag
- */
- if ($item['CONTENT']) {
- $item['DESCRIPTION'] = $item['CONTENT'];
- }
- else if ($item['SUMMARY']) {
- $item['DESCRIPTION'] = $item['SUMMARY'];
- }
-
- /*
- ** Try to resolve and parse the item's publication date. If no
- ** date is found, we use the current date instead.
- */
-
- if ($item['PUBDATE']) $date = $item['PUBDATE']; // RSS 2.0
- else if ($item['DC:DATE']) $date = $item['DC:DATE']; // Dublin core
- else if ($item['DCTERMS:ISSUED']) $date = $item['DCTERMS:ISSUED']; // Dublin core
- else if ($item['DCTERMS:CREATED']) $date = $item['DCTERMS:CREATED']; // Dublin core
- else if ($item['DCTERMS:MODIFIED']) $date = $item['DCTERMS:MODIFIED']; // Dublin core
- else if ($item['ISSUED']) $date = $item['ISSUED']; // Atom XML
- else if ($item['CREATED']) $date = $item['CREATED']; // Atom XML
- else if ($item['MODIFIED']) $date = $item['MODIFIED']; // Atom XML
- else $date = 'now';
-
- $timestamp = strtotime($date); // As of PHP 5.1.0, strtotime returns FALSE on failure instead of -1.
- if ($timestamp <= 0) {
- $timestamp = aggregator_parse_w3cdtf($date); // Returns FALSE on failure
- if (!$timestamp) {
- $timestamp = time(); // better than nothing
- }
- }
-
- /*
- ** Save this item. Try to avoid duplicate entries as much as
- ** possible. If we find a duplicate entry, we resolve it and
- ** pass along it's ID such that we can update it if needed.
- */
-
- if ($link && $link != $feed['link'] && $link != $feed['url']) {
- $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND link = '%s'", $feed['fid'], $link));
- }
- else {
- $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND title = '%s'", $feed['fid'], $title));
- }
-
- aggregator_save_item(array('iid' => $entry->iid, 'fid' => $feed['fid'], 'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' => $item['AUTHOR'], 'description' => $item['DESCRIPTION']));
- }
-
- /*
- ** Remove all items that are older than flush item timer:
- */
-
- $age = time() - variable_get('aggregator_clear', 9676800);
- $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed['fid'], $age);
-
- if (db_num_rows($result)) {
- $items = array();
- while ($item = db_fetch_object($result)) {
- $items[] = $item->iid;
- }
- db_query('DELETE FROM {aggregator_category_item} WHERE iid IN ('. implode(', ', $items) .')');
- db_query('DELETE FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed['fid'], $age);
- }
-
- return 1;
-}
-
-function aggregator_save_item($edit) {
- if ($edit['iid'] && $edit['title']) {
- db_query("UPDATE {aggregator_item} SET title = '%s', link = '%s', author = '%s', description = '%s' WHERE iid = %d", $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['iid']);
- }
- else if ($edit['iid']) {
- db_query('DELETE FROM {aggregator_item} WHERE iid = %d', $edit['iid']);
- db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d', $edit['iid']);
- }
- else if ($edit['title'] && $edit['link']) {
- $edit['iid'] = db_next_id('{aggregator_item}_iid');
- db_query("INSERT INTO {aggregator_item} (iid, fid, title, link, author, description, timestamp) VALUES (%d, %d, '%s', '%s', '%s', '%s', %d)", $edit['iid'], $edit['fid'], $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['timestamp']);
- // file the items in the categories indicated by the feed
- $categories = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']);
- while ($category = db_fetch_object($categories)) {
- db_query('INSERT INTO {aggregator_category_item} (cid, iid) VALUES (%d, %d)', $category->cid, $edit['iid']);
- }
- }
-}
-
-function aggregator_get_feed($fid) {
- return db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid));
-}
-
-function aggregator_get_category($cid) {
- return db_fetch_array(db_query('SELECT * FROM {aggregator_category} WHERE cid = %d', $cid));
-}
-
-function aggregator_view() {
- $result = db_query('SELECT f.*, COUNT(i.iid) AS items FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.etag, f.modified, f.image, f.block ORDER BY f.title');
-
- $output .= '<h3>'. t('Feed overview') .'</h3>';
-
- $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), array('data' => t('Operations'), 'colspan' => '3'));
- $rows = array();
- while ($feed = db_fetch_object($result)) {
- $rows[] = array(l($feed->title, "aggregator/sources/$feed->fid"), format_plural($feed->items, '1 item', '%count items'), ($feed->checked ? t('%time ago', array('%time' => format_interval(time() - $feed->checked))) : t('never')), ($feed->checked ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - time()))) : t('never')), l(t('edit'), "admin/aggregator/edit/feed/$feed->fid"), l(t('remove items'), "admin/aggregator/remove/$feed->fid"), l(t('update items'), "admin/aggregator/update/$feed->fid"));
- }
- $output .= theme('table', $header, $rows);
-
- $result = db_query('SELECT c.cid, c.title, count(ci.iid) as items FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title');
-
- $output .= '<h3>'. t('Category overview') .'</h3>';
-
- $header = array(t('Title'), t('Items'), t('Operations'));
- $rows = array();
- while ($category = db_fetch_object($result)) {
- $rows[] = array(l($category->title, "aggregator/categories/$category->cid"), format_plural($category->items, '1 item', '%count items'), l(t('edit'), "admin/aggregator/edit/category/$category->cid"));
- }
- $output .= theme('table', $header, $rows);
-
- return $output;
-}
-
-/**
- * Menu callback; removes all items from a feed, then redirects to the overview page.
- */
-function aggregator_admin_remove_feed($feed) {
- aggregator_remove(aggregator_get_feed($feed));
- drupal_goto('admin/aggregator');
-}
-
-/**
- * Menu callback; refreshes a feed, then redirects to the overview page.
- */
-function aggregator_admin_refresh_feed($feed) {
- aggregator_refresh(aggregator_get_feed($feed));
- drupal_goto('admin/aggregator');
-}
-
-/**
- * Menu callback; displays the aggregator administration page.
- */
-function aggregator_admin_overview() {
- return aggregator_view();
-}
-
-/**
- * Menu callback; displays the most recent items gathered from any feed.
- */
-function aggregator_page_last() {
- return _aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_item} i INNER JOIN {aggregator_feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC, i.iid DESC', arg(1));
-}
-
-/**
- * Menu callback; displays all the items captured from a particular feed.
- */
-function aggregator_page_source() {
- $feed = db_fetch_object(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', arg(2)));
- $info = theme('aggregator_feed', $feed);
-
- return _aggregator_page_list('SELECT * FROM {aggregator_item} WHERE fid = '. $feed->fid .' ORDER BY timestamp DESC, iid DESC', arg(3), $info);
-}
-
-/**
- * Menu callback; displays all the items aggregated in a particular category.
- */
-function aggregator_page_category() {
- $category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
-
- return _aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = '. $category->cid .' ORDER BY timestamp DESC, iid DESC', arg(3));
-}
-
-/**
- * Prints an aggregator page listing a number of feed items. Various
- * menu callbacks use this function to print their feeds.
- */
-function _aggregator_page_list($sql, $op, $header = '') {
- $categorize = (user_access('administer news feeds') && ($op == 'categorize'));
-
- $output = '<div id="aggregator">';
-
- $form['header'] = array('#value' => $header);
- $output .= $form['header']['#value'];
-
- $result = pager_query($sql, 20);
- $categories = array();
- while ($item = db_fetch_object($result)) {
- $form['items'][$item->iid] = array('#value' => theme('aggregator_page_item', $item));
- $output .= $form['items'][$item->iid]['#value'];
- $form['categories'][$item->iid] = array();
-
- if ($categorize) {
-
- $categories_result = db_query('SELECT c.cid, c.title, ci.iid FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid AND ci.iid = %d', $item->iid);
- $selected = array();
- while ($category = db_fetch_object($categories_result)) {
- if (!$done) {
- $categories[$category->cid] = check_plain($category->title);
- }
- if ($category->iid) {
- $selected[] = $category->cid;
- }
- }
- $done = true;
- $form['categories'][$item->iid] = array(
- '#type' => variable_get('aggregator_category_selector', 'checkboxes'),
- '#default_value' => $selected, '#options' => $categories,
- '#size' => 10, '#multiple' => true
- );
- }
- }
- $output .= '</div>';
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save categories'));
- $form['pager'] = array('#value' => theme('pager', NULL, 20, 0));
- $output .= $form['pager']['#value'];
-
- // arg(1) is undefined if we are at the top aggregator URL
- // is there a better way to do this?
- if (!arg(1)) {
- $form['feed_icon'] = array('#value' => theme('feed_icon', url('aggregator/rss')));
- }
- elseif (arg(1) == 'categories' && arg(2) && !arg(3)) {
- $form['feed_icon'] = array('#value' => theme('feed_icon', url('aggregator/rss/' . arg(2))));
- }
- $output .= $form['feed_icon']['#value'];
-
- return ($categorize) ? drupal_get_form('aggregator_page_list', $form) : $output;
-}
-
-function theme_aggregator_page_list($form) {
- $output = '<div id="aggregator">';
- $output .= form_render($form['header']);
- $rows = array();
- if ($form['items']) {
- foreach (element_children($form['items']) as $key) {
- if (is_array($form['items'][$key])) {
- $rows[] = array(form_render($form['items'][$key]), array('data' => form_render($form['categories'][$key]), 'class' => 'categorize-item'));
- }
- }
- }
- $output .= theme('table', array('', t('Categorize')), $rows);
- $output .= form_render($form['submit']);
- $output .= '</div>';
- $output .= form_render($form);
- return $output;
-}
-
-function aggregator_page_list_validate($form_id, &$form) {
- if (!user_access('administer news feeds')) {
- form_error($form, t('You are not allowed to categorize this feed item.'));
- }
-}
-
-function aggregator_page_list_submit($form_id, $form_values) {
- foreach ($form_values as $iid => $selection) {
- db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d', $iid);
- foreach ($selection as $cid) {
- if ($cid) {
- db_query('INSERT INTO {aggregator_category_item} (cid, iid) VALUES (%d, %d)', $cid, $iid);
- }
- }
- }
- drupal_set_message(t('The categories have been saved.'));
-}
-
-/**
- * Menu callback; displays all the feeds used by the aggregator.
- */
-function aggregator_page_sources() {
- $result = db_query('SELECT f.fid, f.title, f.description, f.image, MAX(i.timestamp) AS last FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.description, f.image ORDER BY last DESC, f.title');
- $output = "<div id=\"aggregator\">\n";
- while ($feed = db_fetch_object($result)) {
- $output .= '<h2>'. check_plain($feed->title) ."</h2>\n";
-
- // Most recent items:
- $list = array();
- if (variable_get('aggregator_summary_items', 3)) {
- $items = db_query_range('SELECT i.title, i.timestamp, i.link FROM {aggregator_item} i WHERE i.fid = %d ORDER BY i.timestamp DESC', $feed->fid, 0, variable_get('aggregator_summary_items', 3));
- while ($item = db_fetch_object($items)) {
- $list[] = theme('aggregator_summary_item', $item);
- }
- }
- $output .= theme('item_list', $list);
- $output .= '<div class="links">'. theme('links', array(l(t('more'), 'aggregator/sources/'. $feed->fid))) ."</div>\n";
- }
- $output .= theme('xml_icon', url('aggregator/opml'));
- $output .= '</div>';
- return $output;
-}
-
-/**
- * Menu callback; generate an RSS 0.92 feed of aggregator items or categories.
- */
-function aggregator_page_rss() {
- global $base_url;
-
- // arg(2) is the passed cid, only select for that category
- $result = NULL;
- if (arg(2)) {
- $category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
- $url = '/categories/' . $category->cid;
- $title = ' ' . t('in category') . ' ' . $category->title;
- $sql = 'SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = %d ORDER BY timestamp DESC, iid DESC';
- $result = db_query_range($sql, $category->cid, 0, variable_get('feed_default_items', 10));
- }
- // or, get the default aggregator items
- else {
- $sql = 'SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_item} i INNER JOIN {aggregator_feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC, i.iid DESC';
- $result = db_query_range($sql, 0, variable_get('feed_default_items', 10));
- }
-
- while ($item = db_fetch_object($result)) {
- switch (variable_get('feed_item_length', 'teaser')) {
- case 'teaser':
- $teaser = node_teaser($item->description);
- if ($teaser != $item_description) {
- $teaser .= '<p><a href="'. check_url($item->link) .'">'. t('read more') ."</a></p>\n";
- }
- $item->description = $teaser;
- break;
- case 'title':
- $item->description = '';
- break;
- }
- $items .= format_rss_item($item->ftitle . ': ' . $item->title, $item->link, $item->description, array('pubDate' => date('r', $item->timestamp)));
- }
-
- $output .= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
- $output .= "<rss version=\"2.0\">\n";
- $output .= format_rss_channel(variable_get('site_name', t('Drupal')) . ' ' . t('aggregator'), $base_url . '/' . url('aggregator' . $url), variable_get('site_name', t('Drupal')) . ' - ' . t('aggregated feeds') . $title, $items, 'en');
- $output .= "</rss>\n";
-
- drupal_set_header('Content-Type: text/xml; charset=utf-8');
- print $output;
-}
-
-/**
- * Menu callback; generates an OPML representation of all feeds.
- */
-function aggregator_page_opml($cid = NULL) {
- if ($cid) {
- $result = db_query('SELECT f.title, f.url FROM {aggregator_feed} f LEFT JOIN {aggregator_category_feed} c on f.fid = c.fid WHERE c.cid = %d ORDER BY title', $cid);
- }
- else {
- $result = db_query('SELECT * FROM {aggregator_feed} ORDER BY title');
- }
-
- $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
- $output .= "<opml version=\"1.1\">\n";
- $output .= "<head>\n";
- $output .= '<title>'. check_plain(variable_get('site_name', 'Drupal')) ."</title>\n";
- $output .= '<dateModified>'. gmdate('r') ."</dateModified>\n";
- $output .= "</head>\n";
- $output .= "<body>\n";
-
- while ($feed = db_fetch_object($result)) {
- $output .= '<outline text="'. check_plain($feed->title) .'" xmlUrl="'. check_url($feed->url) ."\" />\n";
- }
-
- $output .= "</body>\n";
- $output .= "</opml>\n";
-
- drupal_set_header('Content-Type: text/xml; charset=utf-8');
- print $output;
-}
-
-/**
- * Menu callback; displays all the categories used by the aggregator.
- */
-function aggregator_page_categories() {
- $result = db_query('SELECT c.cid, c.title, c.description FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid LEFT JOIN {aggregator_item} i ON ci.iid = i.iid GROUP BY c.cid, c.title, c.description');
- $output = "<div id=\"aggregator\">\n";
-
- while ($category = db_fetch_object($result)) {
- $output .= '<h2>'. check_plain($category->title) ."</h2>\n";
- if (variable_get('aggregator_summary_items', 3)) {
- $list = array();
- $items = db_query_range('SELECT i.title, i.timestamp, i.link, f.title as feed_title, f.link as feed_link FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON i.iid = ci.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE ci.cid = %d ORDER BY i.timestamp DESC', $category->cid, 0, variable_get('aggregator_summary_items', 3));
- while ($item = db_fetch_object($items)) {
- $list[] = theme('aggregator_summary_item', $item);
- }
- $output .= theme('item_list', $list);
- }
- $output .= '<div class="links">'. theme('links', array(l(t('more'), 'aggregator/categories/'. $category->cid))) ."</div>\n";
- }
- $output .= '</div>';
-
- return $output;
-}
-
-/**
- * Format a news feed.
- *
- * @ingroup themeable
- */
-function theme_aggregator_feed($feed) {
- $output = '<div class="feed-source">';
- $output .= theme('feed_icon', $feed->url) ."\n";
- $output .= $feed->image . ' <h3 class="feed-title"><a href="'. check_url($feed->link) .'">'. check_plain($feed->title) ."</a></h3>\n";
- $output .= '<div class="feed-description"><em>'. t('Description:') .'</em> '. aggregator_filter_xss($feed->description) ."</div>\n";
-
- $updated = t('%time ago', array('%time' => format_interval(time() - $feed->checked)));
- if (user_access('administer news feeds')) {
- $updated = l($updated, 'admin/aggregator');
- }
-
- $output .= '<div class="feed-updated"><em>'. t('Updated:') . "</em> $updated</div>";
- $output .= "</div>\n";
-
- return $output;
-}
-
-/**
- * Format an individual feed item for display in the block.
- *
- * @ingroup themeable
- */
-function theme_aggregator_block_item($item, $feed = 0) {
- global $user;
-
- if ($user->uid && module_exist('blog') && user_access('edit own blog')) {
- if ($image = theme('image', 'misc/blog.png', t('blog it'), t('blog it'))) {
- $output .= '<div class="icon">'. l($image, 'node/add/blog', array('title' => t('Comment on this news item in your personal blog.'), 'class' => 'blog-it'), "iid=$item->iid", NULL, FALSE, TRUE) .'</div>';
- }
- }
-
- // Display the external link to the item.
- $output .= '<a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n";
-
- return $output;
-}
-
-/**
- * Return a themed item heading for summary pages located at "aggregator/sources"
- * and "aggregator/categories".
- *
- * @param $item The item object from the aggregator module.
- * @return A string containing the output.
- *
- * @ingroup themeable
- */
-function theme_aggregator_summary_item($item) {
- $output = '<a href="'. check_url($item->link) .'">'. check_plain($item->title) .'</a> <span class="age">'. t('%age old', array('%age' => format_interval(time() - $item->timestamp))) .'</span>';
- if ($item->feed_link) {
- $output .= ', <span class="source"><a href="'. check_url($item->feed_link) .'">'. check_plain($item->feed_title) .'</a></span>';
- }
- return $output ."\n";
-}
-
-/**
- * Format an individual feed item for display on the aggregator page.
- *
- * @ingroup themeable
- */
-function theme_aggregator_page_item($item) {
-
- $source = '';
- if ($item->ftitle && $item->fid) {
- $source = l($item->ftitle, "aggregator/sources/$item->fid", array('class' => 'feed-item-source')) . ' -';
- }
-
- if (date('Ymd', $item->timestamp) == date('Ymd')) {
- $source_date = t('%ago ago', array('%ago' => format_interval(time() - $item->timestamp)));
- }
- else {
- $source_date = format_date($item->timestamp, 'custom', variable_get('date_format_medium', 'D, Y-m-d H:i'));
- }
-
- $output .= "<div class=\"feed-item\">\n";
- $output .= '<h3 class="feed-item-title"><a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a></h3>\n";
- $output .= "<div class=\"feed-item-meta\">$source <span class=\"feed-item-date\">$source_date</span></div>\n";
-
- if ($item->description) {
- $output .= '<div class="feed-item-body">'. aggregator_filter_xss($item->description) ."</div>\n";
- }
-
- $result = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = %d ORDER BY c.title', $item->iid);
- $categories = array();
- while ($category = db_fetch_object($result)) {
- $categories[] = l($category->title, 'aggregator/categories/'. $category->cid);
- }
- if ($categories) {
- $output .= '<div class="feed-item-categories">'. t('Categories') .': '. implode(', ', $categories) ."</div>\n";
- }
-
- $output .= "</div>\n";
-
- return $output;
-}
-
-/**
- * Safely render HTML content, as allowed.
- */
-function aggregator_filter_xss($value) {
- return filter_xss($value, preg_split('/\s+|<|>/', variable_get("aggregator_allowed_html_tags", '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'), -1, PREG_SPLIT_NO_EMPTY));
-}
-
-/**
- * Helper function for drupal_map_assoc.
- */
-function _aggregator_items($count) {
- return format_plural($count, '1 item', '%count items');
-}
diff --git a/modules/archive/archive.module b/modules/archive/archive.module
deleted file mode 100644
index 46f4995..0000000
--- a/modules/archive/archive.module
+++ /dev/null
@@ -1,291 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Displays a calendar to navigate old content.
- */
-
-/**
- * Implementation of hook_help().
- */
-function archive_help($section) {
- switch ($section) {
- case 'admin/help#archive':
- $output = '<p>'. t('The archive page allows content to be viewed by date. It also provides a monthly calendar view that users can use to navigate through content.') .'</p>';
- $output .= '<p>'. t('To view the archive by date, select the date in the calendar. Administrators can enable the <em>browse archives</em> block in block administration to allow users to browse by calendar. Clicking on a date in the monthly calendar view shows the content for that date. Users can navigate to different months using arrows beside the month\'s name in the calendar display. The current date will be highlighted in the calendar.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>view your <a href="%archive">archive by day</a>.</li>
-<li>enable the <em>browse archives</em> block at <a href="%admin-block">administer &gt;&gt; block</a>.</li>
-</ul>
-', array('%archive' => url('archive'), '%admin-block' => url('admin/block')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%archive">Archive page</a>.', array('%archive' => 'http://drupal.org/handbook/modules/archive/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Displays a calendar for navigating older content.');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function archive_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'archive',
- 'title' => t('archives'),
- 'access' => user_access('access content'),
- 'callback' => 'archive_page',
- 'type' => MENU_SUGGESTED_ITEM);
- }
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates a calendar for the current month, with links to the archives
- * for each day.
- */
-function archive_block($op = 'list', $delta = 0) {
- if ($op == 'list') {
- $blocks[0]['info'] = t('Calendar to browse archives');
- return $blocks;
- }
- else if ($op == 'view' && user_access('access content')) {
- $block['subject'] = t('Browse archives');
- $block['content'] = archive_calendar();
- return $block;
- }
-}
-
-/**
- * Generates a monthly calendar, for display in the archive block.
- */
-function archive_calendar() {
- global $user;
-
- // Extract today's date:
- $start_of_today = mktime(0, 0, 0, date('n', time()), date('d', time()), date('Y', time())) + $user->timezone;
- $end_of_today = mktime(23, 59, 59, date('n', time()), date('d', time()), date('Y', time())) + $user->timezone;
-
- // Extract the requested date:
- if (arg(0) == 'archive' && arg(3)) {
- $year = arg(1);
- $month = arg(2);
- $day = arg(3);
-
- $requested = mktime(0, 0, 0, $month, $day, $year) + $user->timezone;
- }
- else {
- $year = date('Y', time());
- $month = date('n', time());
- $day = date('d', time());
-
- $requested = $end_of_today + $user->timezone;
- }
-
- $start_of_month = mktime(0, 0, 0, $month, 1, $year);
-
- // Extract first day of the month:
- $first = date('w', $start_of_month);
-
- // Extract last day of the month:
- $last = date('t', $start_of_month);
-
- $end_of_month = mktime(23, 59, 59, $month, $last, $year);
-
- $cache = cache_get("archive:calendar:$day-$month-$year");
-
- if (!empty($cache)) {
- return $cache->data;
- }
-
- // Calculate previous and next months dates and check for shorter months (28/30 days)
- $prevmonth = mktime(23, 59, 59, $month - 1, 1, $year);
- $prev = mktime(23, 59, 59, $month - 1, min(date('t', $prevmonth), $day), $year);
- $nextmonth = mktime(23, 59, 59, $month + 1, 1, $year);
- $next = mktime(23, 59, 59, $month + 1, min(date('t', $nextmonth), $day), $year);
-
- $sql = 'SELECT n.nid, n.created FROM {node} n WHERE n.status = 1 AND n.created > %d AND n.created < %d ORDER BY n.created';
- $sql = db_rewrite_sql($sql);
- $result = db_query($sql, $start_of_month, $end_of_month);
-
- $days_with_posts = array();
- while ($day_with_post = db_fetch_object($result)) {
- $daynum = date('j', $day_with_post->created + $user->timezone);
- if (isset($days_with_posts[$daynum])) {
- $days_with_posts[$daynum]++;
- }
- else {
- $days_with_posts[$daynum] = 1;
- }
- }
-
- // Generate calendar header:
- $output .= "\n<!-- calendar -->\n";
- $output .= '<div class="calendar">';
- $output .= '<table summary="'. t('A calendar to browse the archives') .".\">\n";
- $output .= ' <caption>'. l('«', 'archive/'. date('Y/m/d', $prev), array('title' => t('Previous month'))) .' '. format_date($requested, 'custom', 'F') . date(' Y', $requested) .' '. ($nextmonth <= time() ? l('»', 'archive/'. date('Y/m/d', $next), array('title' => t('Next month'))) : ' ') ."</caption>\n";
-
- // First day of week (0 => Sunday, 1 => Monday, ...)
- $weekstart = variable_get('date_first_day', 0);
-
- // Last day of week
- ($weekstart - 1 == -1) ? $lastday = 6 : $lastday = $weekstart - 1;
-
- // Generate the days of the week:
- $firstcolumn = mktime(0, 0, 0, 3, 20 + $weekstart, 1994);
-
- $output .= " <tr class=\"header-week\">\n";
- $days = array(t('Sunday') => t('Su'), t('Monday') => t('Mo'), t('Tuesday') => t('Tu'), t('Wednesday') => t('We'), t('Thursday') => t('Th'), t('Friday') => t('Fr'), t('Saturday') => t('Sa'));
- if ($weekstart) {
- $days = array_merge(array_slice($days, $weekstart), array_slice($days, 0, $weekstart));
- }
-
- foreach ($days as $fullname => $name) {
- $output .= ' <th abbr="'. $fullname .'">'. $name . "</th>\n";
- }
- $output .= "</tr>\n";
-
- // Initialize temporary variables:
- $nday = 1;
- $sday = $first;
-
- // Loop through all the days of the month:
- while ($nday <= $last) {
- // Set up blank days for first week of the month (allowing individual blank day styling):
- if ($first != $weekstart) {
- $blankdays = ($first - $weekstart + 7) % 7;
- $output .= " <tr class=\"row-week\">" . str_repeat("<td class=\"day-blank\">&nbsp;</td>\n", $blankdays);
- $first = $weekstart;
- }
- // Start every week on a new line:
- if ($sday == $weekstart) {
- $output .= " <tr class=\"row-week\">\n";
- }
-
- // Print one cell:
- $date = mktime(0, 0, 0, $month, $nday, $year) + $user->timezone;
- if (isset($days_with_posts[$nday])) {
- $daytext = l($nday, "archive/$year/$month/$nday", array("title" => format_plural($days_with_posts[$nday], "1 post", "%count posts")));
- $dayclass = 'day-link';
- }
- else {
- $daytext = $nday;
- $dayclass = 'day-normal';
- }
- if ($date == $requested) {
- $output .= " <td class=\"day-selected\">$daytext</td>\n";
- }
- else if ($date == $start_of_today) {
- $output .= " <td class=\"day-today\">$daytext</td>\n";
- }
- else if ($date > $end_of_today) {
- $output .= " <td class=\"day-future\">$daytext</td>\n";
- }
- else {
- $output .= " <td class=\"$dayclass\">$daytext</td>\n";
- }
-
- // Start every week on a new line:
- if ($sday == $lastday) {
- $output .= " </tr>\n";
- }
-
- // Update temporary variables:
- $sday++;
- $sday = $sday % 7;
- $nday++;
- }
-
- // Complete the calendar (allowing individual blank day styling):
- if ($sday != $weekstart) {
- $end = (7 - $sday + $weekstart) % 7;
- $output .= str_repeat("<td class=\"day-blank\">&nbsp;</td>\n", $end) . "</tr>\n";
- }
-
- $output .= "</table></div>\n\n";
-
- cache_set("archive:calendar:$day-$month-$year", $output, CACHE_TEMPORARY);
-
- return $output;
-}
-
-/**
- * Menu callback; lists all nodes posted on a given date.
- */
-function archive_page($year = 0, $month = 0, $day = 0) {
- global $user;
-
- $date = mktime(0, 0, 0, $month, $day, $year) - $user->timezone;
- $date_end = mktime(0, 0, 0, $month, $day + 1, $year) - $user->timezone;
-
- // Display form.
- $output = archive_browse_form($year, $month, $day);
-
- if ($year && $month && $day) {
- // Fetch nodes for the selected date, if one was specified.
- $sql = 'SELECT n.nid, n.created FROM {node} n WHERE n.status = 1 AND n.created > %d AND n.created < %d ORDER BY n.created';
- $sql = db_rewrite_sql($sql);
- $result = db_query_range($sql, $date, $date_end, 0, 20);
- if (db_num_rows($result) > 0) {
- while ($nid = db_fetch_object($result)) {
- $output .= node_view(node_load($nid->nid), 1);
- }
- }
- else {
- $output .= theme('box', t('No posts found.'), '');
- }
- }
- else {
- $output .= theme('box', t('No posts found.'), '');
- }
-
- return $output;
-}
-
-/**
- * Generate a form that retrieves archives for a certain date.
- */
-function archive_browse_form($year, $month, $day) {
- // Prepare the values of the form fields.
- $years = drupal_map_assoc(range(2000, 2010));
- $months = array(1 => t('January'), 2 => t('February'), 3 => t('March'), 4 => t('April'), 5 => t('May'), 6 => t('June'), 7 => t('July'), 8 => t('August'), 9 => t('September'), 10 => t('October'), 11 => t('November'), 12 => t('December'));
- $days = drupal_map_assoc(range(1, 31));
-
- $form['year'] = array('#type' => 'select',
- '#default_value' => ($year ? $year : date('Y')),
- '#options' => $years,
- );
- $form['month'] = array('#type' => 'select',
- '#default_value' => ($month ? $month : date('m')),
- '#options' => $months,
- );
- $form['day'] = array('#type' => 'select',
- '#default_value' => ($day ? $day : date('d')),
- '#options' => $days,
- );
- $form['show'] = array('#type' => 'submit',
- '#value' => t('Show'),
- );
-
- return drupal_get_form('archive_browse_form', $form);
-}
-
-/**
- * Process archive browse form submission.
- */
-function archive_browse_form_submit($form_id, $form_values) {
- return('archive/'. $form_values['year'] .'/'. $form_values['month'] .'/'. $form_values['day']);
-}
-
-/**
- * Form theme function; displays the archive date navigation form inline.
- */
-function theme_archive_browse_form($form) {
- $output = '<div class="container-inline archive">' . form_render($form) . '</div>';
- return $output;
-}
diff --git a/modules/block/block.module b/modules/block/block.module
deleted file mode 100644
index b908a5e..0000000
--- a/modules/block/block.module
+++ /dev/null
@@ -1,633 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Controls the boxes that are displayed around the main content.
- */
-
-/**
- * Implementation of hook_help().
- */
-function block_help($section) {
- switch ($section) {
- case 'admin/help#block':
- $output = '<p>'. t('Blocks are the boxes of related/grouped data that are visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but administrators can also create their own defined blocks.') .'</p>';
- $output .= '<p>'. t('The sidebar each block appears in depends on both which theme you are using (some are left-only, some right, some both), and on the settings in block management.') .'</p>';
- $output .= '<p>'. t('The block management screen lets you specify the vertical sort-order of the blocks within a sidebar. You do this by assigning a weight to each block. Lighter blocks (smaller weight) "float up" towards the top of the sidebar. Heavier ones "sink down" towards the bottom of it.') .'</p>';
- $output .= t('<p>A block\'s visibility depends on:</p>
-<ul>
-<li>Its enabled checkbox. Disabled blocks are never shown.</li>
-<li>Its throttle checkbox. Throttled blocks are hidden during high server loads.</li>
-<li>Its path options. Blocks can be configured to only show/hide on certain pages.</li>
-<li>User settings. Administrators can choose to let your users decide whether to show/hide certain blocks.</li>
-<li>Its function. Dynamic blocks (such as those defined by modules) may be empty on certain pages and will not be shown.</li>
-</ul>
-');
- $output .= '<h3>'. t('Module blocks') .'</h3>';
- $output .= '<p>'. t('Module blocks are available when modules are enabled. These blocks can be administered in block administration.') .'</p>';
- $output .= '<h3>'. t('Administrator defined blocks') .'</h3>';
- $output .= '<p>'. t('An administrator defined block contains content supplied by the administrator. Each admin-defined block consists of a title, a description, and a body which can be as long as you wish. The Drupal engine will render the content of the block.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>enable throttle and configure blocks at <a href="%admin-block">administer &gt;&gt; blocks</a>.</li>
-<li>add a block at <a href="%admin-block-add">administer &gt;&gt; blocks &gt;&gt; add block</a>.</li>
-</ul>
-', array('%admin-block' => url('admin/block'), '%admin-block-add' => url('admin/block/add')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%block">Block page</a>.', array('%block' => 'http://drupal.org/handbook/modules/block/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Controls the boxes that are displayed around the main content.');
- case 'admin/block':
- return t("
-<p>Blocks are content rendered into regions, often boxes in the left and right side bars of the web site. They are made available by modules or created manually.</p>
-<p>Only enabled blocks are shown. You can position the blocks by deciding which area of the page they will show up on (e.g., a sidebar) and in which order they appear (weight). Highlighting on this page shows the regions where content will be rendered.</p>
-<p>If you want certain blocks to disable themselves temporarily during high server loads, check the 'Throttle' box. You can configure the auto-throttle on the <a href=\"%throttle\">throttle configuration page</a> after having enabled the throttle module.</p>
-", array('%throttle' => url('admin/settings/throttle')));
- case 'admin/block/add':
- return t('<p>Here you can create a new block. Once you have created this block you must make it active and give it a place on the page using <a href="%overview">blocks</a>. The title is used when displaying the block. The description is used in the "block" column on the <a href="%overview">blocks</a> page.</p>', array('%overview' => url('admin/block')));
- }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function block_perm() {
- return array('administer blocks', 'use PHP for block visibility');
-}
-
-/**
- * Implementation of hook_menu().
- */
-function block_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/block', 'title' => t('blocks'),
- 'access' => user_access('administer blocks'),
- 'callback' => 'block_admin_display');
- $items[] = array('path' => 'admin/block/list', 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- $items[] = array('path' => 'admin/block/configure', 'title' => t('configure block'),
- 'access' => user_access('administer blocks'),
- 'callback' => 'block_admin_configure',
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/block/delete', 'title' => t('delete block'),
- 'access' => user_access('administer blocks'),
- 'callback' => 'block_box_delete',
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/block/add', 'title' => t('add block'),
- 'access' => user_access('administer blocks'),
- 'callback' => 'block_box_add',
- 'type' => MENU_LOCAL_TASK);
- foreach (list_themes() as $key => $theme) {
- if ($theme->status) {
- if ($key == variable_get('theme_default', 'bluemarine')) {
- $items[] = array('path' => 'admin/block/list/' . $key, 'title' => t('%key settings', array('%key' => $key)),
- 'access' => user_access('administer blocks'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- }
- else {
- $items[] = array('path' => 'admin/block/list/' . $key, 'title' => t('%key settings', array('%key' => $key)),
- 'access' => user_access('administer blocks'), 'type' => MENU_LOCAL_TASK);
- }
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates the administrator-defined blocks for display.
- */
-function block_block($op = 'list', $delta = 0, $edit = array()) {
- switch ($op) {
- case 'list':
- $blocks = array();
-
- $result = db_query('SELECT bid, title, info FROM {boxes} ORDER BY title');
- while ($block = db_fetch_object($result)) {
- $blocks[$block->bid]['info'] = $block->info ? check_plain($block->info) : check_plain($block->title);
- }
- return $blocks;
-
- case 'configure':
- $box = block_box_get($delta);
- if (filter_access($box['format'])) {
- return block_box_form($box);
- }
- break;
-
- case 'save':
- block_box_save($edit, $delta);
- break;
-
- case 'view':
- $block = db_fetch_object(db_query('SELECT * FROM {boxes} WHERE bid = %d', $delta));
- $data['subject'] = check_plain($block->title);
- $data['content'] = check_markup($block->body, $block->format, FALSE);
- return $data;
- }
-}
-
-/**
- * Update the 'blocks' DB table with the blocks currently exported by modules.
- *
- * @param $order_by php <a
- * href="http://www.php.net/manual/en/function.array-multisort.php">array_multisort()</a>
- * style sort ordering, eg. "weight", SORT_ASC, SORT_STRING.
- *
- * @return
- * Blocks currently exported by modules, sorted by $order_by.
- */
-function _block_rehash($order_by = array('weight')) {
- global $theme_key;
-
- init_theme();
-
- $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s'", $theme_key);
- while ($old_block = db_fetch_object($result)) {
- $old_blocks[$old_block->module][$old_block->delta] = $old_block;
- }
-
- db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key);
-
- foreach (module_list() as $module) {
- $module_blocks = module_invoke($module, 'block', 'list');
- if ($module_blocks) {
- foreach ($module_blocks as $delta => $block) {
- $block['module'] = $module;
- $block['delta'] = $delta;
- // If previously written to database, load values.
- if ($old_blocks[$module][$delta]) {
- $block['status'] = $old_blocks[$module][$delta]->status;
- $block['weight'] = $old_blocks[$module][$delta]->weight;
- $block['region'] = $old_blocks[$module][$delta]->region;
- $block['visibility'] = $old_blocks[$module][$delta]->visibility;
- $block['pages'] = $old_blocks[$module][$delta]->pages;
- $block['custom'] = $old_blocks[$module][$delta]->custom;
- $block['throttle'] = $old_blocks[$module][$delta]->throttle;
- }
- // Otherwise, use any set values, or else substitute defaults.
- else {
- $properties = array ('status' => 0, 'weight' => 0, 'region' => 'left', 'pages' => '', 'custom' => 0);
- foreach ($properties as $property => $default) {
- if (!isset ($block[$property])) {
- $block[$property] = $default;
- }
- }
- }
-
- // reinsert blocks into table
- db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)",
- $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle']);
- $blocks[] = $block;
-
- // build array to sort on
- $order[$order_by[0]][] = $block[$order_by[0]];
- }
- }
- }
-
- // sort
- array_multisort($order[$order_by[0]], $order_by[1] ? $order_by[1] : SORT_ASC, $order_by[2] ? $order_by[2] : SORT_REGULAR, $blocks);
-
- return $blocks;
-}
-
-/**
- * Generate main block administration form.
- */
-function block_admin_display() {
- global $theme_key, $custom_theme;
-
- // If non-default theme configuration has been selected, set the custom theme.
- if (arg(3)) {
- $custom_theme = arg(3);
- }
- else {
- $custom_theme = variable_get('theme_default', 'bluemarine');
- }
- init_theme();
-
- $throttle = module_exist('throttle');
- $blocks = _block_rehash();
- $block_regions = system_region_list($theme_key);
-
- $form['#action'] = arg(3) ? url('admin/block/list/' . $theme_key) : url('admin/block');
- $form['#tree'] = TRUE;
- foreach ($blocks as $block) {
- $form[$block['module']][$block['delta']]['info'] = array('#value' => $block['info']);
- $form[$block['module']][$block['delta']]['status'] = array('#type' => 'checkbox', '#default_value' => $block['status']);
- $form[$block['module']][$block['delta']]['theme'] = array('#type' => 'hidden', '#value' => $theme_key);
- $form[$block['module']][$block['delta']]['weight'] = array('#type' => 'weight', '#default_value' => $block['weight']);
- $form[$block['module']][$block['delta']]['region'] = array('#type' => 'select',
- '#default_value' => isset($block['region']) ? $block['region'] : system_default_region(),
- '#options' => $block_regions,
- );
-
- if ($throttle) {
- $form[$block['module']][$block['delta']]['throttle'] = array('#type' => 'checkbox', '#default_value' => $block['throttle']);
- }
- $form[$block['module']][$block['delta']]['configure'] = array('#value' => l(t('configure'), 'admin/block/configure/'. $block['module'] .'/'. $block['delta']));
- if ($block['module'] == 'block') {
- $form[$block['module']][$block['delta']]['delete'] = array('#value' => l(t('delete'), 'admin/block/delete/'. $block['delta']));
- }
- }
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save blocks'));
-
- return drupal_get_form('block_admin_display', $form);
-}
-
-/**
- * Process main block administration form submission.
- */
-function block_admin_display_submit($form_id, $form_values) {
- foreach ($form_values as $module => $blocks) {
- foreach ($blocks as $delta => $block) {
- db_query("UPDATE {blocks} SET status = %d, weight = %d, region = '%s', throttle = %d WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $block['status'], $block['weight'], $block['region'], $block['throttle'], $module, $delta, $block['theme']);
- }
- }
- drupal_set_message(t('The block settings have been updated.'));
- cache_clear_all();
-}
-
-/**
- * Theme main block administration form submission.
- */
-function theme_block_admin_display($form) {
- global $theme_key;
-
- $throttle = module_exist('throttle');
- $block_regions = system_region_list($theme_key);
-
- // Highlight regions on page to provide visual reference.
- foreach ($block_regions as $key => $value) {
- drupal_set_content($key, '<div class="block-region">' . $value . '</div>');
- }
- $regions = array();
- $disabled = array();
- foreach (element_children($form) as $module) {
- // Do not take form control structures.
- if (is_array($form[$module])) {
- foreach ($form[$module] as $delta => $element) {
- // Only take form elements that are blocks.
- if (is_array($form[$module][$delta]['info'])) {
- $block = $form[$module][$delta];
- $row = array(array('data' => form_render($block['info']), 'class' => 'block'), form_render($block['status']) . form_render($block['theme']), form_render($block['weight']), form_render($block['region']));
- if ($throttle) {
- $row[] = form_render($block['throttle']);
- }
- $row[] = form_render($block['configure']);
- $row[] = $block['delete'] ? form_render($block['delete']) : '';
- if ($block['status']['#default_value']) {
- $regions[$block['region']['#default_value']][] = $row;
- }
- else if ($block['region']['#default_value'] <= 1) {
- $disabled[] = $row;
- }
- }
- }
- }
- }
-
- $rows = array();
- if (count($regions)) {
- foreach ($regions as $region => $row) {
- $region_title = t('%region', array ('%region' => ucfirst($block_regions[$region])));
- $rows[] = array(array('data' => $region_title, 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
- $rows = array_merge($rows, $row);
- }
- }
- if (count($disabled)) {
- $rows[] = array(array('data' => t('Disabled'), 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
- $rows = array_merge($rows, $disabled);
- }
- $header = array(t('Block'), t('Enabled'), t('Weight'), t('Placement'));
- if ($throttle) {
- $header[] = t('Throttle');
- }
- $header[] = array('data' => t('Operations'), 'colspan' => 2);
- $output = theme('table', $header, $rows, array('id' => 'blocks'));
- $output .= form_render($form['submit']);
- // Also render the form_id as there is no form_render($form) call (as form_render does not appear to handle the
- // multi-dimensional block form array very well).
- $output .= form_render($form['form_id']);
-
- return $output;
-}
-
-function block_box_get($bid) {
- return db_fetch_array(db_query('SELECT * FROM {boxes} WHERE bid = %d', $bid));
-}
-
-/**
- * Menu callback; displays the block configuration form.
- */
-function block_admin_configure($module = NULL, $delta = 0) {
-
- $form['module'] = array('#type' => 'value', '#value' => $module);
- $form['delta'] = array('#type' => 'value', '#value' => $delta);
-
- $edit = db_fetch_array(db_query("SELECT pages, visibility, custom FROM {blocks} WHERE module = '%s' AND delta = '%s'", $module, $delta));
-
- // Module-specific block configurations.
- if ($settings = module_invoke($module, 'block', 'configure', $delta)) {
- $form['block_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Block specific settings'),
- '#collapsible' => true,
- );
-
- foreach ($settings as $k => $v) {
- $form['block_settings'][$k] = $v;
- }
- }
-
- // Get the block subject for the page title.
- $info = module_invoke($module, 'block', 'list');
- drupal_set_title(t("'%name' block", array('%name' => $info[$delta]['info'])));
-
- // Standard block configurations.
-
- $form['user_vis_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('User specific visibility settings'),
- '#collapsible' => true,
- );
- $form['user_vis_settings']['custom'] = array(
- '#type' => 'radios',
- '#title' => t('Custom visibility settings'),
- '#options' => array(t('Users cannot control whether or not they see this block.'), t('Show this block by default, but let individual users hide it.'), t('Hide this block by default but let individual users show it.')),
- '#description' => t('Allow individual users to customize the visibility of this block in their account settings.'),
- '#default_value' => $edit['custom'],
- );
- $form['page_vis_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Page specific visibility settings'),
- '#collapsible' => true,
- );
- $access = user_access('use PHP for block visibility');
-
- if ($edit['visibility'] == 2 && !$access) {
- $form['page_vis_settings'] = array();
- $form['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2);
- $form['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $edit['pages']);
- }
- else {
- $options = array(t('Show on every page except the listed pages.'), t('Show on only the listed pages.'));
- $description = t("Enter one page per line as Drupal paths. 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' => theme('placeholder', 'blog'), '%blog-wildcard' => theme('placeholder', 'blog/*'), '%front' => theme('placeholder', '<front>')));
-
- if ($access) {
- $options[] = t('Show if the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
- $description .= t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => theme('placeholder', '<?php ?>')));
- }
- $form['page_vis_settings']['visibility'] = array(
- '#type' => 'radios',
- '#title' => t('Show block on specific pages'),
- '#options' => $options,
- '#default_value' => $edit['visibility'],
- );
- $form['page_vis_settings']['pages'] = array(
- '#type' => 'textarea',
- '#title' => t('Pages'),
- '#default_value' => $edit['pages'],
- '#description' => $description,
- );
- }
-
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save block'),
- );
-
- return drupal_get_form('block_admin_configure', $form);
-}
-
-function block_admin_configure_validate($form_id, $form_values) {
- if ($form_values['module'] == 'block') {
- if (empty($form_values['info']) || db_num_rows(db_query("SELECT bid FROM {boxes} WHERE bid != %d AND info = '%s'", $form_values['delta'], $form_values['info']))) {
- form_set_error('info', t('Please ensure that each block description is unique.'));
- }
- }
-}
-
-function block_admin_configure_submit($form_id, $form_values) {
- if (!form_get_errors()) {
- db_query("UPDATE {blocks} SET visibility = %d, pages = '%s', custom = %d WHERE module = '%s' AND delta = '%s'", $form_values['visibility'], $form_values['pages'], $form_values['custom'], $form_values['module'], $form_values['delta']);
- module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values);
- drupal_set_message(t('The block configuration has been saved.'));
- cache_clear_all();
- return 'admin/block';
- }
-}
-
-/**
- * Menu callback; displays the block creation form.
- */
-function block_box_add() {
- $form = block_box_form();
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save block'));
-
- return drupal_get_form('block_box_add', $form);
-}
-
-function block_box_add_validate($form_id, $form_values) {
- if (empty($form_values['info']) || db_num_rows(db_query("SELECT info FROM {boxes} WHERE info = '%s'", $form_values['info']))) {
- form_set_error('info', t('Please ensure that each block description is unique.'));
- }
-}
-
-function block_box_add_submit($form_id, $form_values) {
- if (!form_get_errors()) {
- if (block_box_save($form_values)) {
- drupal_set_message(t('The block has been created.'));
- return 'admin/block';
- }
- }
-}
-
-/**
- * Menu callback; confirm deletion of custom blocks.
- */
-function block_box_delete($bid = 0) {
- $box = block_box_get($bid);
- $form['info'] = array('#type' => 'hidden', '#value' => $box['info'] ? $box['info'] : $box['title']);
- $form['bid'] = array('#type' => 'hidden', '#value' => $bid);
-
- return confirm_form('block_box_delete_confirm', $form, t('Are you sure you want to delete the block %name?', array('%name' => theme('placeholder', $info))), 'admin/block', '', t('Delete'), t('Cancel'));
-}
-
-/**
- * Deletion of custom blocks.
- */
-function block_box_delete_confirm_submit($form_id, $form_values) {
- db_query('DELETE FROM {boxes} WHERE bid = %d', $form_values['bid']);
- drupal_set_message(t('The block %name has been removed.', array('%name' => theme('placeholder', $form_values['info']))));
- cache_clear_all();
- return 'admin/block';
-};
-
-function block_box_form($edit = array()) {
- $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 <a href="%overview">block overview page</a>.', array('%overview' => url('admin/block'))),
- '#required' => TRUE,
- '#weight' => -19,
- );
- $form['title'] = array(
- '#type' => 'textfield',
- '#title' => t('Block title'),
- '#default_value' => $edit['title'],
- '#maxlength' => 64,
- '#description' => t('The title of the block as shown to the user.'),
- '#weight' => -18,
- );
- $form['body_filter']['#weight'] = -17;
- $form['body_filter']['body'] = array(
- '#type' => 'textarea',
- '#title' => t('Block body'),
- '#default_value' => $edit['body'],
- '#rows' => 15,
- '#description' => t('The content of the block as shown to the user.'),
- '#weight' => -17,
- );
- $form['body_filter']['format'] = filter_form($edit['format'], -16);
-
- return $form;
-}
-
-function block_box_save($edit, $delta = NULL) {
- if (!filter_access($edit['format'])) {
- $edit['format'] = FILTER_FORMAT_DEFAULT;
- }
-
- if (isset($delta)) {
- db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['format'], $delta);
- }
- else {
- db_query("INSERT INTO {boxes} (title, body, info, format) VALUES ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['format']);
- }
- return true;
-}
-
-/**
- * Implementation of hook_user().
- *
- * Allow users to decide which custom blocks to display when they visit
- * the site.
- */
-function block_user($type, $edit, &$user, $category = NULL) {
- switch ($type) {
- case 'form':
- if ($category == 'account') {
- $result = db_query('SELECT * FROM {blocks} WHERE status = 1 AND custom != 0 ORDER BY weight, module, delta');
- $form['block'] = array('#type' => 'fieldset', '#title' => t('Block configuration'), '#weight' => 3, '#collapsible' => TRUE, '#tree' => TRUE);
- while ($block = db_fetch_object($result)) {
- $data = module_invoke($block->module, 'block', 'list');
- if ($data[$block->delta]['info']) {
- $return = TRUE;
- $form['block'][$block->module][$block->delta] = array('#type' => 'checkbox', '#title' => $data[$block->delta]['info'], '#default_value' => isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : ($block->custom == 1));
- }
- }
-
- if ($return) {
- return $form;
- }
- }
-
- break;
- case 'validate':
- if (!$edit['block']) {
- $edit['block'] = array();
- }
- return $edit;
- }
-}
-
-/**
- * Return 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 <i>module</i>_<i>delta</i>.
- * 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
- * Add a proper primary key (bid) to the blocks table so we don't have
- * to mess around with this <i>module</i>_<i>delta</i> construct.
- * Currently, the blocks table has no primary key defined!
- */
-function block_list($region) {
- global $user, $theme_key;
-
- static $blocks = array();
-
- if (!count($blocks)) {
- $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s' AND status = 1 ORDER BY region, weight, module", $theme_key);
- while ($block = db_fetch_object($result)) {
- if (!isset($blocks[$block->region])) {
- $blocks[$block->region] = array();
- }
- // Use the user's block visibility setting, if necessary
- if ($block->custom != 0) {
- if ($user->uid && isset($user->block[$block->module][$block->delta])) {
- $enabled = $user->block[$block->module][$block->delta];
- }
- else {
- $enabled = ($block->custom == 1);
- }
- }
- else {
- $enabled = TRUE;
- }
-
- // Match path if necessary
- if ($block->pages) {
- if ($block->visibility < 2) {
- $path = drupal_get_path_alias($_GET['q']);
- $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($block->pages, '/')) .')$/';
- $page_match = !($block->visibility xor preg_match($regexp, $path));
- }
- else {
- $page_match = drupal_eval($block->pages);
- }
- }
- else {
- $page_match = TRUE;
- }
-
- if ($enabled && $page_match) {
- // Check the current throttle status and see if block should be displayed
- // based on server load.
- if (!($block->throttle && (module_invoke('throttle', 'status') > 0))) {
- $array = module_invoke($block->module, 'block', 'view', $block->delta);
- if (isset($array) && is_array($array)) {
- foreach ($array as $k => $v) {
- $block->$k = $v;
- }
- }
- }
- if (isset($block->content) && $block->content) {
- $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block;
- }
- }
- }
- }
- // Create an empty array if there were no entries
- if (!isset($blocks[$region])) {
- $blocks[$region] = array();
- }
- return $blocks[$region];
-}
-
-
diff --git a/modules/blog/blog.module b/modules/blog/blog.module
deleted file mode 100644
index cd93d00..0000000
--- a/modules/blog/blog.module
+++ /dev/null
@@ -1,305 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables keeping an easily and regularly updated web page or a blog.
- */
-
-/**
- * Implementation of hook_node_info().
- */
-function blog_node_info() {
- return array('blog' => array('name' => t('blog entry'), 'base' => 'blog'));
-}
-
-/**
- * Implementation of hook_perm().
- */
-function blog_perm() {
- return array('edit own blog');
-}
-
-/**
- * Implementation of hook_access().
- */
-function blog_access($op, $node) {
- global $user;
-
- if ($op == 'create') {
- return user_access('edit own blog') && $user->uid;
- }
-
- if ($op == 'update' || $op == 'delete') {
- if (user_access('edit own blog') && ($user->uid == $node->uid)) {
- return TRUE;
- }
- }
-}
-
-/**
- * Implementation of hook_user().
- */
-function blog_user($type, &$edit, &$user) {
- if ($type == 'view' && user_access('edit own blog', $user)) {
- $items[] = array('title' => t('Blog'),
- 'value' => l(t('view recent blog entries'), "blog/$user->uid", array('title' => t("Read %username's latest blog entries.", array('%username' => $user->name)))),
- 'class' => 'blog',
- );
- return array(t('History') => $items);
- }
-}
-
-/**
- * Implementation of hook_help().
- */
-function blog_help($section) {
- switch ($section) {
- case 'admin/help#blog':
- $output = '<p>'. t('The blog module allows registered users to maintain an online weblog (commonly known as a blog), often referred to as an online journal or diary. Blogs are made up of individual posts that are time stamped and are typically viewed by date as you would a diary. Blogs often contain links to webpages users have read and/or agree/disagree with.') .'</p>';
- $output .= '<p>'. t('The blog module adds a <em>user blogs</em> navigation link to the site, which takes any visitor to a page that displays the most recent blog entries from all the users on the site. The navigation menu has a <em>create a blog entry</em> link (which takes you to a submission form) and a <em>view personal blog</em> link (which displays your blog entries as other people will see them). The blog module also creates a <em>recent blog posts</em> block that can be enabled.') .'</p>';
- $output .= '<p>'. t('If a user has the ability to post blogs, then the import module (news aggregator) will display a blog-it link next to each news item in its lists. Clicking on this takes the user to the blog submission form, with the title, a link to the item, and a link to the source into the body text already in the text box, ready for the user to add a comment or explanation. This actively encourages people to add blog entries about things they see and hear elsewhere in the website and from your syndicated partner sites.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>read your blog via your user profile at <a href="%user">my account</a>.</li>
-<li>post a blog at <a href="%node-add-blog">create content &gt;&gt; personal blog entry</a>.</li>
-<li>administer blog at <a href="%admin-node-configure-types-blog">administer &gt;&gt; content &gt;&gt; configure &gt;&gt; content types &gt;&gt; personal blog entry</a>.</li>
-<li>administer blog api at <a href="%admin-settings-blogapi">administer &gt;&gt; settings &gt;&gt; blogapi</a>.</li>
-<li>enable the "recent blog posts" block at <a href="%admin-block">administer &gt;&gt; blocks</a> to show the 10 most recent blog posts.</li>
-</ul>
-', array('%user' => url('user'), '%node-add-blog' => url('node/add/blog'), '%admin-node-configure-types-blog' => url('admin/node/configure/types/blog'), '%admin-settings-blogapi' => url('admin/settings/blogapi'), '%admin-block' => url('admin/block')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%blog">Blog page</a>.', array('%blog' => 'http://drupal.org/handbook/modules/blog/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Enables keeping an easily and regularly updated web page or a blog.');
- case 'node/add#blog':
- return t("A blog is a regularly updated journal or diary made up of individual posts shown in reversed chronological order. A blog is tightly coupled to the author so each user will have his 'own' blog.");
- }
-}
-
-/**
- * Displays an RSS feed containing recent blog entries of a given user.
- */
-function blog_feed_user($uid = 0) {
- global $user;
-
- if ($uid) {
- $account = user_load(array('uid' => $uid, 'status' => 1));
- }
- else {
- $account = $user;
- }
-
- $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title, r.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND u.uid = %d AND n.status = 1 ORDER BY n.created DESC"), $uid, 0, variable_get('feed_default_items', 10));
- $channel['title'] = $account->name ."'s blog";
- $channel['link'] = url("blog/$uid", NULL, NULL, TRUE);
- $channel['description'] = $term->description;
- node_feed($result, $channel);
-}
-
-/**
- * Displays an RSS feed containing recent blog entries of all users.
- */
-function blog_feed_last() {
- $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title, r.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.created DESC"), 0, variable_get('feed_default_items', 10));
- $channel['title'] = variable_get('site_name', 'drupal') .' blogs';
- $channel['link'] = url('blog', NULL, NULL, TRUE);
- $channel['description'] = $term->description;
- node_feed($result, $channel);
-}
-
-/**
- * Menu callback; displays a Drupal page containing recent blog entries.
- */
-function blog_page($a = NULL, $b = NULL) {
-
- if (is_numeric($a)) { // $a is a user ID
- if ($b == 'feed') {
- return blog_feed_user($a);
- }
- else {
- return blog_page_user($a);
- }
- }
- else if ($a == 'feed') {
- return blog_feed_last();
- }
- else {
- return blog_page_last();
- }
-}
-
-/**
- * Displays a Drupal page containing recent blog entries of a given user.
- */
-function blog_page_user($uid) {
- global $user;
-
- $account = user_load(array((is_numeric($uid) ? 'uid' : 'name') => $uid, 'status' => 1));
-
- if ($account->uid) {
- drupal_set_title($title = t("%name's blog", array('%name' => $account->name)));
-
- if (($account->uid == $user->uid) && user_access('edit own blog')) {
- $output = '<li>'. l(t('Post new blog entry.'), "node/add/blog") .'</li>';
- }
- else if ($account->uid == $user->uid) {
- $output = '<li>'. t('You are not allowed to post a new blog entry.') .'</li>';
- }
-
- if ($output) {
- $output = '<ul>'. $output .'</ul>';
- }
- else {
- $output = '';
- }
-
- $result = pager_query(db_rewrite_sql("SELECT n.nid, n.sticky, n.created FROM {node} n WHERE type = 'blog' AND n.uid = %d AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10), 0, NULL, $account->uid);
- while ($node = db_fetch_object($result)) {
- $output .= node_view(node_load($node->nid), 1);
- }
- $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
- $output .= theme('feed_icon', url("blog/$account->uid/feed"));
-
- drupal_add_link(array('rel' => 'alternate',
- 'type' => 'application/rss+xml',
- 'title' => t('RSS - %title', array('%title' => $title)),
- 'href' => url("blog/$account->uid/feed")));
- return $output;
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Displays a Drupal page containing recent blog entries of all users.
- */
-function blog_page_last() {
- global $user;
-
- $output = '';
-
- $result = pager_query(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.created DESC"), variable_get('default_nodes_main', 10));
-
- while ($node = db_fetch_object($result)) {
- $output .= node_view(node_load($node->nid), 1);
- }
- $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
- $output .= theme('feed_icon', url('blog/feed'));
-
- drupal_add_link(array('rel' => 'alternate',
- 'type' => 'application/rss+xml',
- 'title' => t('RSS - blogs'),
- 'href' => url("blog/feed")));
- return $output;
-}
-
-/**
- * Implementation of hook_form().
- */
-function blog_form(&$node) {
- global $nid;
- $iid = $_GET['iid'];
-
-
- if (empty($node->body)) {
- /*
- ** If the user clicked a "blog it" link, we load the data from the
- ** database and quote it in the blog:
- */
-
- if ($nid && $blog = node_load($nid)) {
- $node->body = '<em>'. $blog->body .'</em> ['. l($blog->name, "node/$nid") .']';
- }
-
- if ($iid && $item = db_fetch_object(db_query('SELECT i.*, f.title as ftitle, f.link as flink FROM {aggregator_item} i, {aggregator_feed} f WHERE i.iid = %d AND i.fid = f.fid', $iid))) {
- $node->title = $item->title;
- // Note: $item->description has been validated on aggregation.
- $node->body = '<a href="'. check_url($item->link) .'">'. check_plain($item->title) .'</a> - <em>'. $item->description .'</em> [<a href="'. check_url($item->flink) .'">'. check_plain($item->ftitle) ."</a>]\n";
- }
-
- }
-
- $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
- $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
- $form['body_filter']['filter'] = filter_form($node->format);
- return $form;
-}
-
-/**
- * Implementation of hook_view().
- */
-function blog_view(&$node, $teaser = FALSE, $page = FALSE) {
- if ($page) {
- // Breadcrumb navigation
- $breadcrumb[] = array('path' => 'blog', 'title' => t('blogs'));
- $breadcrumb[] = array('path' => 'blog/'. $node->uid, 'title' => t("%name's blog", array('%name' => $node->name)));
- $breadcrumb[] = array('path' => 'node/'. $node->nid);
- menu_set_location($breadcrumb);
- }
- $node = node_prepare($node, $teaser);;
-}
-
-/**
- * Implementation of hook_link().
- */
-function blog_link($type, $node = 0, $main = 0) {
- $links = array();
-
- if ($type == 'node' && $node->type == 'blog') {
- if (arg(0) != 'blog' || arg(1) != $node->uid) {
- $links[] = l(t("%username's blog", array('%username' => $node->name)), "blog/$node->uid", array('title' => t("Read %username's latest blog entries.", array('%username' => $node->name))));
- }
- }
-
- return $links;
-}
-
-/**
- * Implementation of hook_menu().
- */
-function blog_menu($may_cache) {
- global $user;
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'node/add/blog', 'title' => t('blog entry'),
- 'access' => user_access('edit own blog'));
- $items[] = array('path' => 'blog', 'title' => t('blogs'),
- 'callback' => 'blog_page',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array('path' => 'blog/'. $user->uid, 'title' => t('my blog'),
- 'access' => user_access('edit own blog'),
- 'type' => MENU_DYNAMIC_ITEM);
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- *
- * Displays the most recent 10 blog titles.
- */
-function blog_block($op = 'list', $delta = 0) {
- global $user;
- if ($op == 'list') {
- $block[0]['info'] = t('Recent blog posts');
- return $block;
- }
- else if ($op == 'view') {
- if (user_access('access content')) {
- $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title, n.created FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.created DESC"), 0, 10);
- if (db_num_rows($result)) {
- $block['content'] = node_title_list($result);
- $block['content'] .= '<div class="more-link">'. l(t('more'), 'blog', array('title' => t('Read the latest blog entries.'))) .'</div>';
- $block['subject'] = t('Recent blog posts');
- return $block;
- }
- }
- }
-}
-
-
diff --git a/modules/blogapi/blogapi.module b/modules/blogapi/blogapi.module
deleted file mode 100644
index ce025f9..0000000
--- a/modules/blogapi/blogapi.module
+++ /dev/null
@@ -1,737 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enable users to post using applications that support XML-RPC blog APIs.
- */
-
-/**
- * Implementation of hook_help().
- */
-function blogapi_help($section) {
- switch ($section) {
- case 'admin/help#blogapi':
- $output = '<p>'. t('The blog API module enables a post to be posted to a site via external GUI applications. Many users perfer to use external tools to improve their ability to read and post responses in a customized way. The blog api provides users the freedom to use the blogging tools they want but still have the blogging server of choice.') .'</p>';
- $output .= '<p>'. t('When this module is enabled and configured you can use programs like <a href="%external-http-ecto-kung-foo-tv">Ecto</a> to create and publish posts from your desktop. Blog API module supports several XML-RPC based blogging APIs such as the <a href="%-">Blogger API</a>, <a href="%external-http-www-xmlrpc-com-metaWeblogApi">MetaWeblog API</a>, and most of the <a href="%external-http-www-movabletype-org-docs-mtmanual_programmatic-html">Movable Type API</a>. Any desktop blogging tools or other services (e.g. <a href="%external-http-www-flickr-com">Flickr\'s</a> "post to blog") that support these APIs should work with this site.', array('%external-http-ecto-kung-foo-tv' => 'http://ecto.kung-foo.tv/', '%-' => url('http://www.blogger.com/developers/api/1_docs/'), '%external-http-www-xmlrpc-com-metaWeblogApi' => 'http://www.xmlrpc.com/metaWeblogApi', '%external-http-www-movabletype-org-docs-mtmanual_programmatic-html' => 'http://www.movabletype.org/docs/mtmanual_programmatic.html', '%external-http-www-flickr-com' => 'http://www.flickr.com')) .'</p>';
- $output .= '<p>'. t('This module also allows site administrators to configure which content types can be posted via the external applications. So, for instance, users can post forum topics as well as blog posts. Where supported, the external applications will display each content type as a separate "blog".<!--break-->') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>view the XML-RPC page on your site at &gt;&gt; <a href="%file-xmlrpc">xmlrpc.php</a>.</li>
-<li><a href="%admin-settings-blogapi">administer &gt;&gt; settings &gt;&gt; blog api</a>.</li>
-</ul>
-', array('%file-xmlrpc' => 'xmlrpc.php', '%admin-settings-blogapi' => url('admin/settings/blogapi')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%blogapi">BlogApi page</a>.', array('%blogapi' => 'http://drupal.org/handbook/modules/blogapi/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows users to post content using applications that support XML-RPC blog APIs.');
- }
-}
-
-/**
- * Implementation of hook_xmlrpc().
- */
-function blogapi_xmlrpc() {
- return array(
- array(
- 'blogger.getUsersBlogs',
- 'blogapi_blogger_get_users_blogs',
- array('array', 'string', 'string', 'string'),
- t('Returns a list of weblogs to which an author has posting privileges.')),
- array(
- 'blogger.getUserInfo',
- 'blogapi_blogger_get_user_info',
- array('struct', 'string', 'string', 'string'),
- t('Returns information about an author in the system.')),
- array(
- 'blogger.newPost',
- 'blogapi_blogger_new_post',
- array('string', 'string', 'string', 'string', 'string', 'string', 'boolean'),
- t('Creates a new post, and optionally publishes it.')),
- array(
- 'blogger.editPost',
- 'blogapi_blogger_edit_post',
- array('boolean', 'string', 'string', 'string', 'string', 'string', 'boolean'),
- t('Updates the information about an existing post.')),
- array(
- 'blogger.getPost',
- 'blogapi_blogger_get_post',
- array('struct', 'string', 'string', 'string', 'string'),
- t('Returns information about a specific post.')),
- array(
- 'blogger.deletePost',
- 'blogapi_blogger_delete_post',
- array('boolean', 'string', 'string', 'string', 'string', 'boolean'),
- t('Deletes a post.')),
- array(
- 'blogger.getRecentPosts',
- 'blogapi_blogger_get_recent_posts',
- array('array', 'string', 'string', 'string', 'string', 'int'),
- t('Returns a list of the most recent posts in the system.')),
- array(
- 'metaWeblog.newPost',
- 'blogapi_metaweblog_new_post',
- array('string', 'string', 'string', 'string', 'struct', 'boolean'),
- t('Creates a new post, and optionally publishes it.')),
- array(
- 'metaWeblog.editPost',
- 'blogapi_metaweblog_edit_post',
- array('boolean', 'string', 'string', 'string', 'struct', 'boolean'),
- t('Updates information about an existing post.')),
- array(
- 'metaWeblog.getPost',
- 'blogapi_metaweblog_get_post',
- array('struct', 'string', 'string', 'string'),
- t('Returns information about a specific post.')),
- array(
- 'metaWeblog.newMediaObject',
- 'blogapi_metaweblog_new_media_object',
- array('string', 'string', 'string', 'string', 'struct'),
- t('Uploads a file to your webserver.')),
- array(
- 'metaWeblog.getCategories',
- 'blogapi_metaweblog_get_category_list',
- array('struct', 'string', 'string', 'string'),
- t('Returns a list of all categories to which the post is assigned.')),
- array(
- 'metaWeblog.getRecentPosts',
- 'blogapi_metaweblog_get_recent_posts',
- array('array', 'string', 'string', 'string', 'int'),
- t('Returns a list of the most recent posts in the system.')),
- array(
- 'mt.getRecentPostTitles',
- 'blogapi_mt_get_recent_post_titles',
- array('array', 'string', 'string', 'string', 'int'),
- t('Returns a bandwidth-friendly list of the most recent posts in the system.')),
- array(
- 'mt.getCategoryList',
- 'blogapi_mt_get_category_list',
- array('array', 'string', 'string', 'string'),
- t('Returns a list of all categories defined in the weblog.')),
- array(
- 'mt.getPostCategories',
- 'blogapi_mt_get_post_categories',
- array('array', 'string', 'string', 'string'),
- t('Returns a list of all categories to which the post is assigned.')),
- array(
- 'mt.setPostCategories',
- 'blogapi_mt_set_post_categories',
- array('boolean', 'string', 'string', 'string', 'array'),
- t('Sets the categories for a post.')),
- array(
- 'mt.supportedMethods',
- 'xmlrpc_server_list_methods',
- array('array'),
- t('Retrieve information about the XML-RPC methods supported by the server.')),
- array(
- 'mt.supportedTextFilters',
- 'blogapi_mt_supported_text_filters',
- array('array'),
- t('Retrieve information about the text formatting plugins supported by the server.')),
- array(
- 'mt.getTrackbackPings',
- 'blogapi_mt_get_trackback_pings',
- array('array', 'string'),
- t('Retrieve the list of TrackBack pings posted to a particular entry. This could be used to programmatically retrieve the list of pings for a particular entry, then iterate through each of those pings doing the same, until one has built up a graph of the web of entries referencing one another on a particular topic.')),
- array(
- 'mt.publishPost',
- 'blogap_mti_publish_post',
- array('boolean', 'string', 'string', 'string'),
- t('Publish (rebuild) all of the static files related to an entry from your weblog. Equivalent to saving an entry in the system (but without the ping).')));
-}
-
-/**
- * Blogging API callback. Finds the URL of a user's blog.
- */
-
-function blogapi_blogger_get_users_blogs($appid, $username, $password) {
-
- $user = blogapi_validate_user($username, $password);
- if ($user->uid) {
- $types = _blogapi_get_node_types();
- $structs = array();
- foreach ($types as $type) {
- $structs[] = array('url' => url('blog/' . $user->uid, NULL, NULL, true), 'blogid' => $type, 'blogName' => $user->name . ": " . $type);
- }
- return $structs;
- }
- else {
- return blogapi_error($user);
- }
-}
-
-/**
- * Blogging API callback. Returns profile information about a user.
- */
-function blogapi_blogger_get_user_info($appkey, $username, $password) {
- $user = blogapi_validate_user($username, $password);
-
- if ($user->uid) {
- $name = explode(' ', $user->realname ? $user->realname : $user->name, 2);
- return array(
- 'userid' => $user->uid,
- 'lastname' => $name[1],
- 'firstname' => $name[0],
- 'nickname' => $user->name,
- 'email' => $user->mail,
- 'url' => url('blog/' . $user->uid, NULL, NULL, true));
- }
- else {
- return blogapi_error($user);
- }
-}
-
-/**
- * Blogging API callback. Inserts a new blog post as a node.
- */
-function blogapi_blogger_new_post($appkey, $blogid, $username, $password, $content, $publish) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $edit = array();
- $edit['type'] = _blogapi_blogid($blogid);
- // get the node type defaults
- $node_type_default = variable_get('node_options_'. $edit['type'], array('status', 'promote'));
- $edit['uid'] = $user->uid;
- $edit['name'] = $user->name;
- $edit['promote'] = in_array('promote', $node_type_default);
- $edit['comment'] = variable_get('comment_'. $edit['type'], 2);
- $edit['moderate'] = in_array('moderate', $node_type_default);
- $edit['revision'] = in_array('revision', $node_type_default);
- $edit['format'] = FILTER_FORMAT_DEFAULT;
- $edit['status'] = $publish;
-
- // check for bloggerAPI vs. metaWeblogAPI
- if (is_array($content)) {
- $edit['title'] = $content['title'];
- $edit['body'] = $content['description'];
- _blogapi_mt_extra($edit, $content);
- }
- else {
- $edit['title'] = blogapi_blogger_title($content);
- $edit['body'] = $content;
- }
-
- if (!node_access('create', $edit['type'])) {
- return blogapi_error(t('You do not have permission to create the type of post you wanted to create.'));
- }
-
- if (user_access('administer nodes') && !isset($edit['date'])) {
- $edit['date'] = format_date(time(), 'custom', 'Y-m-d H:i:s O');
- }
-
- node_validate($edit);
- if ($errors = form_get_errors()) {
- return blogapi_error(implode("\n", $errors));
- }
-
- $node = node_submit($edit);
- node_save($node);
- if ($node->nid) {
- watchdog('content', t('%type: added %title using blog API.', array('%type' => '<em>'. t($node->type) .'</em>', '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
- // blogger.newPost returns a string so we cast the nid to a string by putting it in double quotes:
- return "$node->nid";
- }
-
- return blogapi_error(t('Error storing post.'));
-}
-
-/**
- * Blogging API callback. Modifies the specified blog node.
- */
-function blogapi_blogger_edit_post($appkey, $postid, $username, $password, $content, $publish) {
-
- $user = blogapi_validate_user($username, $password);
-
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $node = node_load($postid);
- if (!$node) {
- return blogapi_error(message_na());
- }
- // Let the teaser be re-generated.
- unset($node->teaser);
-
- if (!node_access('update', $node)) {
- return blogapi_error(t('You do not have permission to update this post.'));
- }
-
- $node->status = $publish;
-
- // check for bloggerAPI vs. metaWeblogAPI
- if (is_array($content)) {
- $node->title = $content['title'];
- $node->body = $content['description'];
- _blogapi_mt_extra($node, $content);
- }
- else {
- $node->title = blogapi_blogger_title($content);
- $node->body = $content;
- }
-
- node_validate($node);
- if ($errors = form_get_errors()) {
- return blogapi_error(implode("\n", $errors));
- }
-
- if (user_access('administer nodes') && !isset($edit['date'])) {
- $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
- }
- $node = node_submit($node);
- node_save($node);
- if ($node->nid) {
- watchdog('content', t('%type: updated %title using blog API.', array('%type' => '<em>'. t($node->type) .'</em>', '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
- return true;
- }
-
- return blogapi_error(t('Error storing post.'));
-}
-
-/**
- * Blogging API callback. Returns a specified blog node.
- */
-function blogapi_blogger_get_post($appkey, $postid, $username, $password) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $node = node_load($postid);
-
- return _blogapi_get_post($node, true);
-}
-
-/**
- * Blogging API callback. Removes the specified blog node.
- */
-function blogapi_blogger_delete_post($appkey, $postid, $username, $password, $publish) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- node_delete($postid);
- return true;
-}
-
-/**
- * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE
- * <a href="http://movabletype.org/docs/mtmanual_programmatic.html#item_mt%2EgetRecentPostTitles">
- * returns a bandwidth-friendly list</a>.
- */
-function blogapi_blogger_get_recent_posts($appkey, $blogid, $username, $password, $number_of_posts, $bodies = TRUE) {
- // Remove unused appkey (from bloggerAPI).
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $type = _blogapi_blogid($blogid);
- if ($bodies) {
- $result = db_query_range("SELECT n.nid, n.title, r.body, n.created, u.name FROM {node} n, {node_revisions} r, {users} u WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $number_of_posts);
- }
- else {
- $result = db_query_range("SELECT n.nid, n.title, n.created, u.name FROM {node} n, {users} u WHERE n.uid = u.uid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $number_of_posts);
- }
- $blogs = array ();
- while ($blog = db_fetch_object($result)) {
- $blogs[] = _blogapi_get_post($blog, $bodies);
- }
- return $blogs;
-}
-
-function blogapi_metaweblog_new_post($blogid, $username, $password, $content, $publish) {
- return blogapi_blogger_new_post('0123456789ABCDEF', $blogid, $username, $password, $content, $publish);
-}
-
-function blogapi_metaweblog_edit_post($postid, $username, $password, $content, $publish) {
- return blogapi_blogger_edit_post('0123456789ABCDEF', $postid, $username, $password, $content, $publish);
-}
-
-function blogapi_metaweblog_get_post($postid, $username, $password) {
- return blogapi_blogger_get_post('01234567890ABCDEF', $postid, $username, $password);
-}
-
-/**
- * Blogging API callback. Inserts a file into Drupal.
- */
-function blogapi_metaweblog_new_media_object($blogid, $username, $password, $file) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $name = basename($file['name']);
- $data = $file['bits'];
-
- if (!$data) {
- return blogapi_error(t('No file sent.'));
- }
-
- if (!$file = file_save_data($data, $name)) {
- return blogapi_error(t('Error storing file.'));
- }
-
- // Return the successful result.
- return array('url' => file_create_url($file), 'struct');
-}
-/**
- * Blogging API callback. Returns a list of the taxonomy terms that can be
- * associated with a blog node.
- */
-function blogapi_metaweblog_get_category_list($blogid, $username, $password) {
- $type = _blogapi_blogid($blogid);
- $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $type, 'vid');
- $categories = array();
- if ($vocabularies) {
- foreach ($vocabularies as $vocabulary) {
- $terms = module_invoke('taxonomy', 'get_tree', $vocabulary->vid, 0, -1);
- foreach ($terms as $term) {
- $term_name = $term->name;
- foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) {
- $term_name = $parent->name . '/' . $term_name;
- }
- $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid);
- }
- }
- }
- return $categories;
-}
-
-function blogapi_metaweblog_get_recent_posts($blogid, $username, $password, $number_of_posts) {
- return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, TRUE);
-}
-
-// see above
-function blogapi_mt_get_recent_post_titles($blogid, $username, $password, $number_of_posts) {
- return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, FALSE);
-}
-
-/* **** */
-function blogapi_mt_get_category_list($blogid, $username, $password) {
- return blogapi_metaweblog_get_category_list($blogid, $username, $password);
-}
-
-/**
- * Blogging API callback. Returns a list of the taxonomy terms that are
- * assigned to a particular node.
- */
-function blogapi_mt_get_post_categories($postid, $username, $password) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $terms = module_invoke('taxonomy', 'node_get_terms', $postid, 'tid');
- $categories = array();
- foreach ($terms as $term) {
- $term_name = $term->name;
- foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) {
- $term_name = $parent->name . '/' . $term_name;
- }
- $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid, 'isPrimary' => true);
- }
- return $categories;
-}
-
-/**
- * Blogging API callback. Assigns taxonomy terms to a particular node.
- */
-function blogapi_mt_set_post_categories($postid, $username, $password, $categories) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
-
- $node = node_load($postid);
- $node->taxonomy = array();
- foreach ($categories as $category) {
- $node->taxonomy[] = $category['categoryId'];
- }
- node_save($node);
- return TRUE;
-}
-
-/**
- * Blogging API callback. Sends a list of available input formats.
- */
-function blogapi_mt_supported_text_filters() {
- // NOTE: we're only using anonymous' formats because the MT spec
- // does not allow for per-user formats.
- $formats = filter_formats();
-
- $filters = array();
- foreach ($formats as $format) {
- $filter['key'] = $format->format;
- $filter['label'] = $format->name;
- $filters[] = $filter;
- }
-
- return $filters;
-}
-
-/**
- * Blogging API callback. Can not be implemented without support from
- * trackback module.
- */
-function blogapi_mt_get_trackback_pings() {
- return blogapi_error(t('Not implemented.'));
-}
-
-/**
- * Blogging API callback. Publishes the given node
- */
-function blogap_mti_publish_post($postid, $username, $password) {
- $user = blogapi_validate_user($username, $password);
- if (!$user->uid) {
- return blogapi_error($user);
- }
- $node = node_load($postid);
- if (!$node) {
- return blogapi_error(t('Invalid post.'));
- }
-
- $node->status = 1;
- if (!node_access('update', $node)) {
- return blogapi_error(t('You do not have permission to update this post.'));
- }
-
- node_save($node);
-
- return true;
-}
-
-/**
- * Prepare an error message for returning to the XMLRPC caller.
- */
-function blogapi_error($message) {
- static $xmlrpcusererr;
- if (!is_array($message)) {
- $message = array($message);
- }
-
- $message = implode(' ', $message);
-
- return xmlrpc_error($xmlrpcusererr + 1, strip_tags($message));
-}
-
-/**
- * Ensure that the given user has permission to edit a blog.
- */
-function blogapi_validate_user($username, $password) {
- global $user;
-
- $user = user_authenticate($username, $password);
-
- if ($user->uid) {
- if (user_access('edit own blog', $user)) {
- return $user;
- }
- else {
- return t("You either tried to edit somebody else's blog or you don't have permission to edit your own blog.");
- }
- }
- else {
- return t('Wrong username or password.');
- }
-}
-
-/**
- * For the blogger API, extract the node title from the contents field.
- */
-function blogapi_blogger_title(&$contents) {
- if (eregi('<title>([^<]*)</title>', $contents, $title)) {
- $title = strip_tags($title[0]);
- $contents = ereg_replace('<title>[^<]*</title>', '', $contents);
- }
- else {
- list($title, $contents) = explode("\n", $contents, 2);
- }
- return $title;
-}
-
-function blogapi_settings() {
- $form['blogapi_engine'] = array(
- '#type' => 'select', '#title' => t('XML-RPC Engine'), '#default_value' => variable_get('blogapi_engine', 0),
- '#options' => array(0 => 'Blogger', 1 => 'MetaWeblog', 2 => 'Movabletype'),
- '#description' => t('RSD or Really-Simple-Discovery is a mechanism which allows external blogger tools to discover the APIs they can use to interact with Drupal. Here you can set the preferred method for blogger tools to interact with your site. The common XML-RPC engines are Blogger, MetaWeblog and Movabletype. If you are not sure which is the correct setting, choose Blogger.')
- );
-
- $node_types = node_get_types();
- $defaults = isset($node_types['blog']) ? array('blog' => 1) : array();
- $form['blogapi_node_types'] = array(
- '#type' => 'checkboxes', '#title' => t('Blog types'), '#required' => TRUE,
- '#default_value' => variable_get('blogapi_node_types', $defaults), '#options' => $node_types,
- '#description' => t('Select the content types for which you wish to enable posting via blogapi. Each type will appear as a different "blog" in the client application (if supported).')
- );
-
- return $form;
-}
-
-function blogapi_menu($may_cache) {
- $items = array();
-
- if (drupal_get_path_alias($_GET['q']) == variable_get('site_frontpage', 'node')) {
- drupal_add_link(array('rel' => 'EditURI',
- 'type' => 'application/rsd+xml',
- 'title' => t('RSD'),
- 'href' => url('blogapi/rsd', NULL, NULL, TRUE)));
- }
-
- if ($may_cache) {
- $items[] = array('path' => 'blogapi', 'title' => t('RSD'), 'callback' => 'blogapi_blogapi', 'access' => user_access('access content'), 'type' => MENU_CALLBACK);
- }
-
- return $items;
-}
-
-function blogapi_blogapi() {
- switch (arg(1)) {
- case 'rsd':
- blogapi_rsd();
- break;
- default:
- drupal_not_found();
- break;
- }
-}
-
-function blogapi_rsd() {
- global $base_url;
-
- $xmlrpc = $base_url .'/'. 'xmlrpc.php';
- $base = url('', NULL, NULL, TRUE);
- $blogid = 1; # until we figure out how to handle multiple bloggers
-
- drupal_set_header('Content-Type: application/rsd+xml; charset=utf-8');
- print <<<__RSD__
-<?xml version="1.0"?>
-<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
- <service>
- <engineName>Drupal</engineName>
- <engineLink>http://drupal.org/</engineLink>
- <homePageLink>$base</homePageLink>
- <apis>
- <api name="MetaWeblog" preferred="false" apiLink="$xmlrpc" blogID="$blogid" />
- <api name="Blogger" preferred="true" apiLink="$xmlrpc" blogID="$blogid" />
- <api name="MovableType" preferred="false" apiLink="$xmlrpc" blogID="$blogid" />
- </apis>
- </service>
-</rsd>
-__RSD__;
-}
-
-/**
- * Handles extra information sent by clients according to MovableType's spec.
- */
-function _blogapi_mt_extra(&$node, $struct) {
- if (is_array($node)) {
- $was_array = true;
- $node = (object)$node;
- }
-
- // mt_allow_comments
- if (array_key_exists('mt_allow_comments', $struct)) {
- switch ($struct['mt_allow_comments']) {
- case 0:
- $node->comment = COMMENT_NODE_DISABLED;
- break;
- case 1:
- $node->comment = COMMENT_NODE_READ_WRITE;
- break;
- case 2:
- $node->comment = COMMENT_NODE_READ_ONLY;
- break;
- }
- }
-
- // merge the 3 body sections (description, mt_excerpt, mt_text_more) into
- // one body
- if ($struct['mt_excerpt']) {
- $node->body = $struct['mt_excerpt'] .'<!--break-->'.$node->body;
- }
- if ($struct['mt_text_more']) {
- $node->body = $node->body . '<!--extended-->' . $struct['mt_text_more'];
- }
-
- // mt_tb_ping_urls
- if (function_exists('trackback_send')) {
- if (is_array($struct['mt_tb_ping_urls'])) {
- foreach ($struct['mt_tb_ping_urls'] as $tb_ping_url) {
- $node->tb_url = $tb_ping_url->getVal();
- trackback_send($node);
- unset($node->tb_url); // make sure we don't ping twice
- }
- }
- else {
- $node->tb_url = $struct['mt_tb_ping_urls'];
- }
- }
-
- // mt_convert_breaks
- if ($struct['mt_convert_breaks']) {
- $node->format = $struct['mt_convert_breaks'];
- }
-
- // dateCreated
- if ($struct['dateCreated']) {
- $node->date = format_date(mktime($struct['dateCreated']->hour, $struct['dateCreated']->minute, $struct['dateCreated']->second, $struct['dateCreated']->month, $struct['dateCreated']->day, $struct['dateCreated']->year), 'custom', 'Y-m-d H:i:s O');
- }
-
- if ($was_array) {
- $node = (array)$node;
- }
-}
-
-function _blogapi_get_post($node, $bodies = true) {
- $xmlrpcval = array (
- 'userid' => $node->name,
- 'dateCreated' => xmlrpc_date($node->created),
- 'title' => $node->title,
- 'postid' => $node->nid,
- 'link' => url('node/'.$node->nid, NULL, NULL, true),
- 'permaLink' => url('node/'.$node->nid, NULL, NULL, true),
- );
- if ($bodies) {
- if ($node->comment = 1) {
- $comment = 2;
- }
- if ($node->comment = 2) {
- $comment = 1;
- }
-
- $xmlrpcval['content'] = "<title>$node->title</title>$node->body";
- $xmlrpcval['description'] = $node->body;
- // Add MT specific fields
- $xmlrpcval['mt_allow_comments'] = $comment;
- $xmlrpcval['mt_convert_breaks'] = $node->format;
- }
-
- return $xmlrpcval;
-}
-
-function _blogapi_blogid($id) {
- if (is_numeric($id)) {
- return 'blog';
- }
- else {
- return $id;
- }
-}
-
-function _blogapi_get_node_types() {
- $available_types = array_keys(array_filter(variable_get('blogapi_node_types', array('blog' => 1))));
- $types = array();
- foreach (node_get_types() as $type => $name) {
- if (node_access('create', $type) && in_array($type, $available_types)) {
- $types[] = $type;
- }
- }
-
- return $types;
-}
-
diff --git a/modules/book/book.module b/modules/book/book.module
deleted file mode 100644
index 0f8fc78..0000000
--- a/modules/book/book.module
+++ /dev/null
@@ -1,1045 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Allows users to collaboratively author a book.
- */
-
-/**
- * Implementation of hook_node_info().
- */
-function book_node_info() {
- return array('book' => array('name' => t('book page'), 'base' => 'book'));
-}
-
-/**
- * Implementation of hook_perm().
- */
-function book_perm() {
- return array('outline posts in books', 'create book pages', 'create new books', 'edit book pages', 'edit own book pages', 'see printer-friendly version');
-}
-
-/**
- * Implementation of hook_access().
- */
-function book_access($op, $node) {
- global $user;
-
- if ($op == 'create') {
- // Only registered users can create book pages. Given the nature
- // of the book module this is considered to be a good/safe idea.
- return user_access('create book pages');
- }
-
- if ($op == 'update') {
- // Only registered users can update book pages. Given the nature
- // of the book module this is considered to be a good/safe idea.
- // One can only update a book page if there are no suggested updates
- // of that page waiting for approval. That is, only updates that
- // don't overwrite the current or pending information are allowed.
-
- if ((user_access('edit book pages') && !$node->moderate) || ($node->uid == $user->uid && user_access('edit own book pages'))) {
- return TRUE;
- }
- else {
- // do nothing. node-access() will determine further access
- }
- }
-}
-
-/**
- * Implementation of hook_link().
- */
-function book_link($type, $node = 0, $main = 0) {
-
- $links = array();
-
- if ($type == 'node' && isset($node->parent)) {
- if (!$main) {
- if (book_access('create', $node)) {
- $links[] = l(t('add child page'), "node/add/book/parent/$node->nid");
- }
- if (user_access('see printer-friendly version')) {
- $links[] = l(t('printer-friendly version'),
- 'book/export/html/'. $node->nid,
- array('title' => t('Show a printer-friendly version of this book page and its sub-pages.')));
- }
- }
- }
-
- return $links;
-}
-
-/**
- * Implementation of hook_menu().
- */
-function book_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array(
- 'path' => 'book',
- 'title' => t('books'),
- 'access' => user_access('access content'),
- 'type' => MENU_NORMAL_ITEM,
- 'weight' => 5);
- $items[] = array(
- 'path' => 'node/add/book',
- 'title' => t('book page'),
- 'access' => user_access('create book pages'));
- $items[] = array(
- 'path' => 'admin/node/book',
- 'title' => t('books'),
- 'callback' => 'book_admin',
- 'access' => user_access('administer nodes'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => -1);
- $items[] = array(
- 'path' => 'admin/node/book/list',
- 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK);
- $items[] = array(
- 'path' => 'admin/node/book/orphan',
- 'title' => t('orphan pages'),
- 'callback' => 'book_admin_orphan',
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 8);
- $items[] = array(
- 'path' => 'book',
- 'title' => t('books'),
- 'callback' => 'book_render',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array(
- 'path' => 'book/export',
- 'callback' => 'book_export',
- 'access' => user_access('access content'),
- 'type' => MENU_CALLBACK);
- }
- else {
- // To avoid SQL overhead, check whether we are on a node page and whether the
- // user is allowed to outline posts in books.
- if (arg(0) == 'node' && is_numeric(arg(1)) && user_access('outline posts in books')) {
- // Only add the outline-tab for non-book pages:
- $result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.nid = %d AND n.type != 'book'"), arg(1));
- if (db_num_rows($result) > 0) {
- $items[] = array(
- 'path' => 'node/'. arg(1) .'/outline',
- 'title' => t('outline'),
- 'callback' => 'book_outline',
- 'callback arguments' => array(arg(1)),
- 'access' => user_access('outline posts in books'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 2);
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- *
- * 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($op = 'list', $delta = 0) {
- $block = array();
- if ($op == 'list') {
- $block[0]['info'] = t('Book navigation');
- return $block;
- }
- else if ($op == 'view') {
- // Only display this block when the user is browsing a book:
- if (arg(0) == 'node' && is_numeric(arg(1))) {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), arg(1));
- if (db_num_rows($result) > 0) {
- $node = db_fetch_object($result);
-
- $path = book_location($node);
- $path[] = $node;
-
- $expand = array();
- foreach ($path as $key => $node) {
- $expand[] = $node->nid;
- }
-
- $block['subject'] = check_plain($path[0]->title);
- $block['content'] = book_tree($expand[0], 5, $expand);
- }
- }
-
- return $block;
- }
-}
-
-/**
- * Implementation of hook_load().
- */
-function book_load($node) {
- global $user;
-
- $book = db_fetch_object(db_query('SELECT * FROM {book} WHERE vid = %d', $node->vid));
-
- if (arg(2) == 'edit' && !user_access('administer nodes')) {
- // If a user is about to update a book page, we overload some
- // fields to reflect the changes.
- if ($user->uid) {
- $book->uid = $user->uid;
- $book->name = $user->name;
- }
- else {
- $book->uid = 0;
- $book->name = '';
- }
- }
-
- return $book;
-}
-
-/**
- * Implementation of hook_insert().
- */
-function book_insert($node) {
- db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)", $node->nid, $node->vid, $node->parent, $node->weight);
-}
-
-/**
- * Implementation of hook_update().
- */
-function book_update($node) {
- if ($node->revision) {
- db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)", $node->nid, $node->vid, $node->parent, $node->weight);
- }
- else {
- db_query("UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d", $node->parent, $node->weight, $node->vid);
- }
-}
-
-/**
- * Implementation of hook_delete().
- */
-function book_delete(&$node) {
- db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
-}
-
-/**
- * Implementation of hook_submit().
- */
-function book_submit(&$node) {
- // Set default values for non-administrators.
- if (!user_access('administer nodes')) {
- $node->weight = 0;
- $node->revision = 1;
- }
-}
-
-/**
- * Implementation of hook_form().
- */
-function book_form(&$node) {
- if ($node->nid && !$node->parent && !user_access('create new books')) {
- $form['parent'] = array('#type' => 'value', '#value' => $node->parent);
- }
- else {
- $form['parent'] = array('#type' => 'select',
- '#title' => t('Parent'),
- '#default_value' => ($node->parent ? $node->parent : arg(4)),
- '#options' => book_toc($node->nid),
- '#weight' => -4,
- '#description' => user_access('create new books') ? t('The parent section in which to place this page. Note that each page whose parent is &lt;top-level&gt; is an independent, top-level book.') : t('The parent that this page belongs in.'),
- );
- }
-
- $form['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#required' => TRUE,
- '#default_value' => $node->title,
- '#weight' => -5,
- );
- $form['body_filter']['body'] = array('#type' => 'textarea',
- '#title' => t('Body'),
- '#default_value' => $node->body,
- '#rows' => 20,
- '#required' => TRUE,
- );
- $form['body_filter']['format'] = filter_form($node->format);
-
- $form['log'] = array(
- '#type' => 'textarea',
- '#title' => t('Log message'),
- '#default_value' => $node->log,
- '#weight' => 5,
- '#description' => t('An explanation of the additions or updates being made to help other authors understand your motivations.'),
- );
-
- if (user_access('administer nodes')) {
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $node->weight,
- '#delta' => 15,
- '#weight' => 5,
- '#description' => t('Pages at a given level are ordered first by weight and then by title.'),
- );
- }
- else {
- // If a regular user updates a book page, we create a new revision
- // authored by that user:
- $form['revision'] = array('#type' => 'hidden', '#value' => 1);
- }
-
- return $form;
-}
-
-/**
- * Implementation of function book_outline()
- * Handles all book outline operations.
- */
-function book_outline($nid) {
- $node = node_load($nid);
- $page = book_load($node);
-
- $form['parent'] = array('#type' => 'select',
- '#title' => t('Parent'),
- '#default_value' => $page->parent,
- '#options' => book_toc($node->nid),
- '#description' => t('The parent page in the book.'),
- );
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $page->weight,
- '#delta' => 15,
- '#description' => t('Pages at a given level are ordered first by weight and then by title.'),
- );
- $form['log'] = array('#type' => 'textarea',
- '#title' => t('Log message'),
- '#default_value' => $node->log,
- '#description' => t('An explanation to help other authors understand your motivations to put this post into the book.'),
- );
-
- $form['nid'] = array('#type' => 'value', '#value' => $nid);
- if ($page->nid) {
- $form['update'] = array('#type' => 'submit',
- '#value' => t('Update book outline'),
- );
- $form['remove'] = array('#type' => 'submit',
- '#value' => t('Remove from book outline'),
- );
- }
- else {
- $form['add'] = array('#type' => 'submit', '#value' => t('Add to book outline'));
- }
-
- drupal_set_title(check_plain($node->title));
- return drupal_get_form('book_outline', $form);
-}
-
-/**
- * Handles book outline form submissions.
- */
-function book_outline_submit($form_id, $form_values) {
- $op = $_POST['op'];
- $node = node_load($form_values['nid']);
-
- switch ($op) {
- case t('Add to book outline'):
- db_query('INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)', $node->nid, $node->vid, $form_values['parent'], $form_values['weight']);
- db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid);
- drupal_set_message(t('The post has been added to the book.'));
- break;
- case t('Update book outline'):
- db_query('UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d', $form_values['parent'], $form_values['weight'], $node->vid);
- db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $form_values['log'], $node->vid);
- drupal_set_message(t('The book outline has been updated.'));
- break;
- case t('Remove from book outline'):
- db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
- drupal_set_message(t('The post has been removed from the book.'));
- break;
- }
- return "node/$node->nid";
-}
-
-/**
- * Given a node, this function returns an array of 'book node' objects
- * representing the path in the book tree from the root to the
- * parent of the given node.
- *
- * @param node - a book node object for which to compute the path
- *
- * @return - an array of book node objects representing the path of
- * nodes root to parent of the given node. Returns an empty array if
- * the node does not exist or is not part of a book hierarchy.
- *
- */
-function book_location($node, $nodes = array()) {
- $parent = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), $node->parent));
- if ($parent->title) {
- $nodes = book_location($parent, $nodes);
- $nodes[] = $parent;
- }
- return $nodes;
-}
-
-/**
- * Accumulates the nodes up to the root of the book from the given node in the $nodes array.
- */
-function book_location_down($node, $nodes = array()) {
- $last_direct_child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND b.parent = %d ORDER BY b.weight DESC, n.title DESC'), $node->nid));
- if ($last_direct_child) {
- $nodes[] = $last_direct_child;
- $nodes = book_location_down($last_direct_child, $nodes);
- }
- return $nodes;
-}
-
-/**
- * Fetches the node object of the previous page of the book.
- */
-function book_prev($node) {
- // If the parent is zero, we are at the start of a book so there is no previous.
- if ($node->parent == 0) {
- return NULL;
- }
-
- // Previous on the same level:
- $direct_above = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight < %d OR (b.weight = %d AND n.title < '%s')) ORDER BY b.weight DESC, n.title DESC"), $node->parent, $node->weight, $node->weight, $node->title));
- if ($direct_above) {
- // Get last leaf of $above.
- $path = book_location_down($direct_above);
-
- return $path ? (count($path) > 0 ? array_pop($path) : NULL) : $direct_above;
- }
- else {
- // Direct parent:
- $prev = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d AND n.status = 1 AND n.moderate = 0'), $node->parent));
- return $prev;
- }
-}
-
-/**
- * Fetches the node object of the next page of the book.
- */
-function book_next($node) {
- // get first direct child
- $child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight ASC, n.title ASC'), $node->nid));
- if ($child) {
- return $child;
- }
-
- // No direct child: get next for this level or any parent in this book.
- $path[] = book_location($node); // Path to top-level node including this one.
- $path[] = $node;
-
- while (($leaf = array_pop($path)) && count($path)) {
- $next = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight > %d OR (b.weight = %d AND n.title > '%s')) ORDER BY b.weight ASC, n.title ASC"), $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title));
- if ($next) {
- return $next;
- }
- }
-}
-
-/**
- * Returns the content of a given node. If $teaser if true, returns
- * the teaser rather than full content. Displays the most recently
- * approved revision of a node (if any) unless we have to display this
- * page in the context of the moderation queue.
- */
-function book_content($node, $teaser = FALSE) {
- // Return the page body.
- return node_prepare($node, $teaser);
-}
-
-/**
- * Implementation of hook_view().
- *
- * If not displayed on the main page, we render the node as a page in the
- * book with extra links to the previous and next pages.
- */
-function book_view(&$node, $teaser = FALSE, $page = FALSE) {
- $node = node_prepare($node, $teaser);
-}
-
-/**
- * Implementation of hook_nodeapi().
- *
- * Appends book navigation to all nodes in the book.
- */
-function book_nodeapi(&$node, $op, $teaser, $page) {
- switch ($op) {
- case 'view':
- if (!$teaser) {
- $book = db_fetch_array(db_query('SELECT * FROM {book} WHERE vid = %d', $node->vid));
- if ($book) {
- if ($node->moderate && user_access('administer nodes')) {
- drupal_set_message(t("The post has been submitted for moderation and won't be accessible until it has been approved."));
- }
-
- foreach ($book as $key => $value) {
- $node->$key = $value;
- }
-
- $path = book_location($node);
- // Construct the breadcrumb:
- $node->breadcrumb = array(); // Overwrite the trail with a book trail.
- foreach ($path as $level) {
- $node->breadcrumb[] = array('path' => 'node/'. $level->nid, 'title' => $level->title);
- }
- $node->breadcrumb[] = array('path' => 'node/'. $node->nid);
-
- $node->body .= theme('book_navigation', $node);
-
- if ($page) {
- menu_set_location($node->breadcrumb);
- }
- }
- }
- break;
- case 'delete revision':
- db_query('DELETE FROM {book} WHERE vid = %d', $node->vid);
- break;
- case 'delete':
- db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
- break;
- }
-}
-
-/**
- * Prepares the links to children (TOC) and forward/backward
- * navigation for a node presented as a book page.
- *
- * @ingroup themeable
- */
-function theme_book_navigation($node) {
- $output = '';
-
- if ($node->nid) {
- $tree = book_tree($node->nid);
-
- if ($prev = book_prev($node)) {
- drupal_add_link(array('rel' => 'prev', 'href' => url('node/'. $prev->nid)));
- $links .= l(t('‹ ') . $prev->title, 'node/'. $prev->nid, array('class' => 'page-previous', 'title' => t('Go to previous page')));
- }
- if ($node->parent) {
- drupal_add_link(array('rel' => 'index', 'href' => url('node/'. $node->parent)));
- $links .= l(t('up'), 'node/'. $node->parent, array('class' => 'page-up', 'title' => t('Go to parent page')));
- }
- if ($next = book_next($node)) {
- drupal_add_link(array('rel' => 'next', 'href' => url('node/'. $next->nid)));
- $links .= l($next->title . t(' ›'), 'node/'. $next->nid, array('class' => 'page-next', 'title' => t('Go to next page')));
- }
-
- if (isset($tree) || isset($links)) {
- $output = '<div class="book-navigation">';
- if (isset($tree)) {
- $output .= $tree;
- }
- if (isset($links)) {
- $output .= '<div class="page-links">'. $links .'</div>';
- }
- $output .= '</div>';
- }
- }
-
- return $output;
-}
-
-/**
- * This is a helper function for book_toc().
- */
-function book_toc_recurse($nid, $indent, $toc, $children, $exclude) {
- if ($children[$nid]) {
- foreach ($children[$nid] as $foo => $node) {
- if (!$exclude || $exclude != $node->nid) {
- $toc[$node->nid] = $indent .' '. $node->title;
- $toc = book_toc_recurse($node->nid, $indent .'--', $toc, $children, $exclude);
- }
- }
- }
-
- return $toc;
-}
-
-/**
- * Returns an array of titles and nid entries of book pages in table of contents order.
- */
-function book_toc($exclude = 0) {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 ORDER BY b.weight, n.title'));
-
- while ($node = db_fetch_object($result)) {
- if (!$children[$node->parent]) {
- $children[$node->parent] = array();
- }
- $children[$node->parent][] = $node;
- }
-
- $toc = array();
- // If the user has permission to create new books, add the top-level book page to the menu;
- if (user_access('create new books')) {
- $toc[0] = '<'. t('top-level') .'>';
- }
-
- $toc = book_toc_recurse(0, '', $toc, $children, $exclude);
-
- return $toc;
-}
-
-/**
- * This is a helper function for book_tree()
- */
-function book_tree_recurse($nid, $depth, $children, $unfold = array()) {
- if ($depth > 0) {
- if ($children[$nid]) {
- foreach ($children[$nid] as $foo => $node) {
- if (in_array($node->nid, $unfold)) {
- if ($tree = book_tree_recurse($node->nid, $depth - 1, $children, $unfold)) {
- $output .= '<li class="expanded">';
- $output .= l($node->title, 'node/'. $node->nid);
- $output .= '<ul class="menu">'. $tree .'</ul>';
- $output .= '</li>';
- }
- else {
- $output .= '<li class="leaf">'. l($node->title, 'node/'. $node->nid) .'</li>';
- }
- }
- else {
- if ($tree = book_tree_recurse($node->nid, 1, $children)) {
- $output .= '<li class="collapsed">'. l($node->title, 'node/'. $node->nid) .'</li>';
- }
- else {
- $output .= '<li class="leaf">'. l($node->title, 'node/'. $node->nid) .'</li>';
- }
- }
- }
- }
- }
-
- return $output;
-}
-
-/**
- * Returns an HTML nested list (wrapped in a menu-class div) representing the book nodes
- * as a tree.
- */
-function book_tree($parent = 0, $depth = 3, $unfold = array()) {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title'));
-
- while ($node = db_fetch_object($result)) {
- $list = $children[$node->parent] ? $children[$node->parent] : array();
- $list[] = $node;
- $children[$node->parent] = $list;
- }
-
- if ($tree = book_tree_recurse($parent, $depth, $children, $unfold)) {
- return '<ul class="menu">'. $tree .'</ul>';
- }
-}
-
-/**
- * Menu callback; prints a listing of all books.
- */
-function book_render() {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title'));
-
- $books = array();
- while ($node = db_fetch_object($result)) {
- $books[] = l($node->title, 'node/'. $node->nid);
- }
-
- return theme('item_list', $books);
-}
-
-/**
- * Menu callback; Generates various representation of a book page with
- * all descendants and prints the requested representation to output.
- *
- * 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: HTML (printer friendly output)
- * Other types are supported in contributed modules.
- * @param nid
- * - an integer representing the node id (nid) of the node to export
- *
- */
-function book_export($type = 'html', $nid = 0) {
- $type = drupal_strtolower($type);
- $node_result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), $nid);
- if (db_num_rows($node_result) > 0) {
- $node = db_fetch_object($node_result);
- }
- $depth = count(book_location($node)) + 1;
- $export_function = 'book_export_' . $type;
-
- if (function_exists($export_function)) {
- print call_user_func($export_function, $nid, $depth);
- }
- else {
- drupal_set_message('Unknown export format');
- drupal_not_found();
- }
-}
-
-/**
- * This function is called by book_export() to generate HTML for 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) &lt;div&gt; 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
- * @param depth
- * - an integer giving the depth in the book hierarchy of the node
- which is to be exported
- * @return
- * - string containing HTML representing the node and its children in
- the book hierarchy
-*/
-function book_export_html($nid, $depth) {
- if (user_access('see printer-friendly version')) {
- global $base_url;
- for ($i = 1; $i < $depth; $i++) {
- $output .= "<div class=\"section-$i\">\n";
- }
- $output .= book_recurse($nid, $depth, 'book_node_visitor_html_pre', 'book_node_visitor_html_post');
- for ($i = 1; $i < $depth; $i++) {
- $output .= "</div>\n";
-
- }
- $html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
- $html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">';
- $html .= "<head>\n<title>". check_plain($node->title) ."</title>\n";
- $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
- $html .= '<base href="'. $base_url .'/" />' . "\n";
- $html .= "<style type=\"text/css\">\n@import url(misc/print.css);\n</style>\n";
- $html .= "</head>\n<body>\n". $output . "\n</body>\n</html>\n";
- return $html;
- }
- else {
- drupal_access_denied();
- }
-}
-
-/**
- * How the book's HTML export should be themed
- *
- * @ingroup themeable
- */
-function theme_book_export_html($title, $content) {
- global $base_url;
- $html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
- $html .= '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">';
- $html .= "<head>\n<title>". $title ."</title>\n";
- $html .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
- $html .= '<base href="'. $base_url .'/" />' . "\n";
- $html .= "<style type=\"text/css\">\n@import url(misc/print.css);\n</style>\n";
- $html .= "</head>\n<body>\n". $content . "\n</body>\n</html>\n";
- return $html;
-}
-
-/**
- * Traverses the book tree. Applies the $visit_pre() callback to each
- * node, is called recursively for each child of the node (in weight,
- * title order). Finally appends the output of the $visit_post()
- * callback to the output before returning the generated output.
- *
- * @param nid
- * - the node id (nid) of the root node of the book hierarchy.
- * @param depth
- * - the depth of the given node in the book hierarchy.
- * @param visit_pre
- * - a function callback to be called upon visiting a node in the tree
- * @param visit_post
- * - a function callback to be called after visiting a node in the tree,
- * but before recursively visiting children.
- * @return
- * - the output generated in visiting each node
- */
-function book_recurse($nid = 0, $depth = 1, $visit_pre, $visit_post) {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $nid);
- while ($page = db_fetch_object($result)) {
- // Load the node:
- $node = node_load($page->nid);
-
- if ($node) {
- if (function_exists($visit_pre)) {
- $output .= call_user_func($visit_pre, $node, $depth, $nid);
- }
- else {
- $output .= book_node_visitor_html_pre($node, $depth, $nid);
- }
-
- $children = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND b.parent = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $node->nid);
- while ($childpage = db_fetch_object($children)) {
- $childnode = node_load($childpage->nid);
- if ($childnode->nid != $node->nid) {
- $output .= book_recurse($childnode->nid, $depth + 1, $visit_pre, $visit_post);
- }
- }
- if (function_exists($visit_post)) {
- $output .= call_user_func($visit_post, $node, $depth);
- }
- else {
- # default
- $output .= book_node_visitor_html_post($node, $depth);
- }
- }
- }
-
- return $output;
-}
-
-/**
- * Generates printer-friendly HTML for a node. This function
- * is a 'pre-node' visitor function for book_recurse().
- *
- * @param $node
- * - the node to generate output for.
- * @param $depth
- * - the depth of the given node in the hierarchy. This
- * is used only for generating output.
- * @param $nid
- * - the node id (nid) of the given node. This
- * is used only for generating output.
- * @return
- * - the HTML generated for the given node.
- */
-function book_node_visitor_html_pre($node, $depth, $nid) {
- // Output the content:
- if (node_hook($node, 'content')) {
- $node = node_invoke($node, 'content');
- }
- // Allow modules to change $node->body before viewing.
- node_invoke_nodeapi($node, 'print', $node->body, false);
-
- $output .= "<div id=\"node-". $node->nid ."\" class=\"section-$depth\">\n";
- $output .= "<h1 class=\"book-heading\">". check_plain($node->title) ."</h1>\n";
-
- if ($node->body) {
- $output .= $node->body;
- }
- return $output;
-}
-
-/**
- * Finishes up generation of printer-friendly HTML after visiting a
- * node. This function is a 'post-node' visitor function for
- * book_recurse().
- */
-function book_node_visitor_html_post($node, $depth) {
- return "</div>\n";
-}
-
-function _book_admin_table($nodes = array()) {
- $form = array(
- '#theme' => 'book_admin_table',
- '#tree' => TRUE,
- );
-
- foreach ($nodes as $node) {
- $form = array_merge($form, _book_admin_table_tree($node, 0));
- }
-
- return $form;
-}
-
-function _book_admin_table_tree($node, $depth) {
- $form = array();
-
- $form[] = array(
- 'nid' => array('#type' => 'value', '#value' => $node->nid),
- 'depth' => array('#type' => 'value', '#value' => $depth),
- 'title' => array(
- '#type' => 'textfield',
- '#default_value' => $node->title,
- '#maxlength' => 255,
- ),
- 'weight' => array(
- '#type' => 'weight',
- '#default_value' => $node->weight,
- '#delta' => 15,
- ),
- );
-
- $children = db_query(db_rewrite_sql('SELECT n.nid, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d ORDER BY b.weight, n.title'), $node->nid);
- while ($child = db_fetch_object($children)) {
- $form = array_merge($form, _book_admin_table_tree(node_load($child->nid), $depth + 1));
- }
-
- return $form;
-}
-
-function theme_book_admin_table($form) {
- $header = array(t('Title'), t('Weight'), array('data' => t('Operations'), 'colspan' => '3'));
-
- $rows = array();
- foreach (element_children($form) as $key) {
- $nid = $form[$key]['nid']['#value'];
- $rows[] = array(
- '<div style="padding-left: '. (25 * $form[$key]['depth']['#value']) .'px;">'. form_render($form[$key]['title']) .'</div>',
- form_render($form[$key]['weight']),
- l(t('view'), 'node/'. $nid),
- l(t('edit'), 'node/'. $nid .'/edit'),
- l(t('delete'), 'node/'. $nid .'/delete')
- );
- }
-
- return theme('table', $header, $rows);
-}
-
-/**
- * Display an administrative view of the hierarchy of a book.
- */
-function book_admin_edit($nid) {
- $node = node_load($nid);
- if ($node->nid) {
- drupal_set_title(check_plain($node->title));
- $form = array();
-
- $form['table'] = _book_admin_table(array($node));
- $form['save'] = array(
- '#type' => 'submit',
- '#value' => t('Save book pages'),
- );
-
- return drupal_get_form('book_admin_edit', $form);
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Menu callback; displays a listing of all orphaned book pages.
- */
-function book_admin_orphan() {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, n.status, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid'));
-
- $pages = array();
- while ($page = db_fetch_object($result)) {
- $pages[$page->nid] = $page;
- }
-
- $orphans = array();
- if (count($pages)) {
- foreach ($pages as $page) {
- if ($page->parent && empty($pages[$page->parent])) {
- $orphans[] = node_load($page->nid);
- }
- }
- }
-
- if (count($orphans)) {
- $form = array();
-
- $form['table'] = _book_admin_table($orphans);
- $form['save'] = array(
- '#type' => 'submit',
- '#value' => t('Save book pages'),
- );
-
- return drupal_get_form('book_admin_edit', $form);
- }
- else {
- return '<p>'. t('There are no orphan pages.') .'</p>';
- }
-}
-
-function book_admin_edit_submit($form_id, $form_values) {
- foreach ($form_values['table'] as $row) {
- $node = node_load($row['nid']);
-
- if ($row['title'] != $node->title || $row['weight'] != $node->weight) {
- $node->title = $row['title'];
- $node->weight = $row['weight'];
-
- node_save($node);
- watchdog('content', t('%type: updated %title.', array('%type' => theme('placeholder', t('book')), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
- }
- }
-
- if (is_numeric(arg(3))) {
- // Updating pages in a single book.
- $book = node_load(arg(3));
- drupal_set_message(t('Updated book %title.', array('%title' => theme('placeholder', $book->title))));
- }
- else {
- // Updating the orphan pages.
- drupal_set_message(t('Updated orphan book pages.'));
- }
-}
-
-/**
- * Menu callback; displays the book administration page.
- */
-function book_admin($nid = 0) {
- if ($nid) {
- return book_admin_edit($nid);
- }
- else {
- return book_admin_overview();
- }
-}
-
-/**
- * Returns an administrative overview of all books.
- */
-function book_admin_overview() {
- $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 ORDER BY b.weight, n.title'));
- while ($book = db_fetch_object($result)) {
- $rows[] = array(l($book->title, "node/$book->nid"), l(t('outline'), "admin/node/book/$book->nid"));
- }
- $headers = array(t('Book'), t('Operations'));
-
- return theme('table', $headers, $rows);
-}
-
-/**
- * Implementation of hook_help().
- */
-function book_help($section) {
- switch ($section) {
- case 'admin/help#book':
- $output = '<p>'. t('The <em>book</em> content type is suited for creating structured, multi-page hypertexts such as site resource guides, manuals, and Frequently Asked Questions (FAQs). It permits a document to have chapters, sections, subsections, etc. Authors with suitable permissions can add pages to a collaborative book, placing them into the existing document by adding them to a table of contents menu. ') .'</p>';
- $output .= '<p>'. t('Books have additional <em>previous</em>, <em>up</em>, and <em>next</em> navigation elements at the bottom of each page for moving through the text. Additional navigation may be provided by enabling the <em>book navigation block</em> on the <a href="%admin-block">block administration page</a>.', array('%admin-block' => url('admin/block'))) .'</p>';
- $output .= '<p>'. t('Users can select the <em>printer-friendly version</em> link visible at the bottom of a book page to generate a printer-friendly display of the page and all of its subsections. ') .'</p>';
- $output .= '<p>'. t('Administrators can view a book outline, from which is it possible to change the titles of sections, and their <i>weight</i> (thus reordering sections). From this outline, it is also possible to edit and/or delete book pages. Many content types besides pages (for example, blog entries, stories, and polls) can be added to a collaborative book by choosing the <em>outline</em> tab when viewing the post.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>create new book pages: <a href="%node-add-book">create content &gt;&gt; book page</a>.</li>
-<li>administer individual books (choose a book from list): <a href="%admin-node-book">administer &gt;&gt; content &gt;&gt; books</a>.</li>
-<li>set workflow and other global book settings on the book configuration page: <a href="%admin-settings-content-types-book-page" title="book page content type">administer &gt;&gt; settings &gt;&gt; content types &gt;&gt; configure book page</a>.</li>
-<li>enable the book navigation block: <a href="%admin-block">administer &gt;&gt; blocks</a>.</li>
-<li>control who can create, edit, and outline posts in books by setting access permissions: <a href="%admin-access">administer &gt;&gt; access control</a>.</li>
-</ul>
-', array('%node-add-book' => url('node/add/book'), '%admin-node-book' => url('admin/node/book'), '%admin-settings-content-types-book-page' => url('admin/settings/content-types/book'), '%admin-block' => url('admin/block'), '%admin-access' => url('admin/access')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%book">Book page</a>.', array('%book' => 'http://drupal.org/handbook/modules/book/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows users to collaboratively author a book.');
- case 'admin/node/book':
- return t('<p>The book module offers a means to organize content, authored by many users, in an online manual, outline or FAQ.</p>');
- case 'admin/node/book/orphan':
- return t('<p>Pages in a book are like a tree. As pages are edited, reorganized and removed, child pages might be left with no link to the rest of the book. Such pages are referred to as "orphan pages". On this page, administrators can review their books for orphans and reattach those pages as desired.</p>');
- case 'node/add#book':
- return t("A book is a collaborative writing effort: users can collaborate writing the pages of the book, positioning the pages in the right order, and reviewing or modifying pages previously written. So when you have some information to share or when you read a page of the book and you didn't like it, or if you think a certain page could have been written better, you can do something about it.");
- }
-
- if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'outline') {
- return t('The outline feature allows you to include posts in the <a href="%book">book hierarchy</a>.', array('%book' => url('book')));
- }
-}
-
-
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
deleted file mode 100644
index 755eff9..0000000
--- a/modules/comment/comment.module
+++ /dev/null
@@ -1,1725 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables users to comment on published content.
- *
- * When enabled, the Drupal comment module creates a discussion
- * board for each Drupal node. Users can post comments to discuss
- * a forum topic, weblog post, story, collaborative book page, etc.
- */
-
-/*
- * Constants to define a comment's published state
- */
-define('COMMENT_PUBLISHED', 0);
-define('COMMENT_NOT_PUBLISHED', 1);
-
-/**
- * Constants to define the viewing modes for comment listings
- */
-define('COMMENT_MODE_FLAT_COLLAPSED', 0);
-define('COMMENT_MODE_FLAT_EXPANDED', 1);
-define('COMMENT_MODE_THREADED_COLLAPSED', 2);
-define('COMMENT_MODE_THREADED_EXPANDED', 3);
-
-/**
- * Constants to define the viewing orders for comment listings
- */
-define('COMMENT_ORDER_NEWEST_FIRST', 0);
-define('COMMENT_ORDER_OLDEST_FIRST', 1);
-
-/**
- * Constants to define the position of the comment controls
- */
-define('COMMENT_CONTROLS_ABOVE', 0);
-define('COMMENT_CONTROLS_BELOW', 1);
-define('COMMENT_CONTROLS_ABOVE_BELOW', 2);
-define('COMMENT_CONTROLS_HIDDEN', 3);
-
-/**
- * Constants to define the anonymous poster contact handling
- */
-define('COMMENT_ANONYMOUS_MAYNOT_CONTACT', 0);
-define('COMMENT_ANONYMOUS_MAY_CONTACT', 1);
-define('COMMENT_ANONYMOUS_MUST_CONTACT', 2);
-
-/**
- * Constants to define the comment form location
- */
-define('COMMENT_FORM_SEPARATE_PAGE', 0);
-define('COMMENT_FORM_BELOW', 1);
-
-/**
- * Constants to define a node's comment state
- */
-define('COMMENT_NODE_DISABLED', 0);
-define('COMMENT_NODE_READ_ONLY', 1);
-define('COMMENT_NODE_READ_WRITE', 2);
-
-/**
- * Constants to define if comment preview is optional or required
- */
-define('COMMENT_PREVIEW_OPTIONAL', 0);
-define('COMMENT_PREVIEW_REQUIRED', 1);
-
-/**
- * Implementation of hook_help().
- */
-function comment_help($section) {
- switch ($section) {
- case 'admin/help#comment':
- $output = '<p>'. t('The comment module creates a discussion board for each post. Users can post comments to discuss a forum topic, weblog post, story, collaborative book page, etc. The ability to comment is an important part of involving members in a communtiy dialogue.') .'</p>';
- $output .= '<p>'. t('An administrator can give comment permissions to user groups, and users can (optionally) edit their last comment, assuming no others have been posted since. Attached to each comment board is a control panel for customizing the way that comments are displayed. Users can control the chronological ordering of posts (newest or oldest first) and the number of posts to display on each page. Comments behave like other user submissions. Filters, smileys and HTML that work in nodes will also work with comments. The comment module provides specific features to inform site members when new comments have been posted.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>control access for various comment module functions through access permissions <a href="%admin-access">administer &gt;&gt; access control</a>.</li>
-<li>administer comments <a href="%admin-comment-configure"> administer &gt;&gt; comments &gt;&gt; configure</a>.</li>
-</ul>
-', array('%admin-access' => url('admin/access'), '%admin-comment-configure' => url('admin/comment/configure')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%comment">Comment page</a>.', array('%comment' => 'http://drupal.org/handbook/modules/comment/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows users to comment on and discuss published content.');
- case 'admin/comment':
- case 'admin/comment/new':
- return t("<p>Below is a list of the latest comments posted to your site. Click on a subject to see the comment, the author's name to edit the author's user information , \"edit\" to modify the text, and \"delete\" to remove their submission.</p>");
- case 'admin/comment/approval':
- return t("<p>Below is a list of the comments posted to your site that need approval. To approve a comment, click on \"edit\" and then change its \"moderation status\" to Approved. Click on a subject to see the comment, the author's name to edit the author's user information, \"edit\" to modify the text, and \"delete\" to remove their submission.</p>");
- case 'admin/comment/configure':
- case 'admin/comment/configure/settings':
- return t("<p>Comments can be attached to any node, and their settings are below. The display comes in two types: a \"flat list\" where everything is flush to the left side, and comments come in chronological order, and a \"threaded list\" where replies to other comments are placed immediately below and slightly indented, forming an outline. They also come in two styles: \"expanded\", where you see both the title and the contents, and \"collapsed\" where you only see the title. Preview comment forces a user to look at their comment by clicking on a \"Preview\" button before they can actually add the comment.</p>");
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function comment_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $access = user_access('administer comments');
- $items[] = array('path' => 'admin/comment', 'title' => t('comments'),
- 'callback' => 'comment_admin_overview', 'access' => $access);
-
- // Tabs:
- $items[] = array('path' => 'admin/comment/list', 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- $items[] = array('path' => 'admin/comment/configure', 'title' => t('configure'),
- 'callback' => 'comment_configure', 'access' => $access, 'type' => MENU_LOCAL_TASK);
-
- // Subtabs:
- $items[] = array('path' => 'admin/comment/list/new', 'title' => t('published comments'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- $items[] = array('path' => 'admin/comment/list/approval', 'title' => t('approval queue'),
- 'callback' => 'comment_admin_overview', 'access' => $access,
- 'callback arguments' => array('approval'),
- 'type' => MENU_LOCAL_TASK);
-
- $items[] = array('path' => 'admin/comment/configure/settings', 'title' => t('settings'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
-
- $items[] = array('path' => 'comment/delete', 'title' => t('delete comment'),
- 'callback' => 'comment_delete', 'access' => $access, 'type' => MENU_CALLBACK);
-
- $access = user_access('post comments');
- $items[] = array('path' => 'comment/edit', 'title' => t('edit comment'),
- 'callback' => 'comment_edit', 'access' => $access, 'type' => MENU_CALLBACK);
- }
- else {
- if (arg(0) == 'comment' && arg(1) == 'reply' && is_numeric(arg(2))) {
- $node = node_load(arg(2));
- if ($node->nid) {
- $items[] = array('path' => 'comment/reply', 'title' => t('reply to comment'),
- 'callback' => 'comment_reply', 'access' => node_access('view', $node), 'type' => MENU_CALLBACK);
- }
- }
- if ((arg(0) == 'node') && is_numeric(arg(1)) && is_numeric(arg(2))) {
- $items[] = array('path' => ('node/'. arg(1) .'/'. arg(2)), 'title' => t('view'),
- 'callback' => 'node_page',
- 'type' => MENU_CALLBACK);
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function comment_perm() {
- return array('access comments', 'post comments', 'administer comments', 'post comments without approval');
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates a block with the most recent comments.
- */
-function comment_block($op = 'list', $delta = 0) {
- if ($op == 'list') {
- $blocks[0]['info'] = t('Recent comments');
- return $blocks;
- }
- else if ($op == 'view' && user_access('access comments')) {
- $block['subject'] = t('Recent comments');
- $block['content'] = theme('comment_block');
- return $block;
- }
-}
-
-function theme_comment_block() {
- $result = db_query_range(db_rewrite_sql('SELECT c.nid, c.subject, c.cid, c.timestamp FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid WHERE n.status = 1 AND c.status = %d ORDER BY c.timestamp DESC', 'c'), COMMENT_PUBLISHED, 0, 10);
- $items = array();
- while ($comment = db_fetch_object($result)) {
- $items[] = l($comment->subject, 'node/'. $comment->nid, NULL, NULL, 'comment-'. $comment->cid) .'<br />'. t('%time ago', array('%time' => format_interval(time() - $comment->timestamp)));
- }
- return theme('item_list', $items);
-}
-
-/**
- * Implementation of hook_link().
- */
-function comment_link($type, $node = 0, $main = 0) {
- $links = array();
-
- if ($type == 'node' && $node->comment) {
-
- if ($main) {
- // Main page: display the number of comments that have been posted.
-
- if (user_access('access comments')) {
- $all = comment_num_all($node->nid);
- $new = comment_num_new($node->nid);
-
- if ($all) {
- $links[] = l(format_plural($all, '1 comment', '%count comments'), "node/$node->nid", array('title' => t('Jump to the first comment of this posting.')), NULL, 'comment');
-
- if ($new) {
- $links[] = l(format_plural($new, '1 new comment', '%count new comments'), "node/$node->nid", array('title' => t('Jump to the first new comment of this posting.')), NULL, 'new');
- }
- }
- else {
- if ($node->comment == COMMENT_NODE_READ_WRITE) {
- if (user_access('post comments')) {
- $links[] = l(t('add new comment'), "comment/reply/$node->nid", array('title' => t('Add a new comment to this page.')), NULL, 'comment_form');
- }
- else {
- $links[] = theme('comment_post_forbidden', $node->nid);
- }
- }
- }
- }
- }
- else {
- // Node page: add a "post comment" link if the user is allowed to
- // post comments, if this node is not read-only, and if the comment form isn't already shown
-
- if ($node->comment == COMMENT_NODE_READ_WRITE) {
- if (user_access('post comments')) {
- if (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
- $links[] = l(t('add new comment'), "comment/reply/$node->nid", array('title' => t('Share your thoughts and opinions related to this posting.')), NULL, 'comment_form');
- }
- }
- else {
- $links[] = theme('comment_post_forbidden', $node->nid);
- }
- }
- }
- }
-
- if ($type == 'comment') {
- $links = comment_links($node, $main);
- }
-
- return $links;
-}
-
-function comment_form_alter($form_id, &$form) {
- if (isset($form['type'])) {
- if ($form['type']['#value'] .'_node_settings' == $form_id) {
- $form['workflow']['comment_'. $form['type']['#value']] = array('#type' => 'radios', '#title' => t('Default comment setting'), '#default_value' => variable_get('comment_'. $form['type']['#value'], COMMENT_NODE_READ_WRITE), '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), '#description' => t('Users with the <em>administer comments</em> permission will be able to override this setting.'));
- }
- if ($form['type']['#value'] .'_node_form' == $form_id) {
- $node = $form['#node'];
- if (user_access('administer comments')) {
- $form['comment_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Comment settings'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#weight' => 30,
- );
- $form['comment_settings']['comment'] = array(
- '#type' => 'radios',
- '#parents' => array('comment'),
- '#default_value' => $node->comment,
- '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')),
- );
- }
- else {
- $form['comment_settings']['comment'] = array(
- '#type' => 'value',
- '#value' => $node->comment,
- );
- }
- }
- }
-}
-
-/**
- * Implementation of hook_nodeapi().
- *
- */
-function comment_nodeapi(&$node, $op, $arg = 0) {
- switch ($op) {
- case 'load':
- return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, comment_count FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
- break;
-
- case 'prepare':
- if (!isset($node->comment)) {
- $node->comment = variable_get("comment_$node->type", COMMENT_NODE_READ_WRITE);
- }
- break;
-
- case 'insert':
- db_query('INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d, %d, NULL, %d, 0)', $node->nid, $node->created, $node->uid);
- break;
-
- case 'delete':
- db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
- db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
- break;
-
- case 'update index':
- $text = '';
- $comments = db_query('SELECT subject, comment, format FROM {comments} WHERE nid = %d AND status = %d', $node->nid, COMMENT_PUBLISHED);
- while ($comment = db_fetch_object($comments)) {
- $text .= '<h2>'. check_plain($comment->subject) .'</h2>'. check_markup($comment->comment, $comment->format, FALSE);
- }
- return $text;
-
- case 'search result':
- $comments = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $node->nid));
- return format_plural($comments, '1 comment', '%count comments');
-
- case 'rss item':
- return array(array('key' => 'comments', 'value' => url('node/'. $node->nid, NULL, 'comment', TRUE)));
- }
-}
-
-/**
- * Implementation of hook_user().
- *
- * Provides signature customization for the user's comments.
- */
-function comment_user($type, $edit, &$user, $category = NULL) {
- if ($type == 'form' && $category == 'account') {
- // when user tries to edit his own data
- $form['comment_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Comment settings'),
- '#collapsible' => TRUE,
- '#weight' => 4);
- $form['comment_settings']['signature'] = array(
- '#type' => 'textarea',
- '#title' => t('Signature'),
- '#default_value' => $edit['signature'],
- '#description' => t('Your signature will be publicly displayed at the end of your comments.'));
-
- return $form;
- }
- elseif ($type == 'delete') {
- db_query('UPDATE {comments} SET uid = 0 WHERE uid = %d', $user->uid);
- db_query('UPDATE {node_comment_statistics} SET last_comment_uid = 0 WHERE last_comment_uid = %d', $user->uid);
- }
-}
-
-/**
- * Menu callback; presents the comment settings page.
- */
-function comment_configure() {
- $form['viewing_options'] = array(
- '#type' => 'fieldset',
- '#title' => t('Viewing options'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- );
-
- $form['viewing_options']['comment_default_mode'] = array(
- '#type' => 'radios',
- '#title' => t('Default display mode'),
- '#default_value' => variable_get('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED),
- '#options' => _comment_get_modes(),
- '#description' => t('The default view for comments. Expanded views display the body of the comment. Threaded views keep replies together.'),
- );
-
- $form['viewing_options']['comment_default_order'] = array(
- '#type' => 'radios',
- '#title' => t('Default display order'),
- '#default_value' => variable_get('comment_default_order', COMMENT_ORDER_NEWEST_FIRST),
- '#options' => _comment_get_orders(),
- '#description' => t('The default sorting for new users and anonymous users while viewing comments. These users may change their view using the comment control panel. For registered users, this change is remembered as a persistent user preference.'),
- );
-
- $form['viewing_options']['comment_default_per_page'] = array(
- '#type' => 'select',
- '#title' => t('Default comments per page'),
- '#default_value' => variable_get('comment_default_per_page', 50),
- '#options' => _comment_per_page(),
- '#description' => t('Default number of comments for each page: more comments are distributed in several pages.'),
- );
-
- $form['viewing_options']['comment_controls'] = array(
- '#type' => 'radios',
- '#title' => t('Comment controls'),
- '#default_value' => variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN),
- '#options' => array(
- t('Display above the comments'),
- t('Display below the comments'),
- t('Display above and below the comments'),
- t('Do not display')),
- '#description' => t('Position of the comment controls box. The comment controls let the user change the default display mode and display order of comments.'),
- );
-
- $form['posting_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Posting settings'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- );
-
- $form['posting_settings']['comment_anonymous'] = array(
- '#type' => 'radios',
- '#title' => t('Anonymous commenting'),
- '#default_value' => variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT),
- '#options' => array(
- COMMENT_ANONYMOUS_MAYNOT_CONTACT => t('Anonymous posters may not enter their contact information'),
- COMMENT_ANONYMOUS_MAY_CONTACT => t('Anonymous posters may leave their contact information'),
- COMMENT_ANONYMOUS_MUST_CONTACT => t('Anonymous posters must leave their contact information')),
- '#description' => t('This option is enabled when anonymous users have permission to post comments on the <a href="%url">permissions page</a>.', array('%url' => url('admin/access'))),
- );
- if (!user_access('post comments', user_load(array('uid' => 0)))) {
- $form['posting_settings']['comment_anonymous']['#attributes'] = array('disabled' => 'disabled');
- }
-
- $form['posting_settings']['comment_subject_field'] = array(
- '#type' => 'radios',
- '#title' => t('Comment subject field'),
- '#default_value' => variable_get('comment_subject_field', 1),
- '#options' => array(t('Disabled'), t('Enabled')),
- '#description' => t('Can users provide a unique subject for their comments?'),
- );
-
- $form['posting_settings']['comment_preview'] = array(
- '#type' => 'radios',
- '#title' => t('Preview comment'),
- '#default_value' => variable_get('comment_preview', COMMENT_PREVIEW_REQUIRED),
- '#options' => array(t('Optional'), t('Required')),
- );
-
- $form['posting_settings']['comment_form_location'] = array(
- '#type' => 'radios',
- '#title' => t('Location of comment submission form'),
- '#default_value' => variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE),
- '#options' => array(t('Display on separate page'), t('Display below post or comments')),
- );
-
- return system_settings_form('comment_settings_form', $form);
-}
-
-/**
- * This is *not* a hook_access() implementation. This function is called
- * to determine whether the current user has access to a particular comment.
- *
- * Authenticated users can edit their comments as long they have not been
- * replied to. This prevents people from changing or revising their
- * statements based on the replies their posts got. Furthermore, users
- * can't reply to their own comments and are encouraged instead to extend
- * their original comment.
- */
-function comment_access($op, $comment) {
- global $user;
-
- if ($op == 'edit') {
- return ($user->uid && $user->uid == $comment->uid && comment_num_replies($comment->cid) == 0) || user_access('administer comments');
- }
-}
-
-function comment_node_url() {
- return arg(0) .'/'. arg(1);
-}
-
-function comment_edit($cid) {
- global $user;
-
- $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d', $cid));
- $comment = drupal_unpack($comment);
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- if (comment_access('edit', $comment)) {
- return comment_form((array)$comment);
- }
- else {
- drupal_access_denied();
- }
-}
-
-function comment_reply($nid, $pid = NULL) {
- // set the breadcrumb trail
- $node = node_load($nid);
- menu_set_location(array(array('path' => "node/$nid", 'title' => $node->title), array('path' => "comment/reply/$nid")));
-
- $op = isset($_POST['op']) ? $_POST['op'] : '';
-
- $output = '';
-
- // or are we merely showing the form?
- if (user_access('access comments')) {
-
- if ($op == t('Preview comment')) {
- if (user_access('post comments')) {
- $output .= comment_form(array('pid' => $pid, 'nid' => $nid), NULL);
- }
- else {
- drupal_set_message(t('You are not authorized to post comments.'), 'error');
- drupal_goto("node/$nid");
- }
- }
- else {
- // if this is a reply to another comment, show that comment first
- // else, we'll just show the user the node they're commenting on.
- if ($pid) {
- if ($comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = %d', $pid, COMMENT_PUBLISHED))) {
- $comment = drupal_unpack($comment);
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $output .= theme('comment_view', $comment);
- }
- else {
- drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
- drupal_goto("node/$nid");
- }
- }
- else if (user_access('access content')) {
- $output .= node_view($node);
- }
-
- // should we show the reply box?
- if (node_comment_mode($nid) != COMMENT_NODE_READ_WRITE) {
- drupal_set_message(t("This discussion is closed: you can't post new comments."), 'error');
- drupal_goto("node/$nid");
- }
- else if (user_access('post comments')) {
- $output .= comment_form(array('pid' => $pid, 'nid' => $nid), t('Reply'));
- }
- else {
- drupal_set_message(t('You are not authorized to post comments.'), 'error');
- drupal_goto("node/$nid");
- }
- }
- }
- else {
- drupal_set_message(t('You are not authorized to view comments.'), 'error');
- drupal_goto("node/$nid");
- }
-
- return $output;
-}
-
-/**
- * Accepts a submission of new or changed comment content.
- *
- * @param $edit
- * A comment array.
- *
- * @return
- * If the comment is successfully saved the comment ID is returned. If the comment
- * is not saved, FALSE is returned.
- */
-function comment_save($edit) {
- global $user;
- if (user_access('post comments') && (user_access('administer comments') || node_comment_mode($edit['nid']) == COMMENT_NODE_READ_WRITE)) {
- if (!form_get_errors()) {
- // Check for duplicate comments. Note that we have to use the
- // validated/filtered data to perform such check.
- $duplicate = db_result(db_query("SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND nid = %d AND subject = '%s' AND comment = '%s'", $edit['pid'], $edit['nid'], $edit['subject'], $edit['comment']), 0);
- if ($duplicate != 0) {
- watchdog('content', t('Comment: duplicate %subject.', array('%subject' => theme('placeholder', $edit['subject']))), WATCHDOG_WARNING);
- }
-
- if ($edit['cid']) {
- // Update the comment in the database.
- db_query("UPDATE {comments} SET status = %d, timestamp = %d, subject = '%s', comment = '%s', format = %d, uid = %d, name = '%s', mail = '%s', homepage = '%s' WHERE cid = %d", $edit['status'], $edit['timestamp'], $edit['subject'], $edit['comment'], $edit['format'], $edit['uid'], $edit['name'], $edit['mail'], $edit['homepage'], $edit['cid']);
-
- _comment_update_node_statistics($edit['nid']);
-
- // Allow modules to respond to the updating of a comment.
- comment_invoke_comment($edit, 'update');
-
-
- // Add an entry to the watchdog log.
- watchdog('content', t('Comment: updated %subject.', array('%subject' => theme('placeholder', $edit['subject']))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $edit['nid'], NULL, NULL, 'comment-'. $edit['cid']));
- }
- else {
- // Add the comment to database.
- $status = user_access('post comments without approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
- $roles = variable_get('comment_roles', array());
- $score = 0;
-
- foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
- $score = max($roles[$rid], $score);
- }
-
- $users = serialize(array(0 => $score));
-
- // Here we are building the thread field. See the comment
- // in comment_render().
- if ($edit['pid'] == 0) {
- // This is a comment with no parent comment (depth 0): we start
- // by retrieving the maximum thread level.
- $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $edit['nid']));
-
- // Strip the "/" from the end of the thread.
- $max = rtrim($max, '/');
-
- // Finally, build the thread field for this new comment.
- $thread = int2vancode(vancode2int($max) + 1) .'/';
- }
- else {
- // This is comment with a parent comment: we increase
- // the part of the thread value at the proper depth.
-
- // Get the parent comment:
- $parent = _comment_load($edit['pid']);
-
- // Strip the "/" from the end of the parent thread.
- $parent->thread = (string) rtrim((string) $parent->thread, '/');
-
- // Get the max value in _this_ thread.
- $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
-
- if ($max == '') {
- // First child of this parent.
- $thread = $parent->thread .'.'. int2vancode(0) .'/';
- }
- else {
- // Strip the "/" at the end of the thread.
- $max = rtrim($max, '/');
-
- // We need to get the value at the correct depth.
- $parts = explode('.', $max);
- $parent_depth = count(explode('.', $parent->thread));
- $last = $parts[$parent_depth];
-
- // Finally, build the thread field for this new comment.
- $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
- }
- }
-
- $edit['cid'] = db_next_id('{comments}_cid');
- $edit['timestamp'] = time();
-
- if ($edit['uid'] == $user->uid) {
- $edit['name'] = $user->name;
- }
-
- db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, format, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit['cid'], $edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'], $edit['comment'], $edit['format'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit['name'], $edit['mail'], $edit['homepage']);
-
- _comment_update_node_statistics($edit['nid']);
-
- // Tell the other modules a new comment has been submitted.
- comment_invoke_comment($edit, 'insert');
-
- // Add an entry to the watchdog log.
- watchdog('content', t('Comment: added %subject.', array('%subject' => theme('placeholder', $edit['subject']))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $edit['nid'], NULL, NULL, 'comment-'. $edit['cid']));
- }
-
- // Clear the cache so an anonymous user can see his comment being added.
- cache_clear_all();
-
- // Explain the approval queue if necessary, and then
- // redirect the user to the node he's commenting on.
- if ($status == COMMENT_NOT_PUBLISHED) {
- drupal_set_message(t('Your comment has been queued for moderation by site administrators and will be published after approval.'));
- }
- return $edit['cid'];
- }
- else {
- return FALSE;
- }
- }
- else {
- $txt = t('Comment: unauthorized comment submitted or comment submitted to a closed node %subject.', array('%subject' => theme('placeholder', $edit['subject'])));
- watchdog('content', $txt, WATCHDOG_WARNING);
- drupal_set_message($txt, 'error');
- return FALSE;
- }
-}
-
-function comment_links($comment, $return = 1) {
- global $user;
-
- $links = array();
-
- // If we are viewing just this comment, we link back to the node.
- if ($return) {
- $links[] = l(t('parent'), comment_node_url(), NULL, NULL, "comment-$comment->cid");
- }
-
- if (node_comment_mode($comment->nid) == COMMENT_NODE_READ_WRITE) {
- if (user_access('administer comments') && user_access('post comments')) {
- $links[] = l(t('delete'), "comment/delete/$comment->cid");
- $links[] = l(t('edit'), "comment/edit/$comment->cid");
- $links[] = l(t('reply'), "comment/reply/$comment->nid/$comment->cid");
- }
- else if (user_access('post comments')) {
- if (comment_access('edit', $comment)) {
- $links[] = l(t('edit'), "comment/edit/$comment->cid");
- }
- $links[] = l(t('reply'), "comment/reply/$comment->nid/$comment->cid");
- }
- else {
- $links[] = theme('comment_post_forbidden', $comment->nid);
- }
- }
-
- return $links;
-}
-
-function comment_render($node, $cid = 0) {
- global $user;
-
- $mode = $_GET['mode'];
- $order = $_GET['order'];
- $comments_per_page = $_GET['comments_per_page'];
- $comment_page = $_GET['comment_page'];
-
- $output = '';
-
- if (user_access('access comments')) {
- // Pre-process variables.
- $nid = $node->nid;
- if (empty($nid)) {
- $nid = 0;
- }
-
- if (empty($mode)) {
- $mode = $user->mode ? $user->mode : ($_SESSION['comment_mode'] ? $_SESSION['comment_mode'] : variable_get('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED));
- }
-
- if (empty($order)) {
- $order = $user->sort ? $user->sort : ($_SESSION['comment_sort'] ? $_SESSION['comment_sort'] : variable_get('comment_default_order', COMMENT_ORDER_NEWEST_FIRST));
- }
-
- if (empty($comments_per_page)) {
- $comments_per_page = $user->comments_per_page ? $user->comments_per_page : ($_SESSION['comment_comments_per_page'] ? $_SESSION['comment_comments_per_page'] : variable_get('comment_default_per_page', '50'));
- }
-
- $output .= "<a id=\"comment\"></a>\n";
-
- if ($cid) {
- // Single comment view.
- $result = db_query('SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = %d GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, u.picture, c.homepage, u.uid, u.name, u.picture, u.data, c.score, c.users', $cid, COMMENT_PUBLISHED);
-
- if ($comment = db_fetch_object($result)) {
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $output .= theme('comment_view', $comment, module_invoke_all('link', 'comment', $comment, 1));
- }
- }
- else {
- // Multiple comment view
- $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d AND c.status = %d";
-
- $query .= ' GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, u.picture, c.homepage, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread';
-
- /*
- ** We want to use the standard pager, but threads would need every
- ** comment to build the thread structure, so we need to store some
- ** extra info.
- **
- ** We use a "thread" field to store this extra info. The basic idea
- ** is to store a value and to order by that value. The "thread" field
- ** keeps this data in a way which is easy to update and convenient
- ** to use.
- **
- ** A "thread" value starts at "1". If we add a child (A) to this
- ** comment, we assign it a "thread" = "1.1". A child of (A) will have
- ** "1.1.1". Next brother of (A) will get "1.2". Next brother of the
- ** parent of (A) will get "2" and so on.
- **
- ** First of all note that the thread field stores the depth of the
- ** comment: depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc.
- **
- ** Now to get the ordering right, consider this example:
- **
- ** 1
- ** 1.1
- ** 1.1.1
- ** 1.2
- ** 2
- **
- ** If we "ORDER BY thread ASC" we get the above result, and this is
- ** the natural order sorted by time. However, if we "ORDER BY thread
- ** DESC" we get:
- **
- ** 2
- ** 1.2
- ** 1.1.1
- ** 1.1
- ** 1
- **
- ** Clearly, this is not a natural way to see a thread, and users
- ** will get confused. The natural order to show a thread by time
- ** desc would be:
- **
- ** 2
- ** 1
- ** 1.2
- ** 1.1
- ** 1.1.1
- **
- ** which is what we already did before the standard pager patch. To
- ** achieve this we simply add a "/" at the end of each "thread" value.
- ** This way out thread fields will look like depicted below:
- **
- ** 1/
- ** 1.1/
- ** 1.1.1/
- ** 1.2/
- ** 2/
- **
- ** we add "/" since this char is, in ASCII, higher than every number,
- ** so if now we "ORDER BY thread DESC" we get the correct order. Try
- ** it, it works ;). However this would spoil the "ORDER BY thread ASC"
- ** Here, we do not need to consider the trailing "/" so we use a
- ** substring only.
- */
-
- if ($order == COMMENT_ORDER_NEWEST_FIRST) {
- if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
- $query .= ' ORDER BY c.timestamp DESC';
- }
- else {
- $query .= ' ORDER BY c.thread DESC';
- }
- }
- else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
- if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
- $query .= ' ORDER BY c.timestamp';
- }
- else {
-
- /*
- ** See comment above. Analysis learns that this doesn't cost
- ** too much. It scales much much better than having the whole
- ** comment structure.
- */
-
- $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
- }
- }
-
- // Start a form, for use with comment control.
- $result = pager_query($query, $comments_per_page, 0, "SELECT COUNT(*) FROM {comments} WHERE nid = %d AND status = %d", $nid, COMMENT_PUBLISHED);
- if (db_num_rows($result) && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
- $output .= comment_controls($mode, $order, $comments_per_page);
- }
-
- while ($comment = db_fetch_object($result)) {
- $comment = drupal_unpack($comment);
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $comment->depth = count(explode('.', $comment->thread)) - 1;
-
- if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
- $output .= theme('comment_flat_collapsed', $comment);
- }
- else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
- $output .= theme('comment_flat_expanded', $comment);
- }
- else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
- $output .= theme('comment_thread_collapsed', $comment);
- }
- else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
- $output .= theme('comment_thread_expanded', $comment);
- }
- }
-
- // Use the standard pager; $pager_total is the number of returned rows,
- // is global and defined in pager.inc.
- $output .= theme('pager', NULL, $comments_per_page, 0, array('comments_per_page' => $comments_per_page));
-
- if (db_num_rows($result) && (variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_BELOW || variable_get('comment_controls', COMMENT_CONTROLS_HIDDEN) == COMMENT_CONTROLS_ABOVE_BELOW)) {
- $output .= comment_controls($mode, $order, $comments_per_page);
- }
- }
-
- // If enabled, show new comment form.
- if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW)) {
- $output .= comment_form(array('nid' => $nid), t('Post new comment'));
- }
- }
- return $output;
-}
-
-
-/**
- * Menu callback; delete a comment.
- */
-function comment_delete($cid) {
- $comment = db_fetch_object(db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d', $cid));
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
-
- $output = '';
-
- // We'll only delete if the user has confirmed the
- // deletion using the form in our else clause below.
- if ($comment->cid && $_POST['edit']['confirm']) {
- drupal_set_message(t('The comment and all its replies have been deleted.'));
-
- // Delete comment and its replies.
- _comment_delete_thread($comment);
-
- _comment_update_node_statistics($comment->nid);
-
- // Clear the cache so an anonymous user sees that his comment was deleted.
- cache_clear_all();
-
- drupal_goto("node/$comment->nid");
- }
- else if ($comment->cid) {
- $output = confirm_form('comment_confirm_delete',
- array(),
- t('Are you sure you want to delete the comment %title?', array('%title' => theme('placeholder', $comment->subject))),
- 'node/'. $comment->nid,
- t('Any replies to this comment will be lost. This action cannot be undone.'),
- t('Delete'),
- t('Cancel'));
- }
- else {
- drupal_set_message(t('The comment no longer exists.'));
- }
-
- return $output;
-}
-
-/**
- * Comment operations. We offer different update operations depending on
- * which comment administration page we're on.
- */
-function comment_operations($action = NULL) {
- if ($action == 'publish') {
- $operations = array(
- 'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
- 'delete' => array(t('Delete the selected comments'), '')
- );
- }
- else if ($action == 'unpublish') {
- $operations = array(
- 'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
- 'delete' => array(t('Delete the selected comments'), '')
- );
- }
- else {
- $operations = array(
- 'publish' => array(t('Publish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_PUBLISHED .' WHERE cid = %d'),
- 'unpublish' => array(t('Unpublish the selected comments'), 'UPDATE {comments} SET status = '. COMMENT_NOT_PUBLISHED .' WHERE cid = %d'),
- 'delete' => array(t('Delete the selected comments'), '')
- );
- }
- return $operations;
-}
-
-/**
- * Menu callback; present an administrative comment listing.
- */
-function comment_admin_overview($type = 'new') {
- $edit = $_POST['edit'];
-
- if ($edit['operation'] == 'delete') {
- return comment_multiple_delete_confirm();
- }
-
- // build an 'Update options' form
- $form['options'] = array(
- '#type' => 'fieldset', '#title' => t('Update options'),
- '#prefix' => '<div class="container-inline">', '#suffix' => '</div>'
- );
- $options = array();
- foreach (comment_operations(arg(3) == 'approval' ? 'publish' : 'unpublish') as $key => $value) {
- $options[$key] = $value[0];
- }
- $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'publish');
- $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update'));
-
- // load the comments that we want to display
- $status = ($type == 'approval') ? COMMENT_NOT_PUBLISHED : COMMENT_PUBLISHED;
- $form['header'] = array('#type' => 'value', '#value' => array(
- NULL,
- array('data' => t('Subject'), 'field' => 'subject'),
- array('data' => t('Author'), 'field' => 'name'),
- array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'),
- array('data' => t('Operations'))
- ));
- $result = pager_query('SELECT c.subject, c.nid, c.cid, c.comment, c.timestamp, c.status, c.name, c.homepage, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.status = %d'. tablesort_sql($form['header']['#value']), 50, 0, NULL, $status);
-
- // build a table listing the appropriate comments
- $destination = drupal_get_destination();
- while ($comment = db_fetch_object($result)) {
- $comments[$comment->cid] = '';
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => truncate_utf8($comment->comment, 128)), NULL, 'comment-'. $comment->cid));
- $form['username'][$comment->cid] = array('#value' => theme('username', $comment));
- $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
- $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array(), $destination));
- }
- $form['comments'] = array('#type' => 'checkboxes', '#options' => $comments);
- $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
- return drupal_get_form('comment_admin_overview', $form);
-}
-
-/**
- * We can't execute any 'Update options' if no comments were selected.
- */
-function comment_admin_overview_validate($form_id, $edit) {
- $edit['comments'] = array_diff($edit['comments'], array(0));
- if (count($edit['comments']) == 0) {
- form_set_error('', t('Please select one or more comments to perform the update on.'));
- drupal_goto('admin/comment');
- }
-}
-
-/**
- * Execute the chosen 'Update option' on the selected comments, such as
- * publishing, unpublishing or deleting.
- */
-function comment_admin_overview_submit($form_id, $edit) {
- $operations = comment_operations();
- if ($operations[$edit['operation']][1]) {
- // extract the appropriate database query operation
- $query = $operations[$edit['operation']][1];
- foreach ($edit['comments'] as $cid => $value) {
- if ($value) {
- // perform the update action, then refresh node statistics
- db_query($query, $cid);
- $comment = _comment_load($cid);
- _comment_update_node_statistics($comment->nid);
- // Allow modules to respond to the updating of a comment.
- comment_invoke_comment($comment, $edit['operation']);
- // Add an entry to the watchdog log.
- watchdog('content', t('Comment: updated %subject.', array('%subject' => theme('placeholder', $comment->subject))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $comment->nid, NULL, NULL, 'comment-'. $comment->cid));
- }
- }
- cache_clear_all();
- drupal_set_message(t('The update has been performed.'));
- drupal_goto('admin/comment');
- }
-}
-
-function theme_comment_admin_overview($form) {
- $output = form_render($form['options']);
- if (isset($form['subject']) && is_array($form['subject'])) {
- foreach (element_children($form['subject']) as $key) {
- $row = array();
- $row[] = form_render($form['comments'][$key]);
- $row[] = form_render($form['subject'][$key]);
- $row[] = form_render($form['username'][$key]);
- $row[] = form_render($form['timestamp'][$key]);
- $row[] = form_render($form['operations'][$key]);
- $rows[] = $row;
- }
- }
- else {
- $rows[] = array(array('data' => t('No comments available.'), 'colspan' => '6'));
- }
-
- $output .= theme('table', $form['header']['#value'], $rows);
- if ($form['pager']['#value']) {
- $output .= form_render($form['pager']);
- }
-
- $output .= form_render($form);
-
- return $output;
-}
-
-/**
- * List the selected comments and verify that the admin really wants to delete
- * them.
- */
-function comment_multiple_delete_confirm() {
- $edit = $_POST['edit'];
-
- $form['comments'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
- // array_filter() returns only elements with actual values
- foreach (array_filter($edit['comments']) as $cid => $value) {
- $subject = db_result(db_query('SELECT subject FROM {comments} WHERE cid = %d', $cid));
- $form['comments'][$cid] = array('#type' => 'hidden', '#value' => $cid, '#prefix' => '<li>', '#suffix' => check_plain($subject) .'</li>');
- }
- $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
-
- return confirm_form('comment_multiple_delete_confirm', $form,
- t('Are you sure you want to delete these comments and all their children?'),
- 'admin/comment', t('This action cannot be undone.'),
- t('Delete comments'), t('Cancel'));
-}
-
-/**
- * Perform the actual comment deletion.
- */
-function comment_multiple_delete_confirm_submit($form_id, $edit) {
- if ($edit['confirm']) {
- foreach ($edit['comments'] as $cid => $value) {
- $comment = _comment_load($cid);
- _comment_delete_thread($comment);
- _comment_update_node_statistics($comment->nid);
- cache_clear_all();
- }
- drupal_set_message(t('The comments have been deleted.'));
- }
- drupal_goto('admin/comment');
-}
-
-/**
-*** misc functions: helpers, privates, history
-**/
-
-/**
- * Load the entire comment by cid.
- */
-function _comment_load($cid) {
- return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $cid));
-}
-
-function comment_num_all($nid) {
- static $cache;
-
- if (!isset($cache[$nid])) {
- $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
- }
- return $cache[$nid];
-}
-
-function comment_num_replies($pid) {
- static $cache;
-
- if (!isset($cache[$pid])) {
- $cache[$pid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE pid = %d AND status = %d', $pid, COMMENT_PUBLISHED));
- }
-
- return $cache[$pid];
-}
-
-/**
- * get number of new comments for current user and specified node
- *
- * @param $nid node-id to count comments for
- * @param $timestamp time to count from (defaults to time of last user access
- * to node)
- */
-function comment_num_new($nid, $timestamp = 0) {
- global $user;
-
- if ($user->uid) {
- // Retrieve the timestamp at which the current user last viewed the
- // specified node.
- if (!$timestamp) {
- $timestamp = node_last_viewed($nid);
- }
- $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
-
- // Use the timestamp to retrieve the number of new comments.
- $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND timestamp > %d AND c.status = %d', $nid, $timestamp, COMMENT_PUBLISHED));
-
- return $result;
- }
- else {
- return 0;
- }
-
-}
-
-function comment_validate($edit) {
- global $user;
-
- // Invoke other validation handlers
- comment_invoke_comment($edit, 'validate');
-
- if (isset($edit['date'])) {
- // As of PHP 5.1.0, strtotime returns FALSE upon failure instead of -1.
- if (strtotime($edit['date']) <= 0) {
- form_set_error('date', t('You have to specify a valid date.'));
- }
- }
- if (isset($edit['author']) && !$account = user_load(array('name' => $edit['author']))) {
- form_set_error('author', t('You have to specify a valid author.'));
- }
-
- // Validate the comment's body.
- if (trim($edit['comment']) == '') {
- form_set_error('comment', t('The body of your comment is empty.'));
- }
-
- // Check validity of name, mail and homepage (if given)
- if (!$user->uid || isset($edit['is_anonymous'])) {
- if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) > COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
- if ($edit['name']) {
- $taken = db_result(db_query("SELECT COUNT(uid) FROM {users} WHERE LOWER(name) = '%s'", $edit['name']), 0);
-
- if ($taken != 0) {
- form_set_error('name', t('The name you used belongs to a registered user.'));
- }
-
- }
- else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
- form_set_error('name', t('You have to leave your name.'));
- }
-
- if ($edit['mail']) {
- if (!valid_email_address($edit['mail'])) {
- form_set_error('mail', t('The e-mail address you specified is not valid.'));
- }
- }
- else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
- form_set_error('mail', t('You have to leave an e-mail address.'));
- }
-
- if ($edit['homepage']) {
- if (!valid_url($edit['homepage'], TRUE)) {
- form_set_error('homepage', t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.'));
- }
- }
- }
- }
-
- return $edit;
-}
-
-/*
-** Generate the basic commenting form, for appending to a node or display on a separate page.
-** This is rendered by theme_comment_form.
-*/
-
-function comment_form($edit, $title = NULL) {
- global $user;
-
- $op = isset($_POST['op']) ? $_POST['op'] : '';
-
- if ($user->uid) {
- if ($edit['cid'] && user_access('administer comments')) {
- if ($edit['author']) {
- $author = $edit['author'];
- }
- elseif ($edit['name']) {
- $author = $edit['name'];
- }
- else {
- $author = $edit['registered_name'];
- }
-
- if ($edit['status']) {
- $status = $edit['status'];
- }
- else {
- $status = 0;
- }
-
- if ($edit['date']) {
- $date = $edit['date'];
- }
- else {
- $date = format_date($edit['timestamp'], 'custom', 'Y-m-d H:i O');
- }
-
- $form['admin'] = array(
- '#type' => 'fieldset',
- '#title' => t('Administration'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#weight' => -2,
- );
-
- if ($edit['registered_name'] != '') {
- // The comment is by a registered user
- $form['admin']['author'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored by'),
- '#size' => 30,
- '#maxlength' => 60,
- '#autocomplete_path' => 'user/autocomplete',
- '#default_value' => $author,
- '#weight' => -1,
- );
- }
- else {
- // The comment is by an anonymous user
- $form['is_anonymous'] = array(
- '#type' => 'value',
- '#value' => TRUE,
- );
- $form['admin']['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Authored by'),
- '#size' => 30,
- '#maxlength' => 60,
- '#default_value' => $author,
- '#weight' => -1,
- );
- $form['admin']['mail'] = array(
- '#type' => 'textfield',
- '#title' => t('E-mail'),
- '#maxlength' => 64,
- '#size' => 30,
- '#default_value' => $edit['mail'],
- '#description' => t('The content of this field is kept private and will not be shown publicly.'),
- );
-
- $form['admin']['homepage'] = array(
- '#type' => 'textfield',
- '#title' => t('Homepage'),
- '#maxlength' => 255,
- '#size' => 30,
- '#default_value' => $edit['homepage'],
- );
- }
-
- $form['admin']['date'] = array('#type' => 'textfield', '#parents' => array('date'), '#title' => t('Authored on'), '#size' => 20, '#maxlength' => 25, '#default_value' => $date, '#weight' => -1);
-
- $form['admin']['status'] = array('#type' => 'radios', '#parents' => array('status'), '#title' => t('Status'), '#default_value' => $status, '#options' => array(t('Published'), t('Not published')), '#weight' => -1);
-
- }
- else {
- $form['_author'] = array('#type' => 'item', '#title' => t('Your name'), '#value' => theme('username', $user)
- );
- $form['author'] = array('#type' => 'value', '#value' => $user->name);
- }
- }
- else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MAY_CONTACT) {
- $form['name'] = array('#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $edit['name'] ? $edit['name'] : variable_get('anonymous', 'Anonymous')
- );
-
- $form['mail'] = array('#type' => 'textfield', '#title' => t('E-mail'), '#maxlength' => 64, '#size' => 30, '#default_value' => $edit['mail'], '#description' => t('The content of this field is kept private and will not be shown publicly.')
- );
-
- $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
- }
- else if (variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
- $form['name'] = array('#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $edit['name'] ? $edit['name'] : variable_get('anonymous', 'Anonymous'), '#required' => TRUE);
-
- $form['mail'] = array('#type' => 'textfield', '#title' => t('E-mail'), '#maxlength' => 64, '#size' => 30, '#default_value' => $edit['mail'],'#description' => t('The content of this field is kept private and will not be shown publicly.'), '#required' => TRUE);
-
- $form['homepage'] = array('#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $edit['homepage']);
- }
-
- if (variable_get('comment_subject_field', 1) == 1) {
- $form['subject'] = array('#type' => 'textfield', '#title' => t('Subject'), '#maxlength' => 64, '#default_value' => $edit['subject']);
- }
-
- $form['comment_filter']['comment'] = array('#type' => 'textarea', '#title' => t('Comment'), '#rows' => 15, '#default_value' => $edit['comment'] ? $edit['comment'] : $user->signature, '#required' => TRUE);
- $form['comment_filter']['format'] = filter_form($edit['format']);
-
- $form['cid'] = array('#type' => 'value', '#value' => $edit['cid']);
- $form['pid'] = array('#type' => 'value', '#value' => $edit['pid']);
- $form['nid'] = array('#type' => 'value', '#value' => $edit['nid']);
- $form['uid'] = array('#type' => 'value', '#value' => $edit['uid']);
-
- $form['preview'] = array('#type' => 'button', '#value' => t('Preview comment'), '#weight' => 19);
- $form['#token'] = 'comment' . $edit['nid'] . $edit['pid'];
-
- // Only show post button if preview is optional or if we are in preview mode.
- // We show the post button in preview mode even if there are form errors so that
- // optional form elements (e.g., captcha) can be updated in preview mode.
- if (!form_get_errors() && ((variable_get('comment_preview', COMMENT_PREVIEW_REQUIRED) == COMMENT_PREVIEW_OPTIONAL) || ($op == t('Preview comment')) || ($op == t('Post comment')))) {
- $form['submit'] = array('#type' => 'submit', '#value' => t('Post comment'), '#weight' => 20);
- }
-
- if ($op == t('Preview comment')) {
- $form['#after_build'] = 'comment_form_add_preview';
- }
-
- if ($_REQUEST['destination']) {
- $form['#attributes']['destination'] = $_REQUEST['destination'];
- }
-
- if (empty($edit['cid']) && empty($edit['pid'])) {
- $form['#action'] = url('comment/reply/'. $edit['nid']);
- }
-
- // Graft in extra form additions
- $form = array_merge($form, comment_invoke_comment($form, 'form'));
-
- return theme('box', $title, drupal_get_form('comment_form', $form));
-}
-
-function comment_form_add_preview($form, $edit) {
- global $user;
-
- drupal_set_title(t('Preview comment'));
-
- $output = '';
-
- comment_validate($edit);
- $comment = (object)_comment_form_submit($edit);
-
- // Attach the user and time information.
- if ($edit['author']) {
- $account = user_load(array('name' => $edit['author']));
- }
- elseif ($user->uid && !isset($edit['is_anonymous'])) {
- $account = $user;
- }
- if ($account) {
- $comment->uid = $account->uid;
- $comment->name = check_plain($account->name);
- }
- $comment->timestamp = $edit['timestamp'] ? $edit['timestamp'] : time();
-
- // Preview the comment with security check.
- if (!form_get_errors()) {
- $output .= theme('comment_view', $comment);
- }
- $form['comment_preview'] = array('#value' => $output, '#weight' => -100);
-
- $output = '';
-
- if ($edit['pid']) {
- $comment = db_fetch_object(db_query('SELECT c.*, u.uid, u.name AS registered_name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = %d', $edit['pid'], COMMENT_PUBLISHED));
- $comment = drupal_unpack($comment);
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- $output .= theme('comment_view', $comment);
- }
- else {
- $output .= node_view(node_load($edit['nid']));
- $edit['pid'] = 0;
- }
-
- $form['comment_preview_below'] = array('#value' => $output, '#weight' => 100);
-
- return $form;
-}
-
-function comment_form_validate($form_id, $form_values) {
- comment_validate($form_values);
-}
-
-function _comment_form_submit($form_values) {
- if (!isset($form_values['date'])) {
- $form_values['date'] = 'now';
- }
- $form_values['timestamp'] = strtotime($form_values['date']);
- if (isset($form_values['author'])) {
- $account = user_load(array('name' => $form_values['author']));
- $form_values['uid'] = $account->uid;
- $form_values['name'] = $form_values['author'];
- }
- // Validate the comment's subject. If not specified, extract
- // one from the comment's body.
- if (trim($form_values['subject']) == '') {
- // The body may be in any format, so we:
- // 1) Filter it into HTML
- // 2) Strip out all HTML tags
- // 3) Convert entities back to plain-text.
- // Note: format is checked by check_markup().
- $form_values['subject'] = truncate_utf8(decode_entities(strip_tags(check_markup($form_values['comment'], $form_values['format']))), 29, TRUE);
- }
- return $form_values;
-}
-
-function comment_form_submit($form_id, $form_values) {
- $form_values = _comment_form_submit($form_values);
- if ($cid = comment_save($form_values)) {
- return array('node/'. $form_values['nid'], NULL, "comment-$cid");
- }
-}
-
-/*
-** Renderer or visualization functions this can be optionally
-** overridden by themes.
-*/
-
-function theme_comment_preview($comment, $links = array(), $visible = 1) {
- $output = '<div class="preview">';
- $output .= theme('comment_view', $comment, $links, $visible);
- $output .= '</div>';
- return $output;
-};
-
-function theme_comment_view($comment, $links = array(), $visible = 1) {
-
- // Emit selectors:
- $output = '';
- if (($comment->new = node_mark($comment->nid, $comment->timestamp)) != MARK_READ) {
- $output .= "<a id=\"new\"></a>\n";
- }
-
- $output .= "<a id=\"comment-$comment->cid\"></a>\n";
-
- // Switch to folded/unfolded view of the comment
- if ($visible) {
- $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
-
- // Comment API hook
- comment_invoke_comment($comment, 'view');
-
- $output .= theme('comment', $comment, $links);
- }
- else {
- $output .= theme('comment_folded', $comment);
- }
-
- return $output;
-}
-
-function comment_controls($mode = COMMENT_MODE_THREADED_EXPANDED, $order = COMMENT_ORDER_NEWEST_FIRST, $comments_per_page = 50) {
- $form['mode'] = array('#type' => 'select',
- '#default_value' => $mode,
- '#options' => _comment_get_modes(),
- '#weight' => 1,
- );
- $form['order'] = array(
- '#type' => 'select',
- '#default_value' => $order,
- '#options' => _comment_get_orders(),
- '#weight' => 2,
- );
- foreach (_comment_per_page() as $i) {
- $options[$i] = t('%a comments per page', array('%a' => $i));
- }
- $form['comments_per_page'] = array('#type' => 'select',
- '#default_value' => $comments_per_page,
- '#options' => $options,
- '#weight' => 3,
- );
-
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Save settings'),
- '#weight' => 20,
- );
-
- return drupal_get_form('comment_controls', $form);
-}
-
-function theme_comment_controls($form) {
- $output .= '<div class="container-inline">';
- $output .= form_render($form);
- $output .= '</div>';
- $output .= '<div class="description">'. t('Select your preferred way to display the comments and click "Save settings" to activate your changes.') .'</div>';
- return theme('box', t('Comment viewing options'), $output);
-}
-
-function comment_controls_submit($form_id, $form_values) {
- global $user;
-
- $mode = $form_values['mode'];
- $order = $form_values['order'];
- $comments_per_page = $form_values['comments_per_page'];
-
- if ($user->uid) {
- $user = user_save($user, array('mode' => $mode, 'sort' => $order, 'comments_per_page' => $comments_per_page));
- }
- else {
- $_SESSION['comment_mode'] = $mode;
- $_SESSION['comment_sort'] = $order;
- $_SESSION['comment_comments_per_page'] = $comments_per_page;
- }
-}
-
-function theme_comment($comment, $links = array()) {
- $output = '<div class="comment">';
- $output .= '<div class="subject">'. l($comment->subject, $_GET['q'], NULL, NULL, "comment-$comment->cid") . ' ' . theme('mark', $comment->new) ."</div>\n";
- $output .= '<div class="credit">'. t('by %a on %b', array('%a' => theme('username', $comment), '%b' => format_date($comment->timestamp))) ."</div>\n";
- $output .= '<div class="body">'. $comment->comment .'</div>';
- $output .= '<div class="links">'. theme('links', $links) .'</div>';
- $output .= '</div>';
- return $output;
-}
-
-function theme_comment_folded($comment) {
- $output = "<div class=\"comment-folded\">\n";
- $output .= ' <span class="subject">'. l($comment->subject, comment_node_url() .'/'. $comment->cid, NULL, NULL, "comment-$comment->cid") . ' '. theme('mark', $comment->new) .'</span> ';
- $output .= '<span class="credit">'. t('by') .' '. theme('username', $comment) ."</span>\n";
- $output .= "</div>\n";
- return $output;
-}
-
-function theme_comment_flat_collapsed($comment) {
- return theme('comment_view', $comment, '', 0);
- return '';
-}
-
-function theme_comment_flat_expanded($comment) {
- return theme('comment_view', $comment, module_invoke_all('link', 'comment', $comment, 0));
-}
-
-function theme_comment_thread_collapsed($comment) {
- $output = '<div style="margin-left:'. ($comment->depth * 25) ."px;\">\n";
- $output .= theme('comment_view', $comment, '', 0);
- $output .= "</div>\n";
- return $output;
-}
-
-function theme_comment_thread_expanded($comment) {
- $output = '';
- if ($comment->depth) {
- $output .= '<div style="margin-left:'. ($comment->depth * 25) ."px;\">\n";
- }
-
- $output .= theme('comment_view', $comment, module_invoke_all('link', 'comment', $comment, 0));
-
- if ($comment->depth) {
- $output .= "</div>\n";
- }
- return $output;
-}
-
-function theme_comment_post_forbidden($nid) {
- global $user;
- if ($user->uid) {
- return t("you can't post comments");
- }
- else {
- // we cannot use drupal_get_destination() because these links sometimes appear on /node and taxo listing pages
- if (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
- $destination = "destination=". urlencode("comment/reply/$nid#comment_form");
- }
- else {
- $destination = "destination=". urlencode("node/$nid#comment_form");
- }
-
- if (variable_get('user_register', 1)) {
- return t('<a href="%login">login</a> or <a href="%register">register</a> to post comments', array('%login' => url('user/login', $destination), '%register' => check_url(url('user/register', $destination))));
- }
- else {
- return t('<a href="%login">login</a> to post comments', array('%login' => check_url(url('user/login', $destination))));
- }
- }
-}
-
-function _comment_delete_thread($comment) {
- // Delete the comment:
- db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
- watchdog('content', t('Comment: deleted %subject.', array('%subject' => theme('placeholder', $comment->subject))));
-
- comment_invoke_comment($comment, 'delete');
-
- // Delete the comment's replies
- $result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
- while ($comment = db_fetch_object($result)) {
- $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
- _comment_delete_thread($comment);
- }
-}
-
-/**
- * Return an array of viewing modes for comment listings.
- *
- * We can't use a global variable array because the locale system
- * is not initialized yet when the comment module is loaded.
- */
-function _comment_get_modes() {
- return array(
- COMMENT_MODE_FLAT_COLLAPSED => t('Flat list - collapsed'),
- COMMENT_MODE_FLAT_EXPANDED => t('Flat list - expanded'),
- COMMENT_MODE_THREADED_COLLAPSED => t('Threaded list - collapsed'),
- COMMENT_MODE_THREADED_EXPANDED => t('Threaded list - expanded')
- );
-}
-
-/**
- * Return an array of viewing orders for comment listings.
- *
- * We can't use a global variable array because the locale system
- * is not initialized yet when the comment module is loaded.
- */
-function _comment_get_orders() {
- return array(
- COMMENT_ORDER_NEWEST_FIRST => t('Date - newest first'),
- COMMENT_ORDER_OLDEST_FIRST => t('Date - oldest first')
- );
-}
-
-/**
- * Return an array of "comments per page" settings from which the user
- * can choose.
- */
-function _comment_per_page() {
- return drupal_map_assoc(array(10, 30, 50, 70, 90, 150, 200, 250, 300));
-}
-
-/**
- * Updates the comment statistics for a given node. This should be called any
- * time a comment is added, deleted, or updated.
- *
- * The following fields are contained in the node_comment_statistics table.
- * - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
- * - last_comment_name: the name of the anonymous poster for the last comment
- * - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
- * - comment_count: the total number of approved/published comments on this node.
- */
-function _comment_update_node_statistics($nid) {
- $count = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = %d', $nid, COMMENT_PUBLISHED));
-
- // comments exist
- if ($count > 0) {
- $last_reply = db_fetch_object(db_query_range('SELECT cid, name, timestamp, uid FROM {comments} WHERE nid = %d AND status = %d ORDER BY cid DESC', $nid, COMMENT_PUBLISHED, 0, 1));
- db_query("UPDATE {node_comment_statistics} SET comment_count = %d, last_comment_timestamp = %d, last_comment_name = '%s', last_comment_uid = %d WHERE nid = %d", $count, $last_reply->timestamp, $last_reply->uid ? '' : $last_reply->name, $last_reply->uid, $nid);
- }
-
- // no comments
- else {
- $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid));
- db_query("UPDATE {node_comment_statistics} SET comment_count = 0, last_comment_timestamp = %d, last_comment_name = '', last_comment_uid = %d WHERE nid = %d", $node->created, $node->uid, $nid);
- }
-}
-
-/**
- * Invoke a hook_comment() operation in all modules.
- *
- * @param &$comment
- * A comment object.
- * @param $op
- * A string containing the name of the comment operation.
- * @return
- * The returned value of the invoked hooks.
- */
-function comment_invoke_comment(&$comment, $op) {
- $return = array();
- foreach (module_implements('comment') as $name) {
- $function = $name .'_comment';
- $result = $function($comment, $op);
- if (isset($result) && is_array($result)) {
- $return = array_merge($return, $result);
- }
- else if (isset($result)) {
- $return[] = $result;
- }
- }
- return $return;
-}
-
-/**
- * Generate vancode.
- *
- * Consists of a leading character indicating length, followed by N digits
- * with a numerical value in base 36. Vancodes can be sorted as strings
- * without messing up numerical order.
- *
- * It goes:
- * 00, 01, 02, ..., 0y, 0z,
- * 110, 111, ... , 1zy, 1zz,
- * 2100, 2101, ..., 2zzy, 2zzz,
- * 31000, 31001, ...
- */
-function int2vancode($i = 0) {
- $num = base_convert((int)$i, 10, 36);
- $length = strlen($num);
- return chr($length + ord('0') - 1) . $num;
-}
-
-/**
- * Decode vancode back to an integer.
- */
-function vancode2int($c = '00') {
- return base_convert(substr($c, 1), 36, 10);
-}
diff --git a/modules/contact/contact.module b/modules/contact/contact.module
deleted file mode 100644
index da34b83..0000000
--- a/modules/contact/contact.module
+++ /dev/null
@@ -1,551 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables the use of personal and site-wide contact forms.
- */
-
-/**
- * Implementation of hook_help().
- */
-function contact_help($section) {
- switch ($section) {
- case 'admin/help#contact':
- $output = '<p>'. t('The contact module enables the use of both personal and site-wide contact forms, thereby facilitating easy communication within the community. While personal contact forms allow users to contact each other by e-mail, site-wide forms allow community members to contact the site administration from a central location. Users can specify a subject and message in the contact form, and also request that a copy of the e-mail be sent to their own address.') .'</p>';
- $output .= '<p>'. t("Users can activate/deactivate their personal contact forms in their account settings. Upon activation, a contact tab will appear in their user profiles. Privileged users such as site administrators are able to contact users even if they have chosen not to enable this feature.") .'</p>';
- $output .= '<p>'. t('If the menu module is enabled, a menu item linking to the site-wide contact page is added to the navigation block. It is disabled by default, but can be enabled via the <a href="%menu-module">menu management</a> page. Links to the contact page may also be added to the primary and secondary links using the same page.', array('%menu-module' => url('admin/menu'))) .'</p>';
- $output .= t('Contact module links:') .'<ul>';
- $output .= '<li>'. t('Default site-wide <a href="%contact-page">contact page</a>.', array('%contact-page' => url('contact'))) .'</li>';
- $output .= '<li>'. t('Site-wide contact form <a href="%configuration-page">category configuration</a>.', array('%configuration-page' => url('admin/contact'))) .'</li>';
- $output .= '<li>'. t('Site-wide contact form <a href="%additional-settings">general settings</a>.', array('%additional-settings' => url('admin/contact/settings'))) .'</li>';
- $output .= '<li>'. t('Site-wide contact form <a href="%menu-configuration">menu configuration</a>.', array('%menu-configuration' => url('admin/menu'))) .'</li></ul>';
- $output .= t('For more information, please read the configuration and customization handbook page for the <a href="%contact">contact module</a>.', array('%contact' => url('http://drupal.org/handbook/modules/contact/', NULL, NULL, TRUE)));
- return $output;
- case 'admin/modules#description':
- return t('Enables the use of both personal and site-wide contact forms.');
- case 'admin/contact':
- $output = t('This page lets you setup <a href="%form">your site-wide contact form</a>. To do so, add one or more categories. You can associate different recipients with each category to route e-mails to different people. For example, you can route website feedback to the webmaster and direct product information requests to the sales department. On the <a href="%settings">settings page</a>, you can customize the information shown above the contact form. This can be useful to provide additional contact information such as your postal address and telephone number.', array('%settings' => url('admin/contact/settings'), '%form' => url('contact')));
- if (!module_exist('menu')) {
- $menu_note = t('The menu item can be customized and configured only once the menu module has been <a href="%modules-page">enabled</a>.', array('%modules-page' => url('admin/modules')));
- }
- else {
- $menu_note = '';
- }
- $output .= '<p>'. t('The contact module also adds a <a href="%menu-settings">menu item</a> (disabled by default) to the navigation block.', array('%menu-settings' => url('admin/menu'))) .' '. $menu_note .'</p>';
- return($output);
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function contact_menu($may_cache) {
- $items = array();
- if ($may_cache) {
- $items[] = array('path' => 'admin/contact',
- 'title' => t('contact form'),
- 'callback' => 'contact_admin_categories',
- 'access' => user_access('administer site configuration'),
- );
- $items[] = array('path' => 'admin/contact/category',
- 'title' => t('categories'),
- 'callback' => 'contact_admin_categories',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- );
- $items[] = array('path' => 'admin/contact/category/list',
- 'title' => t('list'),
- 'callback' => 'contact_admin_categories',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- );
- $items[] = array('path' => 'admin/contact/category/add',
- 'title' => t('add category'),
- 'callback' => 'contact_admin_edit',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1,
- );
- $items[] = array('path' => 'admin/contact/category/edit',
- 'title' => t('edit contact category'),
- 'callback' => 'contact_admin_edit',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_CALLBACK,
- );
- $items[] = array('path' => 'admin/contact/category/delete',
- 'title' => t('delete contact'),
- 'callback' => 'contact_admin_delete',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_CALLBACK,
- );
- $items[] = array('path' => 'admin/contact/settings',
- 'title' => t('settings'),
- 'callback' => 'contact_admin_settings',
- 'access' => user_access('administer site configuration'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1,
- );
- $items[] = array('path' => 'contact',
- 'title' => t('contact'),
- 'callback' => 'contact_mail_page',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM,
- );
- }
- else {
- if (arg(0) == 'user' && is_numeric(arg(1))) {
- $items[] = array('path' => "user/". arg(1) ."/contact",
- 'title' => t('contact'),
- 'callback' => 'contact_mail_user',
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 2,
- );
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_user().
- *
- * Allows the user the option of enabling/disabling his personal contact form.
- */
-function contact_user($type, $edit, &$user, $category = NULL) {
- if ($type == 'form' && $category == 'account') {
- $form['contact'] = array('#type' => 'fieldset',
- '#title' => t('Contact settings'),
- '#weight' => 5,
- '#collapsible' => TRUE,
- );
- $form['contact']['contact'] = array('#type' => 'checkbox',
- '#title' => t('Personal contact form'),
- '#default_value' => $edit['contact'],
- '#description' => t('Allow other users to contact you by e-mail via <a href="%url">your personal contact form</a>. Note that while your e-mail address is not made public to other members of the community, privileged users such as site administrators are able to contact you even if you choose not to enable this feature.', array('%url' => url("user/$user->uid/contact"))),
- );
- return $form;
- }
- if ($type == 'validate') {
- return array('contact' => $edit['contact']);
- }
-}
-
-/**
- * Categories/list tab.
- */
-function contact_admin_categories() {
- $result = db_query('SELECT cid, category, recipients, selected FROM {contact} ORDER BY weight, category');
- $rows = array();
- while ($category = db_fetch_object($result)) {
- $rows[] = array($category->category, $category->recipients, ($category->selected ? t('Yes') : t('No')), l(t('edit'), 'admin/contact/category/edit/'. $category->cid), l(t('delete'), 'admin/contact/category/delete/'. $category->cid));
- }
- $header = array(t('Category'), t('Recipients'), t('Selected'), array('data' => t('Operations'), 'colspan' => 2));
-
- return theme('table', $header, $rows);
-}
-
-/**
- * Category edit page.
- */
-function contact_admin_edit($cid = NULL) {
- if (arg(3) == "edit" && $cid > 0) {
- $edit = db_fetch_array(db_query("SELECT * FROM {contact} WHERE cid = %d", $cid));
- }
- $form['category'] = array('#type' => 'textfield',
- '#title' => t('Category'),
- '#maxlength' => 255,
- '#default_value' => $edit['category'],
- '#description' => t("Example: 'website feedback' or 'product information'."),
- '#required' => TRUE,
- );
- $form['recipients'] = array('#type' => 'textarea',
- '#title' => t('Recipients'),
- '#default_value' => $edit['recipients'],
- '#description' => t("Example: 'webmaster@yoursite.com' or 'sales@yoursite.com'. To specify multiple recipients, separate each e-mail address with a comma."),
- '#required' => TRUE,
- );
- $form['reply'] = array('#type' => 'textarea',
- '#title' => t('Auto-reply'),
- '#default_value' => $edit['reply'],
- '#description' => t('Optional auto-reply. Leave empty if you do not want to send the user an auto-reply message.'),
- );
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $edit['weight'],
- '#description' => t('When listing categories, those with lighter (smaller) weights get listed before categories with heavier (larger) weights. Categories with equal weights are sorted alphabetically.'),
- );
- $form['selected'] = array('#type' => 'select',
- '#title' => t('Selected'),
- '#options' => array('0' => t('No'), '1' => t('Yes')),
- '#default_value' => $edit['selected'],
- '#description' => t('Set this to <em>Yes</em> if you would like this category to be selected by default.'),
- );
- $form['cid'] = array('#type' => 'value',
- '#value' => $edit['cid'],
- );
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Submit'),
- );
-
- return drupal_get_form('contact_admin_edit', $form);
-}
-
-/**
- * Validate the contact category edit page form submission.
- */
-function contact_admin_edit_validate($form_id, $form_values) {
- if (empty($form_values['category'])) {
- form_set_error('category', t('You must enter a category.'));
- }
- if (empty($form_values['recipients'])) {
- form_set_error('recipients', t('You must enter one or more recipients.'));
- }
- else {
- $recipients = explode(',', $form_values['recipients']);
- foreach($recipients as $recipient) {
- if (!valid_email_address(trim($recipient))) {
- form_set_error('recipients', t('%recipient is an invalid e-mail address.', array('%recipient' => theme('placeholder', $recipient))));
- }
- }
- }
-}
-
-/**
- * Process the contact category edit page form submission.
- */
-function contact_admin_edit_submit($form_id, $form_values) {
- if ($form_values['selected']) {
- // Unselect all other contact categories.
- db_query('UPDATE {contact} SET selected = 0');
- }
- $recipients = explode(',', $form_values['recipients']);
- foreach($recipients as $key=>$recipient) {
- // E-mail address validation has already been done in _validate.
- $recipients[$key] = trim($recipient);
- }
- $form_values['recipients'] = implode(',', $recipients);
- if (arg(3) == 'add') {
- db_query("INSERT INTO {contact} (category, recipients, reply, weight, selected) VALUES ('%s', '%s', '%s', %d, %d)", $form_values['category'], $form_values['recipients'], $form_values['reply'], $form_values['weight'], $form_values['selected']);
- drupal_set_message(t('Category %category has been added.', array('%category' => theme('placeholder', $form_values['category']))));
- watchdog('mail', t('Contact form: category %category added.', array('%category' => theme('placeholder', $form_values['category']))), WATCHDOG_NOTICE, l(t('view'), 'admin/contact'));
-
- }
- else {
- db_query("UPDATE {contact} SET category = '%s', recipients = '%s', reply = '%s', weight = %d, selected = %d WHERE cid = %d", $form_values['category'], $form_values['recipients'], $form_values['reply'], $form_values['weight'], $form_values['selected'], $form_values['cid']);
- drupal_set_message(t('Category %category has been updated.', array('%category' => theme('placeholder', $form_values['category']))));
- watchdog('mail', t('Contact form: category %category updated.', array('%category' => theme('placeholder', $form_values['category']))), WATCHDOG_NOTICE, l(t('view'), 'admin/contact'));
- }
-
- return 'admin/contact';
-}
-
-/**
- * Category delete page.
- */
-function contact_admin_delete($cid = NULL) {
- if ($info = db_fetch_object(db_query("SELECT category FROM {contact} WHERE cid = %d", $cid))) {
- $form['category'] = array('#type' => 'value',
- '#value' => $info->category,
- );
-
- return confirm_form('contact_admin_delete', $form, t('Are you sure you want to delete %category?', array('%category' => theme('placeholder', $info->category))), 'admin/contact', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
- }
- else {
- drupal_set_message(t('Category not found.'), 'error');
- drupal_goto('admin/contact');
- }
-}
-
-/**
- * Process category delete form submission.
- */
-function contact_admin_delete_submit($form_id, $form_values) {
- db_query("DELETE FROM {contact} WHERE cid = %d", arg(4));
- drupal_set_message(t('Category %category has been deleted.', array('%category' => theme('placeholder', $form_values['category']))));
- watchdog('mail', t('Contact form: category %category deleted.', array('%category' => theme('placeholder', $form_values['category']))), WATCHDOG_NOTICE);
-
- return 'admin/contact';
-}
-
-/**
- * Settings tab. Using a form rather than hook_settings().
- */
-function contact_admin_settings() {
- $form['contact_form_information'] = array('#type' => 'textarea',
- '#title' => t('Additional information'),
- '#default_value' => variable_get('contact_form_information', t('You can leave a message using the contact form below.')),
- '#description' => t('Information to show on the <a href="%form">contact page</a>. Can be anything from submission guidelines to your postal address or telephone number.', array('%form' => url('contact'))),
- );
- $form['contact_hourly_threshold'] = array('#type' => 'select',
- '#title' => t('Hourly threshold'),
- '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50)),
- '#default_value' => variable_get('contact_hourly_threshold', 3),
- '#description' => t('The maximum number of contact form submissions a user can perform per hour.'),
- );
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Save configuration'),
- );
- $form['reset'] = array('#type' => 'submit',
- '#value' => t('Reset to defaults'),
- );
- // Use system_settings_form for the callback.
- return drupal_get_form('contact_admin_settings', $form, 'system_settings_form');
-}
-
-/**
- * Personal contact page.
- */
-function contact_mail_user() {
- global $user;
-
- if ($account = user_load(array('uid' => arg(1)))) {
- $admin_access = user_access('administer users');
- if (!$account->status && !$admin_access) {
- drupal_access_denied();
- }
- else if (!$account->contact && !$admin_access) {
- $output = t('%name is not accepting e-mails.', array('%name' => $account->name));
- }
- else if (!$user->uid) {
- $output = t('Please <a href="%login">login</a> or <a href="%register">register</a> to send %name a message.', array('%login' => url('user/login'), '%register' => url('user/register'), '%name' => $account->name));
- }
- else if (!valid_email_address($user->mail)) {
- $output = t('You need to provide a valid e-mail address to contact other users. Please update your <a href="%url">user information</a> and try again.', array('%url' => url("user/$user->uid/edit")));
- }
- else if (!flood_is_allowed('contact', variable_get('contact_hourly_threshold', 3))) {
- $output = t('You cannot contact more than %number users per hour. Please try again later.', array('%number' => variable_get('contact_hourly_threshold', 3)));
- }
- else {
- drupal_set_title($account->name);
-
- $form['#token'] = $user->name . $user->mail;
- $form['from'] = array('#type' => 'item',
- '#title' => t('From'),
- '#value' => $user->name .' &lt;'. $user->mail .'&gt;',
- );
- $form['to'] = array('#type' => 'item',
- '#title' => t('To'),
- '#value' => $account->name,
- );
- $form['subject'] = array('#type' => 'textfield',
- '#title' => t('Subject'),
- '#maxlength' => 50,
- '#required' => TRUE,
- );
- $form['message'] = array('#type' => 'textarea',
- '#title' => t('Message'),
- '#rows' => 15,
- '#required' => TRUE,
- );
- $form['copy'] = array('#type' => 'checkbox',
- '#title' => t('Send me a copy.'),
- );
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Send e-mail'),
- );
- $output = drupal_get_form('contact_mail_user', $form);
- }
-
- return $output;
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Process the personal contact page form submission.
- */
-function contact_mail_user_submit($form_id, $edit) {
- global $user;
-
- $account = user_load(array('uid' => arg(1), 'status' => 1));
- // Compose the body:
- $message[] = "$account->name,";
- $message[] = t("%name (%name-url) has sent you a message via your contact form (%form-url) at %site.", array('%name' => $user->name, '%name-url' => url("user/$user->uid", NULL, NULL, TRUE), '%form-url' => url($_GET['q'], NULL, NULL, TRUE), '%site' => variable_get('site_name', 'drupal')));
- $message[] = t("If you don't want to receive such e-mails, you can change your settings at %url.", array('%url' => url("user/$account->uid", NULL, NULL, TRUE)));
- $message[] = t('Message:');
- $message[] = $edit['message'];
-
- // Tidy up the body:
- foreach ($message as $key => $value) {
- $message[$key] = wordwrap($value);
- }
-
- // Prepare all fields:
- $to = $account->mail;
- $from = $user->mail;
-
- // Format the subject:
- $subject = '['. variable_get('site_name', 'drupal') .'] '. $edit['subject'];
-
- // Prepare the body:
- $body = implode("\n\n", $message);
-
- // Send the e-mail:
- user_mail($to, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
-
- // Send a copy if requested:
- if ($edit['copy']) {
- user_mail($from, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
- }
-
- // Log the operation:
- flood_register_event('contact');
- watchdog('mail', t('%name-from sent %name-to an e-mail.', array('%name-from' => theme('placeholder', $user->name), '%name-to' => theme('placeholder', $account->name))));
-
- // Set a status message:
- drupal_set_message(t('The message has been sent.'));
-
- // Jump to the user's profile page:
- return "user/$account->uid";
-}
-
-/**
- * Site-wide contact page
- */
-function contact_mail_page() {
- global $user;
-
- $breadcrumb[] = array('path' => 'contact', 'title' => t('contact'));
- menu_set_location($breadcrumb);
-
- if (!flood_is_allowed('contact', variable_get('contact_hourly_threshold', 3))) {
- $output = t("You cannot send more than %number messages per hour. Please try again later.", array('%number' => variable_get('contact_hourly_threshold', 3)));
- }
- else {
- if ($user->uid) {
- $edit['name'] = $user->name;
- $edit['mail'] = $user->mail;
- }
-
- $result = db_query('SELECT cid, category, selected FROM {contact} ORDER BY weight, category');
- while ($category = db_fetch_object($result)) {
- $categories[$category->cid] = $category->category;
- if ($category->selected) {
- $default_category = $category->cid;
- }
- }
-
- if (count($categories) > 0) {
- $form['#token'] = $user->name . $user->mail;
- $form['contact_information'] = array('#value' => variable_get('contact_form_information', t('You can leave us a message using the contact form below.')));
- $form['name'] = array('#type' => 'textfield',
- '#title' => t('Your name'),
- '#maxlength' => 255,
- '#default_value' => $edit['name'],
- '#required' => TRUE,
- );
- $form['mail'] = array('#type' => 'textfield',
- '#title' => t('Your e-mail address'),
- '#maxlength' => 255,
- '#default_value' => $edit['mail'],
- '#required' => TRUE,
- );
- $form['subject'] = array('#type' => 'textfield',
- '#title' => t('Subject'),
- '#maxlength' => 255,
- '#required' => TRUE,
- );
- if (count($categories) > 1) {
- // If there is more than one category available and no default category has been selected,
- // prepend a default placeholder value.
- if (!isset($default_category)) {
- $categories = array(t('--')) + $categories;
- }
- $form['cid'] = array('#type' => 'select',
- '#title' => t('Category'),
- '#default_value' => $default_category,
- '#options' => $categories,
- '#required' => TRUE,
- );
- }
- else {
- // If there is only one category, store its cid.
- $form['cid'] = array('#type' => 'value',
- '#value' => array_shift(array_keys($categories)),
- );
- }
- $form['message'] = array('#type' => 'textarea',
- '#title' => t('Message'),
- '#required' => TRUE,
- );
- $form['copy'] = array('#type' => 'checkbox',
- '#title' => t('Send me a copy.'),
- );
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Send e-mail'),
- );
- $output = drupal_get_form('contact_mail_page', $form);
- }
- else {
- $output = t('The contact form has not been configured.');
- }
- }
-
- return $output;
-}
-
-/**
- * Validate the site-wide contact page form submission.
- */
-function contact_mail_page_validate($form_id, $form_values) {
- if (!$form_values['cid']) {
- form_set_error('category', t('You must select a valid category.'));
- }
- if (!valid_email_address($form_values['mail'])) {
- form_set_error('mail', t('You must enter a valid e-mail address.'));
- }
-}
-
-/**
- * Process the site-wide contact page form submission.
- */
-function contact_mail_page_submit($form_id, $edit) {
-
- // E-mail address of the sender: as the form field is a text field,
- // all instances of \r and \n have been automatically stripped from it.
- $from = $edit['mail'];
-
- // Compose the body:
- $message[] = t("%name sent a message using the contact form at %form.", array('%name' => $edit['name'], '%form' => url($_GET['q'], NULL, NULL, TRUE)));
- $message[] = $edit['message'];
-
- // Tidy up the body:
- foreach ($message as $key => $value) {
- $message[$key] = wordwrap($value);
- }
-
- // Load the category information:
- $contact = db_fetch_object(db_query("SELECT * FROM {contact} WHERE cid = %d", $edit['cid']));
-
- // Format the category:
- $subject = t('[%category] %subject', array('%category' => $contact->category, '%subject' => $edit['subject']));
-
- // Prepare the body:
- $body = implode("\n\n", $message);
-
- // Send the e-mail to the recipients:
- user_mail($contact->recipients, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
-
- // If the user requests it, send a copy.
- if ($edit['copy']) {
- user_mail($from, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
- }
-
- // Send an auto-reply if necessary:
- if ($contact->reply) {
- user_mail($from, $subject, wordwrap($contact->reply), "From: $contact->recipients\nReply-to: $contact->recipients\nX-Mailer: Drupal\nReturn-path: $contact->recipients\nErrors-to: $contact->recipients");
- }
-
- // Log the operation:
- flood_register_event('contact');
- watchdog('mail', t('%name-from sent an e-mail regarding %category.', array('%name-from' => theme('placeholder', $edit['name'] ." <$from>"), '%category' => theme('placeholder', $contact->category))));
-
- // Update user:
- drupal_set_message(t('Your message has been sent.'));
-
- // Jump to home page rather than back to contact page to avoid contradictory messages if flood control has been activated.
- return('');
-}
diff --git a/modules/drupal/drupal.module b/modules/drupal/drupal.module
deleted file mode 100644
index e1566f0..0000000
--- a/modules/drupal/drupal.module
+++ /dev/null
@@ -1,364 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Lets users log in using a Drupal ID and can notify a central server about your site.
- */
-
-define('VERSION', '4.7.0');
-
-/**
- * Implementation of hook_help().
- */
-function drupal_help($section) {
- switch ($section) {
- case 'admin/help#drupal':
- $output = '<p>'. t('The Drupal module uses the XML-RPC network communication protocol to connect your site with a central server that maintains a directory of client sites.') .'</p>';
- $output .= t('<p>Enabling the Drupal module will allow you to:</p>
-<ul>
- <li>register your site with a server, including (optionally) posting information on your installed modules and themes and summary statistics on your number of posts and users, information that can help rank Drupal modules and themes</li>
- <li>enable other sites to register with your site</li>
- <li>allow members on all sites using the Drupal module to log in to your site without registering using their distributed identification</li>
- <li>allow members to log in to any other site that uses the Drupal module, using a login name that looks much like an e-mail address: <em>username@example.com</em></li>
-</ul>
-');
- $output .= '<p>'. t('The Drupal module administration page allows you to set the xml-rpc server page and other related options.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
- <li>run your cron job at your site\'s <a href="%file-cron">cron page</a></li>
- <li>view your <a href="%file-xmlrpc">XML-RPC page</a>.</li>
- <li>administer Drupal <a href="%admin-settings-drupal">administer &gt;&gt; settings &gt;&gt; drupal</a>.</li>
-</ul>
-', array('%file-cron' => 'cron.php', '%file-xmlrpc' => 'xmlrpc.php', '%admin-settings-drupal' => url('admin/settings/drupal')));
- $output .= '<p>'. t('If you maintain a directory of sites, you can list them on a page using the <code>drupal_client_page()</code> function. Sample instructions:
-<ul>
- <li>Enable the page module. Select create content &gt;&gt; page.</li>
- <li>For input format, select PHP code.</li>
- <li>Give the page a title. For body, put:
-<pre>
-&lt;?php
-print drupal_client_page();
-?&gt;
-</pre>
- <li>Save the page.</li>
-</ul>') . '</p>';
-
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%drupal">Drupal page</a>.', array('%drupal' => 'http://drupal.org/handbook/modules/drupal/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Lets you register your site with a central server and improve ranking of Drupal projects by posting information on your installed modules and themes; also enables users to log in using a Drupal ID.');
- case 'admin/settings/drupal':
- return t('<p>Using this your site can "call home" to another Drupal server. By calling home to drupal.org and sending a list of your installed modules and themes, you help rank projects on drupal.org and so assist all Drupal administrators to find the best components for meeting their needs. If you want to register with a different server, you can change the Drupal XML-RPC server setting -- but the server has to be able to handle Drupal XML. Some XML-RPC servers may present directories of all registered sites. To get all your site information listed, go to the <a href="%site-settings">settings page</a> and set the site name, the e-mail address, the slogan, and the mission statement.</p>', array('%site-settings' => url('admin/settings')));
- case 'user/help#drupal':
- return variable_get('drupal_authentication_service', 0) ? t("<p><a href=\"%Drupal\">Drupal</a> is the name of the software that powers %this-site. There are Drupal web sites all over the world, and many of them share their registration databases so that users may freely log in to any Drupal site using a single <strong>Drupal ID</strong>.</p>
-<p>So please feel free to log in to your account here at %this-site with a username from another Drupal site. The format of a Drupal ID is similar to an e-mail address: <strong>username</strong>@<em>server</em>. An example of a valid Drupal ID is <strong>mwlily</strong>@<em>drupal.org</em>.</p>", array('%Drupal' => 'http://drupal.org', '%this-site' => '<em>'. variable_get('site_name', 'this web site') .'</em>')) : NULL;
- }
-}
-
-/**
- * Implementation of hook_settings().
- */
-function drupal_settings() {
- // Check if all required fields are present
- if ((variable_get('site_name', 'drupal') == 'drupal') || (variable_get('site_name', 'drupal') == '')) {
- form_set_error('drupal_directory', t('You must set the name of your site on the <a href="%url">administer &raquo; settings</a> page.', array('%url' => url('admin/settings'))));
- }
- else if (variable_get('site_mail', ini_get('sendmail_from')) == '') {
- form_set_error('drupal_directory', t('You must set an e-mail address for your site on the <a href="%url">administer &raquo; settings</a> page.', array('%url' => url('admin/settings'))));
- }
- else if (variable_get('site_slogan', '') == '') {
- form_set_error('drupal_directory', t('You must set your site slogan on the <a href="%url">administer &raquo; settings</a> page.', array('%url' => url('admin/settings'))));
- }
- else if (variable_get('site_mission', '') == '') {
- form_set_error('drupal_directory', t('You must set your site mission on the <a href="%url">administer &raquo; settings</a> page.' , array('%url' => url('admin/settings'))));
- }
- $options = array('1' => t('Enabled'), '0' => t('Disabled'));
-
- $form['drupal'] = array(
- '#type' => 'fieldset',
- '#title' => t('Post data to another site'),
- '#tree' => FALSE
- );
-
- $form['drupal']['drupal_register'] = array(
- '#type' => 'radios',
- '#title' => t('Register with a Drupal server'),
- '#default_value' => variable_get('drupal_register', 0),
- '#options' => $options,
- '#description' => t("If enabled, your Drupal site will register itself with the specified Drupal XML-RPC server. For this to work properly, you must set your site's name, e-mail address, slogan and mission statement. When the \"Drupal XML-RPC server\" field is set to \"%drupal-xml-rpc\", your web site will register itself with drupal.org. Requires the cron feature to be enabled.", array("%drupal-xml-rpc" => "http://drupal.org/xmlrpc.php", "%drupal-sites" => "http://drupal.org/drupal-sites/"))
- );
-
- $form['drupal']['drupal_server'] = array(
- '#type' => 'textfield',
- '#title' => t('Drupal XML-RPC server'),
- '#default_value' => variable_get('drupal_server', 'http://drupal.org/xmlrpc.php'),
- '#description' => t('The URL of the Drupal XML-RPC server you wish to register with.')
- );
-
- $form['drupal']['drupal_system'] = array(
- '#type' => 'radios',
- '#title' => t('Send system information'),
- '#default_value' => variable_get('drupal_system', 0),
- '#options' => $options,
- '#description' => t("If enabled, your site will send information on its installed components (modules, themes, and theme engines). This information can help in compiling statistics on usage of Drupal projects.")
- );
-
- $form['drupal']['drupal_statistics'] = array(
- '#type' => 'radios',
- '#title' => t('Send statistics'),
- '#default_value' => variable_get('drupal_statistics', 0),
- '#options' => $options,
- '#description' => t("If enabled, your site will send summary statistics on the number of registered users and the total number of posts. No private information will be sent. These data help to improve the ranking statistics of Drupal projects.")
- );
-
- $form['services'] = array(
- '#type' => 'fieldset',
- '#title' => t('Receive data from other sites'),
- '#tree' => FALSE
- );
-
- $form['services']['drupal_client_service'] = array(
- '#type' => 'radios',
- '#title' => t('Allow other Drupal sites to register'),
- '#default_value' => variable_get('drupal_client_service', 0),
- '#options' => $options,
- '#description' => t('If enabled, your Drupal site will allow other sites to register with your site and send information to this site. This functionality can be used to maintain a list of related sites.')
- );
-
- $form['services']['drupal_authentication_service'] = array(
- '#type' => 'radios',
- '#title' => t('Authentication service'),
- '#default_value' => variable_get('drupal_authentication_service', 0),
- '#options' => $options,
- '#description' => t('If enabled, your Drupal site will accept logins with the user names of other Drupal sites, and likewise provide authentication for users logging into other Drupal sites, based on their user accounts here.')
- );
-
- return $form;
-}
-
-/**
- * Implementation of hook_cron(); handles pings to and from the site.
- */
-function drupal_cron() {
- if (time() - variable_get('cron_last', 0) > 21600) {
-
- // If this site acts as a Drupal XML-RPC server, delete the sites that
- // stopped sending "ping" messages.
- if (variable_get('drupal_client_service', 0)) {
- $result = db_query("SELECT cid FROM {client} WHERE changed < %d", time() - 259200);
- while ($client = db_fetch_object($result)) {
- db_query("DELETE FROM {client_system} WHERE cid = %d", $client->cid);
- db_query("DELETE FROM {client} WHERE cid = %d", $client->cid);
- }
- }
-
- // If this site acts as a Drupal XML-RPC client, send a message to the
- // Drupal XML-RPC server.
- if (variable_get('drupal_register', 0) && variable_get('drupal_server', 0)) {
- drupal_notify(variable_get('drupal_server', ''));
- }
- }
-}
-
-/**
- * Callback function from drupal_xmlrpc() called when another site pings this one.
- */
-function drupal_client_ping($client, $system) {
- /*
- ** Parse our parameters:
- */
-
- foreach (array('link', 'name', 'mail', 'slogan', 'mission') as $key) {
- $client[$key] = strip_tags($client[$key]);
- }
-
- /*
- ** Update the data in our database and send back a reply:
- */
-
- if ($client['link'] && $client['name'] && $client['mail'] && $client['slogan'] && $client['mission']) {
- $result = db_query(db_rewrite_sql("SELECT cid FROM {client} WHERE link = '%s'"), $client['link']);
- if (db_num_rows($result)) {
- $record = db_fetch_object($result);
- $client['cid'] = $record->cid;
- // We have an existing record.
- db_query("UPDATE {client} SET link = '%s', name = '%s', mail = '%s', slogan = '%s', mission = '%s', users = %d, nodes = %d, version = '%s', changed = '%s' WHERE cid = %d", $client['uid'], $client['link'], $client['name'], $client['mail'], $client['slogan'], $client['mission'], $client['users'], $client['nodes'], $client['version'], time(), $client['cid']);
- }
- else {
- $client['cid'] = db_next_id('{client}_cid');
- db_query("INSERT INTO {client} (cid, link, name, mail, slogan, mission, users, nodes, version, created, changed) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $client['cid'], $client['link'], $client['name'], $client['mail'], $client['slogan'], $client['mission'], $client['users'], $client['nodes'], $client['version'], time(), time());
- }
- if (is_array($system)) {
- db_query("DELETE FROM {client_system} WHERE cid = %d", $client['cid']);
- foreach ($system as $item) {
- db_query("INSERT INTO {client_system} (cid, name, type) VALUES (%d, '%s', '%s')", $client['cid'], $item['name'], $item['type']);
- }
- }
- watchdog('client ping', t('Ping from %name (%link).', array('%name' => theme('placeholder', $client['name']), '%link' => theme('placeholder', $client['link']))), WATCHDOG_NOTICE, '<a href="'. check_url($client['link']) .'">view</a>');
-
- return TRUE;
- }
- else {
- return 0;
- }
-
-}
-
-/**
- * Formats a list of all clients.
- *
- * This function may be called from a custom page on sites that are
- * Drupal directory servers.
- */
-function drupal_client_page($sort = 'name') {
- $result = db_query('SELECT * FROM {client} ORDER BY %s', $sort);
-
- $clients = array();
- while ($client = db_fetch_object($result)) {
- $clients[] = $client;
- }
-
- return theme('client_list', $clients);
-}
-
-/**
- * Theme a client list.
- */
-function theme_client_list($clients) {
- // Note: All fields except the mission are treated as plain-text.
- // The mission is stripped of any HTML tags to keep the output simple and consistent.
- $output = "\n<dl>\n";
- foreach ($clients as $client) {
- $output .= ' <dt><a href="' . check_url($client->link) . '">' . check_plain($client->name) . '</a> - ' . check_plain($client->slogan) . "</dt>\n";
- $output .= ' <dd>' . strip_tags($client->mission) . "</dd>\n";
- }
- $output .= "</dl>\n";
- return $output;
-}
-
-/**
- * Implementation of hook_xmlrpc().
- */
-function drupal_xmlrpc() {
- $xmlrpc = array();
- if (variable_get('drupal_client_service', 0)) {
- $xmlrpc[] = array(
- 'drupal.client.ping',
- 'drupal_client_ping',
- array('array', 'array', 'array'),
- t('Handling ping request')
- );
- }
- if (variable_get('drupal_authentication_service', 0)) {
- $xmlrpc[] = array(
- 'drupal.login',
- 'drupal_login',
- array('int', 'string', 'string'),
- t('Logging into a Drupal site')
- );
- }
- return $xmlrpc;
-}
-
-/**
- * Sends a ping to the Drupal directory server.
- */
-function drupal_notify($server) {
- global $base_url;
- $client = array(
- 'link' => $base_url,
- 'name' => variable_get('site_name', ''),
- 'mail' => variable_get('site_mail', ''),
- 'slogan' => variable_get('site_slogan', ''),
- 'mission' => variable_get('site_mission', ''),
- 'version' => VERSION
- );
- if (variable_get('drupal_system', 0)) {
- $system = array();
- $result = db_query("SELECT name, type FROM {system} WHERE status = 1");
- while ($item = db_fetch_array($result)) {
- $system[] = $item;
- }
- }
- if (variable_get('drupal_statistics', 0)) {
- $users = db_fetch_object(db_query("SELECT COUNT(uid) AS count FROM {users}"));
- $client['users'] = $users->count;
- $nodes = db_fetch_object(db_query("SELECT COUNT(nid) AS count FROM {node}"));
- $client['nodes'] = $nodes->count;
- }
- $result = xmlrpc($server, 'drupal.client.ping', $client, $system);
-
- if ($result === FALSE) {
- watchdog('server ping', t('Failed to notify %server; error code: %errno; error message: %error_msg.', array('%server' => theme('placeholder', $server), '%errno' => theme('placeholder', xmlrpc_errno()), '%error_msg' => theme('placeholder', xmlrpc_error_msg()))), WATCHDOG_WARNING);
- }
-}
-
-/**
- * Implementation of hook_info().
- */
-function drupal_info($field = 0) {
- $info['name'] = 'Drupal';
- $info['protocol'] = 'XML-RPC';
-
- if ($field) {
- return $info[$field];
- }
- else {
- return $info;
- }
-}
-
-/**
- * Implementation of hook_auth().
- */
-function drupal_auth($username, $password, $server) {
- if (variable_get('drupal_authentication_service', 0)) {
- $result = xmlrpc("http://$server/xmlrpc.php", 'drupal.login', $username, $password);
- if ($result === FALSE) {
- drupal_set_message(t('Error %code : %message', array('%code' => theme('placeholder', xmlrpc_errno()), '%message' => theme('placeholder', xmlrpc_error_msg()))), 'error');
- }
- else {
- return $result;
- }
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function drupal_menu($may_cache) {
- $items = array();
- if ($may_cache) {
- $items[] = array('path' => 'drupal', 'title' => t('Drupal'),
- 'callback' => 'drupal_page_help', 'access' => TRUE,
- 'type' => MENU_SUGGESTED_ITEM
- );
- }
- return $items;
-}
-
-/**
- * Menu callback; print Drupal-authentication-specific information from user/help.
- */
-function drupal_page_help() {
- return drupal_help('user/help#drupal');
-}
-
-/**
- * Callback function from drupal_xmlrpc() for authenticating remote clients.
- *
- * Remote clients are usually other Drupal instances.
- */
-function drupal_login($username, $password) {
- if (variable_get('drupal_authentication_service', 0)) {
- if ($user = user_load(array('name' => $username, 'pass' => $password, 'status' => 1))) {
- return $user->uid;
- }
- else {
- return 0;
- }
- }
-}
-
-
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
deleted file mode 100644
index 929007b..0000000
--- a/modules/filter/filter.module
+++ /dev/null
@@ -1,1347 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Framework for handling filtering of content.
- */
-
-// This is a special format ID which means "use the default format". This value
-// can be passed to the filter APIs as a format ID: this is equivalent to not
-// passing an explicit format at all.
-define('FILTER_FORMAT_DEFAULT', 0);
-
-define('FILTER_HTML_STRIP', 1);
-define('FILTER_HTML_ESCAPE', 2);
-
-/**
- * Implementation of hook_help().
- */
-function filter_help($section) {
- switch ($section) {
- case 'admin/help#filter':
- $output = '<p>'. t('The filter module allows administrators to configure text input formats for the site. For example, an administrator may want a filter to strip out malicious HTML from user\'s comments. Administrators may also want to make URLs linkable even if they are only entered in an unlinked format.') .'</p>';
- $output .= '<p>'. t('Users can choose between the available input formats when creating or editing content. Administrators can configure which input formats are available to which user roles, as well as choose a default input format. Administrators can also create new input formats. Each input format can be configured to use a selection of filters.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>administer input format permissions and settings at <a href="%admin-filters">administer &gt;&gt; input formats</a>.</li>
-<li>configure the filters for each input format at <a href="%admin-filters">administer &gt;&gt; input formats &gt;&gt; configure</a>.</li>
-</ul>
-', array('%admin-filters' => url('admin/filters')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%filter">Filter page</a>.', array('%filter' => 'http://drupal.org/handbook/modules/filter/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Handles the filtering of content in preparation for display.');
-
- case 'admin/filters':
- return t('
-<p><em>Input formats</em> define a way of processing user-supplied text in Drupal. Every input format has its own settings of which <em>filters</em> to apply. Possible filters include stripping out malicious HTML and making URLs clickable.</p>
-<p>Users can choose between the available input formats when submitting content.</p>
-<p>Below you can configure which input formats are available to which roles, as well as choose a default input format (used for imported content, for example).</p>
-<p>Note that (1) the default format is always available to all roles, and (2) all filter formats can always be used by roles with the "administer filters" permission even if they are not explicitly listed in the Roles column of this table.</p>');
-
- case 'admin/filters/'. arg(2):
- return t('
-<p>Every <em>filter</em> performs one particular change on the user input, for example stripping out malicious HTML or making URLs clickable. Choose which filters you want to apply to text in this input format.</p>
-<p>If you notice some filters are causing conflicts in the output, you can <a href="%rearrange">rearrange them</a>.</p>', array('%rearrange' => url('admin/filters/'. arg(2) .'/order')));
-
- case 'admin/filters/'. arg(2) .'/configure':
- return t('
-<p>If you cannot find the settings for a certain filter, make sure you\'ve enabled it on the <a href="%url">view tab</a> first.</p>', array('%url' => url('admin/filters/'. arg(2))));
-
- case 'admin/filters/'. arg(2) .'/order':
- return t('
-<p>Because of the flexible filtering system, you might encounter a situation where one filter prevents another from doing its job. For example: a word in an URL gets converted into a glossary term, before the URL can be converted in a clickable link. When this happens, you will need to rearrange the order in which filters get executed.</p>
-<p>Filters are executed from top-to-bottom. You can use the weight column to rearrange them: heavier filters \'sink\' to the bottom.</p>');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function filter_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/filters',
- 'title' => t('input formats'),
- 'callback' => 'filter_admin_overview',
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/list',
- 'title' => t('list'),
- 'callback' => 'filter_admin_overview',
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/add',
- 'title' => t('add input format'),
- 'callback' => 'filter_admin_format_form',
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/delete',
- 'title' => t('delete input format'),
- 'callback' => 'filter_admin_delete',
- 'type' => MENU_CALLBACK,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'filter/tips',
- 'title' => t('compose tips'),
- 'callback' => 'filter_tips_long',
- 'access' => TRUE,
- 'type' => MENU_SUGGESTED_ITEM,
- );
- }
- else {
- if (arg(0) == 'admin' && arg(1) == 'filters' && is_numeric(arg(2))) {
- $formats = filter_formats();
-
- if (isset($formats[arg(2)])) {
- $items[] = array('path' => 'admin/filters/'. arg(2),
- 'title' => t("'%format' input format", array('%format' => $formats[arg(2)]->name)),
- 'callback' => 'filter_admin_format_form',
- 'callback arguments' => array('format' => $formats[arg(2)]),
- 'type' => MENU_CALLBACK,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/'. arg(2) .'/list',
- 'title' => t('view'),
- 'callback' => 'filter_admin_format_form',
- 'callback arguments' => array('format' => $formats[arg(2)]),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => 0,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/'. arg(2) .'/configure',
- 'title' => t('configure'),
- 'callback' => 'filter_admin_configure',
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 1,
- 'access' => user_access('administer filters'),
- );
- $items[] = array('path' => 'admin/filters/'. arg(2) .'/order',
- 'title' => t('rearrange'),
- 'callback' => 'filter_admin_order',
- 'callback arguments' => array('format' => $formats[arg(2)]),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 2,
- 'access' => user_access('administer filters'),
- );
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function filter_perm() {
- return array('administer filters');
-}
-
-/**
- * Implementation of hook_filter_tips().
- */
-function filter_filter_tips($delta, $format, $long = false) {
- global $base_url;
- switch ($delta) {
- case 0:
- if (variable_get("filter_html_$format", FILTER_HTML_STRIP) == FILTER_HTML_STRIP) {
- if ($allowed_html = variable_get("allowed_html_$format", '<a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>')) {
- switch ($long) {
- case 0:
- return t('Allowed HTML tags') .': '. check_plain($allowed_html);
- case 1:
- $output = '<p>'. t('Allowed HTML tags') .': '. check_plain($allowed_html) .'</p>';
- if (!variable_get("filter_html_help_$format", 1)) {
- return $output;
- }
-
- $output .= t('
-<p>This site allows HTML content. While learning all of HTML may feel intimidating, learning how to use a very small number of the most basic HTML "tags" is very easy. This table provides examples for each tag that is enabled on this site.</p>
-<p>For more information see W3C\'s <a href="http://www.w3.org/TR/html/">HTML Specifications</a> or use your favorite search engine to find other sites that explain HTML.</p>');
- $tips = array(
- 'a' => array( t('Anchors are used to make links to other pages.'), '<a href="'. $base_url .'">'. variable_get('site_name', 'drupal') .'</a>'),
- 'br' => array( t('By default line break tags are automatically added, so use this tag to add additional ones. Use of this tag is different because it is not used with an open/close pair like all the others. Use the extra " /" inside the tag to maintain XHTML 1.0 compatibility'), t('Text with <br />line break')),
- 'p' => array( t('By default paragraph tags are automatically added, so use this tag to add additional ones.'), '<p>'. t('Paragraph one.') .'</p> <p>'. t('Paragraph two.') .'</p>'),
- 'strong' => array( t('Strong'), '<strong>'. t('Strong'). '</strong>'),
- 'em' => array( t('Emphasized'), '<em>'. t('Emphasized') .'</em>'),
- 'cite' => array( t('Cited'), '<cite>'. t('Cited') .'</cite>'),
- 'code' => array( t('Coded text used to show programming source code'), '<code>'. t('Coded') .'</code>'),
- 'b' => array( t('Bolded'), '<b>'. t('Bolded') .'</b>'),
- 'u' => array( t('Underlined'), '<u>'. t('Underlined') .'</u>'),
- 'i' => array( t('Italicized'), '<i>'. t('Italicized') .'</i>'),
- 'sup' => array( t('Superscripted'), t('<sup>Super</sup>scripted')),
- 'sub' => array( t('Subscripted'), t('<sub>Sub</sub>scripted')),
- 'pre' => array( t('Preformatted'), '<pre>'. t('Preformatted') .'</pre>'),
- 'blockquote' => array( t('Block quoted'), '<blockquote>'. t('Block quoted') .'</blockquote>'),
- 'q' => array( t('Quoted inline'), '<q>'. t('Quoted inline') .'</q>'),
- // Assumes and describes tr, td, th.
- 'table' => array( t('Table'), '<table> <tr><th>'. t('Table header') .'</th></tr> <tr><td>'. t('Table cell') .'</td></tr> </table>'),
- 'tr' => NULL, 'td' => NULL, 'th' => NULL,
- 'del' => array( t('Deleted'), '<del>'. t('Deleted') .'</del>'),
- 'ins' => array( t('Inserted'), '<ins>'. t('Inserted') .'</ins>'),
- // Assumes and describes li.
- 'ol' => array( t('Ordered list - use the &lt;li&gt; to begin each list item'), '<ol> <li>'. t('First item') .'</li> <li>'. t('Second item') .'</li> </ol>'),
- 'ul' => array( t('Unordered list - use the &lt;li&gt; to begin each list item'), '<ul> <li>'. t('First item') .'</li> <li>'. t('Second item') .'</li> </ul>'),
- 'li' => NULL,
- // Assumes and describes dt and dd.
- 'dl' => array( t('Definition lists are similar to other HTML lists. &lt;dl&gt; begins the definition list, &lt;dt&gt; begins the definition term and &lt;dd&gt; begins the definition description.'), '<dl> <dt>'. t('First term') .'</dt> <dd>'. t('First definition') .'</dd> <dt>'. t('Second term') .'</dt> <dd>'. t('Second definition') .'</dd> </dl>'),
- 'dt' => NULL, 'dd' => NULL,
- 'h1' => array( t('Header'), '<h1>'. t('Title') .'</h1>'),
- 'h2' => array( t('Header'), '<h2>'. t('Subtitle') .'</h2>'),
- 'h3' => array( t('Header'), '<h3>'. t('Subtitle three') .'</h3>'),
- 'h4' => array( t('Header'), '<h4>'. t('Subtitle four') .'</h4>'),
- 'h5' => array( t('Header'), '<h5>'. t('Subtitle five') .'</h5>'),
- 'h6' => array( t('Header'), '<h6>'. t('Subtitle six') .'</h6>')
- );
- $header = array(t('Tag Description'), t('You Type'), t('You Get'));
- preg_match_all('/<([a-z0-9]+)[^a-z0-9]/i', $allowed_html, $out);
- foreach ($out[1] as $tag) {
- if (array_key_exists($tag, $tips)) {
- if ($tips[$tag]) {
- $rows[] = array(
- array('data' => $tips[$tag][0], 'class' => 'description'),
- array('data' => '<code>'. check_plain($tips[$tag][1]) .'</code>', 'class' => 'type'),
- array('data' => $tips[$tag][1], 'class' => 'get')
- );
- }
- }
- else {
- $rows[] = array(
- array('data' => t('No help provided for tag %tag.', array('%tag' => check_plain($tag))), 'class' => 'description', 'colspan' => 3),
- );
- }
- }
- $output .= theme('table', $header, $rows);
-
- $output .= t('
-<p>Most unusual characters can be directly entered without any problems.</p>
-<p>If you do encounter problems, try using HTML character entities. A common example looks like &amp;amp; for an ampersand &amp; character. For a full list of entities see HTML\'s <a href="http://www.w3.org/TR/html4/sgml/entities.html">entities</a> page. Some of the available characters include:</p>');
- $entities = array(
- array( t('Ampersand'), '&amp;'),
- array( t('Greater than'), '&gt;'),
- array( t('Less than'), '&lt;'),
- array( t('Quotation mark'), '&quot;'),
- );
- $header = array(t('Character Description'), t('You Type'), t('You Get'));
- unset($rows);
- foreach ($entities as $entity) {
- $rows[] = array(
- array('data' => $entity[0], 'class' => 'description'),
- array('data' => '<code>'. check_plain($entity[1]) .'</code>', 'class' => 'type'),
- array('data' => $entity[1], 'class' => 'get')
- );
- }
- $output .= theme('table', $header, $rows);
- return $output;
- }
- }
- else {
- return t('No HTML tags allowed');
- }
- }
- break;
-
- case 1:
- switch ($long) {
- case 0:
- return t('You may post PHP code. You should include &lt;?php ?&gt; tags.');
- case 1:
- return t('
-<h4>Using custom PHP code</h4>
-<p>If you know how to script in PHP, Drupal gives you the power to embed any script you like. It will be executed when the page is viewed and dynamically embedded into the page. This gives you amazing flexibility and power, but of course with that comes danger and insecurity if you don\'t write good code. If you are not familiar with PHP, SQL or with the site engine, avoid experimenting with PHP because you can corrupt your database or render your site insecure or even unusable! If you don\'t plan to do fancy stuff with your content then you\'re probably better off with straight HTML.</p>
-<p>Remember that the code within each PHP item must be valid PHP code - including things like correctly terminating statements with a semicolon. It is highly recommended that you develop your code separately using a simple test script on top of a test database before migrating to your production environment.</p>
-<p>Notes:</p><ul><li>You can use global variables, such as configuration parameters, within the scope of your PHP code but remember that global variables which have been given values in your code will retain these values in the engine afterwards.</li><li>register_globals is now set to <strong>off</strong> by default. If you need form information you need to get it from the "superglobals" $_POST, $_GET, etc.</li><li>You can either use the <code>print</code> or <code>return</code> statement to output the actual content for your item.</li></ul>
-<p>A basic example:</p>
-<blockquote><p>You want to have a box with the title "Welcome" that you use to greet your visitors. The content for this box could be created by going:</p>
-<pre>
- print t("Welcome visitor, ... welcome message goes here ...");
-</pre>
-<p>If we are however dealing with a registered user, we can customize the message by using:</p>
-<pre>
- global $user;
- if ($user->uid) {
- print t("Welcome $user->name, ... welcome message goes here ...");
- }
- else {
- print t("Welcome visitor, ... welcome message goes here ...");
- }
-</pre></blockquote>
-<p>For more in-depth examples, we recommend that you check the existing Drupal code and use it as a starting point, especially for sidebar boxes.</p>');
- }
-
- case 2:
- switch ($long) {
- case 0:
- return t('Lines and paragraphs break automatically.');
- case 1:
- return t('Lines and paragraphs are automatically recognized. The &lt;br /&gt; line break, &lt;p&gt; paragraph and &lt;/p&gt; close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
- }
- }
-}
-
-/**
- * Displays a list of all input formats and which one is the default
- */
-function filter_admin_overview() {
-
- // Overview of all formats.
- $formats = filter_formats();
- $error = false;
-
- $rows = array();
- foreach ($formats as $id => $format) {
- $roles = array();
- foreach (user_roles() as $rid => $name) {
- // Prepare a roles array with roles that may access the filter
- if (strstr($format->roles, ",$rid,")) {
- $roles[] = $name;
- }
- }
- $row = array();
- $default = ($id == variable_get('filter_default_format', 1));
- $options[$id] = '';
- $form[$format->name]['id'] = array('#value' => $id);
- $form[$format->name]['roles'] = array('#value' => $default ? t('All roles may use default format') : ($roles ? implode(', ',$roles) : t('No roles may use this format')));
- $form[$format->name]['configure'] = array('#value' => l(t('configure'), 'admin/filters/'. $id));
- $form[$format->name]['delete'] = array('#value' => $default ? '' : l(t('delete'), 'admin/filters/delete/'. $id));
- }
- $form['default'] = array('#type' => 'radios', '#options' => $options, '#default_value' => variable_get('filter_default_format', 1));
- $form['submit'] = array('#type' => 'submit', '#value' => t('Set default format'));
- return drupal_get_form('filter_admin_overview', $form);
-}
-
-function filter_admin_overview_submit($form_id, $form_values) {
- // Process form submission to set the default format
- if (is_numeric($form_values['default'])) {
- drupal_set_message(t('Default format updated.'));
- variable_set('filter_default_format', $form_values['default']);
- }
-}
-
-function theme_filter_admin_overview($form) {
- foreach ($form as $name => $element) {
- if (isset($element['roles']) && is_array($element['roles'])) {
- $rows[] = array(
- form_render($form['default'][$element['id']['#value']]),
- check_plain($name),
- form_render($element['roles']),
- form_render($element['configure']),
- form_render($element['delete'])
- );
- unset($form[$name]);
- }
- }
- $header = array(t('Default'), t('Name'), t('Roles'), array('data' => t('Operations'), 'colspan' => 2));
- $output = theme('table', $header, $rows);
- $output .= form_render($form);
-
- return $output;
-}
-
-/**
- * Menu callback; confirm deletion of a format.
- */
-function filter_admin_delete() {
- $format = arg(3);
- $format = db_fetch_object(db_query('SELECT * FROM {filter_formats} WHERE format = %d', $format));
-
- if ($format) {
- if ($format->format != variable_get('filter_default_format', 1)) {
- $form['format'] = array('#type' => 'hidden', '#value' => $format->format);
- $form['name'] = array('#type' => 'hidden', '#value' => $format->name);
-
- return confirm_form('filter_admin_delete', $form, t('Are you sure you want to delete the input format %format?', array('%format' => theme('placeholder', $format->name))), 'admin/filters', t('If you have any content left in this input format, it will be switched to the default input format. This action cannot be undone.'), t('Delete'), t('Cancel'));
- }
- else {
- drupal_set_message('The default format cannot be deleted.');
- drupal_goto('admin/filters');
- }
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Process filter delete form submission.
- */
-function filter_admin_delete_submit($form_id, $form_values) {
- db_query("DELETE FROM {filter_formats} WHERE format = %d", $form_values['format']);
- db_query("DELETE FROM {filters} WHERE format = %d", $form_values['format']);
-
- $default = variable_get('filter_default_format', 1);
- // Replace existing instances of the deleted format with the default format.
- db_query("UPDATE {node_revisions} SET format = %d WHERE format = %d", $default, $form_values['format']);
- db_query("UPDATE {comments} SET format = %d WHERE format = %d", $default, $form_values['format']);
- db_query("UPDATE {boxes} SET format = %d WHERE format = %d", $default, $form_values['format']);
-
- cache_clear_all('filter:'. $form_values['format'], true);
- drupal_set_message(t('Deleted input format %format.', array('%format' => theme('placeholder', $form_values['name']))));
-
- return 'admin/filters';
-}
-
-/**
- * Generate a filter format form.
- */
-function filter_admin_format_form($format = NULL) {
- $default = ($format->format == variable_get('filter_default_format', 1));
- if ($default) {
- $help = t('All roles for the default format must be enabled and cannot be changed.');
- $form['default_format'] = array('#type' => 'hidden', '#value' => 1);
- }
-
- $form['name'] = array('#type' => 'textfield',
- '#title' => 'Name',
- '#default_value' => $format->name,
- '#description' => t('Specify a unique name for this filter format.'),
- '#required' => TRUE,
- );
-
- // Add a row of checkboxes for form group.
- $form['roles'] = array('#type' => 'fieldset',
- '#title' => t('Roles'),
- '#description' => $default ? $help : t('Choose which roles may use this filter format. Note that roles with the "administer filters" permission can always use all the filter formats.'),
- '#tree' => TRUE,
- );
-
- foreach (user_roles() as $rid => $name) {
- $checked = strstr($format->roles, ",$rid,");
- $form['roles'][$rid] = array('#type' => 'checkbox',
- '#title' => $name,
- '#default_value' => ($default || $checked),
- );
- if ($default) {
- $form['roles'][$rid]['#attributes'] = array('disabled' => 'disabled');
- }
- }
- // Table with filters
- $all = filter_list_all();
- $enabled = filter_list_format($format->format);
-
- $form['filters'] = array('#type' => 'fieldset',
- '#title' => t('Filters'),
- '#description' => t('Choose the filters that will be used in this filter format.'),
- '#tree' => TRUE,
- );
- foreach ($all as $id => $filter) {
- $form['filters'][$id] = array('#type' => 'checkbox',
- '#title' => $filter->name,
- '#default_value' => isset($enabled[$id]),
- '#description' => module_invoke($filter->module, 'filter', 'description', $filter->delta),
- );
- }
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
-
- if (isset($format)) {
- $form['format'] = array('#type' => 'hidden', '#value' => $format->format);
-
- // Composition tips (guidelines)
- $tips = _filter_tips($format->format, false);
- $extra = l(t('More information about formatting options'), 'filter/tips');
- $tiplist = theme('filter_tips', $tips, false, $extra);
- if (!$tiplist) {
- $tiplist = t('<p>No guidelines available.</p>');
- }
- $group = t('<p>These are the guidelines that users will see for posting in this input format. They are automatically generated from the filter settings.</p>');
- $group .= $tiplist;
- $output = '<h2>'. t('Formatting guidelines') .'</h2>'. $group;
- }
- $output = drupal_get_form('filter_admin_format_form', $form) . $output;
-
- return $output;
-}
-
-/**
- * Validate filter format form submissions.
- */
-function filter_admin_format_form_validate($form_id, $form_values) {
- if (!isset($form_values['format'])) {
- $name = trim($form_values['name']);
- $result = db_fetch_object(db_query("SELECT format FROM {filter_formats} WHERE name='%s'", $name));
- if ($result) {
- form_set_error('name', t('Filter format names need to be unique. A format named %name already exists.', array('%name' => theme('placeholder', $name))));
- }
- }
-}
-
-/**
- * Process filter format form submissions.
- */
-function filter_admin_format_form_submit($form_id, $form_values) {
- $format = isset($form_values['format']) ? $form_values['format'] : NULL;
- $current = filter_list_format($format);
- $name = trim($form_values['name']);
- $cache = TRUE;
-
- // Add a new filter format.
- if (!$format) {
- $new = TRUE;
- db_query("INSERT INTO {filter_formats} (name) VALUES ('%s')", $name);
- $result = db_fetch_object(db_query("SELECT MAX(format) AS format FROM {filter_formats}"));
- $format = $result->format;
- drupal_set_message(t('Added input format %format.', array('%format' => theme('placeholder', $name))));
- }
- else {
- drupal_set_message(t('The input format settings have been updated.'));
- }
-
- db_query("DELETE FROM {filters} WHERE format = %d", $format);
- foreach ($form_values['filters'] as $id => $checked) {
- if ($checked) {
- list($module, $delta) = explode('/', $id);
- // Add new filters to the bottom.
- $weight = isset($current[$id]->weight) ? $current[$id]->weight : 10;
- db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, '%s', %d, %d)", $format, $module, $delta, $weight);
-
- // Check if there are any 'no cache' filters.
- $cache &= !module_invoke($module, 'filter', 'no cache', $delta);
- }
- }
-
- // We store the roles as a string for ease of use.
- // We should always set all roles to true when saving a default role.
- // We use leading and trailing comma's to allow easy substring matching.
- $roles = array();
- if (isset($form_values['roles'])) {
- foreach ($form_values['roles'] as $id => $checked) {
- if ($checked) {
- $roles[] = $id;
- }
- }
- }
- $roles = ','. implode(',', ($form_values['default_format'] ? user_roles() : $roles)) .',';
-
- db_query("UPDATE {filter_formats} SET cache = %d, name='%s', roles = '%s' WHERE format = %d", $cache, $name, $roles, $format);
-
- cache_clear_all('filter:'. $format, true);
-
- // If a new filter was added, return to the main list of filters. Otherwise, stay on edit filter page to show new changes.
- if ($new) {
- return 'admin/filters/';
- }
- else {
- return 'admin/filters/'. $format;
- }
-}
-
-/**
- * Menu callback; display form for ordering filters for a format.
- */
-function filter_admin_order($format = NULL) {
- // Get list (with forced refresh)
- $filters = filter_list_format($format->format);
-
- $form['weights'] = array('#tree' => TRUE);
- foreach ($filters as $id => $filter) {
- $form['names'][$id] = array('#value' => $filter->name);
- $form['weights'][$id] = array('#type' => 'weight', '#default_value' => $filter->weight);
- }
- $form['format'] = array('#type' => 'hidden', '#value' => $format->format);
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
-
- return drupal_get_form('filter_admin_order', $form);
-}
-
-/**
- * Theme filter order configuration form.
- */
-function theme_filter_admin_order($form) {
- $header = array(t('Name'), t('Weight'));
- $rows = array();
- foreach (element_children($form['names']) as $id) {
- // Don't take form control structures
- if (is_array($form['names'][$id])) {
- $rows[] = array(form_render($form['names'][$id]), form_render($form['weights'][$id]));
- }
- }
-
- $output = theme('table', $header, $rows);
- $output .= form_render($form);
-
- return $output;
-}
-
-/**
- * Process filter order configuration form submission.
- */
-function filter_admin_order_submit($form_id, $form_values) {
- foreach ($form_values['weights'] as $id => $weight) {
- list($module, $delta) = explode('/', $id);
- db_query("UPDATE {filters} SET weight = %d WHERE format = %d AND module = '%s' AND delta = %d", $weight, $form_values['format'], $module, $delta);
- }
- drupal_set_message(t('The filter ordering has been saved.'));
-
- cache_clear_all('filter:'. $form_values['format'], true);
-}
-
-/**
- * Menu callback; display settings defined by filters.
- */
-function filter_admin_configure() {
- $format = arg(2);
-
- $list = filter_list_format($format);
- $form = array();
- foreach ($list as $filter) {
- $form_module = module_invoke($filter->module, 'filter', 'settings', $filter->delta, $format);
- if (isset($form_module) && is_array($form_module)) {
- $form = array_merge($form, $form_module);
- }
- }
-
- if (!empty($form)) {
- $output = system_settings_form('filter_admin_configure', $form);
- }
- else {
- $output = t('No settings are available.');
- }
-
- return $output;
-}
-
-/**
- * Retrieve a list of input formats.
- */
-function filter_formats() {
- global $user;
- static $formats;
-
- // Administrators can always use all input formats.
- $all = user_access('administer filters');
-
- if (!isset($formats)) {
- $formats = array();
-
- $query = 'SELECT * FROM {filter_formats}';
-
- // Build query for selecting the format(s) based on the user's roles.
- if (!$all) {
- $where = array();
- foreach ($user->roles as $rid => $role) {
- $where[] = "roles LIKE '%%,%d,%%'";
- $args[] = $rid;
- }
- $query .= ' WHERE '. implode(' OR ', $where) . ' OR format = %d';
- $args[] = variable_get('filter_default_format', 1);
- }
-
- $result = db_query($query, $args);
- while ($format = db_fetch_object($result)) {
- $formats[$format->format] = $format;
- }
- }
- return $formats;
-}
-
-/**
- * Build a list of all filters.
- */
-function filter_list_all() {
- $filters = array();
-
- foreach (module_list() as $module) {
- $list = module_invoke($module, 'filter', 'list');
- if (isset($list) && is_array($list)) {
- foreach ($list as $delta => $name) {
- $filters[$module .'/'. $delta] = (object)array('module' => $module, 'delta' => $delta, 'name' => $name);
- }
- }
- }
-
- uasort($filters, '_filter_list_cmp');
-
- return $filters;
-}
-
-/**
- * Helper function for sorting the filter list by filter name.
- */
-function _filter_list_cmp($a, $b) {
- return strcmp($a->name, $b->name);
-}
-
-/**
- * Check if text in a certain input format is allowed to be cached.
- */
-function filter_format_allowcache($format) {
- static $cache = array();
-
- if (!isset($cache[$format])) {
- $cache[$format] = db_result(db_query('SELECT cache FROM {filter_formats} WHERE format = %d', $format));
- }
- return $cache[$format];
-}
-
-/**
- * Retrieve a list of filters for a certain format.
- */
-function filter_list_format($format) {
- static $filters = array();
-
- if (!isset($filters[$format])) {
- $filters[$format] = array();
- $result = db_query("SELECT * FROM {filters} WHERE format = %d ORDER BY weight ASC", $format);
- while ($filter = db_fetch_object($result)) {
- $list = module_invoke($filter->module, 'filter', 'list');
- if (isset($list) && is_array($list) && isset($list[$filter->delta])) {
- $filter->name = $list[$filter->delta];
- $filters[$format][$filter->module .'/'. $filter->delta] = $filter;
- }
- }
- }
-
- return $filters[$format];
-}
-
-/**
- * @name Filtering functions
- * @{
- * Modules which need to have content filtered can use these functions to
- * interact with the filter system.
- *
- * For more info, see the hook_filter() documentation.
- *
- * Note: because filters can inject JavaScript or execute PHP code, security is
- * vital here. When a user supplies a $format, you should validate it with
- * filter_access($format) before accepting/using it. This is normally done in
- * the validation stage of the node system. You should for example never make a
- * preview of content in a disallowed format.
- */
-
-/**
- * Run all the enabled filters on a piece of text.
- *
- * @param $text
- * The text to be filtered.
- * @param $format
- * The format of the text to be filtered. Specify FILTER_FORMAT_DEFAULT for
- * the default format.
- * @param $check
- * Whether to check the $format with filter_access() first. Defaults to TRUE.
- * Note that this will check the permissions of the current user, so you
- * should specify $check = FALSE when viewing other people's content. When
- * showing content that is not (yet) stored in the database (eg. upon preview),
- * set to TRUE so the user's permissions are checked.
- */
-function check_markup($text, $format = FILTER_FORMAT_DEFAULT, $check = TRUE) {
- // When $check = true, do an access check on $format.
- if (isset($text) && (!$check || filter_access($format))) {
- if ($format == FILTER_FORMAT_DEFAULT) {
- $format = variable_get('filter_default_format', 1);
- }
-
- // Check for a cached version of this piece of text.
- $id = 'filter:'. $format .':'. md5($text);
- if ($cached = cache_get($id)) {
- return $cached->data;
- }
-
- // See if caching is allowed for this format.
- $cache = filter_format_allowcache($format);
-
- // Convert all Windows and Mac newlines to a single newline,
- // so filters only need to deal with one possibility.
- $text = str_replace(array("\r\n", "\r"), "\n", $text);
-
- // Get a complete list of filters, ordered properly.
- $filters = filter_list_format($format);
-
- // Give filters the chance to escape HTML-like data such as code or formulas.
- foreach ($filters as $filter) {
- $text = module_invoke($filter->module, 'filter', 'prepare', $filter->delta, $format, $text);
- }
-
- // Perform filtering.
- foreach ($filters as $filter) {
- $text = module_invoke($filter->module, 'filter', 'process', $filter->delta, $format, $text);
- }
-
- // Store in cache with a minimum expiration time of 1 day.
- if ($cache) {
- cache_set($id, $text, time() + (60 * 60 * 24));
- }
- }
- else {
- $text = message_na();
- }
-
- return $text;
-}
-
-/**
- * Generate a selector for choosing a format in a form.
- *
- * @param $value
- * The ID of the format that is currently selected.
- * @param $weight
- * The weight of the input format.
- * @param $parents
- * Required when defining multiple input formats on a single node or having a different parent than 'format'.
- * @return
- * HTML for the form element.
- */
-function filter_form($value = FILTER_FORMAT_DEFAULT, $weight = NULL, $parents = array('format')) {
- if ($value == FILTER_FORMAT_DEFAULT) {
- $value = variable_get('filter_default_format', 1);
- }
- $formats = filter_formats();
-
- $extra = l(t('More information about formatting options'), 'filter/tips');
-
- if (count($formats) > 1) {
- $form = array(
- '#type' => 'fieldset',
- '#title' => t('Input format'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#weight' => $weight,
- '#validate' => array('filter_form_validate' => array()),
- );
- // Multiple formats available: display radio buttons with tips.
- foreach ($formats as $format) {
- $form[$format->format] = array(
- '#type' => 'radio',
- '#title' => $format->name,
- '#default_value' => $value,
- '#return_value' => $format->format,
- '#parents' => $parents,
- '#description' => theme('filter_tips', _filter_tips($format->format, false)),
- );
- }
- }
- else {
- // Only one format available: use a hidden form item and only show tips.
- $format = array_shift($formats);
- $form['format'][$format->name] = array('#type' => 'value', '#value' => $format->format);
- $tips = _filter_tips(variable_get('filter_default_format', 1), false);
- $form['format']['guidelines'] = array(
- '#type' => 'markup',
- '#title' => t('Formatting guidelines'),
- '#value' => theme('filter_tips', $tips, false, $extra),
- );
- }
- return $form;
-}
-
-function filter_form_validate($form) {
- foreach (element_children($form) as $key) {
- if ($form[$key]['#value'] == $form[$key]['#return_value']) {
- return;
- }
- }
- form_error($form, t('An illegal choice has been detected. Please contact the site administrator.'));
- watchdog('form', t('Illegal choice %choice in %name element.', array('%choice' => theme('placeholder', check_plain($v)), '%name' => theme('placeholder', empty($form['#title']) ? $form['#parents'][0] : $form['#title'])), WATCHDOG_ERROR));
-}
-
-/**
- * Returns true if the user is allowed to access this format.
- */
-function filter_access($format) {
- if (user_access('administer filters') || ($format == FILTER_FORMAT_DEFAULT) || ($format == variable_get('filter_default_format', 1))) {
- return true;
- }
- else {
- $formats = filter_formats();
- return isset($formats[$format]);
- }
-}
-/**
- * @} End of "Filtering functions".
- */
-
-/**
- * Menu callback; show a page with long filter tips.
- */
-function filter_tips_long() {
- $format = arg(2);
- if ($format) {
- $output = theme('filter_tips', _filter_tips($format, true), true);
- }
- else {
- $output = theme('filter_tips', _filter_tips(-1, true), true);
- }
- return $output;
-}
-
-/**
- * Helper function for fetching filter tips.
- */
-function _filter_tips($format, $long = false) {
- if ($format == -1) {
- $formats = filter_formats();
- }
- else {
- $formats = array(db_fetch_object(db_query("SELECT * FROM {filter_formats} WHERE format = %d", $format)));
- }
-
- $tips = array();
-
- foreach ($formats as $format) {
- $filters = filter_list_format($format->format);
-
- $tips[$format->name] = array();
- foreach ($filters as $id => $filter) {
- if ($tip = module_invoke($filter->module, 'filter_tips', $filter->delta, $format->format, $long)) {
- $tips[$format->name][] = array('tip' => $tip, 'id' => $id);
- }
- }
- }
-
- return $tips;
-}
-
-/**
- * Format a set of filter tips.
- *
- * @ingroup themeable
- */
-function theme_filter_tips($tips, $long = false, $extra = '') {
- $output = '';
-
- $multiple = count($tips) > 1;
- if ($multiple) {
- $output = t('input formats') .':';
- }
-
- if (count($tips)) {
- if ($multiple) {
- $output .= '<ul>';
- }
- foreach ($tips as $name => $tiplist) {
- if ($multiple) {
- $output .= '<li>';
- $output .= '<strong>'. $name .'</strong>:<br />';
- }
-
- $tips = '';
- foreach ($tiplist as $tip) {
- $tips .= '<li'. ($long ? ' id="filter-'. str_replace("/", "-", $tip['id']) .'">' : '>') . $tip['tip'] . '</li>';
- }
-
- if ($tips) {
- $output .= "<ul class=\"tips\">$tips</ul>";
- }
-
- if ($multiple) {
- $output .= '</li>';
- }
- }
- if ($multiple) {
- $output .= '</ul>';
- }
- }
-
- return $output;
-}
-
-/**
- * @name Standard filters
- * @{
- * Filters implemented by the filter.module.
- */
-
-/**
- * Implementation of hook_filter(). Contains a basic set of essential filters.
- * - HTML filter:
- * Validates user-supplied HTML, transforming it as necessary.
- * - PHP evaluator:
- * Executes PHP code.
- * - Line break converter:
- * Converts newlines into paragraph and break tags.
- */
-function filter_filter($op, $delta = 0, $format = -1, $text = '') {
- switch ($op) {
- case 'list':
- return array(0 => t('HTML filter'), 1 => t('PHP evaluator'), 2 => t('Line break converter'));
-
- case 'no cache':
- return $delta == 1; // No caching for the PHP evaluator.
-
- case 'description':
- switch ($delta) {
- case 0:
- return t('Allows you to restrict if users can post HTML and which tags to filter out.');
- case 1:
- return t('Runs a piece of PHP code. The usage of this filter should be restricted to administrators only!');
- case 2:
- return t('Converts line breaks into HTML (i.e. &lt;br&gt; and &lt;p&gt; tags).');
- default:
- return;
- }
-
- case 'process':
- switch ($delta) {
- case 0:
- return _filter_html($text, $format);
- case 1:
- return drupal_eval($text);
- case 2:
- return _filter_autop($text);
- default:
- return $text;
- }
-
- case 'settings':
- switch ($delta) {
- case 0:
- return _filter_html_settings($format);
- default:
- return;
- }
-
- default:
- return $text;
- }
-}
-
-/**
- * Settings for the HTML filter.
- */
-function _filter_html_settings($format) {
- $form['filter_html'] = array('#type' => 'fieldset', '#title' => t('HTML filter'), '#collapsible' => TRUE, '#collapsed' => TRUE);
- $form['filter_html']["filter_html_$format"] = array('#type' => 'radios', '#title' => t('Filter HTML tags'), '#default_value' => variable_get("filter_html_$format", FILTER_HTML_STRIP), '#options' => array(FILTER_HTML_STRIP => t('Strip disallowed tags'), FILTER_HTML_ESCAPE => t('Escape all tags')), '#description' => t('How to deal with HTML tags in user-contributed content. If set to "Strip disallowed tags", dangerous tags are removed (see below). If set to "Escape tags", all HTML is escaped and presented as it was typed.'));
- $form['filter_html']["allowed_html_$format"] = array('#type' => 'textfield', '#title' => t('Allowed HTML tags'), '#default_value' => variable_get("allowed_html_$format", '<a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>'), '#size' => 64, '#maxlength' => 255, '#description' => t('If "Strip disallowed tags" is selected, optionally specify tags which should not be stripped. JavaScript event attributes are always stripped.'));
- $form['filter_html']["filter_html_help_$format"] = array('#type' => 'checkbox', '#title' => t('Display HTML help'), '#default_value' => variable_get("filter_html_help_$format", 1), '#description' => t('If enabled, Drupal will display some basic HTML help in the long filter tips.'));
- $form['filter_html']["filter_html_nofollow_$format"] = array('#type' => 'checkbox', '#title' => t('Spam link deterrent'), '#default_value' => variable_get("filter_html_nofollow_$format", FALSE), '#description' => t('If enabled, Drupal will add rel="nofollow" to all links, as a measure to reduce the effectiveness of spam links. Note: this will also prevent valid links from being followed by search engines, therefore it is likely most effective when enabled for anonymous users.'));
- return $form;
-}
-
-/**
- * HTML filter. Provides filtering of input into accepted HTML.
- */
-function _filter_html($text, $format) {
- if (variable_get("filter_html_$format", FILTER_HTML_STRIP) == FILTER_HTML_STRIP) {
- $allowed_tags = preg_split('/\s+|<|>/', variable_get("allowed_html_$format", '<a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>'), -1, PREG_SPLIT_NO_EMPTY);
- $text = filter_xss($text, $allowed_tags);
- }
-
- if (variable_get("filter_html_$format", FILTER_HTML_STRIP) == FILTER_HTML_ESCAPE) {
- // Escape HTML
- $text = check_plain($text);
- }
-
- if (variable_get("filter_html_nofollow_$format", FALSE)) {
- $text = preg_replace('/<a([^>]+)>/i', '<a\\1 rel="nofollow">', $text);
- }
-
- return trim($text);
-}
-
-/**
- * Convert line breaks into <p> and <br> in an intelligent fashion.
- * Based on: http://photomatt.net/scripts/autop
- */
-function _filter_autop($text) {
- // Split at <pre>, <script>, <style> and </pre>, </script>, </style> tags.
- // We don't apply any processing to the contents of these tags to avoid messing
- // up code. We look for matched pairs and allow basic nesting. For example:
- // "processed <pre> ignored <script> ignored </script> ignored </pre> processed"
- $chunks = preg_split('@(</?(?:pre|script|style)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
- // Note: PHP ensures the array consists of alternating delimiters and literals
- // and begins and ends with a literal (inserting NULL as required).
- $ignore = false;
- $ignoretag = '';
- $output = '';
- foreach ($chunks as $i => $chunk) {
- if ($i % 2) {
- // Opening or closing tag?
- $open = ($chunk[1] != '/');
- list($tag) = split('[ >]', substr($chunk, 2 - $open), 2);
- if (!$ignore) {
- if ($open) {
- $ignore = true;
- $ignoretag = $tag;
- }
- }
- // Only allow a matching tag to close it.
- else if (!$open && $ignoretag == $tag) {
- $ignore = false;
- $ignoretag = '';
- }
- }
- else if (!$ignore) {
- $chunk = preg_replace('|\n*$|', '', $chunk) ."\n\n"; // just to make things a little easier, pad the end
- $chunk = preg_replace('|<br />\s*<br />|', "\n\n", $chunk);
- $chunk = preg_replace('!(<(?:table|ul|ol|li|pre|form|blockquote|h[1-6])[^>]*>)!', "\n$1", $chunk); // Space things out a little
- $chunk = preg_replace('!(</(?:table|ul|ol|li|pre|form|blockquote|h[1-6])>)!', "$1\n", $chunk); // Space things out a little
- $chunk = preg_replace("/\n\n+/", "\n\n", $chunk); // take care of duplicates
- $chunk = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $chunk); // make paragraphs, including one at the end
- $chunk = preg_replace('|<p>\s*?</p>|', '', $chunk); // under certain strange conditions it could create a P of entirely whitespace
- $chunk = preg_replace("|<p>(<li.+?)</p>|", "$1", $chunk); // problem with nested lists
- $chunk = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $chunk);
- $chunk = str_replace('</blockquote></p>', '</p></blockquote>', $chunk);
- $chunk = preg_replace('!<p>\s*(</?(?:table|tr|td|th|div|ul|ol|li|pre|select|form|blockquote|p|h[1-6])[^>]*>)!', "$1", $chunk);
- $chunk = preg_replace('!(</?(?:table|tr|td|th|div|ul|ol|li|pre|select|form|blockquote|p|h[1-6])[^>]*>)\s*</p>!', "$1", $chunk);
- $chunk = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $chunk); // make line breaks
- $chunk = preg_replace('!(</?(?:table|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|p|h[1-6])[^>]*>)\s*<br />!', "$1", $chunk);
- $chunk = preg_replace('!<br />(\s*</?(?:p|li|div|th|pre|td|ul|ol)>)!', '$1', $chunk);
- $chunk = preg_replace('/&([^#])(?![A-Za-z0-9]{1,8};)/', '&amp;$1', $chunk);
- }
- $output .= $chunk;
- }
- return $output;
-}
-
-/**
- * Filters XSS. Based on kses by Ulf Harnhammar, see
- * http://sourceforge.net/projects/kses
- *
- * For examples of various XSS attacks, see:
- * http://ha.ckers.org/xss.html
- *
- * This code does four things:
- * - Removes characters and constructs that can trick browsers
- * - Makes sure all HTML entities are well-formed
- * - Makes sure all HTML tags and attributes are well-formed
- * - Makes sure no HTML tags contain URLs with a disallowed protocol (e.g. javascript:)
- *
- * @param $string
- * The string with raw HTML in it. It will be stripped of everything that can cause
- * an XSS attack.
- * @param $allowed_tags
- * An array of allowed tags.
- * @param $format
- * The format to use.
- */
-function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
- // Store the input format
- _filter_xss_split($allowed_tags, TRUE);
- // Remove NUL characters (ignored by some browsers)
- $string = str_replace(chr(0), '', $string);
- // Remove Netscape 4 JS entities
- $string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
-
- // Defuse all HTML entities
- $string = str_replace('&', '&amp;', $string);
- // Change back only well-formed entities in our whitelist
- // Named entities
- $string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
- // Decimal numeric entities
- $string = preg_replace('/&amp;#([0-9]+;)/', '&#\1', $string);
- // Hexadecimal numeric entities
- $string = preg_replace('/&amp;#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
-
- return preg_replace_callback('%
- (
- <[^>]*.(>|$) # a string that starts with a <, up until the > or the end of the string
- | # or
- > # just a >
- )%x', '_filter_xss_split', $string);
-}
-
-/**
- * Processes an HTML tag.
- *
- * @param @m
- * An array with various meaning depending on the value of $store.
- * If $store is TRUE then the array contains the allowed tags.
- * If $store is FALSE then the array has one element, the HTML tag to process.
- * @param $store
- * Whether to store $m.
- * @return
- * If the element isn't allowed, an empty string. Otherwise, the cleaned up
- * version of the HTML element.
- */
-function _filter_xss_split($m, $store = FALSE) {
- static $allowed_html;
-
- if ($store) {
- $allowed_html = array_flip($m);
- return;
- }
-
- $string = $m[1];
-
- if (substr($string, 0, 1) != '<') {
- // We matched a lone ">" character
- return '&gt;';
- }
-
- if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches)) {
- // Seriously malformed
- return '';
- }
-
- $slash = trim($matches[1]);
- $elem = &$matches[2];
- $attrlist = &$matches[3];
-
- if (!isset($allowed_html[strtolower($elem)])) {
- // Disallowed HTML element
- return '';
- }
-
- if ($slash != '') {
- return "</$elem>";
- }
-
- // Is there a closing XHTML slash at the end of the attributes?
- // In PHP 5.1.0+ we could count the changes, currently we need a separate match
- $xhtml_slash = preg_match('%\s?/\s*$%', $attrlist) ? ' /' : '';
- $attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist);
-
- // Clean up attributes
- $attr2 = implode(' ', _filter_xss_attributes($attrlist));
- $attr2 = preg_replace('/[<>]/', '', $attr2);
- $attr2 = strlen($attr2) ? ' '. $attr2 : '';
-
- return "<$elem$attr2$xhtml_slash>";
-}
-
-/**
- * Processes a string of HTML attributes.
- *
- * @return
- * Cleaned up version of the HTML attributes.
- */
-function _filter_xss_attributes($attr) {
- $attrarr = array();
- $mode = 0;
- $attrname = '';
-
- while (strlen($attr) != 0) {
- // Was the last operation successful?
- $working = 0;
-
- switch ($mode) {
- case 0:
- // Attribute name, href for instance
- if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
- $attrname = strtolower($match[1]);
- $skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
- $working = $mode = 1;
- $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
- }
-
- break;
-
- case 1:
- // Equals sign or valueless ("selected")
- if (preg_match('/^\s*=\s*/', $attr)) {
- $working = 1; $mode = 2;
- $attr = preg_replace('/^\s*=\s*/', '', $attr);
- break;
- }
-
- if (preg_match('/^\s+/', $attr)) {
- $working = 1; $mode = 0;
- if (!$skip) {
- $attrarr[] = $attrname;
- }
- $attr = preg_replace('/^\s+/', '', $attr);
- }
-
- break;
-
- case 2:
- // Attribute value, a URL after href= for instance
- if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
- $thisval = filter_xss_bad_protocol($match[1]);
-
- if (!$skip) {
- $attrarr[] = "$attrname=\"$thisval\"";
- }
- $working = 1;
- $mode = 0;
- $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
- break;
- }
-
- if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match)) {
- $thisval = filter_xss_bad_protocol($match[1]);
-
- if (!$skip) {
- $attrarr[] = "$attrname='$thisval'";;
- }
- $working = 1; $mode = 0;
- $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
- break;
- }
-
- if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match)) {
- $thisval = filter_xss_bad_protocol($match[1]);
-
- if (!$skip) {
- $attrarr[] = "$attrname=\"$thisval\"";
- }
- $working = 1; $mode = 0;
- $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
- }
-
- break;
- }
-
- if ($working == 0) {
- // not well formed, remove and try again
- $attr = preg_replace('/
- ^
- (
- "[^"]*("|$) # - a string that starts with a double quote, up until the next double quote or the end of the string
- | # or
- \'[^\']*(\'|$)| # - a string that starts with a quote, up until the next quote or the end of the string
- | # or
- \S # - a non-whitespace character
- )* # any number of the above three
- \s* # any number of whitespaces
- /x', '', $attr);
- $mode = 0;
- }
- }
-
- // the attribute list ends with a valueless attribute like "selected"
- if ($mode == 1) {
- $attrarr[] = $attrname;
- }
- return $attrarr;
-}
-
-/**
- * Processes an HTML attribute value and ensures it does not contain an URL
- * with a disallowed protocol (e.g. javascript:)
- *
- * @param $string
- * The string with the attribute value.
- * @param $decode
- * Whether to decode entities in the $string. Set to FALSE if the $string
- * is in plain text, TRUE otherwise. Defaults to TRUE.
- * @return
- * Cleaned up and HTML-escaped version of $string.
- */
-function filter_xss_bad_protocol($string, $decode = TRUE) {
- static $allowed_protocols;
- if (!isset($allowed_protocols)) {
- $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')));
- }
-
- // Get the plain text representation of the attribute value (i.e. its meaning)
- if ($decode) {
- $string = decode_entities($string);
- }
- // Remove soft hyphen
- $string = str_replace(chr(194) . chr(173), '', $string);
- // Strip protocols
-
- do {
- $before = $string;
- $colonpos = strpos($string, ':');
- if ($colonpos > 0) {
- $protocol = substr($string, 0, $colonpos);
- if (!isset($allowed_protocols[$protocol])) {
- $string = substr($string, $colonpos + 1);
- }
- }
- } while ($before != $string);
- return check_plain($string);
-}
-
-/**
- * @} End of "Standard filters".
- */
-
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
deleted file mode 100644
index 267d3e1..0000000
--- a/modules/forum/forum.module
+++ /dev/null
@@ -1,1120 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enable threaded discussions about general topics.
- */
-
-/**
- * Implementation of hook_help().
- */
-function forum_help($section) {
- switch ($section) {
- case 'admin/help#forum':
- $output = '<p>'. t('The forum module lets you create threaded discussion forums for a particular topic on your site. This is similar to a message board system such as phpBB. Forums are very useful because they allow community members to discuss topics with one another, and they are archived for future reference.') .'</p>';
- $output .= '<p>'. t('Forums can be organized under what are called <em>containers</em>. Containers hold forums and, in turn, forums hold threaded discussions. Both containers and forums can be placed inside other containers and forums. By planning the structure of your containers and forums well, you make it easier for users to find a topic area of interest to them. Forum topics can be moved by selecting a different forum and can be left in the existing forum by selecting <em>leave a shadow copy</em>. Forum topics can also have their own URL.') .'</p>';
- $output .= '<p>'. t('Forums module <strong>requires Taxonomy and Comments module</strong> be enabled.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>administer forums at <a href="%admin-forum">administer &gt;&gt; forums</a>.</li>
-<li>enable the required comment and taxonomy modules at <a href="%admin-modules">administer &gt;&gt; modules</a>.</li>
-<li>read about the comment module at <a href="%admin-help-comment">administer &gt;&gt; help &gt;&gt; comment</a>.</li>
-<li>read about the taxonomy module at <a href="%admin-help-taxonomy">administer &gt;&gt; help &gt;&gt; taxonomy</a>.</li>
-</ul>
-', array('%admin-forum' => url('admin/forum'), '%admin-modules' => url('admin/modules'), '%admin-help-comment' => url('admin/help/comment'), '%admin-help-taxonomy' => url('admin/help/taxonomy')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%forum">Forum page</a>.', array('%forum' => 'http://drupal.org/handbook/modules/forum/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Enables threaded discussions about general topics.');
- case 'admin/forum':
- return t('<p>This is a list of existing containers and forums that you can edit. Containers hold forums and, in turn, forums hold threaded discussions. Both containers and forums can be placed inside other containers and forums. By planning the structure of your containers and forums well, you make it easier for users to find a topic area of interest to them.</p>');
- case 'admin/forum/add/container':
- return t('<p>Containers help you organize your forums. The job of a container is to hold, or contain, other forums that are related. For example, a container named "Food" might hold two forums named "Fruit" and "Vegetables".</p>');
- case 'admin/forum/add/forum':
- return t('<p>A forum holds discussion topics that are related. For example, a forum named "Fruit" might contain topics titled "Apples" and "Bananas".</p>');
- case 'admin/forum/configure':
- return t('This is where you can configure system-wide options for how your forums act and display.');
- case 'node/add#forum':
- return t('Create a new topic for discussion in the forums.');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function forum_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'node/add/forum',
- 'title' => t('forum topic'),
- 'access' => user_access('create forum topics'));
- $items[] = array('path' => 'forum',
- 'title' => t('forums'),
- 'callback' => 'forum_page',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array('path' => 'admin/forum',
- 'title' => t('forums'),
- 'callback' => 'forum_overview',
- 'access' => user_access('administer forums'),
- 'type' => MENU_NORMAL_ITEM);
- $items[] = array('path' => 'admin/forum/list',
- 'title' => t('list'),
- 'access' => user_access('administer forums'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10);
- $items[] = array('path' => 'admin/forum/add/container',
- 'title' => t('add container'),
- 'callback' => 'forum_form_container',
- 'access' => user_access('administer forums'),
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/forum/add/forum',
- 'title' => t('add forum'),
- 'callback' => 'forum_form_forum',
- 'access' => user_access('administer forums'),
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/forum/configure',
- 'title' => t('configure'),
- 'callback' => 'forum_admin_configure',
- 'access' => user_access('administer forums'),
- 'type' => MENU_LOCAL_TASK);
- }
- elseif (is_numeric(arg(4))) {
- $term = taxonomy_get_term(arg(4));
- // Check if this is a valid term.
- if ($term) {
- $items[] = array('path' => 'admin/forum/edit/container',
- 'title' => t('edit container'),
- 'callback' => 'forum_form_container',
- 'callback arguments' => array((array)$term),
- 'access' => user_access('administer forums'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/forum/edit/forum',
- 'title' => t('edit forum'),
- 'callback' => 'forum_form_forum',
- 'callback arguments' => array((array)$term),
- 'access' => user_access('administer forums'),
- 'type' => MENU_CALLBACK);
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_node_info().
- */
-function forum_node_info() {
- return array('forum' => array('name' => t('forum topic'), 'base' => 'forum'));
-}
-
-/**
- * Implementation of hook_access().
- */
-function forum_access($op, $node) {
- global $user;
-
- if ($op == 'create') {
- return user_access('create forum topics');
- }
-
- if ($op == 'update' || $op == 'delete') {
- if (user_access('edit own forum topics') && ($user->uid == $node->uid)) {
- return TRUE;
- }
- }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function forum_perm() {
- return array('create forum topics', 'edit own forum topics', 'administer forums');
-}
-
-/**
- * Implementation of hook_nodeapi().
- */
-function forum_nodeapi(&$node, $op, $teaser, $page) {
- switch ($op) {
- case 'delete revision':
- db_query('DELETE FROM {forum} WHERE vid = %d', $node->vid);
- break;
- }
-}
-
-/**
- * Implementation of hook_taxonomy().
- */
-function forum_taxonomy($op, $type, $term = NULL) {
- if ($op == 'delete' && $term->vid == _forum_get_vid()) {
- switch ($type) {
- case 'term':
- $results = db_query('SELECT f.nid FROM {forum} f WHERE f.tid = %d', $term->tid);
- while ($node = db_fetch_object($results)) {
- // node_delete will also remove any association with non-forum vocabularies.
- node_delete($node->nid);
- }
-
- // For containers, remove the tid from the forum_containers variable.
- $containers = variable_get('forum_containers', array());
- if ($key = array_search($term->tid, $containers)) {
- unset($containers[$key]);
- }
- variable_set('forum_containers', $containers);
- break;
- case 'vocabulary':
- variable_del('forum_nav_vocabulary');
- }
- }
-}
-
-/**
- * Implementation of hook_settings
- */
-function forum_admin_configure() {
-
- $form = array();
- $number = drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100, 10000));
- $form['forum_hot_topic'] = array('#type' => 'select',
- '#title' => t('Hot topic threshold'),
- '#default_value' => variable_get('forum_hot_topic', 15),
- '#options' => $number,
- '#description' => t('The number of posts a topic must have to be considered hot.'),
- );
- $number = drupal_map_assoc(array(10, 25, 50, 75, 100));
- $form['forum_per_page'] = array('#type' => 'select',
- '#title' => t('Topics per page'),
- '#default_value' => variable_get('forum_per_page', 25),
- '#options' => $number,
- '#description' => t('The default number of topics displayed per page; links to browse older messages are automatically being displayed.'),
- );
- $forder = array(1 => t('Date - newest first'), 2 => t('Date - oldest first'), 3 => t('Posts - most active first'), 4=> t('Posts - least active first'));
- $form['forum_order'] = array('#type' => 'radios',
- '#title' => t('Default order'),
- '#default_value' => variable_get('forum_order', '1'),
- '#options' => $forder,
- '#description' => t('The default display order for topics.'),
- );
-
- return system_settings_form('forum_admin_configure', $form);
-}
-
-/**
- * Implementation of hook_form_alter().
- */
-function forum_form_alter($form_id, &$form) {
- // hide critical options from forum vocabulary
- if ($form_id == 'taxonomy_form_vocabulary') {
- if ($form['vid']['#value'] == _forum_get_vid()) {
- $form['help_forum_vocab'] = array(
- '#value' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
- '#weight' => -1,
- );
- $form['nodes']['forum'] = array('#type' => 'checkbox', '#value' => 1, '#title' => t('forum topic'), '#attributes' => array('disabled' => '' ), '#description' => t('forum topic is affixed to the forum vocabulary.'));
- $form['hierarchy'] = array('#type' => 'value', '#value' => 1);
- unset($form['relations']);
- unset($form['tags']);
- unset($form['multiple']);
- $form['required'] = array('#type' => 'value', '#value' => 1);
- }
- else {
- unset($form['nodes']['forum']);
- }
- }
-}
-
-/**
- * Implementation of hook_load().
- */
-function forum_load($node) {
- $forum = db_fetch_object(db_query('SELECT * FROM {forum} WHERE vid = %d', $node->vid));
-
- return $forum;
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates a block containing the currently active forum topics and the
- * most recently added forum topics.
- */
-function forum_block($op = 'list', $delta = 0, $edit = array()) {
- switch ($op) {
- case 'list':
- $blocks[0]['info'] = t('Active forum topics');
- $blocks[1]['info'] = t('New forum topics');
- return $blocks;
-
- case 'configure':
- $form['forum_block_num_'. $delta] = array('#type' => 'select', '#title' => t('Number of topics'), '#default_value' => variable_get('forum_block_num_'. $delta, '5'), '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
- return $form;
-
- case 'save':
- variable_set('forum_block_num_'. $delta, $edit['forum_block_num_'. $delta]);
- break;
-
- case 'view':
- if (user_access('access content')) {
- switch ($delta) {
- case 0:
- $title = t('Active forum topics');
- $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 AND n.type = 'forum' ORDER BY l.last_comment_timestamp DESC");
- $result = db_query_range($sql, 0, variable_get('forum_block_num_0', '5'));
- if (db_num_rows($result)) {
- $content = node_title_list($result);
- }
- break;
-
- case 1:
- $title = t('New forum topics');
- $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.type = 'forum' AND n.status = 1 ORDER BY n.nid DESC");
- $result = db_query_range($sql, 0, variable_get('forum_block_num_1', '5'));
- if (db_num_rows($result)) {
- $content = node_title_list($result);
- }
- break;
- }
-
- if ($content) {
- $content .= '<div class="more-link">'. l(t('more'), 'forum', array('title' => t('Read the latest forum topics.'))) .'</div>';
- }
-
- $block['subject'] = $title;
- $block['content'] = $content;
-
- return $block;
- }
- }
-}
-
-/**
- * Implementation of hook_view().
- */
-function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
- if ($page) {
- $vocabulary = taxonomy_get_vocabulary(variable_get('forum_nav_vocabulary', ''));
- // Breadcrumb navigation
- $breadcrumb = array();
- $breadcrumb[] = array('path' => 'forum', 'title' => $vocabulary->name);
- if ($parents = taxonomy_get_parents_all($node->tid)) {
- $parents = array_reverse($parents);
- foreach ($parents as $p) {
- $breadcrumb[] = array('path' => 'forum/'. $p->tid, 'title' => $p->name);
- }
- }
- $breadcrumb[] = array('path' => 'node/'. $node->nid);
- menu_set_location($breadcrumb);
- }
-
- $node = node_prepare($node, $teaser);
-
- $node->body .= theme('forum_topic_navigation', $node);
-}
-
-/**
- * Implementation of hook_submit().
- *
- * Check in particular that only a "leaf" term in the associated taxonomy
- * vocabulary is selected, not a "container" term.
- */
-function forum_submit(&$node) {
- // Make sure all fields are set properly:
- $node->icon = $node->icon ? $node->icon : '';
-
- if ($node->taxonomy) {
- // Extract the node's proper topic ID.
- $vocabulary = variable_get('forum_nav_vocabulary', '');
- foreach ($node->taxonomy as $term) {
- if (db_result(db_query('SELECT COUNT(*) FROM {term_data} WHERE tid = %d AND vid = %d', $term, $vocabulary))) {
- $node->tid = $term;
- }
- }
- if ($node->tid && $node->shadow) {
- // A shadow copy needs to be created. Retain existing term and add new term.
- $terms = array_keys(taxonomy_node_get_terms($node->nid));
- if (!in_array($node->tid, $terms)) {
- $terms[] = $node->tid;
- }
- $node->taxonomy = $terms;
- }
- }
-}
-
-/**
- * Implementation of hook_validate().
- *
- * Check in particular that only a "leaf" term in the associated taxonomy
- * vocabulary is selected, not a "container" term.
- */
-function forum_validate($node) {
- if ($node->taxonomy) {
- // Extract the node's proper topic ID.
- $vocabulary = variable_get('forum_nav_vocabulary', '');
- $containers = variable_get('forum_containers', array());
- foreach ($node->taxonomy as $term) {
- if (db_result(db_query('SELECT COUNT(*) FROM {term_data} WHERE tid = %d AND vid = %d', $term, $vocabulary))) {
- if (in_array($term, $containers)) {
- $term = taxonomy_get_term($term);
- form_set_error('taxonomy', t('The item %forum is only a container for forums. Please select one of the forums below it.', array('%forum' => theme('placeholder', $term->name))));
- }
- }
- }
- }
-}
-
-/**
- * Implementation of hook_update().
- */
-function forum_update($node) {
- if ($node->is_new || $node->revision) {
- db_query("INSERT INTO {forum} (nid, vid, tid) VALUES (%d, %d, %d)", $node->nid, $node->vid, $node->tid);
- }
- else {
- db_query('UPDATE {forum} SET tid = %d WHERE vid = %d', $node->tid, $node->vid);
- }
-}
-
-/**
- * Implementation of hook_form().
- */
-function forum_form(&$node) {
- $form['title'] = array('#type' => 'textfield', '#title' => t('Subject'), '#default_value' => $node->title, '#required' => TRUE, '#weight' => -5);
-
- if ($node->nid) {
- $forum_terms = taxonomy_node_get_terms_by_vocabulary(_forum_get_vid(), $node->nid);
- // if editing, give option to leave shadows
- $shadow = (count($forum_terms) > 1);
- $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
- }
-
- $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
- $form['body_filter']['format'] = filter_form($node->format);
-
- return $form;
-}
-
-/**
- * Implementation of hook_prepare; assign forum taxonomy when adding a topic from within a forum.
- */
-function forum_prepare(&$node) {
- if (!$node->nid) {
- // new topic
- $node->taxonomy[arg(3)] = 1;
- }
-}
-
-/**
- * Implementation of hook_insert().
- */
-function forum_insert($node) {
- db_query('INSERT INTO {forum} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $node->tid);
-}
-
-/**
- * Implementation of hook_delete().
- */
-function forum_delete(&$node) {
- db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
-}
-
-/**
- * Returns a form for adding a container to the forum vocabulary
- *
- * @param $edit Associative array containing a container term to be added or edited.
- */
-function forum_form_container($edit = array()) {
- // Handle a delete operation.
- if ($_POST['op'] == t('Delete') || $_POST['edit']['confirm']) {
- return _forum_confirm_delete($edit['tid']);
- }
-
- $form['name'] = array(
- '#title' => t('Container name'),
- '#type' => 'textfield',
- '#default_value' => $edit['name'],
- '#maxlength' => 64,
- '#description' => t('The container name is used to identify related forums.'),
- '#required' => TRUE
- );
-
- $form['description'] = array(
- '#type' => 'textarea',
- '#title' => t('Description'),
- '#default_value' => $edit['description'],
- '#description' => t('The container description can give users more information about the forums it contains.')
- );
- $form['parent']['#tree'] = TRUE;
- $form['parent'][0] = _forum_parent_select($edit['tid'], t('Parent'), 'container');
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $edit['weight'],
- '#description' => t('When listing containers, those with with light (small) weights get listed before containers with heavier (larger) weights. Containers with equal weights are sorted alphabetically.')
- );
-
- $form['vid'] = array('#type' => 'hidden',
- '#value' => _forum_get_vid());
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Submit')
- );
- if ($edit['tid']) {
- $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
- $form['tid'] = array('#type' => 'value', '#value' => $edit['tid']);
- }
-
- return drupal_get_form('forum_form_container', $form, 'forum_form');
-}
-
-/**
- * Returns a form for adding a forum to the forum vocabulary
- *
- * @param $edit Associative array containing a forum term to be added or edited.
- */
-function forum_form_forum($edit = array()) {
- // Handle a delete operation.
- if ($_POST['op'] == t('Delete') || $_POST['edit']['confirm']) {
- return _forum_confirm_delete($edit['tid']);
- }
-
- $form['name'] = array('#type' => 'textfield',
- '#title' => t('Forum name'),
- '#default_value' => $edit['name'],
- '#maxlength' => 64,
- '#description' => t('The forum name is used to identify related discussions.'),
- '#required' => TRUE,
- );
- $form['description'] = array('#type' => 'textarea',
- '#title' => t('Description'),
- '#default_value' => $edit['description'],
- '#description' => t('The forum description can give users more information about the discussion topics it contains.'),
- );
- $form['parent']['#tree'] = TRUE;
- $form['parent'][0] = _forum_parent_select($edit['tid'], t('Parent'), 'forum');
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $edit['weight'],
- '#description' => t('When listing forums, those with lighter (smaller) weights get listed before containers with heavier (larger) weights. Forums with equal weights are sorted alphabetically.'),
- );
-
- $form['vid'] = array('#type' => 'hidden', '#value' => _forum_get_vid());
- $form['submit' ] = array('#type' => 'submit', '#value' => t('Submit'));
- if ($edit['tid']) {
- $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
- $form['tid'] = array('#type' => 'hidden', '#value' => $edit['tid']);
- }
-
- return drupal_get_form('forum_form_forum', $form, 'forum_form');
-}
-
-/**
- * Process forum form and container form submissions.
- */
-function forum_form_submit($form_id, $form_values) {
- if ($form_id == 'forum_form_container') {
- $container = TRUE;
- $type = t('forum container');
- }
- else {
- $container = false;
- $type = t('forum');
- }
-
- $status = taxonomy_save_term($form_values);
- switch ($status) {
- case SAVED_NEW:
- if ($container) {
- $containers = variable_get('forum_containers', array());
- $containers[] = $form_values['tid'];
- variable_set('forum_containers', $containers);
- }
- drupal_set_message(t('Created new %type %term.', array('%term' => theme('placeholder', $form_values['name']), '%type' => $type)));
- break;
- case SAVED_UPDATED:
- drupal_set_message(t('The %type %term has been updated.', array('%term' => theme('placeholder', $form_values['name']), '%type' => $type)));
- break;
- }
- return 'admin/forum';
-}
-
-/**
- * Returns a confirmation page for deleting a forum taxonomy term.
- *
- * @param $tid ID of the term to be deleted
- */
-function _forum_confirm_delete($tid) {
- $term = taxonomy_get_term($tid);
-
- $form['tid'] = array('#type' => 'value', '#value' => $tid);
- $form['name'] = array('#type' => 'value', '#value' => $term->name);
-
- return confirm_form('forum_confirm_delete', $form, t('Are you sure you want to delete the forum %name?', array('%name' => theme('placeholder', $term->name))), 'admin/forums', t('Deleting a forum or container will delete all sub-forums and associated posts as well. This action cannot be undone.'), t('Delete'), t('Cancel'));
-}
-
-/**
- * Implementation of forms api _submit call. Deletes a forum after confirmation.
- */
-function forum_confirm_delete_submit($form_id, $form_values) {
- taxonomy_del_term($form_values['tid']);
- drupal_set_message(t('The forum %term and all sub-forums and associated posts have been deleted.', array('%term' => theme('placeholder', $form_values['name']))));
- watchdog('content', t('forum: deleted %term and all its sub-forums and associated posts.', array('%term' => theme('placeholder', $form_values['name']))));
-
- return 'admin/forum';
-}
-
-/**
- * Returns an overview list of existing forums and containers
- */
-function forum_overview() {
- $header = array(t('Name'), t('Operations'));
-
- $tree = taxonomy_get_tree(_forum_get_vid());
- if ($tree) {
- foreach ($tree as $term) {
- if (in_array($term->tid, variable_get('forum_containers', array()))) {
- $rows[] = array(_taxonomy_depth($term->depth) .' '. check_plain($term->name), l(t('edit container'), "admin/forum/edit/container/$term->tid"));
- }
- else {
- $rows[] = array(_taxonomy_depth($term->depth) .' '. check_plain($term->name), l(t('edit forum'), "admin/forum/edit/forum/$term->tid"));
- }
-
- }
- }
- else {
- $rows[] = array(array('data' => '<em>' . t('There are no existing containers or forums. You may add some on the <a href="%container">add container</a> or <a href="%forum">add forum</a> pages.', array('%container' => url('admin/forum/add/container'), '%forum' => url('admin/forum/add/forum'))) . '</em>', 'colspan' => 2));
- }
- return theme('table', $header, $rows);
-}
-
-/**
- * Returns a select box for available parent terms
- *
- * @param $tid ID of the term which is being added or edited
- * @param $title Title to display the select box with
- * @param $child_type Whether the child is forum or container
- */
-function _forum_parent_select($tid, $title, $child_type) {
-
- $parents = taxonomy_get_parents($tid);
- if ($parents) {
- $parent = array_shift($parents);
- $parent = $parent->tid;
- }
- else {
- $parent = 0;
- }
-
- $children = taxonomy_get_tree(_forum_get_vid(), $tid);
-
- // A term can't be the child of itself, nor of its children.
- foreach ($children as $child) {
- $exclude[] = $child->tid;
- }
- $exclude[] = $tid;
-
- $tree = taxonomy_get_tree(_forum_get_vid());
- $options[0] = '<'. t('root') .'>';
- if ($tree) {
- foreach ($tree as $term) {
- if (!in_array($term->tid, $exclude)) {
- $options[$term->tid] = _taxonomy_depth($term->depth) . $term->name;
- }
- }
- }
- if ($child_type == 'container') {
- $description = t('Containers are usually placed at the top (root) level of your forum but you can also place a container inside a parent container or forum.');
- }
- else if ($child_type == 'forum') {
- $description = t('You may place your forum inside a parent container or forum, or at the top (root) level of your forum.');
- }
-
- return array('#type' => 'select', '#title' => $title, '#default_value' => $parent, '#options' => $options, '#description' => $description, '#required' => TRUE);
-}
-
-function forum_term_path($term) {
- return 'forum/'. $term->tid;
-}
-
-/**
- * Returns the vocabulary id for forum navigation.
- */
-function _forum_get_vid() {
- $vid = variable_get('forum_nav_vocabulary', '');
- if (empty($vid)) {
- // Check to see if a forum vocabulary exists
- $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = '%s'", 'forum'));
- if (!$vid) {
- $edit = array('name' => 'Forums', 'multiple' => 0, 'required' => 1, 'hierarchy' => 1, 'relations' => 0, 'module' => 'forum', 'nodes' => array('forum' => 1));
- taxonomy_save_vocabulary($edit);
- $vid = $edit['vid'];
- }
- variable_set('forum_nav_vocabulary', $vid);
- }
-
- return $vid;
-}
-
-/**
- * Formats a topic for display
- *
- * @TODO Give a better description. Not sure where this function is used yet.
- */
-function _forum_format($topic) {
- if ($topic && $topic->timestamp) {
- return t('%time ago<br />by %author', array('%time' => format_interval(time() - $topic->timestamp), '%author' => theme('username', $topic)));
- }
- else {
- return message_na();
- }
-}
-
-/**
- * Returns a list of all forums for a given taxonomy id
- *
- * Forum objects contain the following fields
- * -num_topics Number of topics in the forum
- * -num_posts Total number of posts in all topics
- * -last_post Most recent post for the forum
- *
- * @param $tid
- * Taxonomy ID of the vocabulary that holds the forum list.
- * @return
- * Array of object containing the forum information.
- */
-function forum_get_forums($tid = 0) {
-
- $forums = array();
- $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
-
- if (count($_forums)) {
-
- $counts = array();
-
- $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid WHERE n.status = 1 AND n.type = 'forum' GROUP BY r.tid";
- $sql = db_rewrite_sql($sql);
- $_counts = db_query($sql, $forum->tid);
- while ($count = db_fetch_object($_counts)) {
- $counts[$count->tid] = $count;
- }
- }
-
- foreach ($_forums as $forum) {
- if (in_array($forum->tid, variable_get('forum_containers', array()))) {
- $forum->container = 1;
- }
-
- if ($counts[$forum->tid]) {
- $forum->num_topics = $counts[$forum->tid]->topic_count;
- $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
- }
- else {
- $forum->num_topics = 0;
- $forum->num_posts = 0;
- }
-
- // This query does not use full ANSI syntax since MySQL 3.x does not support
- // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
- // used to join node_comment_statistics to users.
- $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {term_node} tn ON n.nid = tn.nid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
- $sql = db_rewrite_sql($sql);
- $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
-
- $last_post = new StdClass();
- $last_post->timestamp = $topic->last_comment_timestamp;
- $last_post->name = $topic->last_comment_name;
- $last_post->uid = $topic->last_comment_uid;
- $forum->last_post = $last_post;
-
- $forums[$forum->tid] = $forum;
- }
-
- return $forums;
-}
-
-/**
- * Calculate the number of nodes the user has not yet read and are newer
- * than NODE_NEW_LIMIT.
- */
-function _forum_topics_unread($term, $uid) {
- $sql = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid AND tn.tid = %d LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d WHERE n.status = 1 AND n.type = 'forum' AND n.created > %d AND h.nid IS NULL";
- $sql = db_rewrite_sql($sql);
- return db_result(db_query($sql, $term, $uid, NODE_NEW_LIMIT));
-}
-
-function forum_get_topics($tid, $sortby, $forum_per_page) {
- global $user, $forum_topic_list_header;
-
- $forum_topic_list_header = array(
- array('data' => '&nbsp;'),
- array('data' => t('Topic'), 'field' => 'n.title'),
- array('data' => t('Replies'), 'field' => 'l.comment_count'),
- array('data' => t('Created'), 'field' => 'n.created'),
- array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'),
- );
-
- $order = _forum_get_topic_order($sortby);
- for ($i = 0; $i < count($forum_topic_list_header); $i++) {
- if ($forum_topic_list_header[$i]['field'] == $order['field']) {
- $forum_topic_list_header[$i]['sort'] = $order['sort'];
- }
- }
-
- $term = taxonomy_get_term($tid);
-
- $sql = db_rewrite_sql("SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f, {node} n WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = %d AND n.uid = u.uid AND n.vid = f.vid");
- $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
- $sql .= ', n.created DESC'; // Always add a secondary sort order so that the news forum topics are on top.
-
- $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type = 'forum'");
-
- $result = pager_query($sql, $forum_per_page, 0, $sql_count, $tid);
-
- while ($topic = db_fetch_object($result)) {
- if ($user->uid) {
- // folder is new if topic is new or there are new comments since last visit
- if ($topic->tid != $tid) {
- $topic->new = 0;
- }
- else {
- $history = _forum_user_last_visit($topic->nid);
- $topic->new_replies = comment_num_new($topic->nid, $history);
- $topic->new = $topic->new_replies || ($topic->timestamp > $history);
- }
- }
- else {
- // Do not track "new replies" status for topics if the user is anonymous.
- $topic->new_replies = 0;
- $topic->new = 0;
- }
-
- if ($topic->num_comments > 0) {
- $last_reply = new StdClass();
- $last_reply->timestamp = $topic->last_comment_timestamp;
- $last_reply->name = $topic->last_comment_name;
- $last_reply->uid = $topic->last_comment_uid;
- $topic->last_reply = $last_reply;
- }
- $topics[] = $topic;
- }
-
- return $topics;
-}
-
-/**
- * Finds the first unread node for a given forum.
- */
-function _forum_new($tid) {
- global $user;
-
- $sql = "SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type = 'forum' AND h.nid IS NULL AND n.created > %d ORDER BY created";
- $sql = db_rewrite_sql($sql);
- $nid = db_result(db_query_range($sql, $user->uid, $tid, NODE_NEW_LIMIT, 0, 1));
-
- return $nid ? $nid : 0;
-}
-
-/**
- * Menu callback; prints a forum listing.
- */
-function forum_page($tid = 0) {
- if (module_exist('taxonomy') && module_exist('comment')) {
- $forum_per_page = variable_get('forum_per_page', 25);
- $sortby = variable_get('forum_order', 1);
-
- $forums = forum_get_forums($tid);
- $parents = taxonomy_get_parents_all($tid);
- if ($tid && !in_array($tid, variable_get('forum_containers', array()))) {
- $topics = forum_get_topics($tid, $sortby, $forum_per_page);
- }
-
- return theme('forum_display', $forums, $topics, $parents, $tid, $sortby, $forum_per_page);
- }
- else {
- drupal_set_message(t('The forum module requires both the taxonomy module and the comment module to be enabled and configured.'), 'error');
- return ' ';
- }
-}
-
-/**
- * Format the forum body.
- *
- * @ingroup themeable
- */
-function theme_forum_display($forums, $topics, $parents, $tid, $sortby, $forum_per_page) {
- global $user;
- // forum list, topics list, topic browser and 'add new topic' link
-
- $vocabulary = taxonomy_get_vocabulary(variable_get('forum_nav_vocabulary', ''));
- $title = $vocabulary->name;
-
- // Breadcrumb navigation:
- $breadcrumb = array();
- if ($tid) {
- $breadcrumb[] = array('path' => 'forum', 'title' => $title);
- }
-
- if ($parents) {
- $parents = array_reverse($parents);
- foreach ($parents as $p) {
- if ($p->tid == $tid) {
- $title = $p->name;
- }
- else {
- $breadcrumb[] = array('path' => 'forum/'. $p->tid, 'title' => $p->name);
- }
- }
- }
-
- drupal_set_title($title);
-
- $breadcrumb[] = array('path' => $_GET['q']);
- menu_set_location($breadcrumb);
-
- if (count($forums) || count($parents)) {
- $output = '<div id="forum">';
- $output .= '<ul>';
-
- if (module_exist('tracker')) {
- if ($user->uid) {
- $output .= ' <li>'. l(t('My discussions.'), "tracker/$user->uid") .'</li>';
- }
-
- $output .= ' <li>'. l(t('Active discussions.'), 'tracker') .'</li>';
- }
-
- if (user_access('create forum topics')) {
- $output .= '<li>'. l(t('Post new forum topic.'), "node/add/forum/$tid") .'</li>';
- }
- else if ($user->uid) {
- $output .= '<li>'. t('You are not allowed to post a new forum topic.') .'</li>';
- }
- else {
- $output .= '<li>'. t('<a href="%login">Login</a> to post a new forum topic.', array('%login' => url('user/login'))) .'</li>';
- }
- $output .= '</ul>';
-
- $output .= theme('forum_list', $forums, $parents, $tid);
-
- if ($tid && !in_array($tid, variable_get('forum_containers', array()))) {
- drupal_add_link(array('rel' => 'alternate',
- 'type' => 'application/rss+xml',
- 'title' => 'RSS - '. $title,
- 'href' => url('taxonomy/term/'. $tid .'/0/feed')));
-
- $output .= theme('forum_topic_list', $tid, $topics, $sortby, $forum_per_page);
- $output .= theme('feed_icon', url("taxonomy/term/$tid/0/feed"));
- }
- $output .= '</div>';
- }
- else {
- drupal_set_title(t('No forums defined'));
- $output = '';
- }
-
- return $output;
-}
-
-/**
- * Format the forum listing.
- *
- * @ingroup themeable
- */
-function theme_forum_list($forums, $parents, $tid) {
- global $user;
-
- if ($forums) {
-
- $header = array(t('Forum'), t('Topics'), t('Posts'), t('Last post'));
-
- foreach ($forums as $forum) {
- if ($forum->container) {
- $description = '<div style="margin-left: '. ($forum->depth * 30) ."px;\">\n";
- $description .= ' <div class="name">'. l($forum->name, "forum/$forum->tid") ."</div>\n";
-
- if ($forum->description) {
- $description .= ' <div class="description">'. check_plain($forum->description) ."</div>\n";
- }
- $description .= "</div>\n";
-
- $rows[] = array(array('data' => $description, 'class' => 'container', 'colspan' => '4'));
- }
- else {
- $new_topics = _forum_topics_unread($forum->tid, $user->uid);
- $forum->old_topics = $forum->num_topics - $new_topics;
- if (!$user->uid) {
- $new_topics = 0;
- }
-
- $description = '<div style="margin-left: '. ($forum->depth * 30) ."px;\">\n";
- $description .= ' <div class="name">'. l($forum->name, "forum/$forum->tid") ."</div>\n";
-
- if ($forum->description) {
- $description .= ' <div class="description">'. check_plain($forum->description) ."</div>\n";
- }
- $description .= "</div>\n";
-
- $rows[] = array(
- array('data' => $description, 'class' => 'forum'),
- array('data' => $forum->num_topics . ($new_topics ? '<br />'. l(format_plural($new_topics, '1 new', '%count new'), "forum/$forum->tid", NULL, NULL, 'new') : ''), 'class' => 'topics'),
- array('data' => $forum->num_posts, 'class' => 'posts'),
- array('data' => _forum_format($forum->last_post), 'class' => 'last-reply'));
- }
- }
-
- return theme('table', $header, $rows);
-
- }
-
-}
-
-/**
- * Format the topic listing.
- *
- * @ingroup themeable
- */
-function theme_forum_topic_list($tid, $topics, $sortby, $forum_per_page) {
- global $forum_topic_list_header;
-
- if ($topics) {
-
- foreach ($topics as $topic) {
- // folder is new if topic is new or there are new comments since last visit
- if ($topic->tid != $tid) {
- $rows[] = array(
- array('data' => theme('forum_icon', $topic->new, $topic->num_comments, $topic->comment_mode, $topic->sticky), 'class' => 'icon'),
- array('data' => check_plain($topic->title), 'class' => 'title'),
- array('data' => l(t('This topic has been moved'), "forum/$topic->tid"), 'colspan' => '3')
- );
- }
- else {
- $rows[] = array(
- array('data' => theme('forum_icon', $topic->new, $topic->num_comments, $topic->comment_mode, $topic->sticky), 'class' => 'icon'),
- array('data' => l($topic->title, "node/$topic->nid"), 'class' => 'topic'),
- array('data' => $topic->num_comments . ($topic->new_replies ? '<br />'. l(format_plural($topic->new_replies, '1 new', '%count new'), "node/$topic->nid", NULL, NULL, 'new') : ''), 'class' => 'replies'),
- array('data' => _forum_format($topic), 'class' => 'created'),
- array('data' => _forum_format($topic->last_reply), 'class' => 'last-reply')
- );
- }
- }
- }
-
- $output .= theme('table', $forum_topic_list_header, $rows);
- $output .= theme('pager', NULL, $forum_per_page, 0, tablesort_pager());
-
- return $output;
-}
-
-/**
- * Format the icon for each individual topic.
- *
- * @ingroup themeable
- */
-function theme_forum_icon($new_posts, $num_posts = 0, $comment_mode = 0, $sticky = 0) {
-
- if ($num_posts > variable_get('forum_hot_topic', 15)) {
- $icon = $new_posts ? 'hot-new' : 'hot';
- }
- else {
- $icon = $new_posts ? 'new' : 'default';
- }
-
- if ($comment_mode == COMMENT_NODE_READ_ONLY || $comment_mode == COMMENT_NODE_DISABLED) {
- $icon = 'closed';
- }
-
- if ($sticky == 1) {
- $icon = 'sticky';
- }
-
- $output = theme('image', "misc/forum-$icon.png");
-
- if ($new_posts) {
- $output = "<a name=\"new\">$output</a>";
- }
-
- return $output;
-}
-
-/**
- * Format the next/previous forum topic navigation links.
- *
- * @ingroup themeable
- */
-function theme_forum_topic_navigation($node) {
- $output = '';
-
- // get previous and next topic
- $sql = "SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type = 'forum' ORDER BY n.sticky DESC, ". _forum_get_topic_order_sql(variable_get('forum_order', 1));
- $result = db_query(db_rewrite_sql($sql), $node->tid);
-
- while ($topic = db_fetch_object($result)) {
- if ($stop == 1) {
- $next = new StdClass();
- $next->nid = $topic->nid;
- $next->title = $topic->title;
- break;
- }
- if ($topic->nid == $node->nid) {
- $stop = 1;
- }
- else {
- $prev = new StdClass();
- $prev->nid = $topic->nid;
- $prev->title = $topic->title;
- }
- }
-
- if ($prev || $next) {
- $output .= '<div class="forum-topic-navigation">';
-
- if ($prev) {
- $output .= l(t('‹ ') . $prev->title, 'node/'. $prev->nid, array('class' => 'topic-previous', 'title' => t('Go to previous forum topic')));
- }
- if ($next) {
- $output .= l($next->title . t(' ›'), 'node/'. $next->nid, array('class' => 'topic-next', 'title' => t('Go to next forum topic')));
- }
-
- $output .= '</div>';
- }
-
- return $output;
-}
-
-function _forum_user_last_visit($nid) {
- global $user;
- static $history = array();
-
- if (empty($history)) {
- $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = %d', $user->uid);
- while ($t = db_fetch_object($result)) {
- $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT;
- }
- }
- return $history[$nid] ? $history[$nid] : NODE_NEW_LIMIT;
-}
-
-function _forum_get_topic_order($sortby) {
- switch ($sortby) {
- case 1:
- return array('field' => 'l.last_comment_timestamp', 'sort' => 'desc');
- break;
- case 2:
- return array('field' => 'l.last_comment_timestamp', 'sort' => 'asc');
- break;
- case 3:
- return array('field' => 'l.comment_count', 'sort' => 'desc');
- break;
- case 4:
- return array('field' => 'l.comment_count', 'sort' => 'asc');
- break;
- }
-}
-
-function _forum_get_topic_order_sql($sortby) {
- $order = _forum_get_topic_order($sortby);
- return $order['field'] .' '. $order['sort'];
-}
-
-
diff --git a/modules/help/help.module b/modules/help/help.module
deleted file mode 100644
index 4f52411..0000000
--- a/modules/help/help.module
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Manages displaying online help.
- */
-
-/**
- * Implementation of hook_menu().
- */
-function help_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $admin_access = user_access('access administration pages');
-
- $items[] = array('path' => 'admin/help', 'title' => t('help'),
- 'callback' => 'help_main',
- 'access' => $admin_access,
- 'weight' => 9);
-
- foreach (module_implements('help', TRUE) as $module) {
- $items[] = array('path' => 'admin/help/' . $module,
- 'title' => t($module),
- 'callback' => 'help_page',
- 'type' => MENU_CALLBACK,
- 'access' => $admin_access);
- }
- }
-
- return $items;
-}
-
-/**
- * Menu callback; prints a page listing a glossary of Drupal terminology.
- */
-function help_main() {
- $output = t("
- <h2>Help topics</h2>
- <p>Help is available on the following items:</p>
- %help_pages
- <h2>Glossary of Drupal terminology</h2>
- <dl>
- <dt>Block</dt><dd>A small box containing information or content placed in the left-hand or right-hand sidebar of a web page.</dd>
- <dt>Comment</dt><dd>A note attached to a node. Usually intended to clarify, explain, criticize, or express an opinion on the original material.</dd>
- <dt>Moderation</dt>
- <dd>The activity of making sure a post to a Drupal site fits in with what is expected for that Drupal site.
- <dl>
- <dt>Approved</dt><dd>A moderated post which has been accepted by the moderators for publication. (See published).</dd>
- <dt>Waiting</dt><dd>A moderated post which is still being voted on to be accepted for publication. (See published.)</dd>
- </dl>
- </dd>
- <dt>Node</dt><dd>The basic data unit in Drupal. Everything is a node or an extension of a node.</dd>
- <dt>Public</dt><dd>See published.</dd>
- <dt>Published</dt><dd>A node that is viewable by everyone. (See unpublished.)</dd>
- <dt>Role</dt><dd>A classification users are placed into for the purpose of setting users' permissions.</dd>
- <dt>Taxonomy</dt><dd>A division of a collection of things into ordered, classified groups. (See <a href=\"%taxonomy\">taxonomy help</a>.)</dd>
- <dt>Unpublished</dt><dd>A node that is only viewable by administrators and moderators.</dd>
- <dt>User</dt><dd>A person who has an account at your Drupal site, and is logged in with that account.</dd>
- <dt>Visitor</dt><dd>A person who does not have an account at your Drupal site or a person who has an account at your Drupal site but is <strong>not</strong> logged in with that account. Also termed \"anonymous user\".</dd>
- </dl>", array('%help_pages' => help_links_as_list(), '%taxonomy' => url('admin/help/taxonomy')));
-
- return $output;
-}
-
-function help_links_as_list() {
- $modules = array();
- foreach (module_implements('help', TRUE) as $module) {
- if (module_invoke($module, 'help', "admin/help#$module")) {
- $modules[] = $module;
- }
- }
- sort($modules);
-
- // Output pretty four-column list
- $break = ceil(count($modules) / 4);
- $output = '<div class="help-items"><ul>';
- foreach ($modules as $i => $module) {
- $output .= '<li>'. l(t($module), 'admin/help/'. $module) .'</li>';
- if (($i + 1) % $break == 0) {
- $output .= '</ul></div><div class="help-items'. ($i + 1 == $break * 3 ? ' help-items-last' : '') .'"><ul>';
- }
- }
- $output .= '</ul></div><br class="clear" />';
-
- return $output;
-}
-
-/**
- * Implementation of hook_help().
- */
-function help_help($section) {
- switch ($section) {
- case 'admin/help':
- $output = t('<p>This guide explains what the various modules in <a href="%Drupal">Drupal</a> do and how to configure them.</p>
-<p>It is not a substitute for the <a href="%handbook">Drupal handbook</a> available online and should be used in conjunction with it. The online reference handbook might be more up-to-date and has helpful user-contributed comments. It is your definitive reference point for all Drupal documentation.</p>
-', array('%Drupal' => 'http://drupal.org', '%handbook' => 'http://drupal.org/handbook'));
- return $output;
- case 'admin/help#help':
- $output = '<p>'. t('The help module displays context sensitive help information. Users can learn how to use modules and accomplish tasks quicker with less errors by clicking on links in provided by the help module.') .'</p>';
- $output .= t('<p>Modules can make documentation available to other modules with this module. All user help should be presented using this module. Some examples of help: </p>
-<ul>
-<li>The name of a module (unused, but there).</li>
-<li>The description found on the admin/system/modules page.</li>
-<li>The module\'s help text, displayed on the admin/help page and through the module\'s individual help link.</li>
-<li>The help for a distributed authorization module (if applicable).</li>
-<li>The description of a post type (if applicable).</li>
-</ul>
-');
- $output .= '<p>'. t('You can not administer the help system.') .'</p>';
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%help">Help page</a>.', array('%help' => 'http://drupal.org/handbook/modules/help/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Manages the display of online help.');
- }
-}
-
-/**
- * Menu callback; prints a page listing general help for all modules.
- */
-function help_page() {
- $name = arg(2);
- $output = '';
- if (module_hook($name, 'help')) {
- $temp = module_invoke($name, 'help', "admin/help#$name");
- if (empty($temp)) {
- $output .= t("No help is available for module %module.", array('%module' => $name));
- }
- else {
- $output .= $temp;
- }
- }
- return $output;
-}
diff --git a/modules/legacy/legacy.module b/modules/legacy/legacy.module
deleted file mode 100644
index f03db2e..0000000
--- a/modules/legacy/legacy.module
+++ /dev/null
@@ -1,202 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Provides legacy handlers for upgrades from older Drupal installations.
- */
-
-/**
- * Implementation of hook_help().
- */
-function legacy_help($section) {
- switch ($section) {
- case 'admin/help#legacy':
- $output = '<p>'. t('The legacy module provides legacy handlers for upgrades from older installations. These handlers help automatically redirect references to pages from old installations and prevent <em>page not found</em> errors for your site.') .'</p>';
- $output .= '<p>'. t('The legacy module handles legacy style taxonomy page, taxonomy feed, and blog feed paths. It also handles URL upgrades from Drupal 4.1. It rewrites old-style URLs to new-style URLs (clean URLs). ') .'</p>';
- $output .= t('<p>Example Mappings:</p>
-<ul>
-<li><em>taxonomy/page/or/52,97</em> to <em>taxonomy/term/52+97</em>.</li>
-<li><em>taxonomy/feed/or/52,97</em> to <em>taxonomy/term/52+97/0/feed</em>.</li>
-<li><em>blog/feed/52</em> to <em>blog/52/feed</em>.</li>
-<li><em>node/view/52</em> to <em>node/52</em>.</li>
-<li><em>book/view/52</em> to <em>node/52</em>.</li>
-<li><em>user/view/52</em> to <em>user/52</em>.</li>
-</ul>
-');
- $output .= '<p>'. t('Legacy module has no configurable options.') .'</p>';
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%legacy">Legacy page</a>.', array('%legacy' => 'http://drupal.org/handbook/modules/legacy/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Provides legacy handlers for upgrades from older Drupal installations.');
- }
-}
-
-/**
- * Implementation of hook_menu().
- *
- * Registers menu paths used in earlier Drupal versions.
- */
-function legacy_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- // Map "taxonomy/page/or/52,97" to "taxonomy/term/52+97".
- $items[] = array('path' => 'taxonomy/page', 'title' => t('taxonomy'),
- 'callback' => 'legacy_taxonomy_page',
- 'access' => TRUE, 'type' => MENU_CALLBACK);
-
- // Map "taxonomy/feed/or/52,97" to "taxonomy/term/52+97/0/feed".
- $items[] = array('path' => 'taxonomy/feed', 'title' => t('taxonomy'),
- 'callback' => 'legacy_taxonomy_feed',
- 'access' => TRUE, 'type' => MENU_CALLBACK);
-
- // Map "blog/feed/52" to "blog/52/feed".
- $items[] = array('path' => 'blog/feed', 'title' => t('blog'),
- 'callback' => 'legacy_blog_feed',
- 'access' => TRUE, 'type' => MENU_CALLBACK);
- }
- else {
- // Map "node/view/52" to "node/52".
- $items[] = array('path' => 'node/view', 'title' => t('view'),
- 'callback' => 'drupal_goto',
- 'callback arguments' => array('node/'. arg(2), NULL, NULL),
- 'access' => TRUE, 'type' => MENU_CALLBACK);
-
- // Map "book/view/52" to "node/52".
- $items[] = array('path' => 'book/view', 'title' => t('view'),
- 'callback' => 'drupal_goto',
- 'callback arguments' => array('node/'. arg(2), NULL, NULL),
- 'access' => TRUE, 'type' => MENU_CALLBACK);
-
- // Map "user/view/52" to "user/52".
- $items[] = array('path' => 'user/view', 'title' => t('view'),
- 'callback' => 'drupal_goto',
- 'callback arguments' => array('user/'. arg(2), NULL, NULL),
- 'access' => TRUE, 'type' => MENU_CALLBACK);
- }
-
- return $items;
-}
-
-/**
- * Menu callback; redirects users to new taxonomy page paths.
- */
-function legacy_taxonomy_page($operation = 'or', $str_tids = '') {
- if ($operation == 'or') {
- $str_tids = str_replace(',', '+', $str_tids);
- }
- drupal_goto('taxonomy/term/'. $str_tids);
-}
-
-/**
- * Menu callback; redirects users to new taxonomy feed paths.
- */
-function legacy_taxonomy_feed($operation = 'or', $str_tids = '') {
- if ($operation == 'or') {
- $str_tids = str_replace(',', '+', $str_tids);
- }
- drupal_goto('taxonomy/term/'. $str_tids .'/0/feed');
-}
-
-/**
- * Menu callback; redirects users to new blog feed paths.
- */
-function legacy_blog_feed($str_uid = '') {
- // if URL is of form blog/feed/52 redirect
- // if URL is of form blog/feed we have to call blog_feed_last().
- if (is_numeric($str_uid)) {
- drupal_goto('blog/'. $str_uid .'/feed');
- }
- else {
- module_invoke('blog', 'feed_last');
- }
-}
-
-/**
- * Implementation of hook_filter(). Handles URL upgrades from Drupal 4.1.
- */
-function legacy_filter($op, $delta = 0, $format = -1, $text = '') {
- switch ($op) {
- case 'list':
- return array(t('Legacy filter'));
-
- case 'description':
- return t('Replaces URLs from Drupal 4.1 (and lower) with updated equivalents.');
-
- case 'process':
- return _legacy_filter_old_urls($text, $format);
-
- case 'settings':
- return;
-
- default:
- return $text;
- }
-}
-
-/**
- * Rewrite legacy URLs.
- *
- * This is a *temporary* filter to rewrite old-style URLs to new-style
- * URLs (clean URLs). Currently, URLs are being rewritten dynamically
- * (ie. "on output"), however when these rewrite rules have been tested
- * enough, we will use them to permanently rewrite the links in node
- * and comment bodies.
- */
-function _legacy_filter_old_urls($text) {
- if (!variable_get('rewrite_old_urls', 0)) {
- return $text;
- }
-
- global $base_url;
-
- $end = substr($base_url, 12);
-
- if (variable_get('clean_url', '0') == '0') {
- // Relative URLs:
-
- // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
- $text = eregi_replace("\"(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "\"?q=\\1/view/\\2/\\4", $text);
-
- // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2/\\4/\\6" , $text);
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2/\\4", $text);
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2", $text);
-
- // Absolute URLs:
-
- // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
- $text = eregi_replace("$end/(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "$end/?q=\\1/view/\\2/\\4", $text);
-
- // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/?q=\\2/\\4/\\6" , $text);
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/?q=\\2/\\4", $text);
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"$end/?q=\\2", $text);
- }
- else {
- // Relative URLs:
-
- // Rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
- $text = eregi_replace("\"(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "\"\\1/view/\\2/\\4", $text);
-
- // Rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2/\\4/\\6", $text);
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2/\\4", $text);
- $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2", $text);
-
- // Absolute URLs:
-
- // Rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
- $text = eregi_replace("$end/(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "$end/\\1/view/\\2/\\4", $text);
-
- // Rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2/\\4/\\6", $text);
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2/\\4", $text);
- $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2", $text);
- }
-
- return $text;
-}
-
-
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
deleted file mode 100644
index e124f98..0000000
--- a/modules/locale/locale.module
+++ /dev/null
@@ -1,417 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables administrators to manage the site interface languages.
- *
- * When enabled, the site interface can be displayed in different
- * languages. The setup of languages and translations is completely
- * web based. Gettext portable object files are supported.
- */
-
-// ---------------------------------------------------------------------------------
-// Hook implementations (needed on all page loads)
-
-/**
- * Implementation of hook_help().
- */
-function locale_help($section) {
- switch ($section) {
- case 'admin/help#locale':
- $output = '<p>'. t('The locale module allows you to present your Drupal site in a language other than the default English. You can use it to set up a multi-lingual web site or replace given <em>built-in</em> text with text which has been customized for your site. Whenever the locale module encounters text which needs to be displayed, it tries to translate it into the currently selected language. If a translation is not available, then the string is remembered, so you can look up untranslated strings easily.') .'</p>';
- $output .= '<p>'. t('The locale module provides two options for providing translations. The first is the integrated web interface, via which you can search for untranslated strings, and specify their translations. An easier and less time-consuming method is to import existing translations for your language. These translations are available as <em>GNU gettext Portable Object files</em> (<em>.po</em> files for short). Translations for many languages are available for download from the translation page.') .'</p>';
- $output .= '<p>'. t('If an existing translation does not meet your needs, the <em>.po</em> files are easily edited with special editing tools. The locale module\'s import feature allows you to add strings from such files into your site\'s database. The export functionality enables you to share your translations with others, generating Portable Object files from your site strings.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>administer localization at <a href="%admin-locale">administer &gt;&gt; localization</a>.</li>
-<li>manage strings for the localization: <a href="%admin-locale-string-search">administer &gt;&gt; localization &gt;&gt; manage strings</a>.</li>
-<li>add a locale language: <a href="%admin-locale-language-add">administer &gt;&gt; localization &gt;&gt; add language</a>.</li>
-<li>download translation files from the <a href="%external-http-drupal-org-project-Translations">Drupal translations page</a>.
-</li>
-</ul>
-', array('%admin-locale' => url('admin/locale'), '%admin-locale-string-search' => url('admin/locale/string/search'), '%admin-locale-language-add' => url('admin/locale/language/add'), '%external-http-drupal-org-project-Translations' => 'http://drupal.org/project/Translations'));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%locale">Locale page</a>.', array('%locale' => 'http://drupal.org/handbook/modules/locale/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Enables the translation of the user interface to languages other than English.');
- case 'admin/locale':
- case 'admin/locale/language/overview':
- return t("<p>Drupal provides support for the translation of its interface text into different languages. This page provides an overview of the installed languages. You can add a language on the <a href=\"%add-language\">add language page</a>, or directly by <a href=\"%import\">importing a translation</a>. If multiple languages are enabled, registered users will be able to set their preferred language. The site default will be used for anonymous visitors and for users without their own settings.</p><p>Drupal interface translations may be added or extended by several courses: by <a href=\"%import\">importing</a> an existing translation, by <a href=\"%search\">translating everything</a> from scratch, or by a combination of these approaches.</p>", array("%search" => url("admin/locale/string/search"), "%import" => url("admin/locale/language/import"), "%add-language" => url("admin/locale/language/add")));
- case 'admin/locale/language/add':
- return t("<p>You need to add all languages in which you would like to display the site interface. If you can't find the desired language in the quick-add dropdown, then you will need to provide the proper language code yourself. The language code may be used to negotiate with browsers and to present flags, etc., so it is important to pick a code that is standardised for the desired language. You can also add a language by <a href=\"%import\">importing a translation</a>.</p>", array("%import" => url("admin/locale/language/import")));
- case 'admin/locale/language/import':
- return t("<p>This page allows you to import a translation provided in the gettext Portable Object (.po) format. The easiest way to get your site translated is to obtain an existing Drupal translation and to import it. You can find existing translations on the <a href=\"%url\">Drupal translation page</a>. Note that importing a translation file might take a while.</p>", array('%url' => 'http://drupal.org/project/translations'));
- case 'admin/locale/language/export':
- return t("<p>This page allows you to export Drupal strings. The first option is to export a translation so it can be shared. The second option generates a translation template, which contains all Drupal strings, but without their translations. You can use this template to start a new translation using various software packages designed for this task.</p>");
- case 'admin/locale/string/search':
- return t("<p>It is often convenient to get the strings from your setup on the <a href=\"%export\">export page</a>, and use a desktop Gettext translation editor to edit the translations. On this page you can search in the translated and untranslated strings, and the default English texts provided by Drupal.</p>", array("%export" => url("admin/locale/language/export")));
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function locale_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $access = user_access('administer locales');
-
- // Main admin menu item
- $items[] = array('path' => 'admin/locale',
- 'title' => t('localization'),
- 'callback' => 'locale_admin_manage',
- 'access' => $access);
-
- // Top level tabs
- $items[] = array('path' => 'admin/locale/language',
- 'title' => t('manage languages'),
- 'access' => $access,
- 'weight' => -10,
- 'type' => MENU_DEFAULT_LOCAL_TASK);
- $items[] = array('path' => 'admin/locale/string/search',
- 'title' => t('manage strings'),
- 'callback' => 'locale_string_search',
- 'access' => $access,
- 'weight' => 10,
- 'type' => MENU_LOCAL_TASK);
-
- // Manage languages subtabs
- $items[] = array('path' => 'admin/locale/language/overview',
- 'title' => t('list'),
- 'callback' => 'locale_admin_manage',
- 'access' => $access,
- 'weight' => 0,
- 'type' => MENU_DEFAULT_LOCAL_TASK);
- $items[] = array('path' => 'admin/locale/language/add',
- 'title' => t('add language'),
- 'callback' => 'locale_admin_manage_add',
- 'access' => $access,
- 'weight' => 5,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/locale/language/import',
- 'title' => t('import'),
- 'callback' => 'locale_admin_import',
- 'access' => $access,
- 'weight' => 10,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/locale/language/export',
- 'title' => t('export'),
- 'callback' => 'locale_admin_export',
- 'access' => $access,
- 'weight' => 20,
- 'type' => MENU_LOCAL_TASK);
-
- // Language related callbacks
- $items[] = array('path' => 'admin/locale/language/delete',
- 'title' => t('confirm'),
- 'callback' => 'locale_admin_manage_delete_form',
- 'access' => $access,
- 'type' => MENU_CALLBACK);
- }
- else {
- if (is_numeric(arg(4))) {
- // String related callbacks
- $items[] = array('path' => 'admin/locale/string/edit/'. arg(4),
- 'title' => t('edit string'),
- 'callback' => 'locale_admin_string_edit',
- 'callback arguments' => arg(4),
- 'access' => $access,
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/locale/string/delete/'. arg(4),
- 'title' => t('delete string'),
- 'callback' => 'locale_admin_string_delete',
- 'callback arguments' => arg(4),
- 'access' => $access,
- 'type' => MENU_CALLBACK);
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function locale_perm() {
- return array('administer locales');
-}
-
-/**
- * Implementation of hook_user().
- */
-function locale_user($type, $edit, &$user, $category = NULL) {
- $languages = locale_supported_languages();
- if ($type == 'form' && $category == 'account' && count($languages['name']) > 1) {
- if ($user->language == '') {
- $user->language = key($languages['name']);
- }
- $languages['name'] = array_map('check_plain', $languages['name']);
- $form['locale'] = array('#type' => 'fieldset',
- '#title' => t('Interface language settings'),
- '#weight' => 1,
- );
- $form['locale']['language'] = array('#type' => 'radios',
- '#title' => t('Language'),
- '#default_value' => $user->language,
- '#options' => $languages['name'],
- '#description' => t('Selecting a different locale will change the interface language of the site.'),
- );
- return $form;
- }
-}
-
-// ---------------------------------------------------------------------------------
-// Locale core functionality (needed on all page loads)
-
-/**
- * Provides interface translation services.
- *
- * This function is called from t() to translate a string if needed.
- */
-function locale($string) {
- global $locale;
- static $locale_t;
-
- // Store database cached translations in a static var.
- if (!isset($locale_t)) {
- $cache = cache_get("locale:$locale");
-
- if ($cache == 0) {
- locale_refresh_cache();
- $cache = cache_get("locale:$locale");
- }
- $locale_t = unserialize($cache->data);
- }
-
- // We have the translation cached (if it is TRUE, then there is no
- // translation, so there is no point in checking the database)
- if (isset($locale_t[$string])) {
- $string = ($locale_t[$string] === TRUE ? $string : $locale_t[$string]);
- }
-
- // We do not have this translation cached, so get it from the DB.
- else {
- $result = db_query("SELECT s.lid, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.source = '%s' AND t.locale = '%s'", $string, $locale);
- // Translation found
- if ($trans = db_fetch_object($result)) {
- if (!empty($trans->translation)) {
- $locale_t[$string] = $trans->translation;
- $string = $trans->translation;
- }
- }
-
- // Either we have no such source string, or no translation
- else {
- $result = db_query("SELECT lid, source FROM {locales_source} WHERE source = '%s'", $string);
- // We have no such translation
- if ($obj = db_fetch_object($result)) {
- if ($locale) {
- db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '')", $obj->lid, $locale);
- }
- }
- // We have no such source string
- else {
- db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", request_uri(), $string);
- if ($locale) {
- $lid = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $string));
- db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '')", $lid->lid, $locale);
- }
- }
- // Clear locale cache in DB
- cache_clear_all("locale:$locale");
- }
- }
-
- return $string;
-}
-
-/**
- * Refreshes database stored cache of translations.
- *
- * We only store short strings to improve performance and consume less memory.
- */
-function locale_refresh_cache() {
- $languages = locale_supported_languages();
-
- foreach (array_keys($languages['name']) as $locale) {
- $result = db_query("SELECT s.source, t.translation, t.locale FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.locale = '%s' AND LENGTH(s.source) < 75", $locale);
- $t = array();
- while ($data = db_fetch_object($result)) {
- $t[$data->source] = (empty($data->translation) ? TRUE : $data->translation);
- }
- cache_set("locale:$locale", serialize($t));
- }
-}
-
-/**
- * Returns list of languages supported on this site.
- *
- * @param $reset Refresh cached language list.
- * @param $getall Return all languages (even disabled ones)
- */
-function locale_supported_languages($reset = FALSE, $getall = FALSE) {
- static $enabled = NULL;
- static $all = NULL;
-
- if ($reset) {
- unset($enabled); unset($all);
- }
-
- if (is_null($enabled)) {
- $enabled = $all = array();
- $all['name'] = $all['formula'] = $enabled['name'] = $enabled['formula'] = array();
- $result = db_query('SELECT locale, name, formula, enabled FROM {locales_meta} ORDER BY isdefault DESC, enabled DESC, name ASC');
- while ($row = db_fetch_object($result)) {
- $all['name'][$row->locale] = $row->name;
- $all['formula'][$row->locale] = $row->formula;
- if ($row->enabled) {
- $enabled['name'][$row->locale] = $row->name;
- $enabled['formula'][$row->locale] = $row->formula;
- }
- }
- }
- return $getall ? $all : $enabled;
-}
-
-/**
- * Returns plural form index for a specific number.
- *
- * The index is computed from the formula of this language.
- */
-function locale_get_plural($count) {
- global $locale;
- static $locale_formula, $plurals = array();
-
- if (!isset($plurals[$count])) {
- if (!isset($locale_formula)) {
- $languages = locale_supported_languages();
- $locale_formula = $languages['formula'][$locale];
- }
- if ($locale_formula) {
- $n = $count;
- $plurals[$count] = @eval("return intval($locale_formula);");
- return $plurals[$count];
- }
- else {
- $plurals[$count] = -1;
- return -1;
- }
- }
- return $plurals[$count];
-}
-
-// ---------------------------------------------------------------------------------
-// Language management functionality (administration only)
-
-/**
- * Page handler for the language management screen.
- */
-function locale_admin_manage() {
- include_once './includes/locale.inc';
- return _locale_admin_manage_screen();
-}
-
-/**
- * User interface for the language deletion confirmation screen.
- */
-function locale_admin_manage_delete_form() {
- include_once './includes/locale.inc';
- $langcode = arg(4);
-
- // Do not allow deletion of English locale.
- if ($langcode == 'en') {
- drupal_set_message(t('The English locale cannot be deleted.'));
- drupal_goto('admin/locale/language/overview');
- }
-
- // For other locales, warn user that data loss is ahead.
- $languages = locale_supported_languages(FALSE, TRUE);
-
- if (!isset($languages['name'][$langcode])) {
- drupal_not_found();
- }
- else {
- $form['langcode'] = array('#type' => 'value', '#value' => $langcode);
- return confirm_form('locale_admin_manage_delete_form', $form, t('Are you sure you want to delete the language %name?', array('%name' => theme('placeholder', t($languages['name'][$langcode])))), 'admin/locale/language/overview', t('Deleting a language will remove all data associated with it. This action cannot be undone.'), t('Delete'), t('Cancel'));
- }
-}
-
-/**
- * Process language deletion submissions.
- */
-function locale_admin_manage_delete_form_submit($form_id, $form_values) {
- $languages = locale_supported_languages(FALSE, TRUE);
- if (isset($languages['name'][$form_values['langcode']])) {
- db_query("DELETE FROM {locales_meta} WHERE locale = '%s'", $form_values['langcode']);
- db_query("DELETE FROM {locales_target} WHERE locale = '%s'", $form_values['langcode']);
- $message = t('The language %locale has been removed.', array('%locale' => theme('placeholder', t($languages['name'][$form_values['langcode']]))));
- drupal_set_message($message);
- watchdog('locale', $message);
- }
-
- // Changing the locale settings impacts the interface:
- cache_clear_all();
-
- return 'admin/locale/language/overview';
-}
-
-/**
- * Page handler for the language addition screen
- */
-function locale_admin_manage_add() {
- include_once './includes/locale.inc';
- return _locale_admin_manage_add_screen();
-}
-
-// ---------------------------------------------------------------------------------
-// Gettext Portable Object import functionality (administration only)
-
-/**
- * Page handler for the translation import screen
- */
-function locale_admin_import() {
- include_once './includes/locale.inc';
- return _locale_admin_import_screen();
-}
-
-// ---------------------------------------------------------------------------------
-// Gettext Portable Object export functionality (administration only)
-
-/**
- * Page handler for the translation export screen
- */
-function locale_admin_export() {
- include_once './includes/locale.inc';
- return _locale_admin_export_screen();
-}
-
-// ---------------------------------------------------------------------------------
-// String search and editing functionality (administration only)
-
-/**
- * Page handler for the string search.
- */
-function locale_string_search() {
- include_once './includes/locale.inc';
- $output = _locale_string_seek();
- $output .= _locale_string_seek_form();
- return $output;
-}
-
-/**
- * Display the string edit form.
- */
-function locale_admin_string_edit($lid) {
- include_once './includes/locale.inc';
- return _locale_string_edit($lid);
-}
-
-/**
- * Delete a string.
- */
-function locale_admin_string_delete($lid) {
- include_once './includes/locale.inc';
- _locale_string_delete($lid);
-}
diff --git a/modules/menu/menu.module b/modules/menu/menu.module
deleted file mode 100644
index e176e7f..0000000
--- a/modules/menu/menu.module
+++ /dev/null
@@ -1,763 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Allows administrators to customize the site navigation menu.
- */
-
-/**
- * Implementation of hook_help().
- */
-function menu_help($section) {
- switch ($section) {
- case 'admin/help#menu':
- $output = t('<p>The menu module allows for customization of the menus. Menus are useful for providing navigation in your site. The main menu for navigation is called the "navigation" menu. Menus appear in blocks on your site.</p>
-<p>There is a special case for the display of "primary links" and "secondary links" (which are implemented as menus). These are sets of links which are usually displayed in the header of each page (depending on the currently active theme). Any menu can serve as the primary or secondary links, by modifying the settings for the menu module.</p>
-<ul>
-<li>On the administer menu page administrators can "edit" to change the title, description, parent or weight of the menu item. Under the "operations" column, click on "enable/disable" to toggle the menu item on or off. Menu items which are disabled are not deleted; they are merely not available for navigating the site in the sidebar menu block. Note that the default menu items generated by the menu module cannot be deleted, only disabled.</li>
-<li>Using the "add menu" tab submit a title for a new custom menu. Once submitted, the new menu will appear in a list toward the bottom of the administer menu page underneath the main navigation menu.</li>
-<li>Use the "add menu item" tab to create new links in either the navigation or a custom menu. Select the parent item to place the new link within an existing menu structure. For top level menu items, choose the name of the menu in which the link is to be added. This tab is also the way to define primary or secondary links for your site (by adding items to whatever menu is selected as your primary links menu).</li>
-</ul>
-');
- $output .= t('<p>You can</p>
-<ul>
-<li>administer menus at <a href="%admin-menu">administer &gt;&gt; menus</a>.</li>
-<li>turn menus blocks on and off in the <a href="%admin-block">administer &gt;&gt; blocks</a>.</li>
-<li>add a menu at <a href="%admin-menu-menu-add">administer &gt;&gt; menus &gt;&gt; add menu</a>.</li>
-<li>add a menu item at <a href="%admin-menu-item-add">administer &gt;&gt; menus &gt;&gt; add menu item</a>.</li>
-<li>modify menu settings (in particular, to specify a menu to use for primary or secondary links) at <a href="%admin-settings-menus">administer &gt;&gt; settings &gt;&gt; menus</a>.</li>
-</ul>
-', array('%admin-menu' => url('admin/menu'), '%admin-block' => url('admin/block'), '%admin-menu-menu-add' => url('admin/menu/menu/add'), '%admin-menu-item-add' => url('admin/menu/item/add'), '%admin-settings-menus' => url('admin/settings/menu')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%menu">Menu page</a>.', array('%menu' => 'http://drupal.org/handbook/modules/menu/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows administrators to customize the site navigation menu.');
- case 'admin/menu':
- return t('<p>Select an operation from the list to move, change, or delete a menu item.</p>');
- case 'admin/menu/menu/add':
- return t('<p>Enter the name for your new menu. Remember to enable the newly created block in the <a href="%blocks">blocks administration page</a>.</p>', array('%blocks' => url('admin/block')));
- case 'admin/menu/item/add':
- return t('<p>Enter the title, path, position and the weight for your new menu item.</p>');
- case 'admin/settings/menu':
- return t('<p>Customize the menu settings.</p>');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function menu_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/menu',
- 'title' => t('menus'),
- 'callback' => 'menu_overview',
- 'access' => user_access('administer menu'));
- $items[] = array('path' => 'admin/menu/list',
- 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10);
-
- $items[] = array('path' => 'admin/menu/item/add',
- 'title' => t('add menu item'),
- 'callback' => 'menu_edit_item_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/menu/item/edit',
- 'title' => t('edit menu item'),
- 'callback' => 'menu_edit_item_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/menu/item/reset',
- 'title' => t('reset menu item'),
- 'callback' => 'menu_reset_item',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/menu/item/disable',
- 'title' => t('disable menu item'),
- 'callback' => 'menu_disable_item',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/menu/item/delete',
- 'title' => t('delete menu item'),
- 'callback' => 'menu_item_delete_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
-
- $items[] = array('path' => 'admin/menu/menu/add',
- 'title' => t('add menu'),
- 'callback' => 'menu_edit_menu_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'admin/menu/menu/edit',
- 'title' => t('edit menu'),
- 'callback' => 'menu_edit_menu_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/menu/menu/delete',
- 'title' => t('delete menu'),
- 'callback' => 'menu_item_delete_form',
- 'access' => user_access('administer menu'),
- 'type' => MENU_CALLBACK);
-
- $items[] = array('path' => 'admin/settings/menu',
- 'title' => t('menus'),
- 'callback' => 'menu_configure');
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- */
-function menu_block($op = 'list', $delta = 0) {
- if ($op == 'list') {
- $blocks = array();
- $root_menus = menu_get_root_menus();
- foreach ($root_menus as $mid => $title) {
- // Default "Navigation" block is handled by user.module.
- if ($mid != 1) {
- $blocks[$mid]['info'] = $title;
- }
- }
- return $blocks;
- }
- else if ($op == 'view') {
- $item = menu_get_item($delta);
- $data['subject'] = $item['title'];
- $data['content'] = theme('menu_tree', $delta);
- return $data;
- }
-}
-
-/**
- * Implementation of hook_nodeapi().
- */
-function menu_nodeapi(&$node, $op) {
-
- if (user_access('administer menu')) {
- switch ($op) {
- case 'insert':
- case 'update':
- if ($node->menu['delete']) {
- menu_node_form_delete($node);
- menu_rebuild();
- }
- elseif ($node->menu['title']) {
- $node->menu['path'] = ($node->menu['path']) ? $node->menu['path'] : "node/$node->nid";
- menu_edit_item_save($node->menu);
- menu_rebuild();
- }
- break;
-
- case 'delete':
- menu_node_form_delete($node);
- menu_rebuild();
- break;
- }
- }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function menu_perm() {
- return array('administer menu');
-}
-
-/**
- * Implementation of hook_form_alter().
- * Add menu item fields to the node form.
- */
-function menu_form_alter($form_id, &$form) {
- if (user_access('administer menu') && isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
- $edit = isset($_POST['edit']) ? $_POST['edit'] : '';
- $edit['nid'] = $form['nid']['#value'];
-
- $item = array();
- if ($edit['nid'] > 0) {
- $item = db_fetch_array(db_query("SELECT * FROM {menu} WHERE path = 'node/%d'", $edit['nid']));
- if (is_array($edit['menu'])) {
- $item = !is_array($item) ? $edit['menu'] : (($_POST['op'] == t('Preview')) ? array_merge($item, $edit['menu']) : array_merge($edit['menu'], $item));
- }
- }
-
- $form['menu'] = array('#type' => 'fieldset',
- '#title' => t('Menu settings'),
- '#collapsible' => TRUE,
- '#collapsed' => empty($item['title']),
- '#tree' => TRUE,
- '#weight' => 30,
- );
-
- $form['menu']['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $item['title'],
- '#description' => t('The name to display for this link.'),
- );
-
- $form['menu']['description'] = array('#type' => 'textfield',
- '#title' => t('Description'),
- '#default_value' => $item['description'],
- '#description' => t('The description displayed when hovering over a menu item.'),
- );
-
- // Generate a list of possible parents.
- $options = menu_parent_options($item['mid'], variable_get('menu_parent_items', 0));
-
- $form['menu']['pid'] = array('#type' => 'select',
- '#title' => t('Parent item'),
- '#default_value' => $item['pid'],
- '#options' => $options,
- );
-
- $form['menu']['path'] = array('#type' => 'hidden',
- '#value' => $item['path'],
- );
-
- $form['menu']['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $item['weight'],
- '#delta' => 10,
- '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
- );
-
- $form['menu']['mid'] = array('#type' => 'hidden',
- '#value' => $item['mid'] ? $item['mid'] : 0,
- );
-
- $form['menu']['type'] = array('#type' => 'hidden',
- '#value' => $item['type'] ? $item['type'] : MENU_CUSTOM_ITEM,
- );
-
- if ($item['mid'] > 0) {
- $form['menu']['delete'] = array('#type' => 'checkbox',
- '#title' => t('Check to delete this menu item.'),
- '#default_value' => $item['delete'],
- );
-
- $form['menu']['advanced'] = array('#type' => 'item',
- '#value' => t('You may also <a href="%edit">edit the advanced settings</a> for this menu item.', array('%edit' => url("admin/menu/item/edit/{$item['mid']}"))),
- );
- }
- }
-}
-
-/**
- * Menu callback; presents menu configuration options.
- */
-function menu_configure() {
- $menu = menu_get_menu();
- $root_menus = menu_get_root_menus();
-
- $primary_options = $root_menus;
- $primary_options[0] = t('No primary links');
-
- $form['settings_links'] = array('#type' => 'fieldset',
- '#title' => t('Primary and secondary links settings'),
- );
-
- $form['settings_links']['intro'] = array('#type' => 'item',
- '#value' => t('Primary and secondary links provide a navigational menu system which usually (depending on your theme) appears at the top-right of the browser window. The links displayed can be generated either from a custom list created via the <a href="%menu">menu administration</a> page or from a built-in list of menu items such as the navigation menu links.', array('%menu' => url('admin/menu'))),
- );
-
- $form['settings_links']['menu_primary_menu'] = array('#type' => 'select',
- '#title' => t('Menu containing primary links'),
- '#default_value' => variable_get('menu_primary_menu', 0),
- '#options' => $primary_options,
- );
-
- $secondary_options = $root_menus;
- $secondary_options[0] = t('No secondary links');
-
- $form['settings_links']['menu_secondary_menu'] = array('#type' => 'select',
- '#title' => t('Menu containing secondary links'),
- '#default_value' => variable_get('menu_secondary_menu', 0),
- '#options' => $secondary_options,
- '#description' => t('If you select the same menu as primary links then secondary links will display the appropriate second level of your navigation hierarchy.'),
- );
-
- $form['settings_authoring'] = array('#type' => 'fieldset',
- '#title' => t('Post authoring form settings'),
- );
-
- $form['settings_authoring']['intro'] = array('#type' => 'item',
- '#value' => t('The menu module allows on-the-fly creation of menu links in the post authoring forms. The following option limits the menus in which a new link may be added. For e.g. this can be used to force new menu items to be created in the primary links menu or to hide admin menu items.'),
- );
-
- $authoring_options = $root_menus;
- $authoring_options[0] = t('Show all menus');
-
- $form['settings_authoring']['menu_parent_items'] = array('#type' => 'select',
- '#title' => t('Restrict parent items to'),
- '#default_value' => variable_get('menu_parent_items', 0),
- '#options' => $authoring_options,
- '#description' => t('Choose the menu to be made available in the post authoring form. Only this menu item and its children will be shown.'),
- );
-
- return system_settings_form('menu_configure', $form);
-}
-
-/**
- * Menu callback; handle the adding/editing of a new menu.
- */
-function menu_edit_menu_form($mid = 0) {
- if (arg(3) == 'edit') {
- if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) {
- drupal_not_found();
- return;
- }
- }
- else {
- $item = array('mid' => 0, 'pid' => 0, 'path' => '', 'weight' => 0, 'type' => MENU_CUSTOM_MENU);
- }
- $form['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $item['title'],
- '#description' => t('The name of the menu.'),
- '#required' => TRUE,
- );
- $form['mid'] = array('#type' => 'value', '#value' => $item['mid']);
- $form['pid'] = array('#type' => 'value', '#value' => $item['pid']);
- $form['path'] = array('#type' => 'value', '#value' => $item['path']);
- $form['weight'] = array('#type' => 'value', '#value' => $item['weight']);
- $form['type'] = array('#type' => 'value', '#value' => $item['type']);
- $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
-
- // Reuse the submit function of menu_edit_item_form.
- return drupal_get_form('menu_edit_menu_form', $form, 'menu_edit_item_form');
-}
-
-/**
- * Present the menu item editing form.
- */
-function menu_edit_item_form($mid = 0) {
- if (arg(3) == 'edit') {
- if (!($item = db_fetch_array(db_query('SELECT * FROM {menu} WHERE mid = %d', $mid)))) {
- drupal_not_found();
- return;
- }
- }
- else {
- // This is an add form.
- $item = array('mid' => 0, 'pid' => 1, 'weight' => 0, 'type' => MENU_CUSTOM_ITEM);
- }
-
- $form['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $item['title'],
- '#description' => t('The name of the menu item.'),
- '#required' => TRUE,
- );
- $form['description'] = array('#type' => 'textfield',
- '#title' => t('Description'),
- '#default_value' => $item['description'],
- '#description' => t('The description displayed when hovering over a menu item.'),
- );
-
- if ($item['type'] & MENU_CREATED_BY_ADMIN) {
- $form['path'] = array('#type' => 'textfield',
- '#title' => t('Path'),
- '#default_value' => $item['path'],
- '#description' => t('The path this menu item links to. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => theme('placeholder', '<front>'), '%add-node' => theme('placeholder', 'node/add'), '%drupal' => theme('placeholder', 'http://drupal.org'))),
- '#required' => TRUE,
- );
- }
- else {
- $form['_path'] = array('#type' => 'item',
- '#title' => t('Path'),
- '#description' => l($item['path'], $item['path']),
- );
- $form['path'] = array('#type' => 'value', '#value' => $item['path']);
- }
-
- $expanded = $item['type'] & MENU_EXPANDED ? 1 : 0;
- $form['expanded'] = array('#type' => 'checkbox',
- '#title' => t('Expanded'),
- '#default_value' => $expanded,
- '#description' => t('If selected and this menu item has children, the menu will always appear expanded.'),
- );
-
- // Generate a list of possible parents (not including this item or descendants).
- // Default to "Navigation" menu for new items.
- $options = menu_parent_options($item['mid']);
- $form['pid'] = array('#type' => 'select',
- '#title' => t('Parent item'),
- '#default_value' => $item['pid'],
- '#options' => $options,
- );
- $form['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $item['weight'],
- '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
- );
-
- // Always enable menu items (but not menus) when editing them.
- if (!($item['type'] & MENU_IS_ROOT)) {
- $item['type'] |= MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB;
- }
-
- $form['type'] = array('#type' => 'value', '#value' => $item['type']);
- $form['mid'] = array('#type' => 'value', '#value' => $item['mid']);
- $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
-
- return drupal_get_form('menu_edit_item_form', $form);
-}
-
-/**
- * Process menu and menu item add/edit form submissions.
- */
-function menu_edit_item_form_submit($form_id, $form_values) {
- menu_edit_item_save($form_values);
- return 'admin/menu';
-}
-
-/**
- * Menu callback; delete a single custom item.
- */
-function menu_item_delete_form($mid) {
- if (!($menu = db_fetch_object(db_query('SELECT type, title FROM {menu} WHERE mid = %d', $mid)))) {
- drupal_not_found();
- return;
- }
-
- $form['mid'] = array('#type' => 'value', '#value' => $mid);
- $form['type'] = array('#type' => 'value', '#value' => $menu->type);
- $form['title'] = array('#type' => 'value', '#value' => $menu->title);
-
- if ($menu->type & MENU_IS_ROOT) {
- $message = t('Are you sure you want to delete the menu %item?', array('%item' => theme('placeholder', $menu->title)));
- }
- else {
- $message = t('Are you sure you want to delete the custom menu item %item?', array('%item' => theme('placeholder', $menu->title)));
- }
-
- return confirm_form('menu_confirm_delete_form', $form, $message, 'admin/menu', t('This action cannot be undone.'), t('Delete'));
-}
-
-/**
- * Process menu delete form submissions.
- */
-function menu_confirm_delete_form_submit($form_id, $form_values) {
- menu_delete_item($form_values['mid']);
-
- $t_args = array('%title' => theme('placeholder', $form_values['title']));
- if ($form_values['type'] & MENU_IS_ROOT) {
- drupal_set_message(t('The menu %title has been deleted.', $t_args));
- watchdog('menu', t('Deleted menu %title.', $t_args), WATCHDOG_NOTICE);
- }
- else {
- drupal_set_message(t('The menu item %title has been deleted.', $t_args));
- watchdog('menu', t('Deleted menu item %title.', $t_args), WATCHDOG_NOTICE);
- }
-
- return 'admin/menu';
-}
-
-/**
- * Menu callback; reset a single modified item.
- */
-function menu_reset_item($mid) {
- if (isset($mid) && $title = db_result(db_query('SELECT title FROM {menu} WHERE mid = %d', $mid))) {
- $form['mid'] = array('#type' => 'value', '#value' => $mid);
- return confirm_form('menu_reset_item_form', $form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => theme('placeholder', $title))), 'admin/menu', t('Any customizations will be lost. This action cannot be undone.'), t('Reset'));
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Process menu reset item form submissions.
- */
-function menu_reset_item_form_submit($form_id, $form_values) {
- menu_delete_item($form_values['mid']);
- drupal_set_message(t('The menu item was reset to its default settings.'));
-
- return 'admin/menu';
-}
-
-/**
- * Menu callback; hide a menu item.
- */
-function menu_disable_item($mid) {
- $item = menu_get_item($mid);
- $type = $item['type'];
- $type &= ~MENU_VISIBLE_IN_TREE;
- $type &= ~MENU_VISIBLE_IN_BREADCRUMB;
- $type |= MENU_MODIFIED_BY_ADMIN;
- db_query('UPDATE {menu} SET type = %d WHERE mid = %d', $type, $mid);
- drupal_set_message(t('The menu item has been disabled.'));
- drupal_goto('admin/menu');
-}
-
-/**
- * Menu callback; present the main menu management page.
- */
-function menu_overview() {
- menu_rebuild();
-
- return menu_overview_tree();
-}
-
-/**
- * Save changes to a menu item into the database.
- *
- * @return mid
- */
-function menu_edit_item_save($edit) {
- if ($edit['expanded']) {
- $edit['type'] |= MENU_EXPANDED;
- }
- else {
- $edit['type'] &= ~MENU_EXPANDED;
- }
-
- $edit['type'] = $edit['type'] | MENU_MODIFIED_BY_ADMIN;
-
- $status = menu_save_item($edit);
-
- $t_args = array('%title' => theme('placeholder', $edit['title']));
- if ($status == SAVED_UPDATED) {
- drupal_set_message(t('The menu item %title has been updated.', $t_args));
- }
- elseif ($status == SAVED_NEW) {
- drupal_set_message(t('The menu item %title has been added.', $t_args));
- watchdog('menu', t('Added menu item %title.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/menu'));
- }
- return $edit['mid'];
-}
-
-/**
- * Save a menu item to the database.
- *
- * @param $item
- * The menu item to be saved. This is passed by reference, so that the newly
- * generated $item['mid'] can be accessed after an insert takes place.
- *
- * @return $status
- * The operation that was performed in saving. Either SAVED_NEW (if a new
- * menu item was created), or SAVED_UPDATED (if an existing menu item was
- * updated).
- */
-function menu_save_item(&$item) {
- $existing_item = NULL;
-
- // Check that the item already exists in the menu tree, if $item['mid'] is
- // specified.
- if (isset($item['mid'])) {
- $existing_item = menu_get_item($item['mid']);
- }
-
- if ($item['mid'] && !empty($existing_item)) {
- db_query("UPDATE {menu} SET pid = %d, path = '%s', title = '%s', description = '%s', weight = %d, type = %d WHERE mid = %d", $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type'], $item['mid']);
- return SAVED_UPDATED;
- }
- else {
- $item['mid'] = db_next_id('{menu}_mid');
- // Check explicitly for mid <= 2. If the database was improperly prefixed,
- // this would cause a nasty infinite loop or duplicate mid errors.
- // TODO: have automatic prefixing through an installer to prevent this.
- while ($item['mid'] <= 2) {
- $item['mid'] = db_next_id('{menu}_mid');
- }
- db_query("INSERT INTO {menu} (mid, pid, path, title, description, weight, type) VALUES (%d, %d, '%s', '%s', '%s', %d, %d)", $item['mid'], $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type']);
- return SAVED_NEW;
- }
-}
-
-/**
- * Delete a menu item from the database. If $item['mid'] is specified, then
- * this is used to find the existing item; otherwise, $item['path'] is used.
- *
- * @param $item
- * The menu item to be deleted.
- */
-function menu_delete_item($item) {
- if (!is_array($item)) {
- $item = array('mid' => $item);
- }
-
- if ($item['mid']) {
- db_query('DELETE FROM {menu} WHERE mid = %d', $item['mid']);
- }
- elseif ($item['path']) {
- db_query("DELETE FROM {menu} WHERE path = '%s'", $item['path']);
- }
-}
-
-/**
- * Present the menu tree, rendered along with links to edit menu items.
- */
-function menu_overview_tree() {
- $menu = menu_get_menu();
- $root_menus = menu_get_root_menus();
- $header = array(t('Menu item'), t('Expanded'), array('data' => t('Operations'), 'colspan' => '3'));
- $output = '';
-
- foreach ($root_menus as $mid => $title) {
- $operations = array();
- if ($menu['items'][$mid]['type'] & MENU_MODIFIABLE_BY_ADMIN) {
- $operations[] = l(t('edit'), 'admin/menu/menu/edit/'. $mid);
- }
- if ($menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN) {
- $operations[] = l(t('delete'), 'admin/menu/menu/delete/'. $mid);
- }
- $table = theme('item_list', $operations);
- $table .= theme('table', $header, menu_overview_tree_rows($mid));
- $output .= theme('box', $title, $table);
- }
- return $output;
-}
-
-function menu_overview_tree_rows($pid = 0, $depth = 0) {
- $parent_item = menu_get_item($pid);
- $rows = array();
-
- if (isset($parent_item) && isset($parent_item['children'])) {
- usort($parent_item['children'], '_menu_sort');
- foreach ($parent_item['children'] as $mid) {
- $item = menu_get_item($mid);
- // Populate the title field.
- $title = '';
- if ($pid == 0) {
- // Top-level items are menu names, and don't have an associated path.
- $title .= $item['title'];
- }
- else {
- $title .= l($item['title'], $item['path']);
- }
- if ($depth > 0) {
- $title = '-&nbsp;'. $title;
- }
- for ($i = 1; $i < $depth; $i++) {
- $title = '&nbsp;&nbsp;'. $title;
- }
-
- // Populate the operations field.
- $operations = array();
- if (!($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) {
- $operations[] = array('data' => t('locked'), 'colspan' => '3', 'align' => 'center');
- }
- else {
- // Set the edit column.
- if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) {
- $operations[] = array('data' => l(t('edit'), 'admin/menu/item/edit/'. $mid));
- }
- else {
- $operations[] = array('data' => '');
- }
-
- // Set the disable column.
- if ($item['type'] & (MENU_IS_ROOT | MENU_VISIBLE_IF_HAS_CHILDREN)) {
- // Disabling entire menus is done from block admin page.
- // MENU_VISIBLE_IF_HAS_CHILDREN menus are always enabled so hide this operation.
- $operations[] = array('data' => '');
- }
- else if ($item['type'] & MENU_VISIBLE_IN_TREE) {
- $operations[] = array('data' => l(t('disable'), 'admin/menu/item/disable/'. $mid));
- }
- else {
- $operations[] = array('data' => l(t('enable'), 'admin/menu/item/edit/'. $mid));
- }
-
- // Set the reset column.
- if ($item['type'] & MENU_CREATED_BY_ADMIN) {
- $operations[] = array('data' => l(t('delete'), 'admin/menu/item/delete/'. $mid));
- }
- else if ($item['type'] & MENU_MODIFIED_BY_ADMIN) {
- $operations[] = array('data' => l(t('reset'), 'admin/menu/item/reset/'. $mid));
- }
- else {
- $operations[] = array('data' => '');
- }
- }
-
- // Call out disabled items.
- if ($item['type'] & (MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IF_HAS_CHILDREN)) {
- $class = 'menu-enabled';
- }
- else {
- $title .= ' ('. t('disabled') .')';
- $class = 'menu-disabled';
- }
-
- if ($item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_VISIBLE_IN_TREE)) {
- $row = array(array('data' => $title, 'class' => $class), array('data' => ($item['children'] ? (($item['type'] & MENU_EXPANDED) ? t('Yes') : t('No')) : ''), 'class' => $class));
- foreach ($operations as $operation) {
- $operation['class'] = $class;
- $row[] = $operation;
- }
- $rows[] = $row;
- $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth + 1));
- }
- else {
- // Skip items that are hidden and locked; admins will never care about them.
- $rows = array_merge($rows, menu_overview_tree_rows($mid, $depth));
- }
- }
- }
-
- return $rows;
-}
-
-/**
- * Return a list of menu items that are valid possible parents for the
- * given menu item. The list excludes the given item and its children.
- *
- * @param $mid
- * The menu item id for which to generate a list of parents.
- * If $mid == 0 then the complete tree is returned.
- * @param $pid
- * The menu item id of the menu item at which to start the tree.
- * If $pid > 0 then this item will be included in the tree.
- * @param $depth
- * The current depth in the tree - used when recursing to indent the tree.
- * @return
- * An array of menu titles keyed on the mid.
- */
-function menu_parent_options($mid, $pid = 0, $depth = 0) {
- $options = array();
-
- if (!($parent_item = menu_get_item($pid))) {
- return $options;
- }
-
- // Exclude $mid and its children from the list unless $mid is 0.
- if ($mid && $mid == $pid) {
- return $options;
- }
-
- // Add the current $pid to the list.
- if ($pid > 0 && ($parent_item['type'] & (MENU_MODIFIABLE_BY_ADMIN | MENU_IS_ROOT))) {
- $title = ' '. $parent_item['title'];
- for ($i = 0; $i < $depth; $i++) {
- $title = '--'. $title;
- }
- if (!($parent_item['type'] & MENU_VISIBLE_IN_TREE)) {
- $title .= ' ('. t('disabled') .')';
- }
- $options[$pid] = $title;
- $depth ++;
- }
-
- // Add children of $pid to the list recursively.
- if ($parent_item['children']) {
- usort($parent_item['children'], '_menu_sort');
- foreach ($parent_item['children'] as $child) {
- $options += menu_parent_options($mid, $child, $depth);
- }
- }
-
- return $options;
-}
-
-/**
- * Remove the menu item.
- */
-function menu_node_form_delete($node) {
- menu_delete_item(array('path' => 'node/'. $node->nid));
-}
diff --git a/modules/node/node.module b/modules/node/node.module
deleted file mode 100644
index 092523f..0000000
--- a/modules/node/node.module
+++ /dev/null
@@ -1,2421 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * The core that allows content to be submitted to the site.
- */
-
-define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);
-
-/**
- * Implementation of hook_help().
- */
-function node_help($section) {
- switch ($section) {
- case 'admin/help#node':
- $output = '<p>'. t('All content in a website is stored and treated as <b>nodes</b>. Therefore nodes are any postings such as blogs, stories, polls and forums. The node module manages these content types and is one of the strengths of Drupal over other content management systems.') .'</p>';
- $output .= '<p>'. t('Treating all content as nodes allows the flexibility of creating new types of content. It also allows you to painlessly apply new features or changes to all content. Comments are not stored as nodes but are always associated with a node.') .'</p>';
- $output .= t('<p>Node module features</p>
-<ul>
-<li>The list tab provides an interface to search and sort all content on your site.</li>
-<li>The configure settings tab has basic settings for content on your site.</li>
-<li>The configure content types tab lists all content types for your site and lets you configure their default workflow.</li>
-<li>The search tab lets you search all content on your site</li>
-</ul>
-');
- $output .= t('<p>You can</p>
-<ul>
-<li>search for content at <a href="%search">search</a>.</li>
-<li>administer nodes at <a href="%admin-settings-content-types">administer &gt;&gt; settings &gt;&gt; content types</a>.</li>
-</ul>
-', array('%search' => url('search'), '%admin-settings-content-types' => url('admin/settings/content-types')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%node">Node page</a>.', array('%node' => 'http://drupal.org/handbook/modules/node/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows content to be submitted to the site and displayed on pages.');
- case 'admin/node/configure':
- case 'admin/node/configure/settings':
- return t('<p>Settings for the core of Drupal. Almost everything is a node so these settings will affect most of the site.</p>');
- case 'admin/node':
- return t('<p>Below is a list of all of the posts on your site. Other forms of content are listed elsewhere (e.g. <a href="%comments">comments</a>).</p><p>Clicking a title views the post, while clicking an author\'s name views their user information.</p>', array('%comments' => url('admin/comment')));
- case 'admin/node/search':
- return t('<p>Enter a simple pattern to search for a post. This can include the wildcard character *.<br />For example, a search for "br*" might return "bread bakers", "our daily bread" and "brenda".</p>');
- }
-
- if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'revisions') {
- return t('The revisions let you track differences between multiple versions of a post.');
- }
-
- if (arg(0) == 'node' && arg(1) == 'add' && $type = arg(2)) {
- return variable_get($type .'_help', '');
- }
-}
-
-/**
- * Implementation of hook_cron().
- */
-function node_cron() {
- db_query('DELETE FROM {history} WHERE timestamp < %d', NODE_NEW_LIMIT);
-}
-
-/**
- * Gather a listing of links to nodes.
- *
- * @param $result
- * A DB result object from a query to fetch node objects. If your query joins the <code>node_comment_statistics</code> table so that the <code>comment_count</code> field is available, a title attribute will be added to show the number of comments.
- * @param $title
- * A heading for the resulting list.
- *
- * @return
- * An HTML list suitable as content for a block.
- */
-function node_title_list($result, $title = NULL) {
- while ($node = db_fetch_object($result)) {
- $items[] = l($node->title, 'node/'. $node->nid, $node->comment_count ? array('title' => format_plural($node->comment_count, '1 comment', '%count comments')) : '');
- }
-
- return theme('node_list', $items, $title);
-}
-
-/**
- * Format a listing of links to nodes.
- */
-function theme_node_list($items, $title = NULL) {
- return theme('item_list', $items, $title);
-}
-
-/**
- * Update the 'last viewed' timestamp of the specified node for current user.
- */
-function node_tag_new($nid) {
- global $user;
-
- if ($user->uid) {
- if (node_last_viewed($nid)) {
- db_query('UPDATE {history} SET timestamp = %d WHERE uid = %d AND nid = %d', time(), $user->uid, $nid);
- }
- else {
- @db_query('INSERT INTO {history} (uid, nid, timestamp) VALUES (%d, %d, %d)', $user->uid, $nid, time());
- }
- }
-}
-
-/**
- * Retrieves the timestamp at which the current user last viewed the
- * specified node.
- */
-function node_last_viewed($nid) {
- global $user;
- static $history;
-
- if (!isset($history[$nid])) {
- $history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE uid = '$user->uid' AND nid = %d", $nid));
- }
-
- return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0);
-}
-
-/**
- * Decide on the type of marker to be displayed for a given node.
- *
- * @param $nid
- * Node ID whose history supplies the "last viewed" timestamp.
- * @param $timestamp
- * Time which is compared against node's "last viewed" timestamp.
- * @return
- * One of the MARK constants.
- */
-function node_mark($nid, $timestamp) {
- global $user;
- static $cache;
-
- if (!$user->uid) {
- return MARK_READ;
- }
- if (!isset($cache[$nid])) {
- $cache[$nid] = node_last_viewed($nid);
- }
- if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
- return MARK_NEW;
- }
- elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
- return MARK_UPDATED;
- }
- return MARK_READ;
-}
-
-/**
- * Automatically generate a teaser for a node body in a given format.
- */
-function node_teaser($body, $format = NULL) {
-
- $size = variable_get('teaser_length', 600);
-
- // find where the delimiter is in the body
- $delimiter = strpos($body, '<!--break-->');
-
- // If the size is zero, and there is no delimiter, the entire body is the teaser.
- if ($size == 0 && $delimiter === FALSE) {
- return $body;
- }
-
- // We check for the presence of the PHP evaluator filter in the current
- // format. If the body contains PHP code, we do not split it up to prevent
- // parse errors.
- if (isset($format)) {
- $filters = filter_list_format($format);
- if (isset($filters['filter/1']) && strpos($body, '<?') !== FALSE) {
- return $body;
- }
- }
-
- // If a valid delimiter has been specified, use it to chop of the teaser.
- if ($delimiter !== FALSE) {
- return substr($body, 0, $delimiter);
- }
-
- // If we have a short body, the entire body is the teaser.
- if (strlen($body) < $size) {
- return $body;
- }
-
- // In some cases, no delimiter has been specified (e.g. when posting using
- // the Blogger API). In this case, we try to split at paragraph boundaries.
- // When even the first paragraph is too long, we try to split at the end of
- // the next sentence.
- $breakpoints = array('</p>' => 4, '<br />' => 0, '<br>' => 0, "\n" => 0, '. ' => 1, '! ' => 1, '? ' => 1, '。' => 1, '؟ ' => 1);
- foreach ($breakpoints as $point => $charnum) {
- if ($length = strpos($body, $point, $size)) {
- return substr($body, 0, $length + $charnum);
- }
- }
-
- // If all else fails, we simply truncate the string.
- return truncate_utf8($body, $size);
-}
-
-function _node_names($op = '', $node = NULL) {
- static $node_names = array();
- static $node_list = array();
-
- if (empty($node_names)) {
- $node_names = module_invoke_all('node_info');
- foreach ($node_names as $type => $value) {
- $node_list[$type] = $value['name'];
- }
- }
- if ($node) {
- if (is_array($node)) {
- $type = $node['type'];
- }
- elseif (is_object($node)) {
- $type = $node->type;
- }
- elseif (is_string($node)) {
- $type = $node;
- }
- if (!isset($node_names[$type])) {
- return FALSE;
- }
- }
- switch ($op) {
- case 'base':
- return $node_names[$type]['base'];
- case 'list':
- return $node_list;
- case 'name':
- return $node_list[$type];
- }
-}
-
-/**
- * Determine the basename for hook_load etc.
- *
- * @param $node
- * Either a node object, a node array, or a string containing the node type.
- * @return
- * The basename for hook_load, hook_nodeapi etc.
- */
-function node_get_base($node) {
- return _node_names('base', $node);
-}
-
-/**
- * Determine the human readable name for a given type.
- *
- * @param $node
- * Either a node object, a node array, or a string containing the node type.
- * @return
- * The human readable name of the node type.
- */
-function node_get_name($node) {
- return _node_names('name', $node);
-}
-
-/**
- * Return the list of available node types.
- *
- * @param $node
- * Either a node object, a node array, or a string containing the node type.
- * @return
- * An array consisting ('#type' => name) pairs.
- */
-function node_get_types() {
- return _node_names('list');
-}
-
-/**
- * Determine whether a node hook exists.
- *
- * @param &$node
- * Either a node object, node array, or a string containing the node type.
- * @param $hook
- * A string containing the name of the hook.
- * @return
- * TRUE iff the $hook exists in the node type of $node.
- */
-function node_hook(&$node, $hook) {
- return module_hook(node_get_base($node), $hook);
-}
-
-/**
- * Invoke a node hook.
- *
- * @param &$node
- * Either a node object, node array, or a string containing the node type.
- * @param $hook
- * A string containing the name of the hook.
- * @param $a2, $a3, $a4
- * Arguments to pass on to the hook, after the $node argument.
- * @return
- * The returned value of the invoked hook.
- */
-function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
- if (node_hook($node, $hook)) {
- $function = node_get_base($node) ."_$hook";
- return ($function($node, $a2, $a3, $a4));
- }
-}
-
-/**
- * Invoke a hook_nodeapi() operation in all modules.
- *
- * @param &$node
- * A node object.
- * @param $op
- * A string containing the name of the nodeapi operation.
- * @param $a3, $a4
- * Arguments to pass on to the hook, after the $node and $op arguments.
- * @return
- * The returned value of the invoked hooks.
- */
-function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
- $return = array();
- foreach (module_implements('nodeapi') as $name) {
- $function = $name .'_nodeapi';
- $result = $function($node, $op, $a3, $a4);
- if (isset($result) && is_array($result)) {
- $return = array_merge($return, $result);
- }
- else if (isset($result)) {
- $return[] = $result;
- }
- }
- return $return;
-}
-
-/**
- * Load a node object from the database.
- *
- * @param $param
- * Either the nid of the node or an array of conditions to match against in the database query
- * @param $revision
- * Which numbered revision to load. Defaults to the current version.
- * @param $reset
- * Whether to reset the internal node_load cache.
- *
- * @return
- * A fully-populated node object.
- */
-function node_load($param = array(), $revision = NULL, $reset = NULL) {
- static $nodes = array();
-
- if ($reset) {
- $nodes = array();
- }
-
- $arguments = array();
- if (is_numeric($param)) {
- $cachable = $revision == NULL;
- if ($cachable && isset($nodes[$param])) {
- return $nodes[$param];
- }
- $cond = 'n.nid = %d';
- $arguments[] = $param;
- }
- else {
- // Turn the conditions into a query.
- foreach ($param as $key => $value) {
- $cond[] = 'n.'. db_escape_string($key) ." = '%s'";
- $arguments[] = $value;
- }
- $cond = implode(' AND ', $cond);
- }
-
- // Retrieve the node.
- if ($revision) {
- array_unshift($arguments, $revision);
- $node = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, r.vid, n.type, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. $cond), $arguments));
- }
- else {
- $node = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.vid, n.type, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. $cond), $arguments));
- }
-
- if ($node->nid) {
- // Call the node specific callback (if any) and piggy-back the
- // results to the node or overwrite some values.
- if ($extra = node_invoke($node, 'load')) {
- foreach ($extra as $key => $value) {
- $node->$key = $value;
- }
- }
-
- if ($extra = node_invoke_nodeapi($node, 'load')) {
- foreach ($extra as $key => $value) {
- $node->$key = $value;
- }
- }
- }
-
- if ($cachable) {
- $nodes[$param] = $node;
- }
-
- return $node;
-}
-
-/**
- * Save a node object into the database.
- */
-function node_save(&$node) {
- global $user;
-
- $node->is_new = false;
-
- // Apply filters to some default node fields:
- if (empty($node->nid)) {
- // Insert a new node.
- $node->is_new = true;
-
- // Set some required fields:
- if (!$node->created) {
- $node->created = time();
- }
- $node->nid = db_next_id('{node}_nid');
- $node->vid = db_next_id('{node_revisions}_vid');;
- }
- else {
- // We need to ensure that all node fields are filled.
- $node_current = node_load($node->nid);
- foreach ($node as $field => $data) {
- $node_current->$field = $data;
- }
- $node = $node_current;
-
- if ($node->revision) {
- $node->old_vid = $node->vid;
- $node->vid = db_next_id('{node_revisions}_vid');
- }
- }
-
- // If node has never changed, set $node->changed to $node->created
- // If we set $node->created to time(), then 'changed' and 'created' will be
- // different for new nodes which were previewed before submission
- // The changed timestamp is always updated for bookkeeping purposes (revisions, searching, ...)
- $node->changed = $node->changed ? time() : $node->created;
-
- // Split off revisions data to another structure
- $revisions_table_values = array('nid' => $node->nid, 'vid' => $node->vid,
- 'title' => $node->title, 'body' => $node->body,
- 'teaser' => $node->teaser, 'log' => $node->log, 'timestamp' => $node->changed,
- 'uid' => $user->uid, 'format' => $node->format);
- $revisions_table_types = array('nid' => '%d', 'vid' => '%d',
- 'title' => "'%s'", 'body' => "'%s'",
- 'teaser' => "'%s'", 'log' => "'%s'", 'timestamp' => '%d',
- 'uid' => '%d', 'format' => '%d');
- $node_table_values = array('nid' => $node->nid, 'vid' => $node->vid,
- 'title' => $node->title, 'type' => $node->type, 'uid' => $node->uid,
- 'status' => $node->status, 'created' => $node->created,
- 'changed' => $node->changed, 'comment' => $node->comment,
- 'promote' => $node->promote, 'moderate' => $node->moderate,
- 'sticky' => $node->sticky);
- $node_table_types = array('nid' => '%d', 'vid' => '%d',
- 'title' => "'%s'", 'type' => "'%s'", 'uid' => '%d',
- 'status' => '%d', 'created' => '%d',
- 'changed' => '%d', 'comment' => '%d',
- 'promote' => '%d', 'moderate' => '%d',
- 'sticky' => '%d');
-
- //Generate the node table query and the
- //the node_revisions table query
- if ($node->is_new) {
- $node_query = 'INSERT INTO {node} ('. implode(', ', array_keys($node_table_types)) .') VALUES ('. implode(', ', $node_table_types) .')';
- $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')';
- }
- else {
- $arr = array();
- foreach ($node_table_types as $key => $value) {
- $arr[] = $key .' = '. $value;
- }
- $node_table_values[] = $node->nid;
- $node_query = 'UPDATE {node} SET '. implode(', ', $arr) .' WHERE nid = %d';
- if ($node->revision) {
- $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')';
- }
- else {
- $arr = array();
- foreach ($revisions_table_types as $key => $value) {
- $arr[] = $key .' = '. $value;
- }
- $revisions_table_values[] = $node->vid;
- $revisions_query = 'UPDATE {node_revisions} SET '. implode(', ', $arr) .' WHERE vid = %d';
- }
- }
-
- // Insert the node into the database:
- db_query($node_query, $node_table_values);
- db_query($revisions_query, $revisions_table_values);
-
- // Call the node specific callback (if any):
- if ($node->is_new) {
- node_invoke($node, 'insert');
- node_invoke_nodeapi($node, 'insert');
- }
- else {
- node_invoke($node, 'update');
- node_invoke_nodeapi($node, 'update');
- }
-
- // Clear the cache so an anonymous poster can see the node being added or updated.
- cache_clear_all();
-}
-
-/**
- * Generate a display of the given node.
- *
- * @param $node
- * A node array or node object.
- * @param $teaser
- * Whether to display the teaser only, as on the main page.
- * @param $page
- * Whether the node is being displayed by itself as a page.
- * @param $links
- * Whether or not to display node links. Links are omitted for node previews.
- *
- * @return
- * An HTML representation of the themed node.
- */
-function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
- $node = (object)$node;
-
- // Remove the delimiter (if any) that separates the teaser from the body.
- // TODO: this strips legitimate uses of '<!--break-->' also.
- $node->body = str_replace('<!--break-->', '', $node->body);
-
- if ($node->log != '' && !$teaser && $node->moderate) {
- $node->body .= '<div class="log"><div class="title">'. t('Log') .':</div>'. filter_xss($node->log) .'</div>';
- }
-
- // The 'view' hook can be implemented to overwrite the default function
- // to display nodes.
- if (node_hook($node, 'view')) {
- node_invoke($node, 'view', $teaser, $page);
- }
- else {
- $node = node_prepare($node, $teaser);
- }
- // Allow modules to change $node->body before viewing.
- node_invoke_nodeapi($node, 'view', $teaser, $page);
- if ($links) {
- $node->links = module_invoke_all('link', 'node', $node, !$page);
- }
- // unset unused $node part so that a bad theme can not open a security hole
- if ($teaser) {
- unset($node->body);
- }
- else {
- unset($node->teaser);
- }
-
- return theme('node', $node, $teaser, $page);
-}
-
-/**
- * Apply filters to a node in preparation for theming.
- */
-function node_prepare($node, $teaser = FALSE) {
- $node->readmore = (strlen($node->teaser) < strlen($node->body));
- if ($teaser == FALSE) {
- $node->body = check_markup($node->body, $node->format, FALSE);
- }
- else {
- $node->teaser = check_markup($node->teaser, $node->format, FALSE);
- }
- return $node;
-}
-
-/**
- * Generate a page displaying a single node, along with its comments.
- */
-function node_show($node, $cid) {
- $output = node_view($node, FALSE, TRUE);
-
- if (function_exists('comment_render') && $node->comment) {
- $output .= comment_render($node, $cid);
- }
-
- // Update the history table, stating that this user viewed this node.
- node_tag_new($node->nid);
-
- return $output;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function node_perm() {
- return array('administer nodes', 'access content', 'view revisions', 'revert revisions');
-}
-
-/**
- * Implementation of hook_search().
- */
-function node_search($op = 'search', $keys = null) {
- switch ($op) {
- case 'name':
- return t('content');
-
- case 'reset':
- variable_del('node_cron_last');
- variable_del('node_cron_last_nid');
- return;
-
- case 'status':
- $last = variable_get('node_cron_last', 0);
- $last_nid = variable_get('node_cron_last_nid', 0);
- $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1'));
- $remaining = db_result(db_query('SELECT COUNT(*) FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND ((GREATEST(n.created, n.changed, c.last_comment_timestamp) = %d AND n.nid > %d ) OR (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d))', $last, $last_nid, $last, $last, $last));
- return array('remaining' => $remaining, 'total' => $total);
-
- case 'admin':
- $form = array();
- // Output form for defining rank factor weights.
- $form['content_ranking'] = array('#type' => 'fieldset', '#title' => t('Content ranking'));
- $form['content_ranking']['#theme'] = 'node_search_admin';
- $form['content_ranking']['info'] = array('#type' => 'markup', '#value' => '<em>'. t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') .'</em>');
-
- $ranking = array('node_rank_relevance' => t('Keyword relevance'),
- 'node_rank_recent' => t('Recently posted'));
- if (module_exist('comment')) {
- $ranking['node_rank_comments'] = t('Number of comments');
- }
- if (module_exist('statistics') && variable_get('statistics_count_content_views', 0)) {
- $ranking['node_rank_views'] = t('Number of views');
- }
-
- // Note: reversed to reflect that higher number = higher ranking.
- $options = drupal_map_assoc(range(0, 10));
- foreach ($ranking as $var => $title) {
- $form['content_ranking']['factors'][$var] = array('#title' => $title, '#type' => 'select', '#options' => $options, '#default_value' => variable_get($var, 5));
- }
- return $form;
-
- case 'search':
- // Build matching conditions
- list($join1, $where1) = _db_rewrite_sql();
- $arguments1 = array();
- $conditions1 = 'n.status = 1';
-
- if ($type = search_query_extract($keys, 'type')) {
- $types = array();
- foreach (explode(',', $type) as $t) {
- $types[] = "n.type = '%s'";
- $arguments1[] = $t;
- }
- $conditions1 .= ' AND ('. implode(' OR ', $types) .')';
- $keys = search_query_insert($keys, 'type');
- }
-
- if ($category = search_query_extract($keys, 'category')) {
- $categories = array();
- foreach (explode(',', $category) as $c) {
- $categories[] = "tn.tid = %d";
- $arguments1[] = $c;
- }
- $conditions1 .= ' AND ('. implode(' OR ', $categories) .')';
- $join1 .= ' INNER JOIN {term_node} tn ON n.nid = tn.nid';
- $keys = search_query_insert($keys, 'category');
- }
-
- // Build ranking expression (we try to map each parameter to a
- // uniform distribution in the range 0..1).
- $ranking = array();
- $arguments2 = array();
- $join2 = '';
- // Used to avoid joining on node_comment_statistics twice
- $stats_join = false;
- if ($weight = (int)variable_get('node_rank_relevance', 5)) {
- // Average relevance values hover around 0.15
- $ranking[] = '%d * i.relevance';
- $arguments2[] = $weight;
- }
- if ($weight = (int)variable_get('node_rank_recent', 5)) {
- // Exponential decay with half-life of 6 months, starting at last indexed node
- $ranking[] = '%d * POW(2, (GREATEST(n.created, n.changed, c.last_comment_timestamp) - %d) * 6.43e-8)';
- $arguments2[] = $weight;
- $arguments2[] = (int)variable_get('node_cron_last', 0);
- $join2 .= ' INNER JOIN {node} n ON n.nid = i.sid LEFT JOIN {node_comment_statistics} c ON c.nid = i.sid';
- $stats_join = true;
- }
- if (module_exist('comment') && $weight = (int)variable_get('node_rank_comments', 5)) {
- // Inverse law that maps the highest reply count on the site to 1 and 0 to 0.
- $scale = variable_get('node_cron_comments_scale', 0.0);
- $ranking[] = '%d * (2.0 - 2.0 / (1.0 + c.comment_count * %f))';
- $arguments2[] = $weight;
- $arguments2[] = $scale;
- if (!$stats_join) {
- $join2 .= ' LEFT JOIN {node_comment_statistics} c ON c.nid = i.sid';
- }
- }
- if (module_exist('statistics') && variable_get('statistics_count_content_views', 0) &&
- $weight = (int)variable_get('node_rank_views', 5)) {
- // Inverse law that maps the highest view count on the site to 1 and 0 to 0.
- $scale = variable_get('node_cron_views_scale', 0.0);
- $ranking[] = '%d * (2.0 - 2.0 / (1.0 + nc.totalcount * %f))';
- $arguments2[] = $weight;
- $arguments2[] = $scale;
- $join2 .= ' LEFT JOIN {node_counter} nc ON n.nid = nc.nid';
- }
- $select2 = (count($ranking) ? implode(' + ', $ranking) : 'i.relevance') . ' AS score';
-
- // Do search
- $find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid '. $join1 .' INNER JOIN {users} u ON n.uid = u.uid', $conditions1 . (empty($where1) ? '' : ' AND '. $where1), $arguments1, $select2, $join2, $arguments2);
-
- // Load results
- $results = array();
- foreach ($find as $item) {
- $node = node_load($item->sid);
-
- // Get node output (filtered and with module-specific fields).
- if (node_hook($node, 'view')) {
- node_invoke($node, 'view', false, false);
- }
- else {
- $node = node_prepare($node, false);
- }
- // Allow modules to change $node->body before viewing.
- node_invoke_nodeapi($node, 'view', false, false);
-
- // Fetch comments for snippet
- $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
-
- $extra = node_invoke_nodeapi($node, 'search result');
- $results[] = array('link' => url('node/'. $item->sid),
- 'type' => node_get_name($node),
- 'title' => $node->title,
- 'user' => theme('username', $node),
- 'date' => $node->changed,
- 'node' => $node,
- 'extra' => $extra,
- 'snippet' => search_excerpt($keys, $node->body));
- }
- return $results;
- }
-}
-
-/**
- * Implementation of hook_user().
- */
-function node_user($op, &$edit, &$user) {
- if ($op == 'delete') {
- db_query('UPDATE {node} SET uid = 0 WHERE uid = %d', $user->uid);
- db_query('UPDATE {node_revisions} SET uid = 0 WHERE uid = %d', $user->uid);
- }
-}
-
-function theme_node_search_admin($form) {
- $output = form_render($form['info']);
-
- $header = array(t('Factor'), t('Weight'));
- foreach (element_children($form['factors']) as $key) {
- $row = array();
- $row[] = $form['factors'][$key]['#title'];
- unset($form['factors'][$key]['#title']);
- $row[] = form_render($form['factors'][$key]);
- $rows[] = $row;
- }
- $output .= theme('table', $header, $rows);
-
- $output .= form_render($form);
- return $output;
-}
-
-/**
- * Menu callback; presents general node configuration options.
- */
-function node_configure() {
-
- $form['default_nodes_main'] = array(
- '#type' => 'select', '#title' => t('Number of posts on main page'), '#default_value' => variable_get('default_nodes_main', 10),
- '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
- '#description' => t('The default maximum number of posts to display per page on overview pages such as the main page.')
- );
-
- $form['teaser_length'] = array(
- '#type' => 'select', '#title' => t('Length of trimmed posts'), '#default_value' => variable_get('teaser_length', 600),
- '#options' => array(0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'),
- 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'),
- 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')),
- '#description' => t("The maximum number of characters used in the trimmed version of a post. Drupal will use this setting to determine at which offset long posts should be trimmed. The trimmed version of a post is typically used as a teaser when displaying the post on the main page, in XML feeds, etc. To disable teasers, set to 'Unlimited'. Note that this setting will only affect new or updated content and will not affect existing teasers.")
- );
-
- $form['node_preview'] = array(
- '#type' => 'radios', '#title' => t('Preview post'), '#default_value' => variable_get('node_preview', 0),
- '#options' => array(t('Optional'), t('Required')), '#description' => t('Must users preview posts before submitting?')
- );
-
- return system_settings_form('node_configure', $form);
-}
-
-/**
- * Retrieve the comment mode for the given node ID (none, read, or read/write).
- */
-function node_comment_mode($nid) {
- static $comment_mode;
- if (!isset($comment_mode[$nid])) {
- $comment_mode[$nid] = db_result(db_query('SELECT comment FROM {node} WHERE nid = %d', $nid));
- }
- return $comment_mode[$nid];
-}
-
-/**
- * Implementation of hook_link().
- */
-function node_link($type, $node = 0, $main = 0) {
- $links = array();
-
- if ($type == 'node') {
- if (array_key_exists('links', $node)) {
- $links = $node->links;
- }
-
- if ($main == 1 && $node->teaser && $node->readmore) {
- $links[] = l(t('read more'), "node/$node->nid", array('title' => t('Read the rest of this posting.'), 'class' => 'read-more'));
- }
- }
-
- return $links;
-}
-
-/**
- * Implementation of hook_menu().
- */
-function node_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/node', 'title' => t('content'),
- 'callback' => 'node_admin_nodes',
- 'access' => user_access('administer nodes'));
- $items[] = array('path' => 'admin/node/overview', 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
-
- if (module_exist('search')) {
- $items[] = array('path' => 'admin/node/search', 'title' => t('search'),
- 'callback' => 'node_admin_search',
- 'access' => user_access('administer nodes'),
- 'type' => MENU_LOCAL_TASK);
- }
-
- $items[] = array('path' => 'admin/settings/node', 'title' => t('posts'),
- 'callback' => 'node_configure',
- 'access' => user_access('administer nodes'));
- $items[] = array('path' => 'admin/settings/content-types', 'title' => t('content types'),
- 'callback' => 'node_types_configure',
- 'access' => user_access('administer nodes'));
-
- $items[] = array('path' => 'node', 'title' => t('content'),
- 'callback' => 'node_page',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array('path' => 'node/add', 'title' => t('create content'),
- 'callback' => 'node_page',
- 'access' => user_access('access content'),
- 'type' => MENU_ITEM_GROUPING,
- 'weight' => 1);
- $items[] = array('path' => 'rss.xml', 'title' => t('rss feed'),
- 'callback' => 'node_feed',
- 'access' => user_access('access content'),
- 'type' => MENU_CALLBACK);
- }
- else {
- if (arg(0) == 'node' && is_numeric(arg(1))) {
- $node = node_load(arg(1));
- if ($node->nid) {
- $items[] = array('path' => 'node/'. arg(1), 'title' => t('view'),
- 'callback' => 'node_page',
- 'access' => node_access('view', $node),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'node/'. arg(1) .'/view', 'title' => t('view'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- $items[] = array('path' => 'node/'. arg(1) .'/edit', 'title' => t('edit'),
- 'callback' => 'node_page',
- 'access' => node_access('update', $node),
- 'weight' => 1,
- 'type' => MENU_LOCAL_TASK);
- $items[] = array('path' => 'node/'. arg(1) .'/delete', 'title' => t('delete'),
- 'callback' => 'node_delete_confirm',
- 'access' => node_access('delete', $node),
- 'weight' => 1,
- 'type' => MENU_CALLBACK);
- $revisions_access = ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node) && db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', arg(1))) > 1);
- $items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('revisions'),
- 'callback' => 'node_revisions',
- 'access' => $revisions_access,
- 'weight' => 2,
- 'type' => MENU_LOCAL_TASK);
- }
- }
- else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) {
- $items[] = array('path' => 'admin/settings/content-types/'. arg(3),
- 'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))),
- 'type' => MENU_CALLBACK);
- }
- }
-
- return $items;
-}
-
-function node_last_changed($nid) {
- $node = db_fetch_object(db_query('SELECT changed FROM {node} WHERE nid = %d', $nid));
- return ($node->changed);
-}
-
-/**
- * List node administration operations that can be performed.
- */
-function node_operations() {
- $operations = array(
- 'approve' => array(t('Approve the selected posts'), 'UPDATE {node} SET status = 1, moderate = 0 WHERE nid = %d'),
- 'promote' => array(t('Promote the selected posts'), 'UPDATE {node} SET status = 1, promote = 1 WHERE nid = %d'),
- 'sticky' => array(t('Make the selected posts sticky'), 'UPDATE {node} SET status = 1, sticky = 1 WHERE nid = %d'),
- 'demote' => array(t('Demote the selected posts'), 'UPDATE {node} SET promote = 0 WHERE nid = %d'),
- 'unpublish' => array(t('Unpublish the selected posts'), 'UPDATE {node} SET status = 0 WHERE nid = %d'),
- 'delete' => array(t('Delete the selected posts'), '')
- );
- return $operations;
-}
-
-/**
- * List node administration filters that can be applied.
- */
-function node_filters() {
- // Regular filters
- $filters['status'] = array('title' => t('status'),
- 'options' => array('status-1' => t('published'), 'status-0' => t('not published'),
- 'moderate-1' => t('in moderation'), 'moderate-0' => t('not in moderation'),
- 'promote-1' => t('promoted'), 'promote-0' => t('not promoted'),
- 'sticky-1' => t('sticky'), 'sticky-0' => t('not sticky')));
- $filters['type'] = array('title' => t('type'), 'options' => node_get_types());
- // The taxonomy filter
- if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
- $filters['category'] = array('title' => t('category'), 'options' => $taxonomy);
- }
-
- return $filters;
-}
-
-/**
- * Build query for node administration filters based on session.
- */
-function node_build_filter_query() {
- $filters = node_filters();
-
- // Build query
- $where = $args = array();
- $join = '';
- foreach ($_SESSION['node_overview_filter'] as $index => $filter) {
- list($key, $value) = $filter;
- switch($key) {
- case 'status':
- // Note: no exploitable hole as $key/$value have already been checked when submitted
- list($key, $value) = explode('-', $value, 2);
- $where[] = 'n.'. $key .' = %d';
- break;
- case 'category':
- $table = "tn$index";
- $where[] = "$table.tid = %d";
- $join .= "INNER JOIN {term_node} $table ON n.nid = $table.nid ";
- break;
- case 'type':
- $where[] = "n.type = '%s'";
- }
- $args[] = $value;
- }
- $where = count($where) ? 'WHERE '. implode(' AND ', $where) : '';
-
- return array('where' => $where, 'join' => $join, 'args' => $args);
-}
-
-/**
- * Return form for node administration filters.
- */
-function node_filter_form() {
- $session = &$_SESSION['node_overview_filter'];
- $session = is_array($session) ? $session : array();
- $filters = node_filters();
-
- $i = 0;
- $form['filters'] = array('#type' => 'fieldset',
- '#title' => t('Show only items where'),
- '#theme' => 'node_filters',
- );
- foreach ($session as $filter) {
- list($type, $value) = $filter;
- if ($type == 'category') {
- // Load term name from DB rather than search and parse options array.
- $value = module_invoke('taxonomy', 'get_term', $value);
- $value = $value->name;
- }
- else {
- $value = $filters[$type]['options'][$value];
- }
- $string = ($i++ ? '<em>and</em> where <strong>%a</strong> is <strong>%b</strong>' : '<strong>%a</strong> is <strong>%b</strong>');
- $form['filters']['current'][] = array('#value' => t($string, array('%a' => $filters[$type]['title'] , '%b' => $value)));
- }
-
- foreach ($filters as $key => $filter) {
- $names[$key] = $filter['title'];
- $form['filters']['status'][$key] = array('#type' => 'select', '#options' => $filter['options']);
- }
-
- $form['filters']['filter'] = array('#type' => 'radios', '#options' => $names, '#default_value' => 'status');
- $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter')));
- if (count($session)) {
- $form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
- $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
- }
-
- return drupal_get_form('node_filter_form', $form);
-}
-
-/**
- * Theme node administration filter form.
- */
-function theme_node_filter_form(&$form) {
- $output .= '<div id="node-admin-filter">';
- $output .= form_render($form['filters']);
- $output .= '</div>';
- $output .= form_render($form);
- return $output;
-}
-
-/**
- * Theme node administraton filter selector.
- */
-function theme_node_filters(&$form) {
- $output .= '<ul>';
- if (sizeof($form['current'])) {
- foreach (element_children($form['current']) as $key) {
- $output .= '<li>' . form_render($form['current'][$key]) . '</li>';
- }
- }
-
- $output .= '<li><dl class="multiselect">' . (sizeof($form['current']) ? '<dt><em>'. t('and') .'</em> '. t('where') .'</dt>' : '') . '<dd class="a">';
- foreach (element_children($form['filter']) as $key) {
- $output .= form_render($form['filter'][$key]);
- }
- $output .= '</dd>';
-
- $output .= '<dt>'. t('is') .'</dt>' . '<dd class="b">';
-
- foreach (element_children($form['status']) as $key) {
- $output .= form_render($form['status'][$key]);
- }
- $output .= '</dd>';
-
- $output .= '</dl>';
- $output .= '<div class="container-inline" id="node-admin-buttons">'. form_render($form['buttons']) .'</div>';
- $output .= '</li></ul><br class="clear" />';
-
- return $output;
-}
-
-/**
- * Process result from node administration filter form.
- */
-function node_filter_form_submit() {
- global $form_values;
- $op = $_POST['op'];
- $filters = node_filters();
- switch ($op) {
- case t('Filter'):
- case t('Refine'):
- if (isset($form_values['filter'])) {
- $filter = $form_values['filter'];
-
- // Flatten the options array to accomodate hierarchical/nested options.
- $flat_options = form_options_flatten($filters[$filter]['options']);
-
- if (isset($flat_options[$form_values[$filter]])) {
- $_SESSION['node_overview_filter'][] = array($filter, $form_values[$filter]);
- }
- }
- break;
- case t('Undo'):
- array_pop($_SESSION['node_overview_filter']);
- break;
- case t('Reset'):
- $_SESSION['node_overview_filter'] = array();
- break;
- }
-}
-
-/**
- * Generate the content administration overview.
- */
-function node_admin_nodes_submit($form_id, $edit) {
- $operations = node_operations();
- if ($operations[$edit['operation']][1]) {
- // Flag changes
- $operation = $operations[$edit['operation']][1];
- foreach ($edit['nodes'] as $nid => $value) {
- if ($value) {
- db_query($operation, $nid);
- }
- }
- drupal_set_message(t('The update has been performed.'));
- }
-}
-
-function node_admin_nodes_validate($form_id, $edit) {
- $edit['nodes'] = array_diff($edit['nodes'], array(0));
- if (count($edit['nodes']) == 0) {
- if ($edit['operation'] == 'delete') {
- form_set_error('', t('Please select some items to perform the delete operation.'));
- }
- else {
- form_set_error('', t('Please select some items to perform the update on.'));
- }
- }
-}
-
-/**
- * Menu callback: content administration.
- */
-function node_admin_nodes() {
- global $form_values;
- $output = node_filter_form();
-
- if ($_POST['edit']['operation'] == 'delete' && $_POST['edit']['nodes']) {
- return node_multiple_delete_confirm();
- }
-
- $filter = node_build_filter_query();
-
- $result = pager_query('SELECT n.*, u.name, u.uid FROM {node} n '. $filter['join'] .' INNER JOIN {users} u ON n.uid = u.uid '. $filter['where'] .' ORDER BY n.changed DESC', 50, 0, NULL, $filter['args']);
-
- $form['options'] = array('#type' => 'fieldset',
- '#title' => t('Update options'),
- '#prefix' => '<div class="container-inline">',
- '#suffix' => '</div>',
- );
- $options = array();
- foreach (node_operations() as $key => $value) {
- $options[$key] = $value[0];
- }
- $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'approve');
- $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update'));
-
- $destination = drupal_get_destination();
- while ($node = db_fetch_object($result)) {
- $nodes[$node->nid] = '';
- $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)));
- $form['name'][$node->nid] = array('#value' => node_get_name($node));
- $form['username'][$node->nid] = array('#value' => theme('username', $node));
- $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published')));
- $form['operations'][$node->nid] = array('#value' => l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
- }
- $form['nodes'] = array('#type' => 'checkboxes', '#options' => $nodes);
- $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
-
- $form['#method'] = 'post';
- $form['#action'] = url('admin/node');
-
- // Call the form first, to allow for the form_values array to be populated.
- $output .= drupal_get_form('node_admin_nodes', $form);
-
- return $output;
-}
-
-/**
- * Theme node administration overview.
- */
-function theme_node_admin_nodes($form) {
- // Overview table:
- $header = array(NULL, t('Title'), t('Type'), t('Author'), t('Status'), t('Operations'));
-
- $output .= form_render($form['options']);
- if (isset($form['title']) && is_array($form['title'])) {
- foreach (element_children($form['title']) as $key) {
- $row = array();
- $row[] = form_render($form['nodes'][$key]);
- $row[] = form_render($form['title'][$key]);
- $row[] = form_render($form['name'][$key]);
- $row[] = form_render($form['username'][$key]);
- $row[] = form_render($form['status'][$key]);
- $row[] = form_render($form['operations'][$key]);
- $rows[] = $row;
- }
-
- }
- else {
- $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6'));
- }
-
- $output .= theme('table', $header, $rows);
- if ($form['pager']['#value']) {
- $output .= form_render($form['pager']);
- }
-
- $output .= form_render($form);
-
- return $output;
-}
-
-function node_multiple_delete_confirm() {
- $edit = $_POST['edit'];
-
- $form['nodes'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
- // array_filter returns only elements with true values
- foreach (array_filter($edit['nodes']) as $nid => $value) {
- $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid));
- $form['nodes'][$nid] = array('#type' => 'hidden', '#value' => $nid, '#prefix' => '<li>', '#suffix' => check_plain($title) ."</li>\n");
- }
- $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
-
- return confirm_form('node_multiple_delete_confirm', $form,
- t('Are you sure you want to delete these items?'),
- 'admin/node', t('This action cannot be undone.'),
- t('Delete all'), t('Cancel'));
-}
-
-function node_multiple_delete_confirm_submit($form_id, $edit) {
- if ($edit['confirm']) {
- foreach ($edit['nodes'] as $nid => $value) {
- node_delete($nid);
- }
- drupal_set_message(t('The items have been deleted.'));
- }
- return 'admin/node';
-}
-
-/**
- * Menu callback; presents each node type configuration page.
- */
-function node_types_configure($type = NULL) {
- if (isset($type)) {
- $node = new stdClass();
- $node->type = $type;
- $form['submission'] = array('#type' => 'fieldset', '#title' =>t('Submission form') );
- $form['submission'][$type . '_help'] = array(
- '#type' => 'textarea', '#title' => t('Explanation or submission guidelines'), '#default_value' => variable_get($type .'_help', ''),
- '#description' => t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_name($type)))
- );
- $form['submission']['minimum_'. $type .'_size'] = array(
- '#type' => 'select', '#title' => t('Minimum number of words'), '#default_value' => variable_get('minimum_'. $type .'_size', 0), '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)),
- '#description' => t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_name($type)))
- );
- $form['workflow'] = array('#type' => 'fieldset', '#title' =>t('Workflow'));
- $form['type'] = array('#type' => 'value', '#value' => $type);
-
- $form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
- return system_settings_form($type .'_node_settings', $form);
- }
- else {
- $header = array(t('Type'), t('Operations'));
-
- $rows = array();
- foreach (node_get_types() as $type => $name) {
- $rows[] = array($name, l(t('configure'), 'admin/settings/content-types/'. $type));
- }
-
- return theme('table', $header, $rows);
- }
-}
-
-/**
- * Generate an overview table of older revisions of a node.
- */
-function node_revision_overview($node) {
- drupal_set_title(t('Revisions for %title', array('%title' => check_plain($node->title))));
-
- $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2));
-
- $revisions = node_revision_list($node);
-
- $rows = array();
- $revert_permission = FALSE;
- if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
- $revert_permission = TRUE;
- }
- $delete_permission = FALSE;
- if (user_access('administer nodes')) {
- $delete_permission = TRUE;
- }
- foreach ($revisions as $revision) {
- $row = array();
- $operations = array();
-
- if ($revision->current_vid > 0) {
- $row[] = array('data' => t('%date by %username', array('%date' => l(format_date($revision->timestamp, 'small'), "node/$node->nid"), '%username' => theme('username', $revision)))
- . (($revision->log != '') ? '<p class="revision-log">'. filter_xss($revision->log) .'</p>' : ''),
- 'class' => 'revision-current');
- $operations[] = array('data' => theme('placeholder', t('current revision')), 'class' => 'revision-current', 'colspan' => 2);
- }
- else {
- $row[] = t('%date by %username', array('%date' => l(format_date($revision->timestamp, 'small'), "node/$node->nid/revisions/$revision->vid/view"), '%username' => theme('username', $revision)))
- . (($revision->log != '') ? '<p class="revision-log">'. filter_xss($revision->log) .'</p>' : '');
- if ($revert_permission) {
- $operations[] = l(t('revert'), "node/$node->nid/revisions/$revision->vid/revert");
- }
- if ($delete_permission) {
- $operations[] = l(t('delete'), "node/$node->nid/revisions/$revision->vid/delete");
- }
- }
- $rows[] = array_merge($row, $operations);
- }
- $output .= theme('table', $header, $rows);
-
- return $output;
-}
-
-/**
- * Revert to the revision with the specified revision number. A node and nodeapi "update" event is triggered
- * (via the node_save() call) when a revision is reverted.
- */
-function node_revision_revert($nid, $revision) {
- global $user;
-
- $node = node_load($nid, $revision);
- if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
- if ($node->vid) {
- $node->revision = 1;
- $node->log = t('Copy of the revision from %date.', array('%date' => theme('placeholder', format_date($node->revision_timestamp))));
- $node->taxonomy = array_keys($node->taxonomy);
-
- node_save($node);
-
- drupal_set_message(t('%title has been reverted back to the revision from %revision-date', array('%revision-date' => theme('placeholder', format_date($node->revision_timestamp)), '%title' => theme('placeholder', check_plain($node->title)))));
- watchdog('content', t('%type: reverted %title revision %revision.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $revision))));
- }
- else {
- drupal_set_message(t('You tried to revert to an invalid revision.'), 'error');
- }
- drupal_goto('node/'. $nid .'/revisions');
- }
- drupal_access_denied();
-}
-
-/**
- * Delete the revision with specified revision number. A "delete revision" nodeapi event is invoked when a
- * revision is deleted.
- */
-function node_revision_delete($nid, $revision) {
- if (user_access('administer nodes')) {
- $node = node_load($nid);
- if (node_access('delete', $node)) {
- // Don't delete the current revision
- if ($revision != $node->vid) {
- $node = node_load($nid, $revision);
-
- db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $nid, $revision);
- node_invoke_nodeapi($node, 'delete revision');
- drupal_set_message(t('Deleted %title revision %revision.', array('%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $revision))));
- watchdog('content', t('%type: deleted %title revision %revision.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $revision))));
- }
-
- else {
- drupal_set_message(t('Deletion failed. You tried to delete the current revision.'));
- }
- if (db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $nid)) > 1) {
- drupal_goto("node/$nid/revisions");
- }
- else {
- drupal_goto("node/$nid");
- }
- }
- }
-
- drupal_access_denied();
-}
-
-/**
- * Return a list of all the existing revision numbers.
- */
-function node_revision_list($node) {
- $revisions = array();
- $result = db_query('SELECT r.vid, r.title, r.log, r.uid, n.vid AS current_vid, r.timestamp, u.name FROM {node_revisions} r LEFT JOIN {node} n ON n.vid = r.vid INNER JOIN {users} u ON u.uid = r.uid WHERE r.nid = %d ORDER BY r.timestamp DESC', $node->nid);
- while ($revision = db_fetch_object($result)) {
- $revisions[] = $revision;
- }
-
- return $revisions;
-}
-
-function node_admin_search() {
- $output = search_form(url('admin/node/search'), $_POST['edit']['keys'], 'node') . search_data($_POST['edit']['keys'], 'node');
- return $output;
-}
-
-/**
- * Implementation of hook_block().
- */
-function node_block($op = 'list', $delta = 0) {
- if ($op == 'list') {
- $blocks[0]['info'] = t('Syndicate');
- return $blocks;
- }
- else if ($op == 'view') {
- $block['subject'] = t('Syndicate');
- $block['content'] = theme('feed_icon', url('rss.xml'));
-
- return $block;
- }
-}
-
-/**
- * A generic function for generating RSS feeds from a set of nodes.
- *
- * @param $nodes
- * An object as returned by db_query() which contains the nid field.
- * @param $channel
- * An associative array containing title, link, description and other keys.
- * The link should be an absolute URL.
- */
-function node_feed($nodes = 0, $channel = array()) {
- global $base_url, $locale;
-
- if (!$nodes) {
- $nodes = db_query_range(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.created DESC'), 0, variable_get('feed_default_items', 10));
- }
-
- $item_length = variable_get('feed_item_length', 'teaser');
- $namespaces = array('xmlns:dc="http://purl.org/dc/elements/1.1/"');
-
- while ($node = db_fetch_object($nodes)) {
- // Load the specified node:
- $item = node_load($node->nid);
- $link = url("node/$node->nid", NULL, NULL, 1);
-
- if ($item_length != 'title') {
- $teaser = ($item_length == 'teaser') ? TRUE : FALSE;
-
- // Filter and prepare node teaser
- if (node_hook($item, 'view')) {
- node_invoke($item, 'view', $teaser, FALSE);
- }
- else {
- $item = node_prepare($item, $teaser);
- }
-
- // Allow modules to change $node->teaser before viewing.
- node_invoke_nodeapi($item, 'view', $teaser, FALSE);
- }
-
- // Prepare the item description
- switch ($item_length) {
- case 'fulltext':
- $item_text = $item->body;
- break;
- case 'teaser':
- $item_text = $item->teaser;
- if ($item->readmore) {
- $item_text .= '<p>'. l(t('read more'), 'node/'. $item->nid, NULL, NULL, NULL, TRUE) .'</p>';
- }
- break;
- case 'title':
- $item_text = '';
- break;
- }
-
- // Allow modules to add additional item fields
- $extra = node_invoke_nodeapi($item, 'rss item');
- $extra = array_merge($extra, array(array('key' => 'pubDate', 'value' => date('r', $item->created)), array('key' => 'dc:creator', 'value' => $item->name), array('key' => 'guid', 'value' => $item->nid . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false'))));
- foreach ($extra as $element) {
- if ($element['namespace']) {
- $namespaces = array_merge($namespaces, $element['namespace']);
- }
- }
- $items .= format_rss_item($item->title, $link, $item_text, $extra);
- }
-
- $channel_defaults = array(
- 'version' => '2.0',
- 'title' => variable_get('site_name', 'drupal') .' - '. variable_get('site_slogan', ''),
- 'link' => $base_url,
- 'description' => variable_get('site_mission', ''),
- 'language' => $locale
- );
- $channel = array_merge($channel_defaults, $channel);
-
- $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
- $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\" ". implode(' ', $namespaces) .">\n";
- $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
- $output .= "</rss>\n";
-
- drupal_set_header('Content-Type: text/xml; charset=utf-8');
- print $output;
-}
-
-/**
- * Prepare node for save and allow modules to make changes.
- */
-function node_submit($node) {
- global $user;
-
- // Convert the node to an object, if necessary.
- $node = (object)$node;
-
- // Auto-generate the teaser, but only if it hasn't been set (e.g. by a
- // module-provided 'teaser' form item).
- if (!isset($node->teaser)) {
- $node->teaser = isset($node->body) ? node_teaser($node->body, isset($node->format) ? $node->format : NULL) : '';
- }
-
- if (user_access('administer nodes')) {
- // Populate the "authored by" field.
- if ($account = user_load(array('name' => $node->name))) {
- $node->uid = $account->uid;
- }
- else {
- $node->uid = 0;
- }
-
- $node->created = strtotime($node->date);
- }
- else {
- // Force defaults in case people modify the form:
- $node_options = variable_get('node_options_'. $node->type, array('status', 'promote'));
- foreach (array('status', 'moderate', 'promote', 'sticky', 'revision') as $key) {
- $node->$key = in_array($key, $node_options);
- }
- }
-
- // Do node-type-specific validation checks.
- node_invoke($node, 'submit');
- node_invoke_nodeapi($node, 'submit');
-
- $node->validated = TRUE;
-
- return $node;
-}
-
-/**
- * Perform validation checks on the given node.
- */
-function node_validate($node) {
- // Convert the node to an object, if necessary.
- $node = (object)$node;
-
- // Make sure the body has the minimum number of words.
- // todo use a better word counting algorithm that will work in other languages
- if (isset($node->body) && count(explode(' ', $node->body)) < variable_get('minimum_'. $node->type .'_size', 0)) {
- form_set_error('body', t('The body of your %type is too short. You need at least %words words.', array('%words' => variable_get('minimum_'. $node->type .'_size', 0), '%type' => node_get_name($node))));
- }
-
- if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
- form_set_error('changed', t('This content has been modified by another user; changes cannot be saved.'));
- }
-
- if (user_access('administer nodes')) {
- // Validate the "authored by" field.
- if (!empty($node->name) && !($account = user_load(array('name' => $node->name)))) {
- // The use of empty() is mandatory in the context of usernames
- // as the empty string denotes the anonymous user. In case we
- // are dealing with an anonymous user we set the user ID to 0.
- form_set_error('name', t('The username %name does not exist.', array ('%name' => theme('placeholder', $node->name))));
- }
-
- // Validate the "authored on" field. As of PHP 5.1.O, strtotime returns FALSE instead of -1 upon failure.
- if (strtotime($node->date) <= 0) {
- form_set_error('date', t('You have to specify a valid date.'));
- }
- }
-
- // Do node-type-specific validation checks.
- node_invoke($node, 'validate');
- node_invoke_nodeapi($node, 'validate');
-}
-
-function node_form_validate($form_id, $edit) {
- node_validate($edit);
-}
-
-function node_object_prepare(&$node) {
- if (user_access('administer nodes')) {
- // Set up default values, if required.
- if (!isset($node->created)) {
- $node->created = time();
- }
-
- if (!isset($node->date)) {
- $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O');
- }
- }
- node_invoke($node, 'prepare');
- node_invoke_nodeapi($node, 'prepare');
-}
-
-/**
- * Generate the node editing form.
- */
-function node_form($node) {
- $node = (object)$node;
- $form = node_form_array($node);
- return drupal_get_form($node->type .'_node_form', $form, 'node_form');
-}
-
-/**
-* Generate the node editing form array.
-*/
-function node_form_array($node) {
- node_object_prepare($node);
-
- // Set the id of the top-level form tag
- $form['#id'] = 'node-form';
-
- /**
- * Basic node information.
- * These elements are just values so they are not even sent to the client.
- */
- foreach (array('nid', 'vid', 'uid', 'created', 'changed', 'type') as $key) {
- $form[$key] = array('#type' => 'value', '#value' => $node->$key);
- }
-
- // Get the node-specific bits.
- $form = array_merge_recursive($form, node_invoke($node, 'form'));
- if (!isset($form['title']['#weight'])) {
- $form['title']['#weight'] = -5;
- }
-
- $node_options = variable_get('node_options_'. $node->type, array('status', 'promote'));
- // If this is a new node, fill in the default values.
- if (!isset($node->nid)) {
- foreach (array('status', 'moderate', 'promote', 'sticky', 'revision') as $key) {
- $node->$key = in_array($key, $node_options);
- }
- global $user;
- $node->uid = $user->uid;
- }
- else {
- // Nodes being edited should always be preset with the default revision setting.
- $node->revision = in_array('revision', $node_options);
- }
- $form['#node'] = $node;
-
- if (user_access('administer nodes')) {
- // Node author information
- $form['author'] = array('#type' => 'fieldset', '#title' => t('Authoring information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 20);
- $form['author']['name'] = array('#type' => 'textfield', '#title' => t('Authored by'), '#maxlength' => 60, '#autocomplete_path' => 'user/autocomplete', '#default_value' => $node->name ? $node->name : '', '#weight' => -1, '#description' => t('Leave blank for %anonymous.', array('%anonymous' => theme('placeholder', variable_get('anonymous', 'Anonymous')))));
- $form['author']['date'] = array('#type' => 'textfield', '#title' => t('Authored on'), '#maxlength' => 25, '#required' => TRUE, '#default_value' => $node->date);
-
- // Node options for administrators
- $form['options'] = array('#type' => 'fieldset', '#title' => t('Publishing options'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 25);
- $form['options']['status'] = array('#type' => 'checkbox', '#title' => t('Published'), '#default_value' => $node->status);
- $form['options']['moderate'] = array('#type' => 'checkbox', '#title' => t('In moderation queue'), '#default_value' => $node->moderate);
- $form['options']['promote'] = array('#type' => 'checkbox', '#title' => t('Promoted to front page'), '#default_value' => $node->promote);
- $form['options']['sticky'] = array('#type' => 'checkbox', '#title' => t('Sticky at top of lists'), '#default_value' => $node->sticky);
- $form['options']['revision'] = array('#type' => 'checkbox', '#title' => t('Create new revision'), '#default_value' => $node->revision);
- }
- else {
- // Put all of these through as values if the user doesn't have access to them.
- foreach (array('uid', 'created') as $key) {
- $form[$key] = array('#type' => 'value', '#value' => $node->$key);
- }
- }
-
- // Add the buttons.
- $form['preview'] = array('#type' => 'button', '#value' => t('Preview'), '#weight' => 40);
- $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 45);
- if ($node->nid && node_access('delete', $node)) {
- $form['delete'] = array('#type' => 'button', '#value' => t('Delete'), '#weight' => 50);
- }
-
- $form['#after_build'] = 'node_form_add_preview';
-
- return $form;
-}
-
-function node_form_add_preview($form, $edit) {
- $op = isset($_POST['op']) ? $_POST['op'] : '';
- if ($op == t('Preview')) {
- drupal_validate_form($form['form_id']['#value'], $form);
- if (!form_get_errors()) {
- $form['node_preview'] = array('#value' => node_preview((object)$edit), '#weight' => -100);
- }
- }
- if (variable_get('node_preview', 0) && (form_get_errors() || $op != t('Preview'))) {
- unset($form['submit']);
- }
- return $form;
-}
-
-function theme_node_form($form) {
- $output = '<div class="node-form">';
- if (isset($form['node_preview'])) {
- $output .= form_render($form['node_preview']);
- }
-
- $output .= ' <div class="standard">';
- $output .= form_render($form);
- $output .= ' </div>';
- $output .= ' <div class="admin">';
- $output .= ' <div class="authored">';
- $output .= form_render($form['author']);
- $output .= ' </div>';
- $output .= ' <div class="options">';
- $output .= form_render($form['options']);
- $output .= ' </div>';
- $output .= ' </div>';
- $output .= '</div>';
- return $output;
-}
-
-/**
- * Present a node submission form or a set of links to such forms.
- */
-function node_add($type) {
- global $user;
-
- // If a node type has been specified, validate its existence.
- if (array_key_exists($type, node_get_types()) && node_access('create', $type)) {
- // Initialize settings:
- $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type);
-
- $output = node_form($node);
- drupal_set_title(t('Submit %name', array('%name' => node_get_name($node))));
- }
- else {
- // If no (valid) node type has been provided, display a node type overview.
- foreach (node_get_types() as $type => $name) {
- if (module_invoke(node_get_base($type), 'access', 'create', $type)) {
- $out = '<dt>'. l($name, "node/add/$type", array('title' => t('Add a new %s.', array('%s' => $name)))) .'</dt>';
- $out .= '<dd>'. implode("\n", module_invoke_all('help', 'node/add#'. $type)) .'</dd>';
- $item[$name] = $out;
- }
- }
-
- if (isset($item)) {
- uasort($item, 'strnatcasecmp');
- $output = t('Choose the appropriate item from the list:') .'<dl>'. implode('', $item) .'</dl>';
- }
- else {
- $output = t('You are not allowed to create content.');
- }
- }
-
- return $output;
-}
-
-/**
- * Generate a node preview.
- */
-function node_preview($node) {
- if (node_access('create', $node) || node_access('update', $node)) {
- // Load the user's name when needed:
- if (isset($node->name)) {
- // The use of isset() is mandatory in the context of user IDs, because
- // user ID 0 denotes the anonymous user.
- if ($user = user_load(array('name' => $node->name))) {
- $node->uid = $user->uid;
- }
- else {
- $node->uid = 0; // anonymous user
- }
- }
- else if ($node->uid) {
- $user = user_load(array('uid' => $node->uid));
- $node->name = $user->name;
- }
-
- // Set the created time when needed:
- if (empty($node->created)) {
- $node->created = time();
- }
-
- // Extract a teaser, if it hasn't been set (e.g. by a module-provided
- // 'teaser' form item).
- if (!isset($node->teaser)) {
- $node->teaser = node_teaser($node->body, $node->format);
- }
-
- // Display a preview of the node:
- // Previewing alters $node so it needs to be cloned.
- if (!form_get_errors()) {
- $cloned_node = drupal_clone($node);
- $cloned_node->in_preview = TRUE;
- $output = theme('node_preview', $cloned_node);
- }
- drupal_set_title(t('Preview'));
- drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('create content'), 'node/add'), l(t('Submit %name', array('%name' => node_get_name($node))), 'node/add/'. $node->type)));
-
- return $output;
- }
-}
-
-/**
- * Display a node preview for display during node creation and editing.
- *
- * @param $node
- * The node object which is being previewed.
- */
-function theme_node_preview($node) {
- $output = '<div class="preview">';
- if ($node->teaser && $node->teaser != $node->body) {
- drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication. You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.'));
- $output .= '<h3>'. t('Preview trimmed version') .'</h3>';
- $output .= node_view(drupal_clone($node), 1, FALSE, 0);
- $output .= '<h3>'. t('Preview full version') .'</h3>';
- $output .= node_view($node, 0, FALSE, 0);
- }
- else {
- $output .= node_view($node, 0, FALSE, 0);
- }
- $output .= "</div>\n";
-
- return $output;
-}
-
-function node_form_submit($form_id, $edit) {
- global $user;
-
- // Fix up the node when required:
- $node = node_submit($edit);
-
- // Prepare the node's body:
- if ($node->nid) {
- // Check whether the current user has the proper access rights to
- // perform this operation:
- if (node_access('update', $node)) {
- node_save($node);
- watchdog('content', t('%type: updated %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
- drupal_set_message(t('The %post was updated.', array ('%post' => node_get_name($node))));
- }
- }
- else {
- // Check whether the current user has the proper access rights to
- // perform this operation:
- if (node_access('create', $node)) {
- node_save($node);
- watchdog('content', t('%type: added %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
- drupal_set_message(t('Your %post was created.', array ('%post' => node_get_name($node))));
- }
- }
- if ($node->nid) {
- if (node_access('view', $node)) {
- return 'node/'. $node->nid;
- }
- else {
- return '';
- }
- }
- // it is very unlikely we get here
- return FALSE;
-}
-
-/**
- * Menu callback -- ask for confirmation of node deletion
- */
-function node_delete_confirm() {
- $edit = $_POST['edit'];
- $edit['nid'] = $edit['nid'] ? $edit['nid'] : arg(1);
- $node = node_load($edit['nid']);
-
- if (node_access('delete', $node)) {
- $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
- $output = confirm_form('node_delete_confirm', $form,
- t('Are you sure you want to delete %title?', array('%title' => theme('placeholder', $node->title))),
- $_GET['destination'] ? $_GET['destination'] : 'node/'. $node->nid, t('This action cannot be undone.'),
- t('Delete'), t('Cancel') );
- }
-
- return $output;
-}
-
-/**
- * Execute node deletion
- */
-function node_delete_confirm_submit($form_id, $form_values) {
- if ($form_values['confirm']) {
- node_delete($form_values['nid']);
- }
-}
-
-/**
- * Delete a node.
- */
-function node_delete($nid) {
-
- $node = node_load($nid);
-
- if (node_access('delete', $node)) {
- db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);
- db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
-
- // Call the node-specific callback (if any):
- node_invoke($node, 'delete');
- node_invoke_nodeapi($node, 'delete');
-
- // Clear the cache so an anonymous poster can see the node being deleted.
- cache_clear_all();
-
- // Remove this node from the search index if needed.
- if (function_exists('search_wipe')) {
- search_wipe($node->nid, 'node');
- }
- drupal_set_message(t('%title has been deleted.', array('%title' => theme('placeholder', $node->title))));
- watchdog('content', t('%type: deleted %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))));
- }
-}
-
-/**
- * Menu callback for revisions related activities.
- */
-function node_revisions() {
- if (is_numeric(arg(1)) && arg(2) == 'revisions') {
- $op = arg(4) ? arg(4) : 'overview';
- switch ($op) {
- case 'overview':
- $node = node_load(arg(1));
- if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
- return node_revision_overview($node);
- }
- drupal_access_denied();
- return;
- case 'view':
- if (is_numeric(arg(3))) {
- $node = node_load(arg(1), arg(3));
- if ($node->nid) {
- if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
- drupal_set_title(t('Revision of %title from %date', array('%title' => theme('placeholder', $node->title), '%date' => format_date($node->revision_timestamp))));
- return node_show($node, arg(2));
- }
- drupal_access_denied();
- return;
- }
- }
- break;
- case 'revert':
- node_revision_revert(arg(1), arg(3));
- break;
- case 'delete':
- node_revision_delete(arg(1), arg(3));
- break;
- }
- }
- drupal_not_found();
-}
-
-/**
- * Generate a listing of promoted nodes.
- */
-function node_page_default() {
- $result = pager_query(db_rewrite_sql('SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), variable_get('default_nodes_main', 10));
-
- if (db_num_rows($result)) {
- drupal_add_link(array('rel' => 'alternate',
- 'type' => 'application/rss+xml',
- 'title' => t('RSS'),
- 'href' => url('rss.xml', NULL, NULL, TRUE)));
-
- $output = '';
- while ($node = db_fetch_object($result)) {
- $output .= node_view(node_load($node->nid), 1);
- }
- $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
- }
- else {
- $output = t("
- <p>Welcome to your new <a href=\"%drupal\">Drupal</a>-powered website. This message will guide you through your first steps with Drupal, and will disappear once you have posted your first piece of content.</p>
- <p>The first thing you will need to do is <a href=\"%register\">create the first account</a>. This account will have full administration rights and will allow you to configure your website. Once logged in, you can visit the <a href=\"%admin\">administration section</a> and <a href=\"%config\">set up your site's configuration</a>.</p>
- <p>Drupal comes with various modules, each of which contains a specific piece of functionality. You should visit the <a href=\"%modules\">module list</a> and enable those modules which suit your website's needs.</p>
- <p><a href=\"%themes\">Themes</a> handle the presentation of your website. You can use one of the existing themes, modify them or create your own from scratch.</p>
- <p>We suggest you look around the administration section and explore the various options Drupal offers you. For more information, you can refer to the <a href=\"%handbook\">Drupal handbooks online</a>.</p>", array('%drupal' => 'http://drupal.org/', '%register' => url('user/register'), '%admin' => url('admin'), '%config' => url('admin/settings'), '%modules' => url('admin/modules'), '%themes' => url('admin/themes'), '%handbook' => 'http://drupal.org/handbooks'));
- }
-
- return $output;
-}
-
-/**
- * Menu callback; dispatches control to the appropriate operation handler.
- */
-function node_page() {
- $op = arg(1);
-
- if (is_numeric($op)) {
- $op = (arg(2) && !is_numeric(arg(2))) ? arg(2) : 'view';
- }
-
- switch ($op) {
- case 'view':
- if (is_numeric(arg(1))) {
- $node = node_load(arg(1));
- if ($node->nid) {
- drupal_set_title(check_plain($node->title));
- return node_show($node, arg(2));
- }
- else if (db_result(db_query('SELECT nid FROM {node} WHERE nid = %d', arg(1)))) {
- drupal_access_denied();
- }
- else {
- drupal_not_found();
- }
- }
- break;
- case 'add':
- return node_add(arg(2));
- break;
- case 'edit':
- if ($_POST['op'] == t('Delete')) {
- // Note: we redirect from node/uid/edit to node/uid/delete to make the tabs disappear.
- if ($_REQUEST['destination']) {
- $destination = drupal_get_destination();
- unset($_REQUEST['destination']);
- }
- drupal_goto('node/'. arg(1) .'/delete', $destination);
- }
-
- if (is_numeric(arg(1))) {
- $node = node_load(arg(1));
- if ($node->nid) {
- drupal_set_title(check_plain($node->title));
- return node_form($node);
- }
- else if (db_result(db_query('SELECT nid FROM {node} WHERE nid = %d', arg(1)))) {
- drupal_access_denied();
- }
- else {
- drupal_not_found();
- }
- }
- break;
- default:
- drupal_set_title('');
- return node_page_default();
- }
-}
-
-/**
- * shutdown function to make sure we always mark the last node processed.
- */
-function node_update_shutdown() {
- global $last_change, $last_nid;
-
- if ($last_change && $last_nid) {
- variable_set('node_cron_last', $last_change);
- variable_set('node_cron_last_nid', $last_nid);
- }
-}
-
-/**
- * Implementation of hook_update_index().
- */
-function node_update_index() {
- global $last_change, $last_nid;
-
- register_shutdown_function('node_update_shutdown');
-
- $last = variable_get('node_cron_last', 0);
- $last_nid = variable_get('node_cron_last_nid', 0);
- $limit = (int)variable_get('search_cron_limit', 100);
-
- // Store the maximum possible comments per thread (used for ranking by reply count)
- variable_set('node_cron_comments_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(comment_count) FROM {node_comment_statistics}'))));
- variable_set('node_cron_views_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(totalcount) FROM {node_counter}'))));
-
- $result = db_query_range('SELECT GREATEST(c.last_comment_timestamp, n.changed, n.created) as last_change, n.nid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND ((GREATEST(n.created, n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d)) ORDER BY GREATEST(n.created, n.changed, c.last_comment_timestamp) ASC, n.nid ASC', $last, $last_nid, $last, $last, $last, 0, $limit);
-
- while ($node = db_fetch_object($result)) {
- $last_change = $node->last_change;
- $last_nid = $node->nid;
- $node = node_load($node->nid);
-
- // Get node output (filtered and with module-specific fields).
- if (node_hook($node, 'view')) {
- node_invoke($node, 'view', false, false);
- }
- else {
- $node = node_prepare($node, false);
- }
- // Allow modules to change $node->body before viewing.
- node_invoke_nodeapi($node, 'view', false, false);
-
- $text = '<h1>'. check_plain($node->title) .'</h1>'. $node->body;
-
- // Fetch extra data normally not visible
- $extra = node_invoke_nodeapi($node, 'update index');
- foreach ($extra as $t) {
- $text .= $t;
- }
-
- // Update index
- search_index($node->nid, 'node', $text);
- }
-}
-
-/**
- * Implementation of hook_form_alter().
- */
-function node_form_alter($form_id, &$form) {
- // Node publishing options
- if (isset($form['type']) && $form['type']['#value'] .'_node_settings' == $form_id) {
- $form['workflow']['node_options_'. $form['type']['#value']] = array('#type' => 'checkboxes',
- '#title' => t('Default options'),
- '#default_value' => variable_get('node_options_'. $form['type']['#value'], array('status', 'promote')),
- '#options' => array(
- 'status' => t('Published'),
- 'moderate' => t('In moderation queue'),
- 'promote' => t('Promoted to front page'),
- 'sticky' => t('Sticky at top of lists'),
- 'revision' => t('Create new revision'),
- ),
- '#description' => t('Users with the <em>administer nodes</em> permission will be able to override these options.'),
- );
- }
-
- // Advanced node search form
- elseif ($form_id == 'search_form' && arg(1) == 'node') {
- // Keyword boxes:
- $form['advanced'] = array(
- '#type' => 'fieldset',
- '#title' => t('Advanced search'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#attributes' => array('class' => 'search-advanced'),
- );
- $form['advanced']['keywords'] = array(
- '#prefix' => '<div class="criterion">',
- '#suffix' => '</div>',
- );
- $form['advanced']['keywords']['or'] = array(
- '#type' => 'textfield',
- '#title' => t('Containing any of the words'),
- '#size' => 30,
- '#maxlength' => 255,
- );
- $form['advanced']['keywords']['phrase'] = array(
- '#type' => 'textfield',
- '#title' => t('Containing the phrase'),
- '#size' => 30,
- '#maxlength' => 255,
- );
- $form['advanced']['keywords']['negative'] = array(
- '#type' => 'textfield',
- '#title' => t('Containing none of the words'),
- '#size' => 30,
- '#maxlength' => 255,
- );
-
- // Taxonomy box:
- if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
- $form['advanced']['category'] = array(
- '#type' => 'select',
- '#title' => t('Only in the category(s)'),
- '#prefix' => '<div class="criterion">',
- '#size' => 10,
- '#suffix' => '</div>',
- '#options' => $taxonomy,
- '#multiple' => TRUE,
- );
- }
-
- // Node types:
- $types = node_get_types();
- $form['advanced']['type'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Only of the type(s)'),
- '#prefix' => '<div class="criterion">',
- '#suffix' => '</div>',
- '#options' => $types,
- );
- $form['advanced']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Advanced search'),
- '#prefix' => '<div class="action">',
- '#suffix' => '</div><br clear="all" />',
- );
-
- $form['#validate']['node_search_validate'] = array();
- }
-}
-
-/**
- * Form API callback for the search form. Registered in node_form_alter().
- */
-function node_search_validate($form_id, $form_values) {
- // Initialise using any existing basic search keywords.
- $keys = $form_values['processed_keys']['#ref'];
-
- // Insert extra restrictions into the search keywords string.
- if (isset($form_values['type']) && is_array($form_values['type'])) {
- // Retrieve selected types - Forms API sets the value of unselected checkboxes to 0.
- $form_values['type'] = array_filter($form_values['type']);
- if (count($form_values['type'])) {
- $keys = search_query_insert($keys, 'type', implode(',', array_keys($form_values['type'])));
- }
- }
- if (isset($form_values['category']) && is_array($form_values['category'])) {
- $keys = search_query_insert($keys, 'category', implode(',', $form_values['category']));
- }
- if ($form_values['or'] != '') {
- if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' '. $form_values['or'], $matches)) {
- $keys .= ' '. implode(' OR ', $matches[1]);
- }
- }
- if ($form_values['negative'] != '') {
- if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' '. $form_values['negative'], $matches)) {
- $keys .= ' -'. implode(' -', $matches[1]);
- }
- }
- if ($form_values['phrase'] != '') {
- $keys .= ' "'. str_replace('"', ' ', $form_values['phrase']) .'"';
- }
- if (!empty($keys)) {
- $form_values['processed_keys']['#ref'] = trim($keys);
- }
-}
-
-/**
- * @defgroup node_access Node access rights
- * @{
- * The node access system determines who can do what to which nodes.
- *
- * In determining access rights for a node, node_access() first checks
- * whether the user has the "administer nodes" permission. Such users have
- * unrestricted access to all nodes. Then the node module's hook_access()
- * is called, and a TRUE or FALSE return value will grant or deny access.
- * This allows, for example, the blog module to always grant access to the
- * blog author, and for the book module to always deny editing access to
- * PHP pages.
- *
- * If node module does not intervene (returns NULL), then the
- * node_access table is used to determine access. All node access
- * modules are queried using hook_node_grants() to assemble a list of
- * "grant IDs" for the user. This list is compared against the table.
- * If any row contains the node ID in question (or 0, which stands for "all
- * nodes"), one of the grant IDs returned, and a value of TRUE for the
- * operation in question, then access is granted. Note that this table is a
- * list of grants; any matching row is sufficient to grant access to the
- * node.
- *
- * In node listings, the process above is followed except that
- * hook_access() is not called on each node for performance reasons and for
- * proper functioning of the pager system. When adding a node listing to your
- * module, be sure to use db_rewrite_sql() to add
- * the appropriate clauses to your query for access checks.
- *
- * To see how to write a node access module of your own, see
- * node_access_example.module.
- */
-
-/**
- * Determine whether the current user may perform the given operation on the
- * specified node.
- *
- * @param $op
- * The operation to be performed on the node. Possible values are:
- * - "view"
- * - "update"
- * - "delete"
- * - "create"
- * @param $node
- * The node object (or node array) on which the operation is to be performed,
- * or node type (e.g. 'forum') for "create" operation.
- * @param $uid
- * The user ID on which the operation is to be performed.
- * @return
- * TRUE if the operation may be performed.
- */
-function node_access($op, $node = NULL, $uid = NULL) {
- // Convert the node to an object if necessary:
- if ($op != 'create') {
- $node = (object)$node;
- }
- // If the node is in a restricted format, disallow editing.
- if ($op == 'update' && !filter_access($node->format)) {
- return FALSE;
- }
-
- if (user_access('administer nodes')) {
- return TRUE;
- }
-
- if (!user_access('access content')) {
- return FALSE;
- }
-
- // Can't use node_invoke(), because the access hook takes the $op parameter
- // before the $node parameter.
- $access = module_invoke(node_get_base($node), 'access', $op, $node);
- if (!is_null($access)) {
- return $access;
- }
-
- // If the module did not override the access rights, use those set in the
- // node_access table.
- if ($op != 'create' && $node->nid && $node->status) {
- $grants = array();
- foreach (node_access_grants($op, $uid) as $realm => $gids) {
- foreach ($gids as $gid) {
- $grants[] = "(gid = $gid AND realm = '$realm')";
- }
- }
-
- $grants_sql = '';
- if (count($grants)) {
- $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
- }
-
- $sql = "SELECT COUNT(*) FROM {node_access} WHERE (nid = 0 OR nid = %d) $grants_sql AND grant_$op >= 1";
- $result = db_query($sql, $node->nid);
- return (db_result($result));
- }
- return FALSE;
-}
-
-/**
- * Generate an SQL join clause for use in fetching a node listing.
- *
- * @param $node_alias
- * If the node table has been given an SQL alias other than the default
- * "n", that must be passed here.
- * @param $node_access_alias
- * If the node_access table has been given an SQL alias other than the default
- * "na", that must be passed here.
- * @return
- * An SQL join clause.
- */
-function _node_access_join_sql($node_alias = 'n', $node_access_alias = 'na') {
- if (user_access('administer nodes')) {
- return '';
- }
-
- return 'INNER JOIN {node_access} '. $node_access_alias .' ON '. $node_access_alias .'.nid = '. $node_alias .'.nid';
-}
-
-/**
- * Generate an SQL where clause for use in fetching a node listing.
- *
- * @param $op
- * The operation that must be allowed to return a node.
- * @param $node_access_alias
- * If the node_access table has been given an SQL alias other than the default
- * "na", that must be passed here.
- * @return
- * An SQL where clause.
- */
-function _node_access_where_sql($op = 'view', $node_access_alias = 'na', $uid = NULL) {
- if (user_access('administer nodes')) {
- return;
- }
-
- $grants = array();
- foreach (node_access_grants($op, $uid) as $realm => $gids) {
- foreach ($gids as $gid) {
- $grants[] = "($node_access_alias.gid = $gid AND $node_access_alias.realm = '$realm')";
- }
- }
-
- $grants_sql = '';
- if (count($grants)) {
- $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
- }
-
- $sql = "$node_access_alias.grant_$op >= 1 $grants_sql";
- return $sql;
-}
-
-/**
- * Fetch an array of permission IDs granted to the given user ID.
- *
- * The implementation here provides only the universal "all" grant. A node
- * access module should implement hook_node_grants() to provide a grant
- * list for the user.
- *
- * @param $op
- * The operation that the user is trying to perform.
- * @param $uid
- * The user ID performing the operation. If omitted, the current user is used.
- * @return
- * An associative array in which the keys are realms, and the values are
- * arrays of grants for those realms.
- */
-function node_access_grants($op, $uid = NULL) {
- global $user;
-
- if (isset($uid)) {
- $user_object = user_load(array('uid' => $uid));
- }
- else {
- $user_object = $user;
- }
-
- return array_merge(array('all' => array(0)), module_invoke_all('node_grants', $user_object, $op));
-}
-
-/**
- * Determine whether the user has a global viewing grant for all nodes.
- */
-function node_access_view_all_nodes() {
- static $access;
-
- if (!isset($access)) {
- $grants = array();
- foreach (node_access_grants('view') as $realm => $gids) {
- foreach ($gids as $gid) {
- $grants[] = "(gid = $gid AND realm = '$realm')";
- }
- }
-
- $grants_sql = '';
- if (count($grants)) {
- $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
- }
-
- $sql = "SELECT COUNT(*) FROM {node_access} WHERE nid = 0 $grants_sql AND grant_view >= 1";
- $result = db_query($sql);
- $access = db_result($result);
- }
-
- return $access;
-}
-
-/**
- * Implementation of hook_db_rewrite_sql
- */
-function node_db_rewrite_sql($query, $primary_table, $primary_field) {
- if ($primary_field == 'nid' && !node_access_view_all_nodes()) {
- $return['join'] = _node_access_join_sql($primary_table);
- $return['where'] = _node_access_where_sql();
- $return['distinct'] = 1;
- return $return;
- }
-}
-
-/**
- * @} End of "defgroup node_access".
- */
-
-
diff --git a/modules/page/page.module b/modules/page/page.module
deleted file mode 100644
index 320ce8a..0000000
--- a/modules/page/page.module
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables the creation of pages that can be added to the navigation system.
- */
-
-/**
- * Implementation of hook_help().
- */
-function page_help($section) {
- switch ($section) {
- case 'admin/help#page':
- $output = '<p>'. t('The page module allows users to create static pages, which are the most basic type of content. Pages are commonly collected in books via the book module. Users should create a page if the information on the page is static. An example would be an "about" page. ') .'</p>';
- $output .= '<p>'. t('When a page is created, a user can set authoring information, configure publishing options, whether readers will be able to post comments. They can also select the content type of the page (e.g., full HTML, filtered HTML). ') .'</p>';
- $output .= '<p>'. t('As an administrator, you can set the publishing default for a page (in its workflow): you can specify whether a page is by default published, sent to moderation, promoted to the front page, sticky at the top of lists, and whether revisions are enabled by default. You can set the permissions that different user roles have to view, create, and edit pages.') .'</p>';
- $output .= '<p>'. t('If the location module is enabled, then location specific information can be added. If the trackback module is enabled trackbacks can be configured.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>read the node administration help at <a href="%admin-help-node">administer &gt;&gt; help &gt;&gt; node</a>.</li>
-<li>read the page administration help at <a href="%admin-help-page">administer &gt;&gt; help &gt;&gt; page</a>.</li>
-<li>read the story administration help at <a href="%admin-help-story">administer &gt;&gt; help &gt;&gt; story</a>.</li>
-<li>create a page at <a href="%node-add-page">create content &gt;&gt; page</a>.</li>
-<li>administer page content type at <a href="%admin-settings-content-types-page">administer &gt;&gt; settings &gt;&gt; content types &gt;&gt; configure page</a>.</li>
-</ul>
-', array('%admin-help-node' => url('admin/help/node'), '%admin-help-page' => url('admin/help/page'), '%admin-help-story' => url('admin/help/story'), '%node-add-page' => url('node/add/page'), '%admin-settings-content-types-page' => url('admin/settings/content-types/page')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%page">Page page</a>.', array('%page' => 'http://drupal.org/handbook/modules/page/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Enables the creation of pages that can be added to the navigation system.');
- case 'node/add#page':
- return t('If you want to add a static page, like a contact page or an about page, use a page.');
- }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function page_perm() {
- return array('create pages', 'edit own pages');
-}
-
-/**
- * Implementation of hook_node_info().
- */
-function page_node_info() {
- return array('page' => array('name' => t('page'), 'base' => 'page'));
-}
-
-/**
- * Implementation of hook_access().
- */
-function page_access($op, $node) {
- global $user;
-
- if ($op == 'create') {
- return user_access('create pages');
- }
-
- if ($op == 'update' || $op == 'delete') {
- if (user_access('edit own pages') && ($user->uid == $node->uid)) {
- return TRUE;
- }
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function page_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'node/add/page', 'title' => t('page'),
- 'access' => user_access('create pages'));
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_form().
- */
-function page_form(&$node) {
-
- $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
-
- $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
- $form['body_filter']['format'] = filter_form($node->format);
-
- $form['log'] = array(
- '#type' => 'textarea', '#title' => t('Log message'), '#default_value' => $node->log, '#weight' => 5,
- '#description' => t('An explanation of the additions or updates being made to help other authors understand your motivations.')
- );
-
- return $form;
-}
-
-
diff --git a/modules/path/path.module b/modules/path/path.module
deleted file mode 100644
index f6f4312..0000000
--- a/modules/path/path.module
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables users to rename URLs.
- */
-
-/**
- * Implementation of hook_help().
- */
-function path_help($section) {
- switch ($section) {
- case 'admin/help#path':
- $output = '<p>'. t('The path module allows you to specify aliases for Drupal URLs. Such aliases improve readability of URLs for your users and may help internet search engines to index your content more effectively. More than one alias may be created for a given page.') .'</p>';
- $output .= t('<p>Some examples of URL aliases are:</p>
-<ul>
-<li>user/login =&gt; login</li>
-<li>image/tid/16 =&gt; store</li>
-<li>taxonomy/term/7+19+20+21 =&gt; store/products/whirlygigs</li>
-<li>node/3 =&gt; contact</li>
-</ul>
-');
- $output .= '<p>'. t('The path module enables an extra field for aliases in all node input and editing forms (when users have the appropriate permissions). It also provides an interface to view and edit all URL aliases. The two permissions are related to URL aliasing are "administer a list of URL aliases" and "add url aliases". ') .'</p>';
- $output .= '<p>'. t('This module also comes with user-defined mass URL aliasing capabilities, which is useful if you wish to uniformly use URLs different from the default. For example, you may want to have your URLs presented in a different language. Access to the Drupal source code on the web server is required to set up these kinds of aliases. ') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>set the path for a post with the path module.</li>
-<li>add a URL alias: <a href="%admin-path-add">administer &gt;&gt; url aliases &gt;&gt; add alias</a>.</li>
-<li>administer the list of URL aliases: <a href="%admin-path">administer &gt;&gt; url aliases</a>.</li>
-<li>read how to <a href="%external-http-drupal-org-node-15365">configure clean URLs</a> for your webserver.
-<li>enable clean url\'s to remove the =? at <a href="%admin-settings">administer &gt;&gt; settings</a>.</li>
-</ul>
-', array('%admin-path-add' => url('admin/path/add'), '%admin-path' => url('admin/path'), '%external-http-drupal-org-node-15365' => 'http://drupal.org/node/15365', '%admin-settings' => url('admin/settings')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%path">Path page</a>.', array('%path' => 'http://drupal.org/handbook/modules/path/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Allows users to rename URLs.');
- case 'admin/path':
- return t("<p>Drupal provides users complete control over URLs through aliasing. This feature is typically used to make URLs human-readable or easy to remember. For example, one could map the relative URL 'node/1' onto 'about'. Each system path can have multiple aliases.</p>");
- case 'admin/path/add':
- return t('<p>Enter the path you wish to create the alias for, followed by the name of the new alias.</p>');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function path_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'admin/path', 'title' => t('url aliases'),
- 'callback' => 'path_admin',
- 'access' => user_access('administer url aliases'));
- $items[] = array('path' => 'admin/path/edit', 'title' => t('edit alias'),
- 'callback' => 'path_admin_edit',
- 'access' => user_access('administer url aliases'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/path/delete', 'title' => t('delete alias'),
- 'callback' => 'path_admin_delete_confirm',
- 'access' => user_access('administer url aliases'),
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/path/list', 'title' => t('list'),
- 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
- $items[] = array('path' => 'admin/path/add', 'title' => t('add alias'),
- 'callback' => 'path_admin_edit',
- 'access' => user_access('administer url aliases'),
- 'type' => MENU_LOCAL_TASK);
- }
-
- return $items;
-}
-
-/**
- * Menu callback; presents an overview of all URL aliases.
- */
-function path_admin() {
- return path_overview();
-}
-
-/**
- * Menu callback; handles pages for creating and editing URL aliases.
- */
-function path_admin_edit($pid = 0) {
- if ($pid) {
- $alias = path_load($pid);
- drupal_set_title($alias['dst']);
- $output = path_form(path_load($pid));
- }
- else {
- $output = path_form();
- }
-
- return $output;
-}
-
-/**
- * Menu callback; confirms deleting an URL alias
- **/
-function path_admin_delete_confirm($pid) {
- $path = path_load($pid);
- if (user_access('administer url aliases')) {
- $form['pid'] = array('#type' => 'value', '#value' => $pid);
- $output = confirm_form('path_admin_delete_confirm', $form,
- t('Are you sure you want to delete path alias %title?', array('%title' => theme('placeholder', $path['dst']))),
- $_GET['destination'] ? $_GET['destination'] : 'admin/path', t('This action cannot be undone.'),
- t('Delete'), t('Cancel') );
- }
-
- return $output;
-}
-
-/**
- * Execute URL alias deletion
- **/
-function path_admin_delete_confirm_submit($form_id, $form_values) {
- if ($form_values['confirm']) {
- path_admin_delete($form_values['pid']);
- return 'admin/path';
- }
-}
-
-/**
- * Post-confirmation; delete an URL alias.
- */
-function path_admin_delete($pid = 0) {
- db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid);
- drupal_set_message(t('The alias has been deleted.'));
-}
-
-
-
-/**
- * Set an aliased path for a given Drupal path, preventing duplicates.
- */
-function path_set_alias($path = NULL, $alias = NULL, $pid = NULL) {
- if ($path && !$alias) {
- db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
- drupal_clear_path_cache();
- }
- else if (!$path && $alias) {
- db_query("DELETE FROM {url_alias} WHERE dst = '%s'", $alias);
- drupal_clear_path_cache();
- }
- else if ($path && $alias) {
- $path = urldecode($path);
- $path_count = db_result(db_query("SELECT COUNT(src) FROM {url_alias} WHERE src = '%s'", $path));
- $alias = urldecode($alias);
- $alias_count = db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s'", $alias));
-
- // We have an insert:
- if ($path_count == 0 && $alias_count == 0) {
- db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
- drupal_clear_path_cache();
- }
- else if ($path_count >= 1 && $alias_count == 0) {
- if ($pid) {
- db_query("UPDATE {url_alias} SET dst = '%s', src = '%s' WHERE pid = %d", $alias, $path, $pid);
- }
- else {
- db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
- }
- drupal_clear_path_cache();
- }
- else if ($path_count == 0 && $alias_count == 1) {
- db_query("UPDATE {url_alias} SET src = '%s' WHERE dst = '%s'", $path, $alias);
- drupal_clear_path_cache();
- }
- else if ($path_count == 1 && $alias_count == 1) {
- // This will delete the path that alias was originally pointing to:
- path_set_alias(NULL, $alias);
- path_set_alias($path);
- path_set_alias($path, $alias);
- }
- }
-}
-
-/**
- * Return a form for editing or creating an individual URL alias.
- */
-function path_form($edit = '') {
-
- $form['src'] = array('#type' => 'textfield', '#title' => t('Existing system path'), '#default_value' => $edit['src'], '#maxlength' => 64, '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.'));
- $form['dst'] = array('#type' => 'textfield', '#default_value' => $edit['dst'], '#maxlength' => 64, '#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'));
-
- if ($edit['pid']) {
- $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']);
- $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias'));
- }
- else {
- $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias'));
- }
-
- return drupal_get_form('path_form', $form);
-}
-
-/**
- * Implementation of hook_nodeapi().
- *
- * Allows URL aliases for nodes to be specified at node edit time rather
- * than through the administrative interface.
- */
-function path_nodeapi(&$node, $op, $arg) {
- if (user_access('create url aliases') || user_access('administer url aliases')) {
- switch ($op) {
- case 'validate':
- $node->path = trim($node->path);
- if ($node->path && !valid_url($node->path)) {
- form_set_error('path', t('The path is invalid.'));
- }
- else if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND src != '%s'", $node->path, "node/$node->nid"))) {
- form_set_error('path', t('The path is already in use.'));
- }
- break;
-
- case 'load':
- $path = "node/$node->nid";
- $alias = drupal_get_path_alias($path);
- if ($alias != $path) {
- $node->path = $alias;
- }
- break;
-
- case 'insert':
- // Don't try to insert if path is NULL. We may have already set
- // the alias ahead of time.
- if ($node->path) {
- path_set_alias("node/$node->nid", $node->path);
- }
- break;
-
- case 'update':
- path_set_alias("node/$node->nid", $node->path, $node->pid);
- break;
-
- case 'delete':
- $path = "node/$node->nid";
- if (drupal_get_path_alias($path) != $path) {
- path_set_alias($path);
- }
- break;
- }
- }
-}
-
-/**
- * Implementation of hook_form_alter().
- */
-function path_form_alter($form_id, &$form) {
- if (user_access('create url aliases') && isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
- $path = $form['#node']->path;
- $form['path'] = array(
- '#type' => 'fieldset',
- '#title' => t('URL path settings'),
- '#collapsible' => TRUE,
- '#collapsed' => empty($path),
- '#weight' => 30,
- );
- $form['path']['path'] = array(
- '#type' => 'textfield',
- '#default_value' => $path,
- '#maxlength' => 250,
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
- '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
- );
- if ($path) {
- $form['path']['pid'] = array(
- '#type' => 'value',
- '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $path))
- );
- }
- }
-}
-
-
-/**
- * Implementation of hook_perm().
- */
-function path_perm() {
- return array('create url aliases', 'administer url aliases');
-}
-
-/**
- * Return a listing of all defined URL aliases.
- */
-function path_overview() {
- $sql = 'SELECT * FROM {url_alias}';
- $header = array(
- array('data' => t('Alias'), 'field' => 'dst', 'sort' => 'asc'),
- array('data' => t('System'), 'field' => 'src'),
- array('data' => t('Operations'), 'colspan' => '2')
- );
- $sql .= tablesort_sql($header);
- $result = pager_query($sql, 50);
-
- $destination = drupal_get_destination();
- while ($data = db_fetch_object($result)) {
- $rows[] = array($data->dst, $data->src, l(t('edit'), "admin/path/edit/$data->pid", array(), $destination), l(t('delete'), "admin/path/delete/$data->pid", array(), $destination));
- }
-
- if (!$rows) {
- $rows[] = array(array('data' => t('No URL aliases available.'), 'colspan' => '4'));
- }
-
- $output = theme('table', $header, $rows);
- $output .= theme('pager', NULL, 50, 0, tablesort_pager());
- return $output;
-}
-
-/**
- * Fetch a specific URL alias from the database.
- */
-function path_load($pid) {
- return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid));
-}
-
-/**
- * Verify that a new URL alias is valid, and save it to the database.
- */
-function path_form_submit() {
- $edit = $GLOBALS['form_values'];
- $src = $edit['src'];
- $dst = $edit['dst'];
- $pid = $edit['pid'];
-
- if (!valid_url($src)) {
- form_set_error('src', t('The system path %path is invalid.', array('%path' => theme('placeholder', $src))));
- }
-
- if (!valid_url($dst)) {
- form_set_error('dst', t('The alias %alias is invalid.', array('%alias' => theme('placeholder', $dst))));
- }
-
- if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'", $pid, $dst))) {
- form_set_error('dst', t('The alias %alias is already in use.', array('%alias' => theme('placeholder', $dst))));
- }
-
- if (form_get_errors()) {
- return path_form($edit);
- }
- else {
- path_set_alias($src, $dst, $pid);
-
- drupal_set_message(t('The alias has been saved.'));
- return 'admin/path';
- }
-}
-
-
diff --git a/modules/ping/ping.module b/modules/ping/ping.module
deleted file mode 100644
index dfc51f0..0000000
--- a/modules/ping/ping.module
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Alerts other sites that your site has been updated.
- */
-
-/**
- * Implementation of hook_help().
- */
-function ping_help($section) {
- switch ($section) {
- case 'admin/help#ping':
- $output = '<p>'. t('The ping module is useful for notifying interested sites that your site has changed. It automatically sends notifications (called "pings") to the <a href="%external-http-pingomatic-com">pingomatic</a> service to tell it that your site has changed. In turn pingomatic will ping other services such as weblogs.com, Technorati, blo.gs, BlogRolling, Feedster.com, Moreover, etc.', array('%external-http-pingomatic-com' => 'http://pingomatic.com/')) .'</p>';
- $output .= '<p>'. t('The ping module requires <code>cron</code> or a similar periodic job scheduler to be enabled.') .'</p>';
- $output .= t('<p>You can:</p>
-<ul>
-<li> enable or disable the ping module at <a href="%admin-modules">administer &gt;&gt; modules</a>.</li>
-<li>run your cron job at your sites <a href="%file-cron">cron page</a>.</li>
-<li>read about <a href="%external-http-drupal-org-node-23714">configuring cron jobs</a>.</li>
-</ul></p>
-', array('%admin-modules' => url('admin/modules'), '%file-cron' => 'cron.php', '%external-http-drupal-org-node-23714' => 'http://drupal.org/node/23714'));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%ping">Ping page</a>.', array('%ping' => 'http://drupal.org/handbook/modules/ping/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return('Alerts other sites when your site has been updated.');
- }
-}
-
-/**
- * Implementation of hook_cron().
- *
- * Fire off notifications of updates to remote sites.
- */
-function ping_cron() {
- global $base_url;
-
- if (variable_get('site_name', 0) && variable_get('site_slogan', 0)) {
- if (db_num_rows(db_query("SELECT nid FROM {node} WHERE status = 1 AND moderate = 0 AND (created > '". variable_get('cron_last', time()) ."' OR changed > '". variable_get('cron_last', time()) ."')"), 1)) {
- _ping_notify(variable_get('site_name', '') .' - '. variable_get('site_slogan', ''), $base_url);
- }
- }
-}
-
-/**
- * Call hook_ping() in all modules to notify remote sites that there is
- * new content at this one.
- */
-function _ping_notify($name, $url) {
- module_invoke_all('ping', $name, $url);
-}
-
-/**
- * Implementation of hook_ping().
- *
- * Notifies pingomatic.com, blo.gs, and technorati.com of changes at this site.
- */
-function ping_ping($name = '', $url = '') {
-
- $result = xmlrpc('http://rpc.pingomatic.com', 'weblogUpdates.ping', $name, $url);
-
- if ($result === FALSE) {
- watchdog('directory ping', t('Failed to notify pingomatic.com (site).'), WATCHDOG_WARNING);
- }
-}
-
-
diff --git a/modules/poll/poll.module b/modules/poll/poll.module
deleted file mode 100644
index 7d452d5..0000000
--- a/modules/poll/poll.module
+++ /dev/null
@@ -1,503 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables your site to capture votes on different topics in the form of multiple
- * choice questions.
- */
-
-/**
- * Implementation of hook_help().
- */
-function poll_help($section) {
- switch ($section) {
- case 'admin/help#poll':
- $output = '<p>'. t('The poll module can be used to create simple polls for site users. A poll is a simple multiple choice questionnaire which displays the cummulative results of the answers to the poll. Having polls on the site is a good way to get instant feedback from community members.') .'</p>';
- $output .= '<p>'. t('Users can create a poll. The title of the poll should be the question, then enter the answers and the "base" vote counts. You can also choose the time period over which the vote will run.The <a href="%poll">poll</a> item in the navigation menu will take you to a page where you can see all the current polls, vote on them (if you haven\'t already) and view the results.', array('%poll' => url('poll'))) .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>view the <a href="%poll">polls page</a>.</li>
-<li><a href="%admin-node-configure-types-poll">administer &gt;&gt; content &gt;&gt; configure &gt;&gt; poll</a>.</li>
-</ul>
-', array('%poll' => url('poll'), '%admin-node-configure-types-poll' => url('admin/node/configure/types/poll')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%poll">Poll page</a>.', array('%poll' => 'http://drupal.org/handbook/modules/poll/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t("Allows your site to capture votes on different topics in the form of multiple choice questions.");
- case 'node/add#poll':
- return t("A poll is a multiple-choice question which visitors can vote on.");
- }
-}
-
-/**
- * Implementation of hook_access().
- */
-function poll_access($op, $node) {
- if ($op == 'create') {
- return user_access('create polls');
- }
-}
-
-/**
- * Implementation of hook_block().
- *
- * Generates a block containing the latest poll.
- */
-function poll_block($op = 'list', $delta = 0) {
- if (user_access('access content')) {
- if ($op == 'list') {
- $blocks[0]['info'] = t('Most recent poll');
- return $blocks;
- }
- else if ($op == 'view') {
- // Retrieve the latest poll.
- $sql = db_rewrite_sql("SELECT MAX(n.created) FROM {node} n INNER JOIN {poll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1 AND n.moderate = 0");
- $timestamp = db_result(db_query($sql));
- if ($timestamp) {
- $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));
-
- if ($poll->nid) {
- // poll_view() dumps the output into $poll->body.
- poll_view($poll, 1, 0, 1);
- }
- }
- $block['subject'] = t('Poll');
- $block['content'] = $poll->body;
- return $block;
- }
- }
-}
-
-/**
- * Implementation of hook_cron().
- *
- * Closes polls that have exceeded their allowed runtime.
- */
-function poll_cron() {
- $result = db_query('SELECT p.nid FROM {poll} p INNER JOIN {node} n ON p.nid = n.nid WHERE (n.created + p.runtime) < '. time() .' AND p.active = 1 AND p.runtime != 0');
- while ($poll = db_fetch_object($result)) {
- db_query("UPDATE {poll} SET active = 0 WHERE nid = %d", $poll->nid);
- }
-}
-
-/**
- * Implementation of hook_delete().
- */
-function poll_delete($node) {
- db_query("DELETE FROM {poll} WHERE nid = %d", $node->nid);
- db_query("DELETE FROM {poll_choices} WHERE nid = %d", $node->nid);
- db_query("DELETE FROM {poll_votes} WHERE nid = %d", $node->nid);
-}
-
-/**
- * Implementation of hook_submit().
- */
-function poll_submit(&$node) {
- // Renumber fields
- $node->choice = array_values($node->choice);
- $node->teaser = poll_teaser($node);
-}
-
-/**
- * Implementation of hook_validate().
- */
-function poll_validate($node) {
- node_validate_title($node, t('You have to specify a question.'));
-
- if (isset($node->title)) {
- // Check for at least two options and validate amount of votes:
- $realchoices = 0;
- // Renumber fields
- $node->choice = array_values($node->choice);
- foreach ($node->choice as $i => $choice) {
- if ($choice['chtext'] != '') {
- $realchoices++;
- }
- if ($choice['chvotes'] < 0) {
- form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
- }
- }
-
- if ($realchoices < 2) {
- form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
- }
- }
-}
-
-/**
- * Implementation of hook_form().
- */
-function poll_form(&$node) {
- $admin = user_access('administer nodes');
-
- $form['title'] = array('#type' => 'textfield', '#title' => t('Question'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -1);
-
- $form['choice']['choices'] = array('#type' => 'hidden', '#default_value' => max(2, count($node->choice) ? count($node->choice) : 5));
- $form['choice']['morechoices'] = array('#type' => 'checkbox', '#title' => t('Need more choices'), '#default_value' => 0, '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more."), '#weight' => 1);
- $form['choice'] = form_builder('poll_node_form', $form['choice']);
- if ($form['choice']['morechoices']['#value']) {
- $form['choice']['morechoices']['#value'] = 0;
- $form['choice']['choices']['#value'] *= 2;
- }
-
- // if the value was changed in a previous iteration, retain it.
- $node->choices = $form['choice']['choices']['#value'];
-
- // Poll choices
- $form['choice'] += array('#type' => 'fieldset', '#title' => t('Choices'), '#prefix' => '<div class="poll-form">', '#suffix' => '</div>', '#tree' => TRUE);
- for ($a = 0; $a < $node->choices; $a++) {
- $form['choice'][$a]['chtext'] = array('#type' => 'textfield', '#title' => t('Choice %n', array('%n' => ($a + 1))), '#default_value' => $node->choice[$a]['chtext']);
- if ($admin) {
- $form['choice'][$a]['chvotes'] = array('#type' => 'textfield', '#title' => t('Votes for choice %n', array('%n' => ($a + 1))), '#default_value' => (int)$node->choice[$a]['chvotes'], '#size' => 5, '#maxlength' => 7);
- }
- }
-
- // Poll attributes
- $_duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval");
- $_active = array(0 => t('Closed'), 1 => t('Active'));
-
- if ($admin) {
- $form['settings'] = array('#type' => 'fieldset', '#title' => t('Settings'));
- $form['settings']['active'] = array('#type' => 'radios', '#title' => t('Poll status'), '#default_value' => isset($node->active) ? $node->active : 1, '#options' => $_active, '#description' => t('When a poll is closed, visitors can no longer vote for it.'));
- }
- $form['settings']['runtime'] = array('#type' => 'select', '#title' => t('Poll duration'), '#default_value' => $node->runtime ? $node->runtime : 0, '#options' => $_duration, '#description' => t('After this period, the poll will be closed automatically.'));
-
- return $form;
-}
-
-function poll_insert($node) {
- if (!user_access('administer nodes')) {
- // Make sure all votes are 0 initially
- foreach ($node->choice as $i => $choice) {
- $node->choice[$i]['chvotes'] = 0;
- }
- $node->active = 1;
- }
-
- db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)", $node->nid, $node->runtime, $node->active);
-
- foreach ($node->choice as $choice) {
- if ($choice['chtext'] != '') {
- db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
- }
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function poll_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'node/add/poll', 'title' => t('poll'),
- 'access' => user_access('create polls'));
- $items[] = array('path' => 'poll', 'title' => t('polls'),
- 'callback' => 'poll_page',
- 'access' => user_access('access content'),
- 'type' => MENU_SUGGESTED_ITEM);
-
- $items[] = array('path' => 'poll/vote',
- 'title' => t('vote'),
- 'callback' => 'poll_vote',
- 'access' => user_access('vote on polls'),
- 'type' => MENU_CALLBACK);
- }
- else {
- if (arg(0) == 'node' && is_numeric(arg(1))) {
- $node = node_load(arg(1));
-
- if ($node->type == 'poll' && $node->allowvotes) {
- $items[] = array('path' => 'node/'. arg(1) .'/results',
- 'title' => t('results'),
- 'callback' => 'poll_results',
- 'access' => user_access('access content'),
- 'weight' => 3,
- 'type' => MENU_LOCAL_TASK);
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_load().
- */
-function poll_load($node) {
- global $user;
-
- // Load the appropriate choices into the $node object
- $poll = db_fetch_object(db_query("SELECT runtime, active FROM {poll} WHERE nid = %d", $node->nid));
-
- $result = db_query("SELECT chtext, chvotes, chorder FROM {poll_choices} WHERE nid = %d ORDER BY chorder", $node->nid);
- while ($choice = db_fetch_array($result)) {
- $poll->choice[$choice['chorder']] = $choice;
- }
-
- // Determine whether or not this user is allowed to vote
- $poll->allowvotes = FALSE;
- if (user_access('vote on polls') && $poll->active) {
- if ($user->uid && db_num_rows(db_query('SELECT uid FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid)) == 0) {
- $poll->allowvotes = TRUE;
- }
- else if ($user->uid == 0 && db_num_rows(db_query("SELECT hostname FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, $_SERVER['REMOTE_ADDR'])) == 0) {
- $poll->allowvotes = TRUE;
- }
- }
- return $poll;
-}
-
-/**
- * Implementation of hook_node_info().
- */
-function poll_node_info() {
- return array('poll' => array('name' => t("poll"), 'base' => 'poll'));
-}
-
-function poll_page() {
- // List all polls
- $sql = "SELECT n.nid, n.title, p.active, n.created, SUM(c.chvotes) AS votes FROM {node} n INNER JOIN {poll} p ON n.nid = p.nid INNER JOIN {poll_choices} c ON n.nid = c.nid WHERE n.status = 1 AND n.moderate = 0 GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC";
- $sql = db_rewrite_sql($sql);
- $result = pager_query($sql, 15);
- $output = '<ul>';
- while ($node = db_fetch_object($result)) {
- $output .= '<li>'. l($node->title, "node/$node->nid") .' - '. format_plural($node->votes, '1 vote', '%count votes') .' - '. ($node->active ? t('open') : t('closed')) .'</li>';
- }
- $output .= '</ul>';
- $output .= theme("pager", NULL, 15);
- return $output;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function poll_perm() {
- return array('create polls', 'vote on polls');
-}
-
-/**
- * Creates a simple teaser that lists all the choices.
- */
-function poll_teaser($node) {
- $teaser = NULL;
- if (is_array($node->choice)) {
- foreach ($node->choice as $k => $choice) {
- $teaser .= '* '. $choice['chtext'] .'\n';
- }
- }
- return $teaser;
-}
-
-/**
- * Generates the voting form for a poll.
- */
-function poll_view_voting(&$node, $teaser, $page, $block) {
- if ($_POST['op'] == t('Vote')) {
- poll_vote($node);
- }
-
- if ($node->choice) {
- $list = array();
- foreach ($node->choice as $i => $choice) {
- $list[$i] = check_plain($choice['chtext']);
- }
- $form['choice'] = array('#type' => 'radios', '#title' => $page ? '' : check_plain($node->title), '#default_value' => -1, '#options' => $list);
- }
- $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
- $form['vote'] = array('#type' => 'submit', '#value' => t('Vote'));
- $form['#action'] = url('node/'. $node->nid);
- return drupal_get_form('poll_view_voting', $form);
-}
-
-/**
- * Themes the voting form for a poll.
- */
-function theme_poll_view_voting($form) {
- $output .= '<div class="poll">';
- $output .= ' <div class="vote-form">';
- $output .= ' <div class="choices">';
- $output .= form_render($form['choice']);
- $output .= ' </div>';
- $output .= form_render($form['nid']);
- $output .= form_render($form['vote']);
- $output .= ' </div>';
- $output .= form_render($form);
- $output .= '</div>';
- return $output;
-}
-
-/**
- * Generates a graphical representation of the results of a poll.
- */
-function poll_view_results(&$node, $teaser, $page, $block) {
- // Count the votes and find the maximum
- foreach ($node->choice as $choice) {
- $total_votes += $choice['chvotes'];
- $max_votes = max($max_votes, $choice['chvotes']);
- }
-
- foreach ($node->choice as $i => $choice) {
- if ($choice['chtext'] != '') {
- $poll_results .= theme('poll_bar', check_plain($choice['chtext']), round($choice['chvotes'] * 100 / max($total_votes, 1)), format_plural($choice['chvotes'], '1 vote', '%count votes'), $block);
- }
- }
-
- $output .= theme('poll_results', check_plain($node->title), $poll_results, $total_votes, $node->links, $block);
-
- return $output;
-}
-
-function theme_poll_results($title, $results, $votes, $links, $block) {
- if ($block) {
- $output .= '<div class="poll">';
- $output .= '<div class="title">'. $title .'</div>';
- $output .= $results;
- $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>';
- $output .= '</div>';
- $output .= '<div class="links">'. theme('links', $links) .'</div>';
- }
- else {
- $output .= '<div class="poll">';
- $output .= $results;
- $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>';
- $output .= '</div>';
- }
-
- return $output;
-}
-
-function theme_poll_bar($title, $percentage, $votes, $block) {
- if ($block) {
- $output = '<div class="text">'. $title .'</div>';
- $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>';
- $output .= '<div class="percent">'. $percentage .'%</div>';
- }
- else {
- $output = '<div class="text">'. $title .'</div>';
- $output .= '<div class="bar"><div style="width: '. $percentage .'%;" class="foreground"></div></div>';
- $output .= '<div class="percent">'. $percentage .'% ('. $votes .')</div>';
- }
-
- return $output;
-}
-
-/**
- * Callback for the 'results' tab for polls you can vote on
- */
-function poll_results() {
- if ($node = node_load(arg(1))) {
- drupal_set_title(check_plain($node->title));
- return node_show($node, 0);
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Callback for processing a vote
- */
-function poll_vote(&$node) {
- global $user;
- $nid = arg(1);
-
- if ($node = node_load($nid)) {
- $edit = $_POST['edit'];
- $choice = $edit['choice'];
- $vote = $_POST['vote'];
-
- if (isset($choice) && isset($node->choice[$choice])) {
- if ($node->allowvotes) {
- // Mark the user or host as having voted.
- if ($user->uid) {
- db_query('INSERT INTO {poll_votes} (nid, uid) VALUES (%d, %d)', $node->nid, $user->uid);
- }
- else {
- db_query("INSERT INTO {poll_votes} (nid, hostname) VALUES (%d, '%s')", $node->nid, $_SERVER['REMOTE_ADDR']);
- }
-
- // Add one to the votes.
- db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE nid = %d AND chorder = %d", $node->nid, $choice);
-
- $node->allowvotes = FALSE;
- $node->choice[$choice]['chvotes']++;
- drupal_set_message(t('Your vote was recorded.'));
- }
- else {
- drupal_set_message(t("You're not allowed to vote on this poll."), 'error');
- }
- }
- else {
- drupal_set_message(t("You didn't specify a valid poll choice."), 'error');
- }
-
- drupal_goto('node/'. $nid);
- }
- else {
- drupal_not_found();
- }
-}
-
-/**
- * Implementation of hook_view().
- *
- * @param $block
- * An extra parameter that adapts the hook to display a block-ready
- * rendering of the poll.
- */
-function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
- global $user;
- $output = '';
-
- // Special display for side-block
- if ($block) {
- // No 'read more' link
- $node->body = $node->teaser = '';
-
- $links = module_invoke_all('link', 'node', $node, 1);
- $links[] = l(t('older polls'), 'poll', array('title' => t('View the list of polls on this site.')));
- if ($node->allowvotes && $block) {
- $links[] = l(t('results'), 'node/'. $node->nid .'/results', array('title' => t('View the current poll results.')));
- }
-
- $node->links = $links;
- }
-
- if ($node->allowvotes && ($block || arg(2) != 'results')) {
- $output .= poll_view_voting($node, $teaser, $page, $block);
- }
- else {
- $output .= poll_view_results($node, $teaser, $page, $block);
- }
-
- $node->body = $node->teaser = $output;
-}
-
-/**
- * Implementation of hook_update().
- */
-function poll_update($node) {
- db_query('UPDATE {poll} SET runtime = %d, active = %d WHERE nid = %d', $node->runtime, $node->active, $node->nid);
-
- db_query('DELETE FROM {poll_choices} WHERE nid = %d', $node->nid);
- db_query('DELETE FROM {poll_votes} WHERE nid = %d', $node->nid);
- foreach ($node->choice as $choice) {
- $chvotes = (int)$choice['chvotes'];
- $chtext = $choice['chtext'];
-
- if ($chtext != '') {
- db_query("INSERT INTO {poll_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $chtext, $chvotes, $i++);
- }
- }
-}
-
-/**
- * Implementation of hook_user().
- */
-function poll_user($op, &$edit, &$user) {
- if ($op == 'delete') {
- db_query('UPDATE {poll_votes} SET uid = 0 WHERE uid = %d', $user->uid);
- }
-}
diff --git a/modules/profile/profile.module b/modules/profile/profile.module
deleted file mode 100644
index d08b8be..0000000
--- a/modules/profile/profile.module
+++ /dev/null
@@ -1,776 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Support for configurable user profiles.
- */
-
-/**
- * Flags to define the visibility of a profile field.
- */
-define('PROFILE_PRIVATE', 1);
-define('PROFILE_PUBLIC', 2);
-define('PROFILE_PUBLIC_LISTINGS', 3);
-define('PROFILE_HIDDEN', 4);
-
-/**
- * Implementation of hook_help().
- */
-function profile_help($section) {
- switch ($section) {
- case 'admin/help#profile':
- $output = '<p>'. t('The profile module allows you to define custom fields (such as country, real name, age, ...) in the user profile. This permits users of a site to share more information about themselves, and can help community-based sites to organize users around profile fields.') .'</p>';
- $output .= t('<p>The following types of fields can be added to the user profile:</p>
-<ul>
-<li>single-line textfield</li>
-<li>multi-line textfield</li>
-<li>checkbox</li>
-<li>list selection</li>
-<li>freeform list</li>
-<li>URL</li>
-<li>date</li>
-</ul>
-');
- $output .= t('<p>You can</p>
-<ul>
-<li>view user <a href="%profile">profiles</a>.</li>
-<li>administer profile settings: <a href="%admin-settings-profile">administer &gt;&gt; settings &gt;&gt; profiles</a>.</li>
-</ul>
-', array('%profile' => url('profile'), '%admin-settings-profile' => url('admin/settings/profile')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%profile">Profile page</a>.', array('%profile' => 'http://drupal.org/handbook/modules/profile/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Supports configurable user profiles.');
- case 'admin/settings/profile':
- return t('<p>Here you can define custom fields that users can fill in in their user profile (such as <em>country</em>, <em>real name</em>, <em>age</em>, ...).</p>');
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function profile_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'profile',
- 'title' => t('user list'),
- 'callback' => 'profile_browse',
- 'access' => user_access('access user profiles'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array('path' => 'admin/settings/profile',
- 'title' => t('profiles'),
- 'callback' => 'profile_admin_overview');
- $items[] = array('path' => 'admin/settings/profile/add',
- 'title' => t('add field'),
- 'callback' => 'profile_field_form',
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/settings/profile/edit',
- 'title' => t('edit field'),
- 'callback' => 'profile_field_form',
- 'type' => MENU_CALLBACK);
- $items[] = array('path' => 'admin/settings/profile/delete',
- 'title' => t('delete field'),
- 'callback' => 'profile_field_delete',
- 'type' => MENU_CALLBACK);
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_block().
- */
-function profile_block($op = 'list', $delta = 0, $edit = array()) {
-
- if ($op == 'list') {
- $blocks[0]['info'] = t('Author information');
-
- return $blocks;
- }
- else if ($op == 'configure' && $delta == 0) {
- // Compile a list of fields to show
- $fields = array();
- $result = db_query('SELECT name, title, weight, visibility FROM {profile_fields} WHERE visibility IN (%d, %d) ORDER BY weight', PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS);
- while ($record = db_fetch_object($result)) {
- $fields[$record->name] = $record->title;
- }
- $fields['user_profile'] = t('Link to full user profile');
- $form['profile_block_author_fields'] = array('#type' => 'checkboxes',
- '#title' => t('Profile fields to display'),
- '#default_value' => variable_get('profile_block_author_fields', NULL),
- '#options' => $fields,
- '#description' => t('Select which profile fields you wish to display in the block. Only fields designated as public in the <a href="%profile-admin">profile field configuration</a> are available.', array('%profile-admin' => url('admin/settings/profile'))),
- );
- return $form;
- }
- else if ($op == 'save' && $delta == 0) {
- variable_set('profile_block_author_fields', $edit['profile_block_author_fields']);
- }
- else if ($op == 'view') {
- if (user_access('access user profiles')) {
- if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) {
- $node = node_load(arg(1));
- $account = user_load(array('uid' => $node->uid));
-
- if ($use_fields = variable_get('profile_block_author_fields', array())) {
- // Compile a list of fields to show.
- $fields = array();
- $result = db_query('SELECT name, title, type, visibility, weight FROM {profile_fields} WHERE visibility IN (%d, %d) ORDER BY weight', PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS);
- while ($record = db_fetch_object($result)) {
- // Ensure that field is displayed only if it is among the defined block fields and, if it is private, the user has appropriate permissions.
- if (isset($use_fields[$record->name]) && $use_fields[$record->name]) {
- $fields[] = $record;
- }
- }
- }
-
- if ($fields) {
- $fields = _profile_update_user_fields($fields, $account);
- $output .= theme('profile_block', $account, $fields, true);
- }
-
- if (isset($use_fields['user_profile']) && $use_fields['user_profile']) {
- $output .= '<div>' . l(t('View full user profile'), 'user/' . $account->uid) . '</div>';
- }
- }
-
- if ($output) {
- $block['subject'] = t('About %name', array('%name' => $account->name));
- $block['content'] = $output;
- return $block;
- }
- }
- }
-}
-
-/**
- * Implementation of hook_user().
- */
-function profile_user($type, &$edit, &$user, $category = NULL) {
- switch ($type) {
- case 'load':
- return profile_load_profile($user);
- case 'register':
- return profile_form_profile($edit, $user, $category);
- case 'update':
- case 'insert':
- return profile_save_profile($edit, $user, $category);
- case 'view':
- return profile_view_profile($user);
- case 'form':
- return profile_form_profile($edit, $user, $category);
- case 'validate':
- return profile_validate_profile($edit, $category);
- case 'categories':
- return profile_categories();
- case 'delete':
- db_query('DELETE FROM {profile_values} WHERE uid = %d', $user->uid);
- }
-}
-
-/**
- * Menu callback: Generate a form to add/edit a user profile field.
- */
-function profile_field_form($arg = NULL) {
- if (arg(3) == 'edit') {
- if (is_numeric($arg)) {
- $fid = $arg;
-
- $edit = db_fetch_array(db_query('SELECT * FROM {profile_fields} WHERE fid = %d', $fid));
-
- if (!$edit) {
- drupal_not_found();
- return;
- }
- drupal_set_title(t('edit %title', array('%title' => $edit['title'])));
- $form['fid'] = array('#type' => 'value',
- '#value' => $fid,
- );
- $type = $edit['type'];
- }
- else {
- drupal_not_found();
- return;
- }
- }
- else {
- $types = _profile_field_types();
- if (!isset($types[$arg])) {
- drupal_not_found();
- return;
- }
- $type = $arg;
- drupal_set_title(t('add new %type', array('%type' => $types[$type])));
- $edit = array('name' => 'profile_');
- $form['type'] = array('#type' => 'value', '#value' => $type);
- }
- $form['fields'] = array('#type' => 'fieldset',
- '#title' => t('Field settings'),
- );
- $form['fields']['category'] = array('#type' => 'textfield',
- '#title' => t('Category'),
- '#default_value' => $edit['category'],
- '#description' => t('The category the new field should be part of. Categories are used to group fields logically. An example category is "Personal information".'),
- '#required' => TRUE,
- );
- $form['fields']['title'] = array('#type' => 'textfield',
- '#title' => t('Title'),
- '#default_value' => $edit['title'],
- '#description' => t('The title of the new field. The title will be shown to the user. An example title is "Favorite color".'),
- '#required' => TRUE,
- );
- $form['fields']['name'] = array('#type' => 'textfield',
- '#title' => t('Form name'),
- '#default_value' => $edit['name'],
- '#description' => t('The name of the field. The form name is not shown to the user but used internally in the HTML code and URLs.
-Unless you know what you are doing, it is highly recommended that you prefix the form name with <code>profile_</code> to avoid name clashes with other fields. Spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example name is "profile_favorite_color" or perhaps just "profile_color".'),
- '#required' => TRUE,
- );
- $form['fields']['explanation'] = array('#type' => 'textarea',
- '#title' => t('Explanation'),
- '#default_value' => $edit['explanation'],
- '#description' => t('An optional explanation to go with the new field. The explanation will be shown to the user.'),
- );
- if ($type == 'selection') {
- $form['fields']['options'] = array('#type' => 'textarea',
- '#title' => t('Selection options'),
- '#default_value' => $edit['options'],
- '#description' => t('A list of all options. Put each option on a separate line. Example options are "red", "blue", "green", etc.'),
- );
- }
- $form['fields']['weight'] = array('#type' => 'weight',
- '#title' => t('Weight'),
- '#default_value' => $edit['weight'],
- '#delta' => 5,
- '#description' => t('The weights define the order in which the form fields are shown. Lighter fields "float up" towards the top of the category.'),
- );
- $form['fields']['visibility'] = array('#type' => 'radios',
- '#title' => t('Visibility'),
- '#default_value' => isset($edit['visibility']) ? $edit['visibility'] : PROFILE_PUBLIC,
- '#options' => array(PROFILE_HIDDEN => t('Hidden profile field, only accessible by administrators, modules and themes.'), PROFILE_PRIVATE => t('Private field, content only available to privileged users.'), PROFILE_PUBLIC => t('Public field, content shown on profile page but not used on member list pages.'), PROFILE_PUBLIC_LISTINGS => t('Public field, content shown on profile page and on member list pages.')),
- );
- if ($type == 'selection' || $type == 'list') {
- $form['fields']['page'] = array('#type' => 'textfield',
- '#title' => t('Page title'),
- '#default_value' => $edit['page'],
- '#description' => t('The title of the page showing all users with the specified field. The word <code>%value</code> will be substituted with the corresponding value. An example page title is "People whose favorite color is %value". This is only applicable for a public field.'),
- );
- }
- else {
- $form['fields']['page'] = array('#type' => 'textfield',
- '#title' => t('Page title'),
- '#default_value' => $edit['page'],
- '#description' => t('The title of the page showing all users with the specified field. Only applicable if the field is configured to be shown on member listings.'),
- );
- }
- $form['fields']['required'] = array('#type' => 'checkbox',
- '#title' => t('The user must enter a value.'),
- '#default_value' => $edit['required'],
- );
- $form['fields']['register'] = array('#type' => 'checkbox',
- '#title' => t('Visible in user registration form.'),
- '#default_value' => $edit['register'],
- );
- $form['submit'] = array('#type' => 'submit',
- '#value' => t('Save field'),
- );
- return drupal_get_form('profile_field_form', $form);
-}
-
-/**
- * Validate profile_field_form submissions.
- */
-function profile_field_form_validate($form_id, $form_values) {
- // Validate the 'field name':
- if (eregi('[^a-z0-9_-]', $form_values['name'])) {
- form_set_error('name', t('The specified form name contains one or more illegal characters. Spaces or any other special characters expect dash (-) and underscore (_) are not allowed.'));
- }
-
- if (in_array($form_values['name'], user_fields())) {
- form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
- }
- // Validate the category:
- if (!$form_values['category']) {
- form_set_error('category', t('You must enter a category.'));
- }
- if ($form_values['category'] == 'account') {
- form_set_error('category', t('The specified category name is reserved for use by Drupal.'));
- }
- $args1 = array($form_values['title'], $form_values['category']);
- $args2 = array($form_values['name']);
- $query_suffix = '';
-
- if (isset($form_values['fid'])) {
- $args1[] = $args2[] = $form_values['fid'];
- $query_suffix = ' AND fid != %d';
- }
-
- if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s' AND category = '%s'". $query_suffix, $args1))) {
- form_set_error('title', t('The specified title is already in use.'));
- }
- if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'". $query_suffix, $args2))) {
- form_set_error('name', t('The specified name is already in use.'));
- }
-}
-
-/**
- * Process profile_field_form submissions.
- */
-function profile_field_form_submit($form_id, $form_values) {
- if (!isset($form_values['fid'])) {
- db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, register, visibility, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s')", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['category'], $form_values['type'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['options'], $form_values['page']);
-
- drupal_set_message(t('The field has been created.'));
- watchdog('profile', t('Profile field %field added under category %category.', array('%field' => theme('placeholder', $form_values['title']), '%category' => theme('placeholder', $form_values['category']))), WATCHDOG_NOTICE, l(t('view'), 'admin/settings/profile'));
- }
- else {
- db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, register = %d, visibility = %d, options = '%s', page = '%s' WHERE fid = %d", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['category'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['options'], $form_values['page'], $form_values['fid']);
-
- drupal_set_message(t('The field has been updated.'));
- }
- cache_clear_all();
-
- return 'admin/settings/profile';
-}
-
-/**
- * Menu callback; deletes a field from all user profiles.
- */
-function profile_field_delete($fid) {
- $field = db_fetch_object(db_query("SELECT title FROM {profile_fields} WHERE fid = %d", $fid));
- if (!$field) {
- drupal_not_found();
- return;
- }
- $form['fid'] = array('#type' => 'value', '#value' => $fid);
- $form['title'] = array('#type' => 'value', '#value' => $field->title);
-
- return confirm_form('profile_field_delete', $form, t('Are you sure you want to delete the field %field?', array('%field' => theme('placeholder', $field->title))), 'admin/settings/profile', t('This action cannot be undone. If users have entered values into this field in their profile, these entries will also be deleted. If you want to keep the user-entered data, instead of deleting the field you may wish to <a href="%edit-field">edit this field</a> and change it to a %hidden-field so that it may only be accessed by administrators.', array('%edit-field' => url('admin/settings/profile/edit/' . $fid), '%hidden-field' => theme('placeholder', 'hidden profile field')), t('Delete'), t('Cancel')));
-}
-
-/**
- * Process a field delete form submission.
- */
-function profile_field_delete_submit($form_id, $form_values) {
- db_query('DELETE FROM {profile_fields} WHERE fid = %d', $form_values['fid']);
- db_query('DELETE FROM {profile_values} WHERE fid = %d', $form_values['fid']);
-
- cache_clear_all();
-
- drupal_set_message(t('The field %field has been deleted.', array('%field' => theme('placeholder', $form_values['title']))));
- watchdog('profile', t('Profile field %field deleted.', array('%field' => theme('placeholder', $form_values['title']))), WATCHDOG_NOTICE, l(t('view'), 'admin/settings/profile'));
-
- return 'admin/settings/profile';
-}
-
-/**
- * Menu callback; display a listing of all editable profile fields.
- */
-function profile_admin_overview() {
-
- $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
- $rows = array();
- while ($field = db_fetch_object($result)) {
- $rows[] = array(check_plain($field->title), $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/settings/profile/edit/$field->fid"), l(t('delete'), "admin/settings/profile/delete/$field->fid"));
- }
- if (count($rows) == 0) {
- $rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
- }
-
- $header = array(t('Title'), t('Name'), t('Type'), t('Category'), array('data' => t('Operations'), 'colspan' => '2'));
-
- $output = theme('table', $header, $rows);
- $output .= '<h2>'. t('Add new field') .'</h2>';
- $output .= '<ul>';
- foreach (_profile_field_types() as $key => $value) {
- $output .= '<li>'. l($value, "admin/settings/profile/add/$key") .'</li>';
- }
- $output .= '</ul>';
-
- return $output;
-}
-
-/**
- * Menu callback; display a list of user information.
- */
-function profile_browse() {
-
- $name = arg(1);
- list(,,$value) = explode('/', $_GET['q'], 3);
-
- $field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page, visibility FROM {profile_fields} WHERE name = '%s'", $name));
-
- if ($name && $field->fid) {
- // Do not allow browsing of private fields by non-admins.
- if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
- drupal_access_denied();
- return;
- }
-
- // Compile a list of fields to show.
- $fields = array();
- $result = db_query('SELECT name, title, type, weight FROM {profile_fields} WHERE fid != %d AND visibility = %d ORDER BY weight', $field->fid, PROFILE_PUBLIC_LISTINGS);
- while ($record = db_fetch_object($result)) {
- $fields[] = $record;
- }
-
- // Determine what query to use:
- $arguments = array($field->fid);
- switch ($field->type) {
- case 'checkbox':
- $query = 'v.value = 1';
- break;
- case 'selection':
- $query = "v.value = '%s'";
- $arguments[] = $value;
- break;
- case 'list':
- $query = "v.value LIKE '%%%s%%'";
- $arguments[] = $value;
- break;
- default:
- drupal_not_found();
- return;
- }
-
- // Extract the affected users:
- $result = pager_query("SELECT u.uid, u.access FROM {users} u INNER JOIN {profile_values} v ON u.uid = v.uid WHERE v.fid = %d AND $query ORDER BY u.access DESC", 20, 0, NULL, $arguments);
-
- $output = '<div id="profile">';
- while ($account = db_fetch_object($result)) {
- $account = user_load(array('uid' => $account->uid));
- $fields = _profile_update_user_fields($fields, $account);
- $output .= theme('profile_listing', $account, $fields);
- }
- $output .= theme('pager', NULL, 20);
-
- if ($field->type == 'selection' || $field->type == 'list') {
- $title = strtr($field->page, array('%value' => theme('placeholder', $value)));
- }
- else {
- $title = $field->page;
- }
- $output .= '</div>';
-
- drupal_set_title($title);
- return $output;
- }
- else if ($name && !$field->fid) {
- drupal_not_found();
- }
- else {
- // Compile a list of fields to show.
- $fields = array();
- $result = db_query('SELECT name, title, type, weight FROM {profile_fields} WHERE visibility = %d ORDER BY category, weight', PROFILE_PUBLIC_LISTINGS);
- while ($record = db_fetch_object($result)) {
- $fields[] = $record;
- }
-
- // Extract the affected users:
- $result = pager_query("SELECT uid, access FROM {users} WHERE uid > 0 AND status != 0 ORDER BY access DESC", 20, 0, NULL);
-
- $output = '<div id="profile">';
- while ($account = db_fetch_object($result)) {
- $account = user_load(array('uid' => $account->uid));
- $profile = _profile_update_user_fields($fields, $account);
- $output .= theme('profile_listing', $account, $profile);
- }
- $output .= '</div>';
- $output .= theme('pager', NULL, 20);
-
- drupal_set_title(t('user list'));
- return $output;
- }
-}
-
-function profile_load_profile(&$user) {
- $result = db_query('SELECT f.name, f.type, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid);
- while ($field = db_fetch_object($result)) {
- if (empty($user->{$field->name})) {
- $user->{$field->name} = _profile_field_serialize($field->type) ? unserialize($field->value) : $field->value;
- }
- }
-}
-
-function profile_save_profile(&$edit, &$user, $category) {
- if ((arg(0) == 'user' && arg(1) == 'register') || (arg(0) == 'admin' && arg(1) == 'user' && arg(2) == 'create')) {
- $result = db_query('SELECT fid, name, type FROM {profile_fields} WHERE register = 1 AND visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
- }
- else {
- $result = db_query("SELECT fid, name, type FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') AND visibility != %d", $category, PROFILE_HIDDEN);
- // Use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
- }
- while ($field = db_fetch_object($result)) {
- if (_profile_field_serialize($field->type)) {
- $edit[$field->name] = serialize($edit[$field->name]);
- }
- db_query("DELETE FROM {profile_values} WHERE fid = %d AND uid = %d", $field->fid, $user->uid);
- db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]);
- // Mark field as handled (prevents saving to user->data).
- $edit[$field->name] = NULL;
- }
-}
-
-function profile_view_field($user, $field) {
- // Only allow browsing of private fields for admins.
- $browse = user_access('administer users') || $field->visibility != PROFILE_PRIVATE;
-
- if ($value = $user->{$field->name}) {
- switch ($field->type) {
- case 'textfield':
- return check_plain($value);
- case 'textarea':
- return check_markup($value);
- case 'selection':
- return $browse ? l($value, 'profile/'. $field->name .'/'. $value) : check_plain($value);
- case 'checkbox':
- return $browse ? l($field->title, 'profile/'. $field->name) : check_plain($field->title);
- case 'url':
- return '<a href="'. check_url($value) .'">'. check_plain($value) .'</a>';
- case 'date':
- list($format) = explode(' - ', variable_get('date_format_short', 'm/d/Y - H:i'), 2);
- // Note: Avoid PHP's date() because it does not handle dates before
- // 1970 on Windows. This would make the date field useless for e.g.
- // birthdays.
- $replace = array('d' => sprintf('%02d', $value['day']),
- 'j' => $value['day'],
- 'm' => sprintf('%02d', $value['month']),
- 'M' => map_month($value['month']),
- 'Y' => $value['year'],
- 'H:i' => null,
- 'g:ia' => null);
- return strtr($format, $replace);
- case 'list':
- $values = split("[,\n\r]", $value);
- $fields = array();
- foreach ($values as $value) {
- if ($value = trim($value)) {
- $fields[] = $browse ? l($value, "profile/". drupal_urlencode($field->name) ."/". drupal_urlencode($value)) : check_plain($value);
- }
- }
- return implode(', ', $fields);
- }
- }
-}
-
-function profile_view_profile($user) {
-
- profile_load_profile($user);
-
- // Show private fields to administrators and people viewing their own account.
- if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
- $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_HIDDEN);
- }
- else {
- $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND visibility != %d ORDER BY category, weight', PROFILE_PRIVATE, PROFILE_HIDDEN);
- }
-
- while ($field = db_fetch_object($result)) {
- if ($value = profile_view_field($user, $field)) {
- $description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
- $title = ($field->type != 'checkbox') ? check_plain($field->title) : NULL;
- $item = array('title' => $title,
- 'value' => $value,
- 'class' => $field->name,
- );
- $fields[$field->category][] = $item;
- }
- }
- return $fields;
-}
-
-function _profile_form_explanation($field) {
- $output = $field->explanation;
-
- if ($field->type == 'list') {
- $output .= ' '. t('Put each item on a separate line or separate them by commas. No HTML allowed.');
- }
-
- if ($field->visibility == PROFILE_PRIVATE) {
- $output .= ' '. t('The content of this field is kept private and will not be shown publicly.');
- }
-
- return $output;
-}
-
-function profile_form_profile($edit, $user, $category) {
- if (arg(0) == 'user' && arg(1) == 'register') {
- $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND register = 1 ORDER BY category, weight', PROFILE_HIDDEN);
- }
- elseif (arg(0) == 'admin' && arg(1) == 'user' && arg(2) == 'create') {
- $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
- }
- elseif (user_access('administer users')) {
- $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') ORDER BY weight", $category);
- }
- else {
- $result = db_query("SELECT * FROM {profile_fields} WHERE visibility != %d AND LOWER(category) = LOWER('%s') ORDER BY weight", PROFILE_HIDDEN, $category);
- // Use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
- }
-
- while ($field = db_fetch_object($result)) {
- $category = $field->category;
- if (!isset($fields[$category])) {
- $fields[$category] = array('#type' => 'fieldset', '#title' => $category, '#weight' => $w++);
- }
- switch ($field->type) {
- case 'textfield':
- case 'url':
- $fields[$category][$field->name] = array('#type' => 'textfield',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#maxlength' => 255,
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- case 'textarea':
- $fields[$category][$field->name] = array('#type' => 'textarea',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- case 'list':
- $fields[$category][$field->name] = array('#type' => 'textarea',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- case 'checkbox':
- $fields[$category][$field->name] = array('#type' => 'checkbox',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- case 'selection':
- $options = array('--');
- $lines = split("[,\n\r]", $field->options);
- foreach ($lines as $line) {
- if ($line = trim($line)) {
- $options[$line] = $line;
- }
- }
- $fields[$category][$field->name] = array('#type' => 'select',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#options' => $options,
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- case 'date':
- $fields[$category][$field->name] = array('#type' => 'date',
- '#title' => check_plain($field->title),
- '#default_value' => $edit[$field->name],
- '#description' => _profile_form_explanation($field),
- '#required' => $field->required,
- );
- break;
- }
- }
- return $fields;
-}
-
-/**
- * Helper function: update an array of user fields by calling profile_view_field
- */
-function _profile_update_user_fields($fields, $account) {
- foreach ($fields as $key => $field) {
- if ($value = profile_view_field($account, $field)) {
- $fields[$key]->value = $value;
- }
- }
- return $fields;
-}
-
-function profile_validate_profile($edit, $category) {
- if (arg(0) == 'user' && arg(1) == 'register') {
- $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND register = 1 ORDER BY category, weight', PROFILE_HIDDEN);
- }
- elseif (arg(0) == 'admin' && arg(1) == 'user' && arg(2) == 'create') {
- $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
- }
- elseif (user_access('administer users')) {
- $result = db_query("SELECT * FROM {profile_fields} WHERE LOWER(category) = LOWER('%s') ORDER BY weight", $category);
- }
- else {
- $result = db_query("SELECT * FROM {profile_fields} WHERE visibility != %d AND LOWER(category) = LOWER('%s') ORDER BY weight", PROFILE_HIDDEN, $category);
- // Use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
- }
-
- while ($field = db_fetch_object($result)) {
- if ($edit[$field->name]) {
- if ($field->type == 'url') {
- if (!valid_url($edit[$field->name], true)) {
- form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => theme('placeholder', $field->title))));
- }
- }
- }
- else if ($field->required && !user_access('administer users')) {
- form_set_error($field->name, t('The field %field is required.', array('%field' => theme('placeholder', $field->title))));
- }
- }
-
- return $edit;
-}
-
-function profile_categories() {
- $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}");
- while ($category = db_fetch_object($result)) {
- $data[] = array('name' => check_plain($category->category), 'title' => $category->category, 'weight' => 3);
- }
- return $data;
-}
-
-function theme_profile_block($account, $fields = array()) {
-
- $output .= theme('user_picture', $account);
-
- foreach ($fields as $field) {
- if ($field->value) {
- $output .= "<p><strong>$field->title:</strong><br />$field->value</p>\n";
- }
- }
-
- return $output;
-}
-
-function theme_profile_listing($account, $fields = array()) {
-
- $output = "<div class=\"profile\">\n";
- $output .= theme('user_picture', $account);
- $output .= ' <div class="name">'. theme('username', $account) ."</div>\n";
-
- foreach ($fields as $field) {
- if ($field->value) {
- $output .= " <div class=\"field\">$field->value</div>\n";
- }
- }
-
- $output .= "</div>\n";
-
- return $output;
-}
-
-function _profile_field_types($type = NULL) {
- $types = array('textfield' => t('single-line textfield'),
- 'textarea' => t('multi-line textfield'),
- 'checkbox' => t('checkbox'),
- 'selection' => t('list selection'),
- 'list' => t('freeform list'),
- 'url' => t('URL'),
- 'date' => t('date'));
- return isset($type) ? $types[$type] : $types;
-}
-
-function _profile_field_serialize($type = NULL) {
- return $type == 'date';
-}
diff --git a/modules/search/search.module b/modules/search/search.module
deleted file mode 100644
index d7129c1..0000000
--- a/modules/search/search.module
+++ /dev/null
@@ -1,1228 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Enables site-wide keyword searching.
- */
-
-/**
- * Matches Unicode character classes to exclude from the search index.
- *
- * See: http://www.unicode.org/Public/UNIDATA/UCD.html#General_Category_Values
- *
- * The index only contains the following character classes:
- * Lu Letter, Uppercase
- * Ll Letter, Lowercase
- * Lt Letter, Titlecase
- * Lo Letter, Other
- * Nd Number, Decimal Digit
- * No Number, Other
- */
-define('PREG_CLASS_SEARCH_EXCLUDE',
-'\x{0}-\x{2f}\x{3a}-\x{40}\x{5b}-\x{60}\x{7b}-\x{bf}\x{d7}\x{f7}\x{2b0}-'.
-'\x{385}\x{387}\x{3f6}\x{482}-\x{489}\x{559}-\x{55f}\x{589}-\x{5c7}\x{5f3}-'.
-'\x{61f}\x{640}\x{64b}-\x{65e}\x{66a}-\x{66d}\x{670}\x{6d4}\x{6d6}-\x{6ed}'.
-'\x{6fd}\x{6fe}\x{700}-\x{70f}\x{711}\x{730}-\x{74a}\x{7a6}-\x{7b0}\x{901}-'.
-'\x{903}\x{93c}\x{93e}-\x{94d}\x{951}-\x{954}\x{962}-\x{965}\x{970}\x{981}-'.
-'\x{983}\x{9bc}\x{9be}-\x{9cd}\x{9d7}\x{9e2}\x{9e3}\x{9f2}-\x{a03}\x{a3c}-'.
-'\x{a4d}\x{a70}\x{a71}\x{a81}-\x{a83}\x{abc}\x{abe}-\x{acd}\x{ae2}\x{ae3}'.
-'\x{af1}-\x{b03}\x{b3c}\x{b3e}-\x{b57}\x{b70}\x{b82}\x{bbe}-\x{bd7}\x{bf0}-'.
-'\x{c03}\x{c3e}-\x{c56}\x{c82}\x{c83}\x{cbc}\x{cbe}-\x{cd6}\x{d02}\x{d03}'.
-'\x{d3e}-\x{d57}\x{d82}\x{d83}\x{dca}-\x{df4}\x{e31}\x{e34}-\x{e3f}\x{e46}-'.
-'\x{e4f}\x{e5a}\x{e5b}\x{eb1}\x{eb4}-\x{ebc}\x{ec6}-\x{ecd}\x{f01}-\x{f1f}'.
-'\x{f2a}-\x{f3f}\x{f71}-\x{f87}\x{f90}-\x{fd1}\x{102c}-\x{1039}\x{104a}-'.
-'\x{104f}\x{1056}-\x{1059}\x{10fb}\x{10fc}\x{135f}-\x{137c}\x{1390}-\x{1399}'.
-'\x{166d}\x{166e}\x{1680}\x{169b}\x{169c}\x{16eb}-\x{16f0}\x{1712}-\x{1714}'.
-'\x{1732}-\x{1736}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17db}\x{17dd}'.
-'\x{17f0}-\x{180e}\x{1843}\x{18a9}\x{1920}-\x{1945}\x{19b0}-\x{19c0}\x{19c8}'.
-'\x{19c9}\x{19de}-\x{19ff}\x{1a17}-\x{1a1f}\x{1d2c}-\x{1d61}\x{1d78}\x{1d9b}-'.
-'\x{1dc3}\x{1fbd}\x{1fbf}-\x{1fc1}\x{1fcd}-\x{1fcf}\x{1fdd}-\x{1fdf}\x{1fed}-'.
-'\x{1fef}\x{1ffd}-\x{2070}\x{2074}-\x{207e}\x{2080}-\x{2101}\x{2103}-\x{2106}'.
-'\x{2108}\x{2109}\x{2114}\x{2116}-\x{2118}\x{211e}-\x{2123}\x{2125}\x{2127}'.
-'\x{2129}\x{212e}\x{2132}\x{213a}\x{213b}\x{2140}-\x{2144}\x{214a}-\x{2b13}'.
-'\x{2ce5}-\x{2cff}\x{2d6f}\x{2e00}-\x{3005}\x{3007}-\x{303b}\x{303d}-\x{303f}'.
-'\x{3099}-\x{309e}\x{30a0}\x{30fb}-\x{30fe}\x{3190}-\x{319f}\x{31c0}-\x{31cf}'.
-'\x{3200}-\x{33ff}\x{4dc0}-\x{4dff}\x{a015}\x{a490}-\x{a716}\x{a802}\x{a806}'.
-'\x{a80b}\x{a823}-\x{a82b}\x{d800}-\x{f8ff}\x{fb1e}\x{fb29}\x{fd3e}\x{fd3f}'.
-'\x{fdfc}-\x{fe6b}\x{feff}-\x{ff0f}\x{ff1a}-\x{ff20}\x{ff3b}-\x{ff40}\x{ff5b}-'.
-'\x{ff65}\x{ff70}\x{ff9e}\x{ff9f}\x{ffe0}-\x{fffd}');
-
-/**
- * Matches all 'N' Unicode character classes (numbers)
- */
-define('PREG_CLASS_NUMBERS',
-'\x{30}-\x{39}\x{b2}\x{b3}\x{b9}\x{bc}-\x{be}\x{660}-\x{669}\x{6f0}-\x{6f9}'.
-'\x{966}-\x{96f}\x{9e6}-\x{9ef}\x{9f4}-\x{9f9}\x{a66}-\x{a6f}\x{ae6}-\x{aef}'.
-'\x{b66}-\x{b6f}\x{be7}-\x{bf2}\x{c66}-\x{c6f}\x{ce6}-\x{cef}\x{d66}-\x{d6f}'.
-'\x{e50}-\x{e59}\x{ed0}-\x{ed9}\x{f20}-\x{f33}\x{1040}-\x{1049}\x{1369}-'.
-'\x{137c}\x{16ee}-\x{16f0}\x{17e0}-\x{17e9}\x{17f0}-\x{17f9}\x{1810}-\x{1819}'.
-'\x{1946}-\x{194f}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2153}-\x{2183}'.
-'\x{2460}-\x{249b}\x{24ea}-\x{24ff}\x{2776}-\x{2793}\x{3007}\x{3021}-\x{3029}'.
-'\x{3038}-\x{303a}\x{3192}-\x{3195}\x{3220}-\x{3229}\x{3251}-\x{325f}\x{3280}-'.
-'\x{3289}\x{32b1}-\x{32bf}\x{ff10}-\x{ff19}');
-
-/**
- * Matches all 'P' Unicode character classes (punctuation)
- */
-define('PREG_CLASS_PUNCTUATION',
-'\x{21}-\x{23}\x{25}-\x{2a}\x{2c}-\x{2f}\x{3a}\x{3b}\x{3f}\x{40}\x{5b}-\x{5d}'.
-'\x{5f}\x{7b}\x{7d}\x{a1}\x{ab}\x{b7}\x{bb}\x{bf}\x{37e}\x{387}\x{55a}-\x{55f}'.
-'\x{589}\x{58a}\x{5be}\x{5c0}\x{5c3}\x{5f3}\x{5f4}\x{60c}\x{60d}\x{61b}\x{61f}'.
-'\x{66a}-\x{66d}\x{6d4}\x{700}-\x{70d}\x{964}\x{965}\x{970}\x{df4}\x{e4f}'.
-'\x{e5a}\x{e5b}\x{f04}-\x{f12}\x{f3a}-\x{f3d}\x{f85}\x{104a}-\x{104f}\x{10fb}'.
-'\x{1361}-\x{1368}\x{166d}\x{166e}\x{169b}\x{169c}\x{16eb}-\x{16ed}\x{1735}'.
-'\x{1736}\x{17d4}-\x{17d6}\x{17d8}-\x{17da}\x{1800}-\x{180a}\x{1944}\x{1945}'.
-'\x{2010}-\x{2027}\x{2030}-\x{2043}\x{2045}-\x{2051}\x{2053}\x{2054}\x{2057}'.
-'\x{207d}\x{207e}\x{208d}\x{208e}\x{2329}\x{232a}\x{23b4}-\x{23b6}\x{2768}-'.
-'\x{2775}\x{27e6}-\x{27eb}\x{2983}-\x{2998}\x{29d8}-\x{29db}\x{29fc}\x{29fd}'.
-'\x{3001}-\x{3003}\x{3008}-\x{3011}\x{3014}-\x{301f}\x{3030}\x{303d}\x{30a0}'.
-'\x{30fb}\x{fd3e}\x{fd3f}\x{fe30}-\x{fe52}\x{fe54}-\x{fe61}\x{fe63}\x{fe68}'.
-'\x{fe6a}\x{fe6b}\x{ff01}-\x{ff03}\x{ff05}-\x{ff0a}\x{ff0c}-\x{ff0f}\x{ff1a}'.
-'\x{ff1b}\x{ff1f}\x{ff20}\x{ff3b}-\x{ff3d}\x{ff3f}\x{ff5b}\x{ff5d}\x{ff5f}-'.
-'\x{ff65}');
-
-/**
- * Matches all CJK characters that are candidates for auto-splitting
- * (Chinese, Japanese, Korean).
- * Contains kana and BMP ideographs.
- */
-define('PREG_CLASS_CJK', '\x{3041}-\x{30ff}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}'.
-'\x{4e00}-\x{9fbb}\x{f900}-\x{fad9}');
-
-/**
- * Implementation of hook_help().
- */
-function search_help($section) {
- switch ($section) {
- case 'admin/help#search':
- $output = '<p>'. t('The search module adds the ability to search for content by keywords. Search is often the only practical way to find content on a large site. Search is useful for finding users and posts by searching on keywords.') .'</p>';
- $output .= '<p>'. t('The search engine works by maintaining an index of the words in your site\'s content. It indexes the posts and users. You can adjust the settings to tweak the indexing behaviour. Note that the search requires cron to be set up correctly. The index percentage sets the maximum amount of items that will be indexed in one cron run. Set this number lower if your cron is timing out or if PHP is running out of memory.') .'</p>';
- $output .= t('<p>You can</p>
-<ul>
-<li>read about how your site uses cron in the <a href="%admin-help-system">administer &gt;&gt; help &gt;&gt; system</a>.</li>
-<li>run your <a href="%file-cron">cron.php</a>.</li>
-<li>read about <a href="%external-http-drupal-org-node-23714">configuring cron jobs</a>.</li>
-<li><a href="%admin-settings-search">administer &gt;&gt; settings &gt;&gt; search</a>.</ul>
-', array('%admin-help-system' => url('admin/help/system'), '%file-cron' => 'cron.php', '%external-http-drupal-org-node-23714' => 'http://drupal.org/node/23714', '%admin-settings-search' => url('admin/settings/search')));
- $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%search">Search page</a>.', array('%search' => 'http://drupal.org/handbook/modules/search/')) .'</p>';
- return $output;
- case 'admin/modules#description':
- return t('Enables site-wide keyword searching.');
- case 'admin/settings/search':
- return t('
-<p>The search engine works by maintaining an index of the words in your site\'s content. You can adjust the settings below to tweak the indexing behaviour. Note that the search requires cron to be set up correctly.</p>
-');
- case 'search#noresults':
- return t('<p><ul>
-<li>Check if your spelling is correct.</li>
-<li>Remove quotes around phrases to match each word individually: <em>"blue smurf"</em> will match less than <em>blue smurf</em>.</li>
-<li>Consider loosening your query with <em>OR</em>: <em>blue smurf</em> will match less than <em>blue OR smurf</em>.</li>
-</ul></p>');
- }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function search_perm() {
- return array('search content', 'administer search');
-}
-
-/**
- * Implementation of hook_block().
- */
-function search_block($op = 'list', $delta = 0) {
- if ($op == 'list') {
- $blocks[0]['info'] = t('Search form');
- return $blocks;
- }
- else if ($op == 'view' && user_access('search content') && arg(0) != 'search') {
- $block['content'] = search_form('', '', null, '');
- $block['subject'] = t('Search');
- return $block;
- }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function search_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array('path' => 'search', 'title' => t('search'),
- 'callback' => 'search_view',
- 'access' => user_access('search content'),
- 'type' => MENU_SUGGESTED_ITEM);
- $items[] = array('path' => 'admin/settings/search/wipe', 'title' => t('Clear index'),
- 'callback' => 'search_wipe_confirm',
- 'access' => user_access('administer search'),
- 'type' => MENU_CALLBACK);
- }
- else if (arg(0) == 'search') {
- // To remember the user's search keywords when switching across tabs,
- // we dynamically add the keywords to the search tabs' paths.
- $keys = search_get_keys();
- $keys = strlen($keys) ? '/'. $keys : '';
- foreach (module_list() as $name) {
- if (module_hook($name, 'search') && $title = module_invoke($name, 'search', 'name')) {
- $items[] = array('path' => 'search/'. $name . $keys, 'title' => $title,
- 'callback' => 'search_view',
- 'access' => user_access('search content'),
- 'type' => MENU_LOCAL_TASK);
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_validate().
- */
-function search_settings_form_validate($form_id, &$form) {
- if ($_POST['op'] == t('Re-index site')) {
- drupal_goto('admin/settings/search/wipe');
- }
- // If these settings change, the index needs to be rebuilt.
- if ((variable_get('minimum_word_size', 3) != $form['minimum_word_size']) ||
- (variable_get('overlap_cjk', true) != $form['overlap_cjk'])) {
- drupal_set_message(t('The index will be rebuilt.'));
- search_wipe();
- }
-}
-
-/**
- * Menu callback; displays the search module settings page.
- */
-function search_settings() {
- // Collect some stats
- $remaining = 0;
- $total = 0;
- foreach (module_list() as $module) {
- if (module_hook($module, 'search')) {
- $status = module_invoke($module, 'search', 'status');
- $remaining += $status['remaining'];
- $total += $status['total'];
- }
- }
- $count = format_plural($remaining, 'There is 1 item left to index.', 'There are %count items left to index.');
- $percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) . '%';
- $status = '<p><strong>'. t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) .' '. $count .'</strong></p>';
- $form['status'] = array('#type' => 'fieldset', '#title' => t('Indexing status'));
- $form['status']['status'] = array('#type' => 'markup', '#value' => $status);
- $form['status']['wipe'] = array('#type' => 'submit', '#value' => t('Re-index site'));
-
- $items = drupal_map_assoc(array(10, 20, 50, 100, 200, 500));
-
- // Indexing throttle:
- $form['indexing_throttle'] = array('#type' => 'fieldset', '#title' => t('Indexing throttle'));
- $form['indexing_throttle']['search_cron_limit'] = array('#type' => 'select', '#title' => t('Items to index per cron run'), '#default_value' => variable_get('search_cron_limit', 100), '#options' => $items, '#description' => t('The maximum amount of items that will be indexed in one cron run. Set this number lower if your cron is timing out or if PHP is running out of memory.'));
- // Indexing settings:
- $form['indexing_settings'] = array('#type' => 'fieldset', '#title' => t('Indexing settings'));
- $form['indexing_settings']['info'] = array('#type' => 'markup', '#value' => '<em>'. t('<p>Changing the settings below will cause the site index to be rebuilt. The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed.</p><p>The default settings should be appropriate for the majority of sites.</p>') .'</em>');
- $form['indexing_settings']['minimum_word_size'] = array('#type' => 'textfield', '#title' => t('Minimum word length to index'), '#default_value' => variable_get('minimum_word_size', 3), '#size' => 5, '#maxlength' => 3, '#description' => t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).'));
- $form['indexing_settings']['overlap_cjk'] = array('#type' => 'checkbox', '#title' => t('Simple CJK handling'), '#default_value' => variable_get('overlap_cjk', true), '#description' => t('Whether to apply a simple Chinese/Japanese/Korean tokenizer based on overlapping sequences. Turn this off if you want to use an external preprocessor for this instead. Does not affect other languages.'));
-
- // Per module settings
- $form = array_merge($form, module_invoke_all('search', 'admin'));
- return $form;
-}
-
-/**
- * Menu callback: confirm wiping of the index.
- */
-function search_wipe_confirm() {
- return confirm_form('search_wipe_confirm', $form, t('Are you sure you want to re-index the site?'),
- 'admin/settings/search', t(' The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed. This action cannot be undone.'), t('Re-index site'), t('Cancel'));
-}
-
-/**
- * Handler for wipe confirmation
- */
-function search_wipe_confirm_submit($form_id, &$form) {
- if ($form['confirm']) {
- search_wipe();
- drupal_set_message(t('The index will be rebuilt.'));
- return 'admin/settings/search';
- }
-}
-
-/**
- * Wipes a part of or the entire search index.
- *
- * @param $sid
- * (optional) The SID of the item to wipe. If specified, $type must be passed
- * too.
- * @param $type
- * (optional) The type of item to wipe.
- */
-function search_wipe($sid = NULL, $type = NULL) {
- if ($type == NULL && $sid == NULL) {
- module_invoke_all('search', 'reset');
- }
- else {
- db_query("DELETE FROM {search_dataset} WHERE sid = %d AND type = '%s'", $sid, $type);
- db_query("DELETE FROM {search_index} WHERE sid = %d AND type = '%s'", $sid, $type);
- db_query("DELETE FROM {search_index} WHERE fromsid = %d AND fromtype = '%s'", $sid, $type);
- }
-}
-
-/**
- * Marks a word as dirty (or retrieves the list of dirty words). This is used
- * during indexing (cron). Words which are dirty have outdated total counts in
- * the search_total table, and need to be recounted.
- */
-function search_dirty($word = null) {
- static $dirty = array();
- if ($word !== null) {
- $dirty[$word] = true;
- }
- else {
- return $dirty;
- }
-}
-
-/**
- * Implementation of hook_cron().
- *
- * Fires hook_update_index() in all modules and cleans up dirty words (see
- * search_dirty).
- */
-function search_cron() {
- // We register a shutdown function to ensure that search_total is always up
- // to date.
- register_shutdown_function('search_update_totals');
-
- // Update word index
- foreach (module_list() as $module) {
- module_invoke($module, 'update_index');
- }
-}
-
-/**
- * This function is called on shutdown to ensure that search_total is always
- * up to date (even if cron times out or otherwise fails).
- */
-function search_update_totals() {
- // Update word IDF (Inverse Document Frequency) counts for new/changed words
- foreach (search_dirty() as $word => $dummy) {
- // Get total count
- $total = db_result(db_query("SELECT SUM(score) FROM {search_index} WHERE word = '%s'", $word));
- // Apply Zipf's law to equalize the probability distribution
- $total = log10(1 + 1/(max(1, $total)));
- db_query("UPDATE {search_total} SET count = %f WHERE word = '%s'", $total, $word);
- if (!db_affected_rows()) {
- db_query("INSERT INTO {search_total} (word, count) VALUES ('%s', %f)", $word, $total);
- }
- }
- // Find words that were deleted from search_index, but are still in
- // search_total. We use a LEFT JOIN between the two tables and keep only the
- // rows which fail to join.
- $result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL");
- while ($word = db_fetch_object($result)) {
- db_query("DELETE FROM {search_total} WHERE word = '%s'", $word->realword);
- }
-}
-
-/**
- * Simplifies a string according to indexing rules.
- */
-function search_simplify($text) {
- // Decode entities to UTF-8
- $text = decode_entities($text);
-
- // Lowercase
- $text = drupal_strtolower($text);
-
- // Call an external processor for word handling.
- search_preprocess($text);
-
- // Simple CJK handling
- if (variable_get('overlap_cjk', true)) {
- $text = preg_replace_callback('/['. PREG_CLASS_CJK .']+/u', 'search_expand_cjk', $text);
- }
-
- // To improve searching for numerical data such as dates, IP addresses
- // or version numbers, we consider a group of numerical characters
- // separated only by punctuation characters to be one piece.
- // This also means that searching for e.g. '20/03/1984' also returns
- // results with '20-03-1984' in them.
- // Readable regexp: ([number]+)[punctuation]+(?=[number])
- $text = preg_replace('/(['. PREG_CLASS_NUMBERS .']+)['. PREG_CLASS_PUNCTUATION .']+(?=['. PREG_CLASS_NUMBERS .'])/u', '\1', $text);
-
- // The dot, underscore and dash are simply removed. This allows meaningful
- // search behaviour with acronyms and URLs.
- $text = preg_replace('/[._-]+/', '', $text);
-
- // With the exception of the rules above, we consider all punctuation,
- // marks, spacers, etc, to be a word boundary.
- $text = preg_replace('/['. PREG_CLASS_SEARCH_EXCLUDE . ']+/u', ' ', $text);
-
- return $text;
-}
-
-/**
- * Basic CJK tokenizer. Simply splits a string into consecutive, overlapping
- * sequences of characters ('minimum_word_size' long).
- */
-function search_expand_cjk($matches) {
- $min = variable_get('minimum_word_size', 3);
- $str = $matches[0];
- $l = drupal_strlen($str);
- // Passthrough short words
- if ($l <= $min) {
- return ' '. $str .' ';
- }
- $tokens = ' ';
- // FIFO queue of characters
- $chars = array();
- // Begin loop
- for ($i = 0; $i < $l; ++$i) {
- // Grab next character
- $current = drupal_substr($str, 0, 1);
- $str = substr($str, strlen($current));
- $chars[] = $current;
- if ($i >= $min - 1) {
- $tokens .= implode('', $chars) .' ';
- array_shift($chars);
- }
- }
- return $tokens;
-}
-
-/**
- * Splits a string into tokens for indexing.
- */
-function search_index_split($text) {
- static $last = null;
- static $lastsplit = null;
-
- if ($last == $text) {
- return $lastsplit;
- }
- // Process words
- $text = search_simplify($text);
- $words = explode(' ', $text);
- array_walk($words, '_search_index_truncate');
-
- // Save last keyword result
- $last = $text;
- $lastsplit = $words;
-
- return $words;
-}
-
-/**
- * Helper function for array_walk in search_index_split.
- */
-function _search_index_truncate(&$text) {
- $text = truncate_utf8($text, 50);
-}
-
-/**
- * Invokes hook_search_preprocess() in modules.
- */
-function search_preprocess(&$text) {
- foreach (module_implements('search_preprocess') as $module) {
- $text = module_invoke($module, 'search_preprocess', $text);
- }
-}
-
-/**
- * Update the full-text search index for a particular item.
- *
- * @param $sid
- * A number identifying this particular item (e.g. node id).
- *
- * @param $type
- * A string defining this type of item (e.g. 'node')
- *
- * @param $text
- * The content of this item. Must be a piece of HTML text.
- *
- * @ingroup search
- */
-function search_index($sid, $type, $text) {
- $minimum_word_size = variable_get('minimum_word_size', 3);
-
- // Link matching
- global $base_url;
- $node_regexp = '@href=[\'"]?(?:'. preg_quote($base_url, '@') .'/)?(?:\?q=)?/?((?![a-z]+:)[^\'">]+)[\'">]@i';
-
- // Multipliers for scores of words inside certain HTML tags.
- // Note: 'a' must be included for link ranking to work.
- $tags = array('h1' => 25,
- 'h2' => 18,
- 'h3' => 15,
- 'h4' => 12,
- 'h5' => 9,
- 'h6' => 6,
- 'u' => 3,
- 'b' => 3,
- 'i' => 3,
- 'strong' => 3,
- 'em' => 3,
- 'a' => 10);
-
- // Strip off all ignored tags to speed up processing, but insert space before/after
- // them to keep word boundaries.
- $text = str_replace(array('<', '>'), array(' <', '> '), $text);
- $text = strip_tags($text, '<'. implode('><', array_keys($tags)) .'>');
-
- // Split HTML tags from plain text.
- $split = preg_split('/\s*<([^>]+?)>\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
- // Note: PHP ensures the array consists of alternating delimiters and literals
- // and begins and ends with a literal (inserting $null as required).
-
- $tag = false; // Odd/even counter. Tag or no tag.
- $link = false; // State variable for link analyser
- $score = 1; // Starting score per word
- $accum = ' '; // Accumulator for cleaned up data
- $tagstack = array(); // Stack with open tags
- $tagwords = 0; // Counter for consecutive words
- $focus = 1; // Focus state
-
- $results = array(0 => array()); // Accumulator for words for index
-
- foreach ($split as $value) {
- if ($tag) {
- // Increase or decrease score per word based on tag
- list($tagname) = explode(' ', $value, 2);
- $tagname = drupal_strtolower($tagname);
- // Closing or opening tag?
- if ($tagname[0] == '/') {
- $tagname = substr($tagname, 1);
- // If we encounter unexpected tags, reset score to avoid incorrect boosting.
- if (!count($tagstack) || $tagstack[0] != $tagname) {
- $tagstack = array();
- $score = 1;
- }
- else {
- // Remove from tag stack and decrement score
- $score = max(1, $score - $tags[array_shift($tagstack)]);
- }
- if ($tagname == 'a') {
- $link = false;
- }
- }
- else {
- if ($tagstack[0] == $tagname) {
- // None of the tags we look for make sense when nested identically.
- // If they are, it's probably broken HTML.
- $tagstack = array();
- $score = 1;
- }
- else {
- // Add to open tag stack and increment score
- array_unshift($tagstack, $tagname);
- $score += $tags[$tagname];
- }
- if ($tagname == 'a') {
- // Check if link points to a node on this site
- if (preg_match($node_regexp, $value, $match)) {
- $path = drupal_get_normal_path($match[1]);
- if (preg_match('!(?:node|book)/(?:view/)?([0-9]+)!i', $path, $match)) {
- $linknid = $match[1];
- if ($linknid > 0) {
- // Note: ignore links to uncachable nodes to avoid redirect bugs.
- $node = db_fetch_object(db_query('SELECT n.title, n.nid, n.vid, r.format FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid WHERE n.nid = %d', $linknid));
- if (filter_format_allowcache($node->format)) {
- $link = true;
- $linktitle = $node->title;
- }
- }
- }
- }
- }
- }
- // A tag change occurred, reset counter.
- $tagwords = 0;
- }
- else {
- // Note: use of PREG_SPLIT_DELIM_CAPTURE above will introduce empty values
- if ($value != '') {
- if ($link) {
- // Check to see if the node link text is its URL. If so, we use the target node title instead.
- if (preg_match('!^https?://!i', $value)) {
- $value = $linktitle;
- }
- }
- $words = search_index_split($value);
- foreach ($words as $word) {
- // Add word to accumulator
- $accum .= $word .' ';
- $num = is_numeric($word);
- // Check wordlength
- if ($num || drupal_strlen($word) >= $minimum_word_size) {
- // Normalize numbers
- if ($num) {
- $word = (int)ltrim($word, '-0');
- }
-
- if ($link) {
- if (!isset($results[$linknid])) {
- $results[$linknid] = array();
- }
- $results[$linknid][$word] += $score * $focus;
- }
- else {
- $results[0][$word] += $score * $focus;
- // Focus is a decaying value in terms of the amount of unique words up to this point.
- // From 100 words and more, it decays, to e.g. 0.5 at 500 words and 0.3 at 1000 words.
- $focus = min(1, .01 + 3.5 / (2 + count($results[0]) * .015));
- }
- }
- $tagwords++;
- // Too many words inside a single tag probably mean a tag was accidentally left open.
- if (count($tagstack) && $tagwords >= 15) {
- $tagstack = array();
- $score = 1;
- }
- }
- }
- }
- $tag = !$tag;
- }
-
- search_wipe($sid, $type);
-
- // Insert cleaned up data into dataset
- db_query("INSERT INTO {search_dataset} (sid, type, data) VALUES (%d, '%s', '%s')", $sid, $type, $accum);
-
- // Insert results into search index
- foreach ($results[0] as $word => $score) {
- db_query("INSERT INTO {search_index} (word, sid, type, score) VALUES ('%s', %d, '%s', %f)", $word, $sid, $type, $score);
- search_dirty($word);
- }
- unset($results[0]);
-
- // Now insert links to nodes
- foreach ($results as $nid => $words) {
- foreach ($words as $word => $score) {
- db_query("INSERT INTO {search_index} (word, sid, type, fromsid, fromtype, score) VALUES ('%s', %d, '%s', %d, '%s', %f)", $word, $nid, 'node', $sid, $type, $score);
- search_dirty($word);
- }
- }
-}
-
-/**
- * Extract a module-specific search option from a search query. e.g. 'type:book'
- */
-function search_query_extract($keys, $option) {
- if (preg_match('/(^| )'. $option .':([^ ]*)( |$)/i', $keys, $matches)) {
- return $matches[2];
- }
-}
-
-/**
- * Return a query with the given module-specific search option inserted in.
- * e.g. 'type:book'.
- */
-function search_query_insert($keys, $option, $value = '') {
- if (search_query_extract($keys, $option)) {
- $keys = trim(preg_replace('/(^| )'. $option .':[^ ]*/i', '', $keys));
- }
- if ($value != '') {
- $keys .= ' '. $option .':'. $value;
- }
- return $keys;
-}
-
-/**
- * Parse a search query into SQL conditions.
- *
- * We build a query that matches the dataset bodies.
- */
-function search_parse_query($text) {
- $keys = array('positive' => array(), 'negative' => array());
-
- // Tokenize query string
- preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' '. $text, $matches, PREG_SET_ORDER);
-
- if (count($matches) < 1) {
- return NULL;
- }
-
- // Classify tokens
- $or = false;
- foreach ($matches as $match) {
- $phrase = false;
- // Strip off phrase quotes
- if ($match[2]{0} == '"') {
- $match[2] = substr($match[2], 1, -1);
- $phrase = true;
- }
- // Simplify keyword according to indexing rules and external preprocessors
- $words = search_simplify($match[2]);
- // Re-explode in case simplification added more words, except when matching a phrase
- $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
- // Negative matches
- if ($match[1] == '-') {
- $keys['negative'] = array_merge($keys['negative'], $words);
- }
- // OR operator: instead of a single keyword, we store an array of all
- // OR'd keywords.
- elseif ($match[2] == 'OR' && count($keys['positive'])) {
- $last = array_pop($keys['positive']);
- // Starting a new OR?
- if (!is_array($last)) {
- $last = array($last);
- }
- $keys['positive'][] = $last;
- $or = true;
- continue;
- }
- // Plain keyword
- else {
- if ($or) {
- // Add to last element (which is an array)
- $keys['positive'][count($keys['positive']) - 1] = array_merge($keys['positive'][count($keys['positive']) - 1], $words);
- }
- else {
- $keys['positive'] = array_merge($keys['positive'], $words);
- }
- }
- $or = false;
- }
-
- // Convert keywords into SQL statements.
- $query = array();
- $query2 = array();
- $arguments = array();
- $arguments2 = array();
- $matches = 0;
- // Positive matches
- foreach ($keys['positive'] as $key) {
- // Group of ORed terms
- if (is_array($key) && count($key)) {
- $queryor = array();
- $any = false;
- foreach ($key as $or) {
- list($q, $count) = _search_parse_query($or, $arguments2);
- $any |= $count;
- if ($q) {
- $queryor[] = $q;
- $arguments[] = $or;
- }
- }
- if (count($queryor)) {
- $query[] = '('. implode(' OR ', $queryor) .')';
- // A group of OR keywords only needs to match once
- $matches += ($any > 0);
- }
- }
- // Single ANDed term
- else {
- list($q, $count) = _search_parse_query($key, $arguments2);
- if ($q) {
- $query[] = $q;
- $arguments[] = $key;
- // Each AND keyword needs to match at least once
- $matches += $count;
- }
- }
- }
- // Negative matches
- foreach ($keys['negative'] as $key) {
- list($q) = _search_parse_query($key, $arguments2, true);
- if ($q) {
- $query[] = $q;
- $arguments[] = $key;
- }
- }
- $query = implode(' AND ', $query);
-
- // Build word-index conditions for the first pass
- $query2 = substr(str_repeat("i.word = '%s' OR ", count($arguments2)), 0, -4);
-
- return array($query, $arguments, $query2, $arguments2, $matches);
-}
-
-/**
- * Helper function for search_parse_query();
- */
-function _search_parse_query(&$word, &$scores, $not = false) {
- $count = 0;
- // Determine the scorewords of this word/phrase
- if (!$not) {
- $split = explode(' ', $word);
- foreach ($split as $s) {
- $num = is_numeric($s);
- if ($num || drupal_strlen($s) >= variable_get('minimum_word_size', 3)) {
- $s = $num ? ((int)ltrim($s, '-0')) : $s;
- if (!isset($scores[$s])) {
- $scores[$s] = $s;
- $count++;
- }
- }
- }
- }
- // Return matching snippet and number of added words
- return array("d.data ". ($not ? 'NOT ' : '') ."LIKE '%% %s %%'", $count);
-}
-
-/**
- * Do a query on the full-text search index for a word or words.
- *
- * This function is normally only called by each module that support the
- * indexed search (and thus, implements hook_update_index()).
- *
- * Two queries are performed which can be extended by the caller.
- *
- * The first query selects a set of possible matches based on the search index
- * and any extra given restrictions. This is the classic "OR" search.
- *
- * SELECT i.type, i.sid, SUM(i.score*t.count) AS relevance
- * FROM {search_index} i
- * INNER JOIN {search_total} t ON i.word = t.word
- * $join1
- * WHERE $where1 AND (...)
- * GROUP BY i.type, i.sid
- *
- * The second query further refines this set by verifying advanced text
- * conditions (such as AND, negative or phrase matches), and orders the results
- * on a the column or expression 'score':
- *
- * SELECT i.type, i.sid, $select2
- * FROM temp_search_sids i
- * INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type
- * $join2
- * WHERE (...)
- * ORDER BY score DESC
- *
- * @param $keywords
- * A search string as entered by the user.
- *
- * @param $type
- * A string identifying the calling module.
- *
- * @param $join1
- * (optional) Inserted into the JOIN part of the first SQL query.
- * For example "INNER JOIN {node} n ON n.nid = i.sid".
- *
- * @param $where1
- * (optional) Inserted into the WHERE part of the first SQL query.
- * For example "(n.status > %d)".
- *
- * @param $arguments1
- * (optional) Extra SQL arguments belonging to the first query.
- *
- * @param $select2
- * (optional) Inserted into the SELECT pat of the second query. Must contain
- * a column selected as 'score'.
- * defaults to 'i.relevance AS score'
- *
- * @param $join2
- * (optional) Inserted into the JOIN par of the second SQL query.
- * For example "INNER JOIN {node_comment_statistics} n ON n.nid = i.sid"
- *
- * @param $arguments2
- * (optional) Extra SQL arguments belonging to the second query parameter.
- *
- * @param $sort_parameters
- * (optional) SQL arguments for sorting the final results.
- * Default: 'ORDER BY score DESC'
- *
- * @return
- * An array of SIDs for the search results.
- *
- * @ingroup search
- */
-function do_search($keywords, $type, $join1 = '', $where1 = '1', $arguments1 = array(), $select2 = 'i.relevance AS score', $join2 = '', $arguments2 = array(), $sort_parameters = 'ORDER BY score DESC') {
- $query = search_parse_query($keywords);
-
- if ($query[2] == '') {
- form_set_error('keys', t('You must include at least one positive keyword with %count characters or more.', array('%count' => variable_get('minimum_word_size', 3))));
- }
- if ($query === NULL || $query[0] == '' || $query[2] == '') {
- return array();
- }
-
- // First pass: select all possible matching sids, doing a simple index-based OR matching on the keywords.
- // 'matches' is used to reject those items that cannot possibly match the query.
- $conditions = $where1 .' AND ('. $query[2] .") AND i.type = '%s'";
- $arguments = array_merge($arguments1, $query[3], array($type, $query[4]));
- $result = db_query_temporary("SELECT i.type, i.sid, SUM(i.score * t.count) AS relevance, COUNT(*) AS matches FROM {search_index} i INNER JOIN {search_total} t ON i.word = t.word $join1 WHERE $conditions GROUP BY i.type, i.sid HAVING COUNT(*) >= %d", $arguments, 'temp_search_sids');
-
- // Calculate maximum relevance, to normalize it
- $normalize = db_result(db_query('SELECT MAX(relevance) FROM temp_search_sids'));
- if (!$normalize) {
- return array();
- }
- $select2 = str_replace('i.relevance', '('. (1.0 / $normalize) .' * i.relevance)', $select2);
-
- // Second pass: only keep items that match the complicated keywords conditions (phrase search, negative keywords, ...)
- $conditions = '('. $query[0] .')';
- $arguments = array_merge($arguments2, $query[1]);
- $result = db_query_temporary("SELECT i.type, i.sid, $select2 FROM temp_search_sids i INNER JOIN {search_dataset} d ON i.sid = d.sid AND i.type = d.type $join2 WHERE $conditions $sort_parameters", $arguments, 'temp_search_results');
- if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_results'))) == 0) {
- return array();
- }
- $count_query = "SELECT $count";
-
- // Do actual search query
- $result = pager_query("SELECT * FROM temp_search_results", 10, 0, $count_query);
- $results = array();
- while ($item = db_fetch_object($result)) {
- $results[] = $item;
- }
- return $results;
-}
-
-/**
- * Helper function for grabbing search keys.
- */
-function search_get_keys() {
- // Extract keys as remainder of path
- // Note: support old GET format of searches for existing links.
- $path = explode('/', $_GET['q'], 3);
- return count($path) == 3 ? $path[2] : $_REQUEST['keys'];
-}
-
-/**
- * Menu callback; presents the search form and/or search results.
- */
-function search_view() {
- $type = arg(1);
-
- // Search form submits with POST but redirects to GET. This way we can keep
- // the search query URL clean as a whistle:
- // search/type/keyword+keyword
- if (!isset($_POST['edit']['keys'])) {