diff --git a/modules/README.txt b/modules/README.txt deleted file mode 100644 index 927f731..0000000 --- a/modules/README.txt +++ /dev/null @@ -1,8 +0,0 @@ -This directory is reserved for core module files. Custom or contributed -modules should be placed in their own subdirectory of the sites/all/modules -directory. For multisite installations, they can also be placed in a subdirectory -under /sites/{sitename}/modules/, where {sitename} is the name of your site -(e.g., www.example.com). This will allow you to more easily update Drupal core files. - -For more details, see: http://drupal.org/node/176043 - diff --git a/modules/aggregator/aggregator-feed-source.tpl.php b/modules/aggregator/aggregator-feed-source.tpl.php deleted file mode 100644 index 5863fd2..0000000 --- a/modules/aggregator/aggregator-feed-source.tpl.php +++ /dev/null @@ -1,34 +0,0 @@ - -
diff --git a/modules/aggregator/aggregator-item.tpl.php b/modules/aggregator/aggregator-item.tpl.php deleted file mode 100644 index 032e034..0000000 --- a/modules/aggregator/aggregator-item.tpl.php +++ /dev/null @@ -1,45 +0,0 @@ - - diff --git a/modules/aggregator/aggregator-rtl.css b/modules/aggregator/aggregator-rtl.css deleted file mode 100644 index ea59ca3..0000000 --- a/modules/aggregator/aggregator-rtl.css +++ /dev/null @@ -1,4 +0,0 @@ - -#aggregator .feed-source .feed-icon { - float: left; -} diff --git a/modules/aggregator/aggregator-summary-item.tpl.php b/modules/aggregator/aggregator-summary-item.tpl.php deleted file mode 100644 index 08ba2ab..0000000 --- a/modules/aggregator/aggregator-summary-item.tpl.php +++ /dev/null @@ -1,18 +0,0 @@ - - , diff --git a/modules/aggregator/aggregator-summary-items.tpl.php b/modules/aggregator/aggregator-summary-items.tpl.php deleted file mode 100644 index 43a10d3..0000000 --- a/modules/aggregator/aggregator-summary-items.tpl.php +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/modules/aggregator/aggregator-wrapper.tpl.php b/modules/aggregator/aggregator-wrapper.tpl.php deleted file mode 100644 index bb8c8b8..0000000 --- a/modules/aggregator/aggregator-wrapper.tpl.php +++ /dev/null @@ -1,18 +0,0 @@ - - '),
- '#description' => t('A space-separated list of HTML tags allowed in the content of feed items. (Tags in this list are not 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('Number of feed items displayed in feed and category summary pages.')
- );
-
- $form['aggregator_clear'] = array(
- '#type' => 'select', '#title' => t('Discard items older than'),
- '#default_value' => variable_get('aggregator_clear', 9676800), '#options' => $period,
- '#description' => t('The length of time to retain feed items before discarding. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status')))
- );
-
- $form['aggregator_category_selector'] = array(
- '#type' => 'radios', '#title' => t('Category selection type'), '#default_value' => variable_get('aggregator_category_selector', 'checkboxes'),
- '#options' => array('checkboxes' => t('checkboxes'), 'select' => t('multiple selector')),
- '#description' => t('The type of category selection widget displayed on categorization pages. (For a small number of categories, checkboxes are easier to use, while a multiple selector work well with large numbers of categories.)')
- );
-
- return system_settings_form($form);
-}
-
-/**
- * Form builder; Generate a form to add/edit/delete aggregator categories.
- *
- * @ingroup forms
- * @see aggregator_form_category_validate()
- * @see aggregator_form_category_submit()
- */
-function aggregator_form_category(&$form_state, $edit = array('title' => '', 'description' => '', 'cid' => NULL)) {
- $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('Save'));
-
- if ($edit['cid']) {
- $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
- $form['cid'] = array('#type' => 'hidden', '#value' => $edit['cid']);
- }
-
- return $form;
-}
-
-/**
- * Validate aggregator_form_feed form submissions.
- */
-function aggregator_form_category_validate($form, &$form_state) {
- if ($form_state['values']['op'] == t('Save')) {
- // Check for duplicate titles
- if (isset($form_state['values']['cid'])) {
- $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s' AND cid <> %d", $form_state['values']['title'], $form_state['values']['cid']));
- }
- else {
- $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s'", $form_state['values']['title']));
- }
- if ($category) {
- form_set_error('title', t('A category named %category already exists. Please enter a unique title.', array('%category' => $form_state['values']['title'])));
- }
- }
-}
-
-/**
- * Process aggregator_form_category form submissions.
- *
- * @todo Add delete confirmation dialog.
- */
-function aggregator_form_category_submit($form, &$form_state) {
- if ($form_state['values']['op'] == t('Delete')) {
- $title = $form_state['values']['title'];
- // Unset the title:
- unset($form_state['values']['title']);
- }
- aggregator_save_category($form_state['values']);
- if (isset($form_state['values']['cid'])) {
- if (isset($form_state['values']['title'])) {
- drupal_set_message(t('The category %category has been updated.', array('%category' => $form_state['values']['title'])));
- if (arg(0) == 'admin') {
- $form_state['redirect'] = 'admin/content/aggregator/';
- return;
- }
- else {
- $form_state['redirect'] = 'aggregator/categories/'. $form_state['values']['cid'];
- return;
- }
- }
- else {
- watchdog('aggregator', 'Category %category deleted.', array('%category' => $title));
- drupal_set_message(t('The category %category has been deleted.', array('%category' => $title)));
- if (arg(0) == 'admin') {
- $form_state['redirect'] = 'admin/content/aggregator/';
- return;
- }
- else {
- $form_state['redirect'] = 'aggregator/categories/';
- return;
- }
- }
- }
- else {
- watchdog('aggregator', 'Category %category added.', array('%category' => $form_state['values']['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/content/aggregator'));
- drupal_set_message(t('The category %category has been added.', array('%category' => $form_state['values']['title'])));
- }
-}
diff --git a/modules/aggregator/aggregator.css b/modules/aggregator/aggregator.css
deleted file mode 100644
index 9d2f4bd..0000000
--- a/modules/aggregator/aggregator.css
+++ /dev/null
@@ -1,37 +0,0 @@
-
-#aggregator .feed-source .feed-title {
- margin-top: 0;
-}
-#aggregator .feed-source .feed-image img {
- margin-bottom: 0.75em;
-}
-#aggregator .feed-source .feed-icon {
- float: right; /* LTR */
- display: block;
-}
-#aggregator .feed-item {
- margin-bottom: 1.5em;
-}
-#aggregator .feed-item-title {
- margin-bottom: 0;
- font-size: 1.3em;
-}
-#aggregator .feed-item-meta, #aggregator .feed-item-body {
- margin-bottom: 0.5em;
-}
-#aggregator .feed-item-categories {
- font-size: 0.9em;
-}
-#aggregator td {
- vertical-align: bottom;
-}
-#aggregator td.categorize-item {
- white-space: nowrap;
-}
-#aggregator .categorize-item .news-item .body {
- margin-top: 0;
-}
-#aggregator .categorize-item h3 {
- margin-bottom: 1em;
- margin-top: 0;
-}
diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info
deleted file mode 100644
index 62e28d4..0000000
--- a/modules/aggregator/aggregator.info
+++ /dev/null
@@ -1,11 +0,0 @@
-name = Aggregator
-description = "Aggregates syndicated content (RSS, RDF, and Atom feeds)."
-package = Core - optional
-version = VERSION
-core = 6.x
-
-; Information added by Drupal.org packaging script on 2016-02-24
-version = "6.38"
-project = "drupal"
-datestamp = "1456343372"
-
diff --git a/modules/aggregator/aggregator.install b/modules/aggregator/aggregator.install
deleted file mode 100644
index 2e918ed..0000000
--- a/modules/aggregator/aggregator.install
+++ /dev/null
@@ -1,240 +0,0 @@
- 'Stores categories for aggregator feeds and feed items.',
- 'fields' => array(
- 'cid' => array(
- 'type' => 'serial',
- 'not null' => TRUE,
- 'description' => 'Primary Key: Unique aggregator category ID.',
- ),
- 'title' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Title of the category.',
- ),
- 'description' => array(
- 'type' => 'text',
- 'not null' => TRUE,
- 'size' => 'big',
- 'description' => 'Description of the category',
- ),
- 'block' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'size' => 'tiny',
- 'description' => 'The number of recent items to show within the category block.',
- )
- ),
- 'primary key' => array('cid'),
- 'unique keys' => array('title' => array('title')),
- );
-
- $schema['aggregator_category_feed'] = array(
- 'description' => 'Bridge table; maps feeds to categories.',
- 'fields' => array(
- 'fid' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => "The feed's {aggregator_feed}.fid.",
- ),
- 'cid' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'The {aggregator_category}.cid to which the feed is being assigned.',
- )
- ),
- 'primary key' => array('cid', 'fid'),
- 'indexes' => array('fid' => array('fid')),
- );
-
- $schema['aggregator_category_item'] = array(
- 'description' => 'Bridge table; maps feed items to categories.',
- 'fields' => array(
- 'iid' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => "The feed item's {aggregator_item}.iid.",
- ),
- 'cid' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'The {aggregator_category}.cid to which the feed item is being assigned.',
- )
- ),
- 'primary key' => array('cid', 'iid'),
- 'indexes' => array('iid' => array('iid')),
- );
-
- $schema['aggregator_feed'] = array(
- 'description' => 'Stores feeds to be parsed by the aggregator.',
- 'fields' => array(
- 'fid' => array(
- 'type' => 'serial',
- 'not null' => TRUE,
- 'description' => 'Primary Key: Unique feed ID.',
- ),
- 'title' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Title of the feed.',
- ),
- 'url' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'URL to the feed.',
- ),
- 'refresh' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'How often to check for new feed items, in seconds.',
- ),
- 'checked' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'Last time feed was checked for new items, as Unix timestamp.',
- ),
- 'link' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'The parent website of the feed; comes from the <link> element in the feed.',
- ),
- 'description' => array(
- 'type' => 'text',
- 'not null' => TRUE,
- 'size' => 'big',
- 'description' => "The parent website's description; comes from the <description> element in the feed.",
- ),
- 'image' => array(
- 'type' => 'text',
- 'not null' => TRUE,
- 'size' => 'big',
- 'description' => 'An image representing the feed.',
- ),
- 'etag' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Entity tag HTTP response header, used for validating cache.',
- ),
- 'modified' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'When the feed was last modified, as a Unix timestamp.',
- ),
- 'block' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'size' => 'tiny',
- 'description' => "Number of items to display in the feed's block.",
- )
- ),
- 'primary key' => array('fid'),
- 'unique keys' => array(
- 'url' => array('url'),
- 'title' => array('title'),
- ),
- );
-
- $schema['aggregator_item'] = array(
- 'description' => 'Stores the individual items imported from feeds.',
- 'fields' => array(
- 'iid' => array(
- 'type' => 'serial',
- 'not null' => TRUE,
- 'description' => 'Primary Key: Unique ID for feed item.',
- ),
- 'fid' => array(
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'The {aggregator_feed}.fid to which this item belongs.',
- ),
- 'title' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Title of the feed item.',
- ),
- 'link' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Link to the feed item.',
- ),
- 'author' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Author of the feed item.',
- ),
- 'description' => array(
- 'type' => 'text',
- 'not null' => TRUE,
- 'size' => 'big',
- 'description' => 'Body of the feed item.',
- ),
- 'timestamp' => array(
- 'type' => 'int',
- 'not null' => FALSE,
- 'description' => 'Post date of feed item, as a Unix timestamp.',
- ),
- 'guid' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => FALSE,
- 'description' => 'Unique identifier for the feed item.',
- )
- ),
- 'primary key' => array('iid'),
- 'indexes' => array('fid' => array('fid')),
- );
-
- return $schema;
-}
diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module
deleted file mode 100644
index 0afb9f5..0000000
--- a/modules/aggregator/aggregator.module
+++ /dev/null
@@ -1,940 +0,0 @@
-'. t('The aggregator is a powerful on-site syndicator and news reader that gathers fresh content from RSS-, RDF-, and Atom-based feeds made available across the web. Thousands of sites (particularly news sites and blogs) publish their latest headlines and posts in feeds, using a number of standardized XML-based formats. Formats supported by the aggregator include RSS, RDF, and Atom.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) .'
'. t('Feeds contain feed items, or individual posts published by the site providing the feed. Feeds may be grouped in categories, generally by topic. Users view feed items in the main aggregator display or by their source. Administrators can add, edit and delete feeds and choose how often to check each feed for newly updated items. The most recent items in either a feed or category can be displayed as a block through the blocks administration page. A machine-readable OPML file of all feeds is available. A correctly configured cron maintenance task is required to update feeds automatically.', array('@aggregator' => url('aggregator'), '@aggregator-sources' => url('aggregator/sources'), '@feededit' => url('admin/content/aggregator'), '@admin-block' => url('admin/build/block'), '@aggregator-opml' => url('aggregator/opml'), '@cron' => url('admin/reports/status'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Aggregator module.', array('@aggregator' => 'http://drupal.org/handbook/modules/aggregator/')) .'
'; - return $output; - case 'admin/content/aggregator': - $output = ''. t('Thousands of sites (particularly news sites and blogs) publish their latest headlines and posts in feeds, using a number of standardized XML-based formats. Formats supported by the aggregator include RSS, RDF, and Atom.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) .'
'; - $output .= ''. t('Current feeds are listed below, and new feeds may be added. For each feed or feed category, the latest items block may be enabled at the blocks administration page.', array('@addfeed' => url('admin/content/aggregator/add/feed'), '@block' => url('admin/build/block'))) .'
'; - return $output; - case 'admin/content/aggregator/add/feed': - return ''. t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') .'
'; - case 'admin/content/aggregator/add/category': - return ''. t('Categories allow feed items from different feeds to be grouped together. For example, several sport-related feeds may belong to a category named Sports. Feed items may be grouped automatically (by selecting a category when creating or editing a feed) or manually (via the Categorize page available from feed item listings). Each category provides its own feed page and block.') .'
'; - } -} - -/** - * Implementation of hook_theme() - */ -function aggregator_theme() { - return array( - 'aggregator_wrapper' => array( - 'arguments' => array('content' => NULL), - 'file' => 'aggregator.pages.inc', - 'template' => 'aggregator-wrapper', - ), - 'aggregator_categorize_items' => array( - 'arguments' => array('form' => NULL), - 'file' => 'aggregator.pages.inc', - ), - 'aggregator_feed_source' => array( - 'arguments' => array('feed' => NULL), - 'file' => 'aggregator.pages.inc', - 'template' => 'aggregator-feed-source', - ), - 'aggregator_block_item' => array( - 'arguments' => array('item' => NULL, 'feed' => 0), - ), - 'aggregator_summary_items' => array( - 'arguments' => array('summary_items' => NULL, 'source' => NULL), - 'file' => 'aggregator.pages.inc', - 'template' => 'aggregator-summary-items', - ), - 'aggregator_summary_item' => array( - 'arguments' => array('item' => NULL), - 'file' => 'aggregator.pages.inc', - 'template' => 'aggregator-summary-item', - ), - 'aggregator_item' => array( - 'arguments' => array('item' => NULL), - 'file' => 'aggregator.pages.inc', - 'template' => 'aggregator-item', - ), - 'aggregator_page_opml' => array( - 'arguments' => array('feeds' => NULL), - 'file' => 'aggregator.pages.inc', - ), - 'aggregator_page_rss' => array( - 'arguments' => array('feeds' => NULL, 'category' => NULL), - 'file' => 'aggregator.pages.inc', - ), - ); -} - -/** - * Implementation of hook_menu(). - */ -function aggregator_menu() { - $items['admin/content/aggregator'] = array( - 'title' => 'Feed aggregator', - 'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", - 'page callback' => 'aggregator_admin_overview', - 'access arguments' => array('administer news feeds'), - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/add/feed'] = array( - 'title' => 'Add feed', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_feed'), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'parent' => 'admin/content/aggregator', - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/add/category'] = array( - 'title' => 'Add category', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category'), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'parent' => 'admin/content/aggregator', - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/remove/%aggregator_feed'] = array( - 'title' => 'Remove items', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_admin_remove_feed', 4), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/update/%aggregator_feed'] = array( - 'title' => 'Update items', - 'page callback' => 'aggregator_admin_refresh_feed', - 'page arguments' => array(4), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['admin/content/aggregator/settings'] = array( - 'title' => 'Settings', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_admin_settings'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 10, - 'access arguments' => array('administer news feeds'), - 'file' => 'aggregator.admin.inc', - ); - $items['aggregator'] = array( - 'title' => 'Feed aggregator', - 'page callback' => 'aggregator_page_last', - 'access arguments' => array('access news feeds'), - 'weight' => 5, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/sources'] = array( - 'title' => 'Sources', - 'page callback' => 'aggregator_page_sources', - 'access arguments' => array('access news feeds'), - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/categories'] = array( - 'title' => 'Categories', - 'page callback' => 'aggregator_page_categories', - 'access callback' => '_aggregator_has_categories', - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/rss'] = array( - 'title' => 'RSS feed', - 'page callback' => 'aggregator_page_rss', - 'access arguments' => array('access news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/opml'] = array( - 'title' => 'OPML feed', - 'page callback' => 'aggregator_page_opml', - 'access arguments' => array('access news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/categories/%aggregator_category'] = array( - 'title callback' => '_aggregator_category_title', - 'title arguments' => array(2), - 'page callback' => 'aggregator_page_category', - 'page arguments' => array(2), - 'access callback' => 'user_access', - 'access arguments' => array('access news feeds'), - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/categories/%aggregator_category/view'] = array( - 'title' => 'View', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['aggregator/categories/%aggregator_category/categorize'] = array( - 'title' => 'Categorize', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_page_category', 2), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/categories/%aggregator_category/configure'] = array( - 'title' => 'Configure', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category', 2), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 1, - 'file' => 'aggregator.admin.inc', - ); - $items['aggregator/sources/%aggregator_feed'] = array( - 'page callback' => 'aggregator_page_source', - 'page arguments' => array(2), - 'access arguments' => array('access news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/sources/%aggregator_feed/view'] = array( - 'title' => 'View', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['aggregator/sources/%aggregator_feed/categorize'] = array( - 'title' => 'Categorize', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_page_source', 2), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'aggregator.pages.inc', - ); - $items['aggregator/sources/%aggregator_feed/configure'] = array( - 'title' => 'Configure', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_feed', 2), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 1, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/edit/feed/%aggregator_feed'] = array( - 'title' => 'Edit feed', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_feed', 5), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.admin.inc', - ); - $items['admin/content/aggregator/edit/category/%aggregator_category'] = array( - 'title' => 'Edit category', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('aggregator_form_category', 5), - 'access arguments' => array('administer news feeds'), - 'type' => MENU_CALLBACK, - 'file' => 'aggregator.admin.inc', - ); - - return $items; -} - -/** - * Menu callback. - * - * @return - * An aggregator category title. - */ -function _aggregator_category_title($category) { - return $category['title']; -} - -/** - * Implementation of hook_init(). - */ -function aggregator_init() { - drupal_add_css(drupal_get_path('module', 'aggregator') .'/aggregator.css'); -} - -/** - * Find out whether there are any aggregator categories. - * - * @return - * TRUE if there is at least one category and the user has access to them, FALSE otherwise. - */ -function _aggregator_has_categories() { - return user_access('access news feeds') && db_result(db_query('SELECT COUNT(*) FROM {aggregator_category}')); -} - -/** - * 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 = 'list', $delta = 0, $edit = array()) { - 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' => $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' => $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') { - if (user_access('access news feeds')) { - 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); - $read_more = theme('more_link', url('aggregator/sources/'. $feed->fid), t("View this feed's recent news.")); - } - 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); - $read_more = theme('more_link', url('aggregator/categories/'. $category->cid), t("View this category's recent news.")); - } - break; - } - $items = array(); - while ($item = db_fetch_object($result)) { - $items[] = theme('aggregator_block_item', $item); - } - - // Only display the block if there are items to show. - if (count($items) > 0) { - $block['content'] = theme('item_list', $items) . $read_more; - } - } - } - if (isset($block)) { - return $block; - } -} - -/** - * Add/edit/delete aggregator categories. - * - * @param $edit - * An associative array describing the category to be added/edited/deleted. - */ -function aggregator_save_category($edit) { - $link_path = 'aggregator/categories/'; - if (!empty($edit['cid'])) { - $link_path .= $edit['cid']; - if (!empty($edit['title'])) { - db_query("UPDATE {aggregator_category} SET title = '%s', description = '%s' WHERE cid = %d", $edit['title'], $edit['description'], $edit['cid']); - $op = 'update'; - } - else { - db_query('DELETE FROM {aggregator_category} WHERE cid = %d', $edit['cid']); - // Make sure there is no active block for this category. - db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'aggregator', 'category-' . $edit['cid']); - $edit['title'] = ''; - $op = 'delete'; - } - } - else if (!empty($edit['title'])) { - // A single unique id for bundles and feeds, to use in blocks - db_query("INSERT INTO {aggregator_category} (title, description, block) VALUES ('%s', '%s', 5)", $edit['title'], $edit['description']); - $link_path .= db_last_insert_id('aggregator_category', 'cid'); - $op = 'insert'; - } - if (isset($op)) { - menu_link_maintain('aggregator', $op, $link_path, $edit['title']); - } -} - -/** - * Add/edit/delete an aggregator feed. - * - * @param $edit - * An associative array describing the feed to be added/edited/deleted. - */ -function aggregator_save_feed($edit) { - if (!empty($edit['fid'])) { - // An existing feed is being modified, delete the category listings. - db_query('DELETE FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']); - } - if (!empty($edit['fid']) && !empty($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 (!empty($edit['fid'])) { - $items = array(); - $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $edit['fid']); - while ($item = db_fetch_object($result)) { - $items[] = "iid = $item->iid"; - } - if (!empty($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']); - // Make sure there is no active block for this feed. - db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'aggregator', 'feed-' . $edit['fid']); - } - else if (!empty($edit['title'])) { - db_query("INSERT INTO {aggregator_feed} (title, url, refresh, block, description, image) VALUES ('%s', '%s', %d, 5, '', '')", $edit['title'], $edit['url'], $edit['refresh']); - // A single unique id for bundles and feeds, to use in blocks. - $edit['fid'] = db_last_insert_id('aggregator_feed', 'fid'); - } - if (!empty($edit['title'])) { - // The feed is being saved, save the categories as well. - if (!empty($edit['category'])) { - foreach ($edit['category'] as $cid => $value) { - if ($value) { - db_query('INSERT INTO {aggregator_category_feed} (fid, cid) VALUES (%d, %d)', $edit['fid'], $cid); - } - } - } - } -} - -/** - * Removes all items from a feed. - * - * @param $feed - * An associative array describing the feed to be cleared. - */ -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 (!empty($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' => $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 (!empty($attributes['REL']) && $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; - $items += array($item => array()); - switch ($element) { - case 'ITEM': - $items[$item] += array($tag => ''); - $items[$item][$tag] .= $data; - break; - case 'IMAGE': - case 'LOGO': - $image += array($tag => ''); - $image[$tag] .= $data; - break; - case 'LINK': - if ($data) { - $items[$item] += array($tag => ''); - $items[$item][$tag] .= $data; - } - break; - case 'CONTENT': - $items[$item] += array('CONTENT' => ''); - $items[$item]['CONTENT'] .= $data; - break; - case 'SUMMARY': - $items[$item] += array('SUMMARY' => ''); - $items[$item]['SUMMARY'] .= $data; - break; - case 'TAGLINE': - case 'SUBTITLE': - $channel += array('DESCRIPTION' => ''); - $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 += array($tag => ''); - $channel[$tag] .= $data; - } -} - -/** - * Checks a news feed for new items. - * - * @param $feed - * An associative array describing the feed to be refreshed. - */ -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' => $feed['title']))); - break; - case 301: - $feed['url'] = $result->redirect_url; - watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed['title'], '%url' => $feed['url'])); - // Deliberate no break. - case 200: - case 302: - case 307: - // Filter the input data: - if (aggregator_parse_feed($result->data, $feed)) { - $modified = empty($result->headers['Last-Modified']) ? 0 : 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 (!empty($image['LINK']) && !empty($image['URL']) && !empty($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 = ' '. t('The blog module allows registered users to maintain an online journal, or blog. Blogs are made up of individual blog entries, and the blog entries are most often displayed in descending order by creation time.') .' '. t('There is an (optional) Blogs menu item added to the Navigation menu, which displays all blogs available on your site, and a My blog item displaying the current user\'s blog entries. The Blog entry menu item under Create content allows new blog entries to be created.') .' '. t('Each blog entry is displayed with an automatic link to other blogs created by the same user. By default, blog entries have comments enabled and are automatically promoted to the site front page. The blog module also creates a Recent blog posts block that may be enabled at the blocks administration page.', array('@blocks' => url('admin/build/block'))) .' '. t('When using the aggregator module an automatic blog it icon is displayed next to the items in a feed\'s latest items block. Clicking this icon populates a blog entry with a title (the title of the feed item) and body (a link to the source item on its original site and illustrative content suitable for use in a block quote). Blog authors can use this feature to easily comment on items of interest that appear in aggregator feeds from other sites. To use this feature, be sure to enable the aggregator module, add and configure a feed from another site, and position the feed\'s latest items block.', array('@modules' => url('admin/build/modules'), '@feeds' => url('admin/content/aggregator'), '@blocks' => url('admin/build/block'))) .' '. t('For more information, see the online handbook entry for Blog module.', array('@blog' => 'http://drupal.org/handbook/modules/blog/')) .''), -1, PREG_SPLIT_NO_EMPTY));
-}
-
-/**
- * Helper function for drupal_map_assoc.
- *
- * @param $count
- * Items count.
- * @return
- * Plural-formatted "@count items"
- */
-function _aggregator_items($count) {
- return format_plural($count, '1 item', '@count items');
-}
diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc
deleted file mode 100644
index fb43a86..0000000
--- a/modules/aggregator/aggregator.pages.inc
+++ /dev/null
@@ -1,490 +0,0 @@
-title));
- $feed_source = theme('aggregator_feed_source', $feed);
-
- // It is safe to include the fid in the query because it's loaded from the
- // database by aggregator_feed_load.
- $items = aggregator_feed_items_load('SELECT * FROM {aggregator_item} WHERE fid = '. $feed->fid .' ORDER BY timestamp DESC, iid DESC');
-
- return _aggregator_page_list($items, arg(3), $feed_source);
-}
-
-/**
- * Menu callback; displays all the items aggregated in a particular category.
- *
- * If there are two arguments then this function is called as a form.
- *
- * @param $arg1
- * If there are two arguments then $arg1 is $form_state. Otherwise, $arg1 is $category.
- * @param $arg2
- * If there are two arguments then $arg2 is $category.
- * @return
- * The items HTML.
- */
-function aggregator_page_category($arg1, $arg2 = NULL) {
- // If there are two arguments then we are called as a form, $arg1 is
- // $form_state and $arg2 is $category. Otherwise, $arg1 is $category.
- $category = is_array($arg2) ? $arg2 : $arg1;
-
- drupal_add_feed(url('aggregator/rss/'. $category['cid']), variable_get('site_name', 'Drupal') .' '. t('aggregator - @title', array('@title' => $category['title'])));
-
- // It is safe to include the cid in the query because it's loaded from the
- // database by aggregator_category_load.
- $items = aggregator_feed_items_load('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, i.iid DESC');
-
- return _aggregator_page_list($items, arg(3));
-}
-
-/**
- * Load feed items by passing a SQL query.
- *
- * @param $sql
- * The query to be executed.
- * @return
- * An array of the feed items.
- */
-function aggregator_feed_items_load($sql) {
- $items = array();
- if (isset($sql)) {
- $result = pager_query($sql, 20);
- while ($item = db_fetch_object($result)) {
- $result_category = 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);
- $item->categories = array();
- while ($item_categories = db_fetch_object($result_category)) {
- $item->categories[] = $item_categories;
- }
- $items[$item->iid] = $item;
- }
- }
- return $items;
-}
-
-/**
- * Prints an aggregator page listing a number of feed items.
- *
- * Various menu callbacks use this function to print their feeds.
- *
- * @param $items
- * The items to be listed.
- * @param $op
- * Which form should be added to the items. Only 'categorize' is now recognized.
- * @param $feed_source
- * The feed source URL.
- * @return
- * The items HTML.
- */
-function _aggregator_page_list($items, $op, $feed_source = '') {
- if (user_access('administer news feeds') && ($op == 'categorize')) {
- // Get form data.
- $output = aggregator_categorize_items($items, $feed_source);
- }
- else {
- // Assemble themed output.
- $output = $feed_source;
- foreach ($items as $item) {
- $output .= theme('aggregator_item', $item);
- }
- $output = theme('aggregator_wrapper', $output);
- }
- return $output;
-}
-
-/**
- * Form builder; build the page list form.
- *
- * @param $items
- * An array of the feed items.
- * @param $feed_source
- * The feed source URL.
- * @return
- * The form structure.
- * @ingroup forms
- * @see aggregator_categorize_items_validate()
- * @see aggregator_categorize_items_submit()
- */
-function aggregator_categorize_items($items, $feed_source = '') {
- $form['#submit'][] = 'aggregator_categorize_items_submit';
- $form['#validate'][] = 'aggregator_categorize_items_validate';
- $form['#theme'] = 'aggregator_categorize_items';
- $form['feed_source'] = array('#value' => $feed_source);
- $categories = array();
- $done = FALSE;
- $form['items'] = array();
- $form['categories'] = array('#tree' => TRUE);
- foreach ($items as $item) {
- $form['items'][$item->iid] = array('#value' => theme('aggregator_item', $item));
- $form['categories'][$item->iid] = array();
- $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
- );
- }
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save categories'));
-
- return $form;
-}
-
-/**
- * Validate aggregator_categorize_items form submissions.
- */
-function aggregator_categorize_items_validate($form, &$form_state) {
- if (!user_access('administer news feeds')) {
- form_error($form, t('You are not allowed to categorize this feed item.'));
- }
-}
-
-/**
- * Process aggregator_categorize_items form submissions.
- */
-function aggregator_categorize_items_submit($form, &$form_state) {
- if (!empty($form_state['values']['categories'])) {
- foreach ($form_state['values']['categories'] 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.'));
-}
-
-/**
- * Theme the page list form for assigning categories.
- *
- * @param $form
- * An associative array containing the structure of the form.
- * @return
- * The output HTML.
- * @ingroup themeable
- */
-function theme_aggregator_categorize_items($form) {
- $output = drupal_render($form['feed_source']);
- $rows = array();
- if ($form['items']) {
- foreach (element_children($form['items']) as $key) {
- if (is_array($form['items'][$key])) {
- $rows[] = array(
- drupal_render($form['items'][$key]),
- array('data' => drupal_render($form['categories'][$key]), 'class' => 'categorize-item'),
- );
- }
- }
- }
- $output .= theme('table', array('', t('Categorize')), $rows);
- $output .= drupal_render($form['submit']);
- $output .= drupal_render($form);
- return theme('aggregator_wrapper', $output);
-}
-
-/**
- * Process variables for aggregator-wrapper.tpl.php.
- *
- * @see aggregator-wrapper.tpl.php
- */
-function template_preprocess_aggregator_wrapper(&$variables) {
- $variables['pager'] = theme('pager', NULL, 20, 0);
-}
-
-/**
- * Process variables for aggregator-item.tpl.php.
- *
- * @see aggregator-item.tpl.php
- */
-function template_preprocess_aggregator_item(&$variables) {
- $item = $variables['item'];
-
- $variables['feed_url'] = check_url($item->link);
- $variables['feed_title'] = check_plain($item->title);
- $variables['content'] = aggregator_filter_xss($item->description);
-
- $variables['source_url'] = '';
- $variables['source_title'] = '';
- if (isset($item->ftitle) && isset($item->fid)) {
- $variables['source_url'] = url("aggregator/sources/$item->fid");
- $variables['source_title'] = check_plain($item->ftitle);
- }
- if (date('Ymd', $item->timestamp) == date('Ymd')) {
- $variables['source_date'] = t('%ago ago', array('%ago' => format_interval(time() - $item->timestamp)));
- }
- else {
- $variables['source_date'] = format_date($item->timestamp, 'custom', variable_get('date_format_medium', 'D, m/d/Y - H:i'));
- }
-
- $variables['categories'] = array();
- foreach ($item->categories as $category) {
- $variables['categories'][$category->cid] = l($category->title, 'aggregator/categories/'. $category->cid);
- }
-}
-
-/**
- * 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 = '';
- while ($feed = db_fetch_object($result)) {
- // Most recent items:
- $summary_items = 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)) {
- $summary_items[] = theme('aggregator_summary_item', $item);
- }
- }
- $feed->url = url('aggregator/sources/'. $feed->fid);
- $output .= theme('aggregator_summary_items', $summary_items, $feed);
- }
- $output .= theme('feed_icon', url('aggregator/opml'), t('OPML feed'));
-
- return theme('aggregator_wrapper', $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 = '';
- while ($category = db_fetch_object($result)) {
- if (variable_get('aggregator_summary_items', 3)) {
- $summary_items = 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)) {
- $summary_items[] = theme('aggregator_summary_item', $item);
- }
- }
- $category->url = url('aggregator/categories/'. $category->cid);
- $output .= theme('aggregator_summary_items', $summary_items, $category);
- }
-
- return theme('aggregator_wrapper', $output);
-}
-
-/**
- * Menu callback; generate an RSS 0.92 feed of aggregator items or categories.
- */
-function aggregator_page_rss() {
- $result = NULL;
- // arg(2) is the passed cid, only select for that category
- if (arg(2)) {
- $category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
- $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, i.iid DESC';
- $result = db_query_range($sql, $category->cid, 0, variable_get('feed_default_items', 10));
- }
- // or, get the default aggregator items
- else {
- $category = NULL;
- $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));
- }
-
- $feeds = array();
- while ($item = db_fetch_object($result)) {
- $feeds[] = $item;
- }
- return theme('aggregator_page_rss', $feeds, $category);
-}
-
-/**
- * Theme the RSS output.
- *
- * @param $feeds
- * An array of the feeds to theme.
- * @param $category
- * A common category, if any, for all the feeds.
- * @ingroup themeable
- */
-function theme_aggregator_page_rss($feeds, $category = NULL) {
- drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
-
- $items = '';
- $feed_length = variable_get('feed_item_length', 'teaser');
- foreach ($feeds as $feed) {
- switch ($feed_length) {
- case 'teaser':
- $teaser = node_teaser($feed->description);
- if ($teaser != $feed->description) {
- $teaser .= '\n";
- }
- $feed->description = $teaser;
- break;
- case 'title':
- $feed->description = '';
- break;
- }
- $items .= format_rss_item($feed->ftitle .': '. $feed->title, $feed->link, $feed->description, array('pubDate' => date('r', $feed->timestamp)));
- }
-
- $site_name = variable_get('site_name', 'Drupal');
- $url = url((isset($category) ? 'aggregator/categories/'. $category->cid : 'aggregator'), array('absolute' => TRUE));
- $description = isset($category) ? t('@site_name - aggregated feeds in category @title', array('@site_name' => $site_name, '@title' => $category->title)) : t('@site_name - aggregated feeds', array('@site_name' => $site_name));
-
- $output = "\n";
- $output .= "
'. t('Ecto, a blogging client available for both Mac OS X and Microsoft Windows, can be used with Blog API. Blog API also supports Blogger API, MetaWeblog API, and most of the Movable Type API. Blogging clients and other services (e.g. Flickr\'s "post to blog") that support these APIs may also be compatible.', array('@ecto-link' => url('http://infinite-sushi.com/software/ecto/'), '@blogger-api' => url('http://www.blogger.com/developers/api/1_docs/'), '@metaweblog-api' => url('http://www.xmlrpc.com/metaWeblogApi'), '@movabletype-api' => url('http://www.movabletype.org/docs/mtmanual_programmatic.html'), '@flickr' => url('http://www.flickr.com'))) .'
'; - $output .= ''. t('Select the content types available to external clients on the Blog API settings page. If supported and available, each content type will be displayed as a separate "blog" by the external client.', array('@blogapi-settings' => url('admin/settings/blogapi'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Blog API module.', array('@blogapi' => url('http://drupal.org/handbook/modules/blogapi/'))) .'
'; - return $output; - } -} - -/** - * Implementation of hook_perm(). - */ -function blogapi_perm() { - return array('administer content with blog api'); -} - -/** - * 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 blogs 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 blog.')), - 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.publishPost', - 'blogapi_mt_publish_post', - array('boolean', 'string', 'string', 'string'), - t('Publish (rebuild) all of the static files related to an entry from your blog. 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, array('absolute' => 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, array('absolute' => 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); - } - - if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { - // Return an error if not configured type. - return $error; - } - - $edit = array(); - $edit['type'] = $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['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 this type of post.')); - } - - if (user_access('administer nodes') && !isset($edit['date'])) { - $edit['date'] = format_date(time(), 'custom', 'Y-m-d H:i:s O'); - } - - node_invoke_nodeapi($edit, 'blogapi new'); - - $valid = blogapi_status_error_check($edit, $publish); - if ($valid !== TRUE) { - return $valid; - } - - 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', '@type: added %title using blog API.', array('@type' => $node->type, '%title' => $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(t('n/a')); - } - // 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.')); - } - // Save the original status for validation of permissions. - $original_status = $node->status; - $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_invoke_nodeapi($node, 'blogapi edit'); - - $valid = blogapi_status_error_check($node, $original_status); - if ($valid !== TRUE) { - return $valid; - } - - 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', '@type: updated %title using Blog API.', array('@type' => $node->type, '%title' => $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); -} - -/** - * Check that the user has permission to save the node with the chosen status. - * - * @return - * TRUE if no error, or the blogapi_error(). - */ -function blogapi_status_error_check($node, $original_status) { - - $node = (object) $node; - - $node_type_default = variable_get('node_options_'. $node->type, array('status', 'promote')); - - // If we don't have the 'administer nodes' permission and the status is - // changing or for a new node the status is not the content type's default, - // then return an error. - if (!user_access('administer nodes') && (($node->status != $original_status) || (empty($node->nid) && $node->status != in_array('status', $node_type_default)))) { - if ($node->status) { - return blogapi_error(t('You do not have permission to publish this type of post. Please save it as a draft instead.')); - } - else { - return blogapi_error(t('You do not have permission to save this post as a draft. Please publish it instead.')); - } - } - return 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 - * - * returns a bandwidth-friendly list. - */ -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); - } - - if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { - // Return an error if not configured type. - return $error; - } - - if ($bodies) { - $result = db_query_range("SELECT n.nid, n.title, r.body, r.format, n.comment, 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", $blogid, $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", $blogid, $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); - } - - $usersize = 0; - $uploadsize = 0; - - $roles = array_intersect(user_roles(FALSE, 'administer content with blog api'), $user->roles); - - foreach ($roles as $rid => $name) { - $extensions .= ' '. strtolower(variable_get("blogapi_extensions_$rid", variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'))); - $usersize= max($usersize, variable_get("blogapi_usersize_$rid", variable_get('blogapi_usersize_default', 1)) * 1024 * 1024); - $uploadsize = max($uploadsize, variable_get("blogapi_uploadsize_$rid", variable_get('blogapi_uploadsize_default', 1)) * 1024 * 1024); - } - - $filesize = strlen($file['bits']); - - if ($filesize > $uploadsize) { - return blogapi_error(t('It is not possible to upload the file, because it exceeded the maximum filesize of @maxsize.', array('@maxsize' => format_size($uploadsize)))); - } - - if (_blogapi_space_used($user->uid) + $filesize > $usersize) { - return blogapi_error(t('The file can not be attached to this post, because the disk quota of @quota has been reached.', array('@quota' => format_size($usersize)))); - } - - // Only allow files with whitelisted extensions and convert remaining dots to - // underscores to prevent attacks via non-terminal executable extensions with - // files such as exploit.php.jpg. - - $whitelist = array_unique(explode(' ', trim($extensions))); - - $name = basename($file['name']); - - if ($extension_position = strrpos($name, '.')) { - $filename = drupal_substr($name, 0, $extension_position); - $final_extension = drupal_substr($name, $extension_position + 1); - - if (!in_array(strtolower($final_extension), $whitelist)) { - return blogapi_error(t('It is not possible to upload the file, because it is only possible to upload files with the following extensions: @extensions', array('@extensions' => implode(' ', $whitelist)))); - } - - $filename = str_replace('.', '_', $filename); - $filename .= '.'. $final_extension; - } - - $data = $file['bits']; - - if (!$data) { - return blogapi_error(t('No file sent.')); - } - - if (!$file = file_save_data($data, $filename)) { - return blogapi_error(t('Error storing file.')); - } - - $row = new stdClass(); - $row->uid = $user->uid; - $row->filepath = $file; - $row->filesize = $filesize; - - drupal_write_record('blogapi_files', $row); - - // 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) { - $user = blogapi_validate_user($username, $password); - if (!$user->uid) { - return blogapi_error($user); - } - - if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { - // Return an error if not configured type. - return $error; - } - - $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $blogid, '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); -} - -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); - } - - $node = node_load($postid); - $terms = module_invoke('taxonomy', 'node_get_terms', $node, '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']; - } - $validated = blogapi_mt_validate_terms($node); - if ($validated !== TRUE) { - return $validated; - } - node_save($node); - return TRUE; -} - -/** - * Blogging API helper - find allowed taxonomy terms for a node type. - */ -function blogapi_mt_validate_terms($node) { - // We do a lot of heavy lifting here since taxonomy module doesn't have a - // stand-alone validation function. - if (module_exists('taxonomy')) { - $found_terms = array(); - if (!empty($node->taxonomy)) { - $term_list = array_unique($node->taxonomy); - $params = $term_list; - $params[] = $node->type; - $result = db_query(db_rewrite_sql("SELECT t.tid, t.vid FROM {term_data} t INNER JOIN {vocabulary_node_types} n ON t.vid = n.vid WHERE t.tid IN (". db_placeholders($term_list) .") AND n.type = '%s'", 't', 'tid'), $params); - $found_terms = array(); - $found_count = 0; - while ($term = db_fetch_object($result)) { - $found_terms[$term->vid][$term->tid] = $term->tid; - $found_count++; - } - // If the counts don't match, some terms are invalid or not accessible to this user. - if (count($term_list) != $found_count) { - return blogapi_error(t('Invalid categories submitted.')); - } - } - // Look up all the vocabularies for this node type. - $result2 = db_query(db_rewrite_sql("SELECT v.vid, v.name, v.required, v.multiple FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s'", 'v', 'vid'), $node->type); - // Check each vocabulary associated with this node type. - while ($vocabulary = db_fetch_object($result2)) { - // Required vocabularies must have at least one term. - if ($vocabulary->required && empty($found_terms[$vocabulary->vid])) { - return blogapi_error(t('A category from the @vocabulary_name vocabulary is required.', array('@vocabulary_name' => $vocabulary->name))); - } - // Vocabularies that don't allow multiple terms may have at most one. - if (!($vocabulary->multiple) && (isset($found_terms[$vocabulary->vid]) && count($found_terms[$vocabulary->vid]) > 1)) { - return blogapi_error(t('You may only choose one category from the @vocabulary_name vocabulary.'), array('@vocabulary_name' => $vocabulary->name)); - } - } - } - elseif (!empty($node->taxonomy)) { - return blogapi_error(t('Error saving categories. This feature is not available.')); - } - 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. Publishes the given node - */ -function blogapi_mt_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.')); - } - - // Nothing needs to be done if already published. - if ($node->status) { - return; - } - - if (!node_access('update', $node) || !user_access('administer nodes')) { - return blogapi_error(t('You do not have permission to update this post.')); - } - - $node->status = 1; - 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(array('name' => $username, 'pass' => $password)); - - if ($user->uid) { - if (user_access('administer content with blog api', $user)) { - return $user; - } - else { - return t('You do not have permission to edit this 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(''. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))).'
'); - - $roles = user_roles(0, 'administer content with blog api'); - $form['roles'] = array('#type' => 'value', '#value' => $roles); - - foreach ($roles as $rid => $role) { - $form['settings_role_'. $rid] = array( - '#type' => 'fieldset', - '#title' => t('Settings for @role', array('@role' => $role)), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $form['settings_role_'. $rid]['blogapi_extensions_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Permitted file extensions'), - '#default_value' => variable_get('blogapi_extensions_'. $rid, $blogapi_extensions_default), - '#maxlength' => 255, - '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'), - ); - $form['settings_role_'. $rid]['blogapi_uploadsize_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Maximum file size per upload'), - '#default_value' => variable_get('blogapi_uploadsize_'. $rid, $blogapi_uploadsize_default), - '#size' => 5, - '#maxlength' => 5, - '#description' => t('The maximum size of a file a user can upload (in megabytes).'), - ); - $form['settings_role_'. $rid]['blogapi_usersize_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Total file size per user'), - '#default_value' => variable_get('blogapi_usersize_'. $rid, $blogapi_usersize_default), - '#size' => 5, - '#maxlength' => 5, - '#description' => t('The maximum size of all files a user can have on the site (in megabytes).'), - ); - } - - return system_settings_form($form); -} - -function blogapi_menu() { - $items['blogapi/rsd'] = array( - 'title' => 'RSD', - 'page callback' => 'blogapi_rsd', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['admin/settings/blogapi'] = array( - 'title' => 'Blog API', - 'description' => 'Configure the content types available to external blogging clients.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('blogapi_admin_settings'), - 'access arguments' => array('administer site configuration'), - 'type' => MENU_NORMAL_ITEM, - ); - - return $items; -} - -function blogapi_init() { - if (drupal_is_front_page()) { - drupal_add_link(array('rel' => 'EditURI', - 'type' => 'application/rsd+xml', - 'title' => t('RSD'), - 'href' => url('blogapi/rsd', array('absolute' => TRUE)))); - } -} - -function blogapi_rsd() { - global $base_url; - - $xmlrpc = $base_url .'/xmlrpc.php'; - $base = url('', array('absolute' => 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__ - -'. t("It is important to remember that color module saves a modified copy of the theme's specified stylesheets in the files directory. This means that if you make any manual changes to your theme's stylesheet, you must save your color settings again, even if they haven't changed. This causes the color module generated version of the stylesheets in the files directory to be recreated using the new version of the original file.") .'
'; - $output .= ''. t('To change the color settings for a compatible theme, select the "configure" link for the theme on the themes administration page.', array('@themes' => url('admin/build/themes'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Color module.', array('@color' => 'http://drupal.org/handbook/modules/color/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_theme(). - */ -function color_theme() { - return array( - 'color_scheme_form' => array( - 'arguments' => array('form' => NULL), - ), - ); -} - -/** - * Implementation of hook_form_alter(). - */ -function color_form_alter(&$form, $form_state, $form_id) { - // Insert the color changer into the theme settings page. - if ($form_id == 'system_theme_settings' && color_get_info(arg(4)) && function_exists('gd_info')) { - if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) { - // Disables the color changer when the private download method is used. - // TODO: This should be solved in a different way. See issue #181003. - drupal_set_message(t('The color picker only works if the download method is set to public.', array('@url' => url('admin/settings/file-system'))), 'warning'); - } - else { - $form['color'] = array( - '#type' => 'fieldset', - '#title' => t('Color scheme'), - '#weight' => -1, - '#attributes' => array('id' => 'color_scheme_form'), - '#theme' => 'color_scheme_form', - ); - $form['color'] += color_scheme_form($form_state, arg(4)); - $form['#validate'][] = 'color_scheme_form_validate'; - $form['#submit'][] = 'color_scheme_form_submit'; - } - } - - // Use the generated screenshot in the theme list. - if ($form_id == 'system_theme_select_form' || $form_id == 'system_themes') { - $themes = list_themes(); - foreach (element_children($form) as $theme) { - if ($screenshot = variable_get('color_'. $theme .'_screenshot', NULL)) { - if (isset($form[$theme]['screenshot'])) { - $form[$theme]['screenshot']['#value'] = theme('image', $screenshot, '', '', array('class' => 'screenshot'), FALSE); - } - } - } - } -} - -/** - * Callback for the theme to alter the resources used. - */ -function _color_page_alter(&$vars) { - global $language, $theme_key; - - // Override stylesheets. - $color_paths = variable_get('color_'. $theme_key .'_stylesheets', array()); - if (!empty($color_paths)) { - // Loop over theme CSS files and try to rebuild CSS array with rewritten - // stylesheets. Keep the orginal order intact for CSS cascading. - $new_theme_css = array(); - - foreach ($vars['css']['all']['theme'] as $old_path => $old_preprocess) { - // Add the non-colored stylesheet first as we might not find a - // re-colored stylesheet for replacement later. - $new_theme_css[$old_path] = $old_preprocess; - - // Loop over the path array with recolored CSS files to find matching - // paths which could replace the non-recolored paths. - foreach ($color_paths as $color_path) { - // Color module currently requires unique file names to be used, - // which allows us to compare different file paths. - if (basename($old_path) == basename($color_path)) { - // Pull out the non-colored and add rewritten stylesheet. - unset($new_theme_css[$old_path]); - $new_theme_css[$color_path] = $old_preprocess; - - // If the current language is RTL and the CSS file had an RTL variant, - // pull out the non-colored and add rewritten RTL stylesheet. - if ($language->direction == LANGUAGE_RTL) { - $rtl_old_path = str_replace('.css', '-rtl.css', $old_path); - $rtl_color_path = str_replace('.css', '-rtl.css', $color_path); - if (file_exists($rtl_color_path)) { - unset($new_theme_css[$rtl_old_path]); - $new_theme_css[$rtl_color_path] = $old_preprocess; - } - } - break; - } - } - } - $vars['css']['all']['theme'] = $new_theme_css; - $vars['styles'] = drupal_get_css($vars['css']); - } - - // Override logo. - $logo = variable_get('color_'. $theme_key .'_logo', NULL); - if ($logo && $vars['logo'] && preg_match('!'. $theme_key .'/logo.png$!', $vars['logo'])) { - $vars['logo'] = base_path() . $logo; - } -} - -/** - * Retrieve the color.module info for a particular theme. - */ -function color_get_info($theme) { - $path = drupal_get_path('theme', $theme); - $file = $path .'/color/color.inc'; - if ($path && file_exists($file)) { - include $file; - return $info; - } -} - -/** - * Helper function to retrieve the color palette for a particular theme. - */ -function color_get_palette($theme, $default = false) { - // Fetch and expand default palette - $fields = array('base', 'link', 'top', 'bottom', 'text'); - $info = color_get_info($theme); - $keys = array_keys($info['schemes']); - foreach (explode(',', array_shift($keys)) as $k => $scheme) { - $palette[$fields[$k]] = $scheme; - } - - // Load variable - return $default ? $palette : variable_get('color_'. $theme .'_palette', $palette); -} - -/** - * Form callback. Returns the configuration form. - */ -function color_scheme_form(&$form_state, $theme) { - $base = drupal_get_path('module', 'color'); - $info = color_get_info($theme); - - // Add Farbtastic color picker - drupal_add_css('misc/farbtastic/farbtastic.css', 'module', 'all', FALSE); - drupal_add_js('misc/farbtastic/farbtastic.js'); - - // Add custom CSS/JS - drupal_add_css($base .'/color.css', 'module', 'all', FALSE); - drupal_add_js($base .'/color.js'); - drupal_add_js(array('color' => array( - 'reference' => color_get_palette($theme, true) - )), 'setting'); - - // See if we're using a predefined scheme - $current = implode(',', variable_get('color_'. $theme .'_palette', array())); - // Note: we use the original theme when the default scheme is chosen. - $current = isset($info['schemes'][$current]) ? $current : ($current == '' ? reset($info['schemes']) : ''); - - // Add scheme selector - $info['schemes'][''] = t('Custom'); - $form['scheme'] = array( - '#type' => 'select', - '#title' => t('Color set'), - '#options' => $info['schemes'], - '#default_value' => $current, - ); - - // Add palette fields - $palette = color_get_palette($theme); - $names = array( - 'base' => t('Base color'), - 'link' => t('Link color'), - 'top' => t('Header top'), - 'bottom' => t('Header bottom'), - 'text' => t('Text color') - ); - $form['palette']['#tree'] = true; - foreach ($palette as $name => $value) { - $form['palette'][$name] = array( - '#type' => 'textfield', - '#title' => $names[$name], - '#default_value' => $value, - '#size' => 8, - ); - } - $form['theme'] = array('#type' => 'value', '#value' => arg(4)); - $form['info'] = array('#type' => 'value', '#value' => $info); - - return $form; -} - -/** - * Theme color form. - * - * @ingroup themeable - */ -function theme_color_scheme_form($form) { - // Include stylesheet - $theme = $form['theme']['#value']; - $info = $form['info']['#value']; - $path = drupal_get_path('theme', $theme) .'/'; - drupal_add_css($path . $info['preview_css']); - $output = ''; - // Wrapper - $output .= 'Sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
'. t("Personal contact forms allow users to be contacted via e-mail, while keeping recipient e-mail addresses private. Users may enable or disable their personal contact forms by editing their My account page. If enabled, a Contact tab leading to their personal contact form is available on their user profile. Site administrators have access to all personal contact forms (even if they have been disabled). The Contact tab is only visible when viewing another user's profile (users do not see their own Contact tab).") .'
'; - $output .= ''. t('The contact page provides a simple form for visitors to leave comments, feedback, or other requests. Messages are routed by selecting a category from a list of administrator-defined options; each category has its own set of e-mail recipients. Common categories for a business site include, for example, "Website feedback" (messages are forwarded to web site administrators) and "Product information" (messages are forwarded to members of the sales department). The actual e-mail addresses defined within a category are not displayed. Only users in roles with the access site-wide contact form permission may access the contact page.', array('@contact' => url('contact'))) .'
'; - $output .= ''. t('A link to your site\'s contact page from the main Navigation menu is created, but is disabled by default. Create a similar link on another menu by adding a menu item pointing to the path "contact"', array('@contact' => url('contact'))) .'
'; - $output .= ''. t('Customize the contact page with additional information (like physical location, mailing address, and telephone number) using the contact form settings page. The settings page also provides configuration options for the maximum number of contact form submissions a user may perform per hour, and the default status of users\' personal contact forms.', array('@contact-settings' => url('admin/build/contact/settings'), '@contact' => url('contact'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Contact module.', array('@contact' => url('http://drupal.org/handbook/modules/contact/', array('absolute' => TRUE)))) .'
'; - return $output; - case 'admin/build/contact': - $output = ''. t('This page lets you set up your site-wide contact form. 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 settings page, 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/build/contact/settings'), '@form' => url('contact'))) .'
'; - if (!module_exists('menu')) { - $menu_note = t('The menu item can be customized and configured only once the menu module has been enabled.', array('@modules-page' => url('admin/settings/modules'))); - } - else { - $menu_note = ''; - } - $output .= ''. t('The contact module also adds a menu item (disabled by default) to the navigation block.', array('@menu-settings' => url('admin/build/menu'))) .' '. $menu_note .'
'; - return $output; - } -} - -/** - * Implementation of hook_perm - */ -function contact_perm() { - return array('access site-wide contact form', 'administer site-wide contact form'); -} -/** - * Implementation of hook_menu(). - */ -function contact_menu() { - $items['admin/build/contact'] = array( - 'title' => 'Contact form', - 'description' => 'Create a system contact form and set up categories for the form to use.', - 'page callback' => 'contact_admin_categories', - 'access arguments' => array('administer site-wide contact form'), - 'file' => 'contact.admin.inc', - ); - $items['admin/build/contact/list'] = array( - 'title' => 'List', - 'page callback' => 'contact_admin_categories', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'file' => 'contact.admin.inc', - ); - $items['admin/build/contact/add'] = array( - 'title' => 'Add category', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('contact_admin_edit', 3), - 'access arguments' => array('administer site-wide contact form'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 1, - 'file' => 'contact.admin.inc', - ); - $items['admin/build/contact/edit/%contact'] = array( - 'title' => 'Edit contact category', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('contact_admin_edit', 3, 4), - 'access arguments' => array('administer site-wide contact form'), - 'type' => MENU_CALLBACK, - 'file' => 'contact.admin.inc', - ); - $items['admin/build/contact/delete/%contact'] = array( - 'title' => 'Delete contact', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('contact_admin_delete', 4), - 'access arguments' => array('administer site-wide contact form'), - 'type' => MENU_CALLBACK, - 'file' => 'contact.admin.inc', - ); - $items['admin/build/contact/settings'] = array( - 'title' => 'Settings', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('contact_admin_settings'), - 'access arguments' => array('administer site-wide contact form'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 2, - 'file' => 'contact.admin.inc', - ); - $items['contact'] = array( - 'title' => 'Contact', - 'page callback' => 'contact_site_page', - 'access arguments' => array('access site-wide contact form'), - 'type' => MENU_SUGGESTED_ITEM, - 'file' => 'contact.pages.inc', - ); - $items['user/%user/contact'] = array( - 'title' => 'Contact', - 'page callback' => 'contact_user_page', - 'page arguments' => array(1), - 'type' => MENU_LOCAL_TASK, - 'access callback' => '_contact_user_tab_access', - 'access arguments' => array(1), - 'weight' => 2, - 'file' => 'contact.pages.inc', - ); - return $items; -} - -/** - * Menu access callback for a user's personal contact form. - * - * @param $account - * A user account object. - * @return - * TRUE if the current user has access to the requested user's contact form, - * or FALSE otherwise. - */ -function _contact_user_tab_access($account) { - global $user; - - // Anonymous users cannot use or have contact forms. - if (!$user->uid || !$account->uid) { - return FALSE; - } - - // User administrators should always have access to personal contact forms. - if (user_access('administer users')) { - return TRUE; - } - - // Users may not contact themselves. - if ($user->uid == $account->uid) { - return FALSE; - } - - // If the requested user has disabled their contact form, or this preference - // has not yet been saved, do not allow users to contact them. - if (empty($account->contact)) { - return FALSE; - } - - return TRUE; -} - -/** - * Load the data for a single contact category. - */ -function contact_load($cid) { - $contact = db_fetch_array(db_query("SELECT * FROM {contact} WHERE cid = %d", $cid)); - return empty($contact) ? FALSE : $contact; -} - -/** - * 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' => !empty($edit['contact']) ? $edit['contact'] : FALSE, - '#description' => t('Allow other users to contact you by e-mail via your personal contact form. 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; - } - elseif ($type == 'validate') { - return array('contact' => isset($edit['contact']) ? $edit['contact'] : FALSE); - } - elseif ($type == 'insert') { - $edit['contact'] = variable_get('contact_default_status', 1); - } -} - -/** - * Implementation of hook_mail(). - */ -function contact_mail($key, &$message, $params) { - $language = $message['language']; - switch ($key) { - case 'page_mail': - case 'page_copy': - $contact = $params['contact']; - $message['subject'] .= t('[!category] !subject', array('!category' => $contact['category'], '!subject' => $params['subject']), $language->language); - $message['body'][] = t("!name sent a message using the contact form at !form.", array('!name' => $params['name'], '!form' => url($_GET['q'], array('absolute' => TRUE, 'language' => $language))), $language->language); - $message['body'][] = $params['message']; - break; - case 'page_autoreply': - $contact = $params['contact']; - $message['subject'] .= t('[!category] !subject', array('!category' => $contact['category'], '!subject' => $params['subject']), $language->language); - $message['body'][] = $contact['reply']; - break; - case 'user_mail': - case 'user_copy': - $user = $params['user']; - $account = $params['account']; - $message['subject'] .= '['. variable_get('site_name', 'Drupal') .'] '. $params['subject']; - $message['body'][] = "$account->name,"; - $message['body'][] = 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", array('absolute' => TRUE, 'language' => $language)), '!form-url' => url($_GET['q'], array('absolute' => TRUE, 'language' => $language)), '!site' => variable_get('site_name', 'Drupal')), $language->language); - $message['body'][] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url("user/$account->uid", array('absolute' => TRUE, 'language' => $language))), $language->language); - $message['body'][] = t('Message:', NULL, $language->language); - $message['body'][] = $params['message']; - break; - } -} diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc deleted file mode 100644 index db9c942..0000000 --- a/modules/contact/contact.pages.inc +++ /dev/null @@ -1,235 +0,0 @@ - variable_get('contact_hourly_threshold', 3))); - } - else { - $output = drupal_get_form('contact_mail_page'); - } - - return $output; -} - -function contact_mail_page() { - global $user; - - $form = $categories = array(); - - $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->uid ? $user->name . $user->mail : ''; - $form['contact_information'] = array('#value' => filter_xss_admin(variable_get('contact_form_information', t('You can leave a message using the contact form below.')))); - $form['name'] = array('#type' => 'textfield', - '#title' => t('Your name'), - '#maxlength' => 255, - '#default_value' => $user->uid ? $user->name : '', - '#required' => TRUE, - ); - $form['mail'] = array('#type' => 'textfield', - '#title' => t('Your e-mail address'), - '#maxlength' => 255, - '#default_value' => $user->uid ? $user->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)) { - $default_category = t('- Please choose -'); - $categories = array($default_category) + $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. - $category_keys = array_keys($categories); - $form['cid'] = array('#type' => 'value', - '#value' => array_shift($category_keys), - ); - } - $form['message'] = array('#type' => 'textarea', - '#title' => t('Message'), - '#required' => TRUE, - ); - // We do not allow anonymous users to send themselves a copy - // because it can be abused to spam people. - if ($user->uid) { - $form['copy'] = array('#type' => 'checkbox', - '#title' => t('Send yourself a copy.'), - ); - } - else { - $form['copy'] = array('#type' => 'value', '#value' => FALSE); - } - $form['submit'] = array('#type' => 'submit', - '#value' => t('Send e-mail'), - ); - } - else { - drupal_set_message(t('The contact form has not been configured. Add one or more categories to the form.', array('@add' => url('admin/build/contact/add'))), 'error'); - } - return $form; -} - -/** - * Validate the site-wide contact page form submission. - */ -function contact_mail_page_validate($form, &$form_state) { - if (!$form_state['values']['cid']) { - form_set_error('cid', t('You must select a valid category.')); - } - if (!valid_email_address($form_state['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, &$form_state) { - global $language; - - $values = $form_state['values']; - - // 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 = $values['mail']; - - // Load category properties and save form values for email composition. - $contact = contact_load($values['cid']); - $values['contact'] = $contact; - - // Send the e-mail to the recipients using the site default language. - drupal_mail('contact', 'page_mail', $contact['recipients'], language_default(), $values, $from); - - // If the user requests it, send a copy using the current language. - if ($values['copy']) { - drupal_mail('contact', 'page_copy', $from, $language, $values, $from); - } - - // Send an auto-reply if necessary using the current language. - if ($contact['reply']) { - drupal_mail('contact', 'page_autoreply', $from, $language, $values, $contact['recipients']); - } - - flood_register_event('contact'); - watchdog('mail', '%name-from sent an e-mail regarding %category.', array('%name-from' => $values['name'] ." [$from]", '%category' => $contact['category'])); - 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. - $form_state['redirect'] = ''; -} - -/** - * Personal contact page. - */ -function contact_user_page($account) { - global $user; - - if (!valid_email_address($user->mail)) { - $output = t('You need to provide a valid e-mail address to contact other users. Please update your user information 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(check_plain($account->name)); - $output = drupal_get_form('contact_mail_user', $account); - } - - return $output; -} - -function contact_mail_user(&$form_state, $recipient) { - global $user; - $form['#token'] = $user->name . $user->mail; - $form['recipient'] = array('#type' => 'value', '#value' => $recipient); - $form['from'] = array('#type' => 'item', - '#title' => t('From'), - '#value' => theme('username', $user) .' <'. check_plain($user->mail) .'>', - ); - $form['to'] = array('#type' => 'item', - '#title' => t('To'), - '#value' => theme('username', $recipient), - ); - $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 yourself a copy.'), - ); - $form['submit'] = array('#type' => 'submit', - '#value' => t('Send e-mail'), - ); - return $form; -} - -/** - * Process the personal contact page form submission. - */ -function contact_mail_user_submit($form, &$form_state) { - global $user, $language; - - $account = $form_state['values']['recipient']; - - // Send from the current user to the requested user. - $to = $account->mail; - $from = $user->mail; - - // Save both users and all form values for email composition. - $values = $form_state['values']; - $values['account'] = $account; - $values['user'] = $user; - - // Send the e-mail in the requested user language. - drupal_mail('contact', 'user_mail', $to, user_preferred_language($account), $values, $from); - - // Send a copy if requested, using current page language. - if ($form_state['values']['copy']) { - drupal_mail('contact', 'user_copy', $from, $language, $values, $from); - } - - flood_register_event('contact'); - watchdog('mail', '%name-from sent %name-to an e-mail.', array('%name-from' => $user->name, '%name-to' => $account->name)); - drupal_set_message(t('The message has been sent.')); - - // Back to the requested users profile page. - $form_state['redirect'] = "user/$account->uid"; -} diff --git a/modules/dblog/dblog-rtl.css b/modules/dblog/dblog-rtl.css deleted file mode 100644 index 60a402d..0000000 --- a/modules/dblog/dblog-rtl.css +++ /dev/null @@ -1,6 +0,0 @@ - -#edit-type-wrapper, #edit-severity-wrapper { - float: right; - padding-right: 0; - padding-left: .8em; -} diff --git a/modules/dblog/dblog.admin.inc b/modules/dblog/dblog.admin.inc deleted file mode 100644 index 853a2ce..0000000 --- a/modules/dblog/dblog.admin.inc +++ /dev/null @@ -1,329 +0,0 @@ - 'select', - '#title' => t('Discard log entries above the following row limit'), - '#default_value' => variable_get('dblog_row_limit', 1000), - '#options' => drupal_map_assoc(array(100, 1000, 10000, 100000, 1000000)), - '#description' => t('The maximum number of rows to keep in the database log. Older entries will be automatically discarded. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status'))) - ); - - return system_settings_form($form); -} - -/** - * Menu callback; displays a listing of log messages. - */ -function dblog_overview() { - $filter = dblog_build_filter_query(); - $rows = array(); - $icons = array( - WATCHDOG_DEBUG => '', - WATCHDOG_INFO => '', - WATCHDOG_NOTICE => '', - WATCHDOG_WARNING => theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning')), - WATCHDOG_ERROR => theme('image', 'misc/watchdog-error.png', t('error'), t('error')), - WATCHDOG_CRITICAL => theme('image', 'misc/watchdog-error.png', t('critical'), t('critical')), - WATCHDOG_ALERT => theme('image', 'misc/watchdog-error.png', t('alert'), t('alert')), - WATCHDOG_EMERG => theme('image', 'misc/watchdog-error.png', t('emergency'), t('emergency')), - ); - $classes = array( - WATCHDOG_DEBUG => 'dblog-debug', - WATCHDOG_INFO => 'dblog-info', - WATCHDOG_NOTICE => 'dblog-notice', - WATCHDOG_WARNING => 'dblog-warning', - WATCHDOG_ERROR => 'dblog-error', - WATCHDOG_CRITICAL => 'dblog-critical', - WATCHDOG_ALERT => 'dblog-alert', - WATCHDOG_EMERG => 'dblog-emerg', - ); - - $output = drupal_get_form('dblog_filter_form'); - - $header = array( - ' ', - array('data' => t('Type'), 'field' => 'w.type'), - array('data' => t('Date'), 'field' => 'w.wid', 'sort' => 'desc'), - t('Message'), - array('data' => t('User'), 'field' => 'u.name'), - array('data' => t('Operations')), - ); - - $sql = "SELECT w.wid, w.uid, w.severity, w.type, w.timestamp, w.message, w.variables, w.link, u.name FROM {watchdog} w INNER JOIN {users} u ON w.uid = u.uid"; - $tablesort = tablesort_sql($header); - if (!empty($filter['where'])) { - $result = pager_query($sql ." WHERE ". $filter['where'] . $tablesort, 50, 0, NULL, $filter['args']); - } - else { - $result = pager_query($sql . $tablesort, 50); - } - - while ($dblog = db_fetch_object($result)) { - $rows[] = array('data' => - array( - // Cells - $icons[$dblog->severity], - t($dblog->type), - format_date($dblog->timestamp, 'small'), - l(truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE), 'admin/reports/event/'. $dblog->wid, array('html' => TRUE)), - theme('username', $dblog), - filter_xss($dblog->link), - ), - // Attributes for tr - 'class' => "dblog-". preg_replace('/[^a-z]/i', '-', $dblog->type) .' '. $classes[$dblog->severity] - ); - } - - if (!$rows) { - $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); - } - - $output .= theme('table', $header, $rows, array('id' => 'admin-dblog')); - $output .= theme('pager', NULL, 50, 0); - - return $output; -} - -/** - * Menu callback; generic function to display a page of the most frequent - * dblog events of a specified type. - */ -function dblog_top($type) { - - $header = array( - array('data' => t('Count'), 'field' => 'count', 'sort' => 'desc'), - array('data' => t('Message'), 'field' => 'message') - ); - - $result = pager_query("SELECT COUNT(wid) AS count, message, variables FROM {watchdog} WHERE type = '%s' GROUP BY message, variables ". tablesort_sql($header), 30, 0, "SELECT COUNT(DISTINCT(message)) FROM {watchdog} WHERE type = '%s'", $type); - - $rows = array(); - while ($dblog = db_fetch_object($result)) { - $rows[] = array($dblog->count, truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE)); - } - - if (empty($rows)) { - $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 2)); - } - - $output = theme('table', $header, $rows); - $output .= theme('pager', NULL, 30, 0); - - return $output; -} - -/** - * Menu callback; displays details about a log message. - */ -function dblog_event($id) { - $severity = watchdog_severity_levels(); - $output = ''; - $result = db_query('SELECT w.*, u.name, u.uid FROM {watchdog} w INNER JOIN {users} u ON w.uid = u.uid WHERE w.wid = %d', $id); - if ($dblog = db_fetch_object($result)) { - $rows = array( - array( - array('data' => t('Type'), 'header' => TRUE), - t($dblog->type), - ), - array( - array('data' => t('Date'), 'header' => TRUE), - format_date($dblog->timestamp, 'large'), - ), - array( - array('data' => t('User'), 'header' => TRUE), - theme('username', $dblog), - ), - array( - array('data' => t('Location'), 'header' => TRUE), - l($dblog->location, $dblog->location), - ), - array( - array('data' => t('Referrer'), 'header' => TRUE), - l($dblog->referer, $dblog->referer), - ), - array( - array('data' => t('Message'), 'header' => TRUE), - _dblog_format_message($dblog), - ), - array( - array('data' => t('Severity'), 'header' => TRUE), - $severity[$dblog->severity], - ), - array( - array('data' => t('Hostname'), 'header' => TRUE), - check_plain($dblog->hostname), - ), - array( - array('data' => t('Operations'), 'header' => TRUE), - $dblog->link, - ), - ); - $attributes = array('class' => 'dblog-event'); - $output = theme('table', array(), $rows, $attributes); - } - return $output; -} - -/** - * Build query for dblog administration filters based on session. - */ -function dblog_build_filter_query() { - if (empty($_SESSION['dblog_overview_filter'])) { - return; - } - - $filters = dblog_filters(); - - // Build query - $where = $args = array(); - foreach ($_SESSION['dblog_overview_filter'] as $key => $filter) { - $filter_where = array(); - foreach ($filter as $value) { - $filter_where[] = $filters[$key]['where']; - $args[] = $value; - } - if (!empty($filter_where)) { - $where[] = '('. implode(' OR ', $filter_where) .')'; - } - } - $where = !empty($where) ? implode(' AND ', $where) : ''; - - return array( - 'where' => $where, - 'args' => $args, - ); -} - - -/** - * List dblog administration filters that can be applied. - */ -function dblog_filters() { - $filters = array(); - - foreach (_dblog_get_message_types() as $type) { - $types[$type] = t($type); - } - - if (!empty($types)) { - $filters['type'] = array( - 'title' => t('Type'), - 'where' => "w.type = '%s'", - 'options' => $types, - ); - } - - $filters['severity'] = array( - 'title' => t('Severity'), - 'where' => 'w.severity = %d', - 'options' => watchdog_severity_levels(), - ); - - return $filters; -} - -/** - * Formats a log message for display. - * - * @param $dblog - * An object with at least the message and variables properties - */ -function _dblog_format_message($dblog) { - // Legacy messages and user specified text - if ($dblog->variables === 'N;') { - return $dblog->message; - } - // Message to translate with injected variables - else { - return t($dblog->message, unserialize($dblog->variables)); - } -} - - -/** - * Return form for dblog administration filters. - * - * @ingroup forms - * @see dblog_filter_form_submit() - * @see dblog_filter_form_validate() - */ -function dblog_filter_form() { - $session = &$_SESSION['dblog_overview_filter']; - $session = is_array($session) ? $session : array(); - $filters = dblog_filters(); - - $form['filters'] = array( - '#type' => 'fieldset', - '#title' => t('Filter log messages'), - '#theme' => 'dblog_filters', - '#collapsible' => TRUE, - '#collapsed' => empty($session), - ); - foreach ($filters as $key => $filter) { - $form['filters']['status'][$key] = array( - '#title' => $filter['title'], - '#type' => 'select', - '#multiple' => TRUE, - '#size' => 8, - '#options' => $filter['options'], - ); - if (!empty($session[$key])) { - $form['filters']['status'][$key]['#default_value'] = $session[$key]; - } - } - - $form['filters']['buttons']['submit'] = array( - '#type' => 'submit', - '#value' => t('Filter'), - ); - if (!empty($session)) { - $form['filters']['buttons']['reset'] = array( - '#type' => 'submit', - '#value' => t('Reset') - ); - } - - return $form; -} - -/** - * Validate result from dblog administration filter form. - */ -function dblog_filter_form_validate($form, &$form_state) { - if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['type']) && empty($form_state['values']['severity'])) { - form_set_error('type', t('You must select something to filter by.')); - } -} - -/** - * Process result from dblog administration filter form. - */ -function dblog_filter_form_submit($form, &$form_state) { - $op = $form_state['values']['op']; - $filters = dblog_filters(); - switch ($op) { - case t('Filter'): - foreach ($filters as $name => $filter) { - if (isset($form_state['values'][$name])) { - $_SESSION['dblog_overview_filter'][$name] = $form_state['values'][$name]; - } - } - break; - case t('Reset'): - $_SESSION['dblog_overview_filter'] = array(); - break; - } - return 'admin/reports/dblog'; -} diff --git a/modules/dblog/dblog.css b/modules/dblog/dblog.css deleted file mode 100644 index 52f62a4..0000000 --- a/modules/dblog/dblog.css +++ /dev/null @@ -1,39 +0,0 @@ - -#edit-type-wrapper, #edit-severity-wrapper { - float: left; /* LTR */ - padding-right: .8em; /* LTR */ - margin: 0.1em; - /** - * In Opera 9, DOM elements with the property of "overflow: auto" - * will partially hide its contents with unnecessary scrollbars when - * its immediate child is floated without an explicit width set. - */ - width: 15em; -} -#dblog-filter-form .form-item select.form-select { - width: 100%; -} -tr.dblog-user { - background: #ffd; -} -tr.dblog-user .active { - background: #eed; -} -tr.dblog-content { - background: #ddf; -} -tr.dblog-content .active { - background: #cce; -} -tr.dblog-page-not-found, tr.dblog-access-denied { - background: #dfd; -} -tr.dblog-page-not-found .active, tr.dblog-access-denied .active { - background: #cec; -} -tr.dblog-error { - background: #ffc9c9; -} -tr.dblog-error .active { - background: #eeb9b9; -} diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info deleted file mode 100644 index deb6f1a..0000000 --- a/modules/dblog/dblog.info +++ /dev/null @@ -1,11 +0,0 @@ -name = Database logging -description = Logs and records system events to the database. -package = Core - optional -version = VERSION -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/dblog/dblog.install b/modules/dblog/dblog.install deleted file mode 100644 index f350e51..0000000 --- a/modules/dblog/dblog.install +++ /dev/null @@ -1,119 +0,0 @@ - 'Table that contains logs of all system events.', - 'fields' => array( - 'wid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique watchdog event ID.', - ), - 'uid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {users}.uid of the user who triggered the event.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Type of log message, for example "user" or "page not found."', - ), - 'message' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'Text of log message to be passed into the t() function.', - ), - 'variables' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'Serialized array of variables that match the message string and that is passed into the t() function.', - ), - 'severity' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The severity level of the event; ranges from 0 (Emergency) to 7 (Debug)', - ), - 'link' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Link to view the result of the event.', - ), - 'location' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'URL of the origin of the event.', - ), - 'referer' => array( - 'type' => 'text', - 'not null' => FALSE, - 'description' => 'URL of referring page.', - ), - 'hostname' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Hostname of the user who triggered the event.', - ), - 'timestamp' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Unix timestamp of when event occurred.', - ), - ), - 'primary key' => array('wid'), - 'indexes' => array('type' => array('type')), - ); - - return $schema; -} - -/** - * @addtogroup updates-6.x-extra - * @{ - */ - -/** - * Allow longer referrers. - */ -function dblog_update_6000() { - $ret = array(); - db_change_field($ret, 'watchdog', 'referer', 'referer', array('type' => 'text', 'not null' => FALSE)); - return $ret; -} - -/** - * @} End of "addtogroup updates-6.x-extra". - * The next series of updates should start at 7000. - */ diff --git a/modules/dblog/dblog.module b/modules/dblog/dblog.module deleted file mode 100644 index 149b3ed..0000000 --- a/modules/dblog/dblog.module +++ /dev/null @@ -1,166 +0,0 @@ -'. t('The dblog module monitors your system, capturing system events in a log to be reviewed by an authorized individual at a later time. This is useful for site administrators who want a quick overview of activities on their site. The logs also record the sequence of events, so it can be useful for debugging site errors.') .''; - $output .= ''. t('The dblog log is simply a list of recorded events containing usage data, performance data, errors, warnings and operational information. Administrators should check the dblog report on a regular basis to ensure their site is working properly.') .'
'; - $output .= ''. t('For more information, see the online handbook entry for Dblog module.', array('@dblog' => 'http://drupal.org/handbook/modules/dblog/')) .'
'; - return $output; - case 'admin/reports/dblog': - return ''. t('The dblog module monitors your website, capturing system events in a log to be reviewed by an authorized individual at a later time. The dblog log is simply a list of recorded events containing usage data, performance data, errors, warnings and operational information. It is vital to check the dblog report on a regular basis as it is often the only way to tell what is going on.') .'
'; - } -} - -/** - * Implementation of hook_theme() - */ -function dblog_theme() { - return array( - 'dblog_filters' => array( - 'arguments' => array('form' => NULL), - ), - ); -} - -/** - * Implementation of hook_menu(). - */ -function dblog_menu() { - $items['admin/settings/logging/dblog'] = array( - 'title' => 'Database logging', - 'description' => 'Settings for logging to the Drupal database logs. This is the most common method for small to medium sites on shared hosting. The logs are viewable from the admin pages.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('dblog_admin_settings'), - 'access arguments' => array('administer site configuration'), - 'file' => 'dblog.admin.inc', - ); - - $items['admin/reports/dblog'] = array( - 'title' => 'Recent log entries', - 'description' => 'View events that have recently been logged.', - 'page callback' => 'dblog_overview', - 'access arguments' => array('access site reports'), - 'weight' => -1, - 'file' => 'dblog.admin.inc', - ); - $items['admin/reports/page-not-found'] = array( - 'title' => "Top 'page not found' errors", - 'description' => "View 'page not found' errors (404s).", - 'page callback' => 'dblog_top', - 'page arguments' => array('page not found'), - 'access arguments' => array('access site reports'), - 'file' => 'dblog.admin.inc', - ); - $items['admin/reports/access-denied'] = array( - 'title' => "Top 'access denied' errors", - 'description' => "View 'access denied' errors (403s).", - 'page callback' => 'dblog_top', - 'page arguments' => array('access denied'), - 'access arguments' => array('access site reports'), - 'file' => 'dblog.admin.inc', - ); - $items['admin/reports/event/%'] = array( - 'title' => 'Details', - 'page callback' => 'dblog_event', - 'page arguments' => array(3), - 'access arguments' => array('access site reports'), - 'type' => MENU_CALLBACK, - 'file' => 'dblog.admin.inc', - ); - return $items; -} - -function dblog_init() { - if (arg(0) == 'admin' && arg(1) == 'reports') { - // Add the CSS for this module - drupal_add_css(drupal_get_path('module', 'dblog') .'/dblog.css', 'module', 'all', FALSE); - } -} - - - -/** - * Implementation of hook_cron(). - * - * Remove expired log messages. - */ -function dblog_cron() { - // Cleanup the watchdog table - $max = db_result(db_query('SELECT MAX(wid) FROM {watchdog}')); - db_query('DELETE FROM {watchdog} WHERE wid <= %d', $max - variable_get('dblog_row_limit', 1000)); -} - -/** - * Implementation of hook_user(). - */ -function dblog_user($op, &$edit, &$user) { - if ($op == 'delete') { - db_query('UPDATE {watchdog} SET uid = 0 WHERE uid = %d', $user->uid); - } -} - -function _dblog_get_message_types() { - $types = array(); - - $result = db_query('SELECT DISTINCT(type) FROM {watchdog} ORDER BY type'); - while ($object = db_fetch_object($result)) { - $types[] = $object->type; - } - - return $types; -} - -/** - * Implementation of hook_watchdog(). - */ -function dblog_watchdog($log = array()) { - $current_db = db_set_active(); - db_query("INSERT INTO {watchdog} - (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) - VALUES - (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)", - $log['user']->uid, - $log['type'], - $log['message'], - serialize($log['variables']), - $log['severity'], - $log['link'], - $log['request_uri'], - $log['referer'], - $log['ip'], - $log['timestamp']); - - if ($current_db) { - db_set_active($current_db); - } -} - -/** - * Theme dblog administration filter selector. - * - * @ingroup themeable - */ -function theme_dblog_filters($form) { - $output = ''; - foreach (element_children($form['status']) as $key) { - $output .= drupal_render($form['status'][$key]); - } - $output .= ' '; - return $output; -} - diff --git a/modules/openid/login-bg.png b/modules/openid/login-bg.png deleted file mode 100644 index ed46173..0000000 Binary files a/modules/openid/login-bg.png and /dev/null differ diff --git a/modules/openid/openid.css b/modules/openid/openid.css deleted file mode 100644 index 900c0d4..0000000 --- a/modules/openid/openid.css +++ /dev/null @@ -1,39 +0,0 @@ - -#edit-openid-identifier { - background-image: url("login-bg.png"); - background-position: 0% 50%; - background-repeat: no-repeat; - padding-left: 20px; -} -div#edit-openid-identifier-wrapper { - display: block; -} -html.js #user-login-form div#edit-openid-identifier-wrapper, -html.js #user-login div#edit-openid-identifier-wrapper { - display: none; -} -html.js #user-login-form li.openid-link, -html.js #user-login li.openid-link { - display : block; - list-style: none; -} -#user-login-form ul { - margin-top: 0; -} -#user-login ul { - margin: 0 0 5px; -} -#user-login ul li { - margin: 0; -} -#user-login-form li.openid-link, -#user-login-form li.user-link, -#user-login li.openid-link, -#user-login li.user-link { - display: none; -} -#user-login-form li.openid-link a, -#user-login li.openid-link a { - background: transparent url("login-bg.png") no-repeat 0 2px; - padding: 0 20px; -} \ No newline at end of file diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc deleted file mode 100644 index 70dbee9..0000000 --- a/modules/openid/openid.inc +++ /dev/null @@ -1,428 +0,0 @@ - $val) { - $query[] = $key .'='. urlencode($val); - } - - $sep = (strpos($url, '?') === FALSE) ? '?' : '&'; - header('Location: '. $url . $sep . implode('&', $query), TRUE, 302); - exit; -} - -/** - * Creates a js auto-submit redirect for (for the 2.x protocol) - */ -function openid_redirect($url, $message) { - $output = ''. t('This site supports OpenID, a secure way to log into many websites using a single username and password. OpenID can reduce the necessity of managing many usernames and passwords for many websites.', array('@openid-net' => 'http://openid.net')) .'
'; - $output .= ''. t('To use OpenID you must first establish an identity on a public or private OpenID server. If you do not have an OpenID and would like one, look into one of the free public providers. You can find out more about OpenID at this website.', array('@openid-providers' => 'http://openid.net/get/', '@openid-net' => 'http://openid.net')) .'
'; - $output .= ''. t('If you already have an OpenID, enter the URL to your OpenID server below (e.g. myusername.openidprovider.com). Next time you login, you will be able to use this URL instead of a regular username and password. You can have multiple OpenID servers if you like; just keep adding them here.') .'
'; - return $output; - - case 'admin/help#openid': - $output = ''. t('OpenID is a secure method for logging into many websites with a single username and password. It does not require special software, and it does not share passwords with any site to which it is associated; including your site.') .'
'; - $output .= ''. t('Users can create accounts using their OpenID, assign one or more OpenIDs to an existing account, and log in using an OpenID. This lowers the barrier to registration, which is good for the site, and offers convenience and security to the users. OpenID is not a trust system, so email verification is still necessary. The benefit stems from the fact that users can have a single password that they can use on many websites. This means they can easily update their single password from a centralized location, rather than having to change dozens of passwords individually.') .'
'; - $output .= ''. t('The basic concept is as follows: A user has an account on an OpenID server. This account provides them with a unique URL (such as myusername.openidprovider.com). When the user comes to your site, they are presented with the option of entering this URL. Your site then communicates with the OpenID server, asking it to verify the identity of the user. If the user is logged into their OpenID server, the server communicates back to your site, verifying the user. If they are not logged in, the OpenID server will ask the user for their password. At no point does your site record, or need to record the user\'s password.') .'
'; - $output .= ''. t('More information on OpenID is available at OpenID.net.', array('@openid-net' => url('http://openid.net'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for OpenID module.', array('@handbook' => 'http://drupal.org/handbook/modules/openid')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_user(). - */ -function openid_user($op, &$edit, &$account, $category = NULL) { - if ($op == 'insert' && isset($_SESSION['openid']['values'])) { - // The user has registered after trying to login via OpenID. - if (variable_get('user_email_verification', TRUE)) { - drupal_set_message(t('Once you have verified your email address, you may log in via OpenID.')); - } - unset($_SESSION['openid']); - } -} - -/** - * Implementation of hook_form_alter : adds OpenID login to the login forms. - */ -function openid_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'user_login_block' || $form_id == 'user_login') { - drupal_add_css(drupal_get_path('module', 'openid') .'/openid.css', 'module'); - drupal_add_js(drupal_get_path('module', 'openid') .'/openid.js'); - if (!empty($form_state['post']['openid_identifier'])) { - $form['name']['#required'] = FALSE; - $form['pass']['#required'] = FALSE; - unset($form['#submit']); - $form['#validate'] = array('openid_login_validate'); - } - - $items = array(); - $items[] = array( - 'data' => l(t('Log in using OpenID'), '#'), - 'class' => 'openid-link', - ); - $items[] = array( - 'data' => l(t('Cancel OpenID login'), '#'), - 'class' => 'user-link', - ); - - $form['openid_links'] = array( - '#value' => theme('item_list', $items), - '#weight' => 1, - ); - - $form['links']['#weight'] = 2; - - $form['openid_identifier'] = array( - '#type' => 'textfield', - '#title' => t('Log in using OpenID'), - '#size' => ($form_id == 'user_login') ? 58 : 13, - '#maxlength' => 255, - '#weight' => -1, - '#description' => l(t('What is OpenID?'), 'http://openid.net/', array('external' => TRUE)), - ); - $form['openid.return_to'] = array('#type' => 'hidden', '#value' => url('openid/authenticate', array('absolute' => TRUE, 'query' => user_login_destination()))); - } - elseif ($form_id == 'user_register' && isset($_SESSION['openid']['values'])) { - // We were unable to auto-register a new user. Prefill the registration - // form with the values we have. - $form['name']['#default_value'] = $_SESSION['openid']['values']['name']; - $form['mail']['#default_value'] = $_SESSION['openid']['values']['mail']; - // If user_email_verification is off, hide the password field and just fill - // with random password to avoid confusion. - if (!variable_get('user_email_verification', TRUE)) { - $form['pass']['#type'] = 'hidden'; - $form['pass']['#value'] = user_password(); - } - $form['auth_openid'] = array('#type' => 'hidden', '#value' => $_SESSION['openid']['values']['auth_openid']); - $form['openid_display'] = array( - '#type' => 'item', - '#title' => t('Your OpenID'), - '#description' => t('This OpenID will be attached to your account after registration.'), - '#value' => check_plain($_SESSION['openid']['values']['auth_openid']), - ); - } - return $form; -} - -/** - * Login form _validate hook - */ -function openid_login_validate($form, &$form_state) { - $return_to = $form_state['values']['openid.return_to']; - if (empty($return_to)) { - $return_to = url('', array('absolute' => TRUE)); - } - - openid_begin($form_state['values']['openid_identifier'], $return_to, $form_state['values']); -} - -/** - * The initial step of OpenID authentication responsible for the following: - * - Perform discovery on the claimed OpenID. - * - If possible, create an association with the Provider's endpoint. - * - Create the authentication request. - * - Perform the appropriate redirect. - * - * @param $claimed_id The OpenID to authenticate - * @param $return_to The endpoint to return to from the OpenID Provider - */ -function openid_begin($claimed_id, $return_to = '', $form_values = array()) { - module_load_include('inc', 'openid'); - - $claimed_id = _openid_normalize($claimed_id); - - $services = openid_discovery($claimed_id); - if (count($services) == 0) { - form_set_error('openid_identifier', t('Sorry, that is not a valid OpenID. Please ensure you have spelled your ID correctly.')); - return; - } - - // Store discovered information in the users' session so we don't have to rediscover. - $_SESSION['openid']['service'] = $services[0]; - // Store the claimed id - $_SESSION['openid']['claimed_id'] = $claimed_id; - // Store the login form values so we can pass them to - // user_exteral_login later. - $_SESSION['openid']['user_login_values'] = $form_values; - - $op_endpoint = $services[0]['uri']; - // If bcmath is present, then create an association - $assoc_handle = ''; - if (function_exists('bcadd')) { - $assoc_handle = openid_association($op_endpoint); - } - - // Now that there is an association created, move on - // to request authentication from the IdP - // First check for LocalID. If not found, check for Delegate. Fall - // back to $claimed_id if neither is found. - if (!empty($services[0]['localid'])) { - $identity = $services[0]['localid']; - } - else if (!empty($services[0]['delegate'])) { - $identity = $services[0]['delegate']; - } - else { - $identity = $claimed_id; - } - - if (isset($services[0]['types']) && is_array($services[0]['types']) && in_array(OPENID_NS_2_0 .'/server', $services[0]['types'])) { - $claimed_id = $identity = 'http://specs.openid.net/auth/2.0/identifier_select'; - } - $authn_request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $services[0]['version']); - - if ($services[0]['version'] == 2) { - openid_redirect($op_endpoint, $authn_request); - } - else { - openid_redirect_http($op_endpoint, $authn_request); - } -} - -/** - * Completes OpenID authentication by validating returned data from the OpenID - * Provider. - * - * @param $response Array of returned values from the OpenID Provider. - * - * @return $response Response values for further processing with - * $response['status'] set to one of 'success', 'failed' or 'cancel'. - */ -function openid_complete($response = array()) { - global $base_url; - module_load_include('inc', 'openid'); - - if (count($response) == 0) { - $response = _openid_response(); - } - - // Default to failed response - $response['status'] = 'failed'; - if (isset($_SESSION['openid']['service']['uri']) && isset($_SESSION['openid']['claimed_id'])) { - $service = $_SESSION['openid']['service']; - $claimed_id = $_SESSION['openid']['claimed_id']; - unset($_SESSION['openid']['service']); - unset($_SESSION['openid']['claimed_id']); - if (isset($response['openid.mode'])) { - if ($response['openid.mode'] == 'cancel') { - $response['status'] = 'cancel'; - } - else { - if (openid_verify_assertion($service, $response)) { - // If the returned claimed_id is different from the session claimed_id, - // then we need to do discovery and make sure the op_endpoint matches. - if ($service['version'] == 2) { - // Returned Claimed Identifier could contain unique fragment - // identifier to allow identifier recycling so we need to preserve - // it in the response. - $response_claimed_id = _openid_normalize($response['openid.claimed_id']); - - if ($response_claimed_id != $claimed_id || $response_claimed_id != $response['openid.identity']) { - $disco = openid_discovery($response['openid.claimed_id']); - - if ($disco[0]['uri'] != $service['uri']) { - return $response; - } - - if (!empty($disco[0]['localid'])) { - $identity = $disco[0]['localid']; - } - else if (!empty($disco[0]['delegate'])) { - $identity = $disco[0]['delegate']; - } - else { - $identity = FALSE; - } - - // The OP-Local Identifier (if different than the Claimed - // Identifier) must be present in the XRDS document. - if ($response_claimed_id != $response['openid.identity'] && (!$identity || $identity != $response['openid.identity'])) { - return $response; - } - } - } - else { - $response['openid.claimed_id'] = $claimed_id; - } - // Verify that openid.return_to matches the current URL (see OpenID - // Authentication 2.0, section 11.1). - // While OpenID Authentication 1.1, section 4.3 does not mandate - // return_to verification, the received return_to should still - // match these constraints. - $return_to_parts = parse_url($response['openid.return_to']); - - $base_url_parts = parse_url($base_url); - $current_parts = parse_url($base_url_parts['scheme'] .'://'. $base_url_parts['host'] . request_uri()); - - if ($return_to_parts['scheme'] != $current_parts['scheme'] || - $return_to_parts['host'] != $current_parts['host'] || - $return_to_parts['path'] != $current_parts['path']) { - - return $response; - } - // Verify that all query parameters in the openid.return_to URL have - // the same value in the current URL. In addition, the current URL - // contains a number of other parameters added by the OpenID Provider. - parse_str(isset($return_to_parts['query']) ? $return_to_parts['query'] : '', $return_to_query_parameters); - foreach ($return_to_query_parameters as $name => $value) { - if (!array_key_exists($name, $_GET) || $_GET[$name] != $value) { - return $response; - } - } - $response['status'] = 'success'; - } - } - } - } - return $response; -} - -/** - * Perform discovery on a claimed ID to determine the OpenID provider endpoint. - * - * @param $claimed_id The OpenID URL to perform discovery on. - * - * @return Array of services discovered (including OpenID version, endpoint - * URI, etc). - */ -function openid_discovery($claimed_id) { - module_load_include('inc', 'openid'); - module_load_include('inc', 'openid', 'xrds'); - - $services = array(); - - $xrds_url = $claimed_id; - if (_openid_is_xri($claimed_id)) { - $xrds_url = 'http://xri.net/'. $claimed_id; - } - $url = @parse_url($xrds_url); - if ($url['scheme'] == 'http' || $url['scheme'] == 'https') { - // For regular URLs, try Yadis resolution first, then HTML-based discovery - $headers = array('Accept' => 'application/xrds+xml'); - $result = drupal_http_request($xrds_url, $headers); - - if (!isset($result->error)) { - if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) { - // Parse XML document to find URL - $services = xrds_parse($result->data); - } - else { - $xrds_url = NULL; - if (isset($result->headers['X-XRDS-Location'])) { - $xrds_url = $result->headers['X-XRDS-Location']; - } - else { - // Look for meta http-equiv link in HTML head - $xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data); - } - if (!empty($xrds_url)) { - $headers = array('Accept' => 'application/xrds+xml'); - $xrds_result = drupal_http_request($xrds_url, $headers); - if (!isset($xrds_result->error)) { - $services = xrds_parse($xrds_result->data); - } - } - } - - // Check for HTML delegation - if (count($services) == 0) { - // Look for 2.0 links - $uri = _openid_link_href('openid2.provider', $result->data); - $delegate = _openid_link_href('openid2.local_id', $result->data); - $version = 2; - - // 1.0 links - if (empty($uri)) { - $uri = _openid_link_href('openid.server', $result->data); - $delegate = _openid_link_href('openid.delegate', $result->data); - $version = 1; - } - if (!empty($uri)) { - $services[] = array('uri' => $uri, 'delegate' => $delegate, 'version' => $version); - } - } - } - } - return $services; -} - -/** - * Attempt to create a shared secret with the OpenID Provider. - * - * @param $op_endpoint URL of the OpenID Provider endpoint. - * - * @return $assoc_handle The association handle. - */ -function openid_association($op_endpoint) { - module_load_include('inc', 'openid'); - - // Remove Old Associations: - db_query("DELETE FROM {openid_association} WHERE created + expires_in < %d", time()); - - // Check to see if we have an association for this IdP already - $assoc_handle = db_result(db_query("SELECT assoc_handle FROM {openid_association} WHERE idp_endpoint_uri = '%s'", $op_endpoint)); - if (empty($assoc_handle)) { - $mod = OPENID_DH_DEFAULT_MOD; - $gen = OPENID_DH_DEFAULT_GEN; - $r = _openid_dh_rand($mod); - $private = bcadd($r, 1); - $public = bcpowmod($gen, $private, $mod); - - // If there is no existing association, then request one - $assoc_request = openid_association_request($public); - $assoc_message = _openid_encode_message(_openid_create_message($assoc_request)); - $assoc_headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); - $assoc_result = drupal_http_request($op_endpoint, $assoc_headers, 'POST', $assoc_message); - if (isset($assoc_result->error)) { - return FALSE; - } - - $assoc_response = _openid_parse_message($assoc_result->data); - if (isset($assoc_response['mode']) && $assoc_response['mode'] == 'error') { - return FALSE; - } - - if ($assoc_response['session_type'] == 'DH-SHA1') { - $spub = _openid_dh_base64_to_long($assoc_response['dh_server_public']); - $enc_mac_key = base64_decode($assoc_response['enc_mac_key']); - $shared = bcpowmod($spub, $private, $mod); - $assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key)); - } - db_query("INSERT INTO {openid_association} (idp_endpoint_uri, session_type, assoc_handle, assoc_type, expires_in, mac_key, created) VALUES('%s', '%s', '%s', '%s', %d, '%s', %d)", - $op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], time()); - - $assoc_handle = $assoc_response['assoc_handle']; - } - - return $assoc_handle; -} - -/** - * Authenticate a user or attempt registration. - * - * @param $response Response values from the OpenID Provider. - */ -function openid_authentication($response) { - module_load_include('inc', 'openid'); - - $identity = $response['openid.claimed_id']; - - $account = user_external_load($identity); - if (isset($account->uid)) { - if (!variable_get('user_email_verification', TRUE) || $account->login) { - user_external_login($account, $_SESSION['openid']['user_login_values']); - } - else { - drupal_set_message(t('You must validate your email address for this account before logging in via OpenID')); - } - } - elseif (variable_get('user_register', 1)) { - // Register new user - $form_state['redirect'] = NULL; - // Only signed SREG keys are included as required by OpenID Simple - // Registration Extension 1.0, section 4. - $signed_keys = explode(',', $response['openid.signed']); - $form_state['values']['name'] = in_array('sreg.nickname', $signed_keys) ? $response['openid.sreg.nickname'] : ''; - $form_state['values']['mail'] = in_array('sreg.email', $signed_keys) ? $response['openid.sreg.email'] : ''; - $form_state['values']['pass'] = user_password(); - $form_state['values']['status'] = variable_get('user_register', 1) == 1; - $form_state['values']['response'] = $response; - $form_state['values']['auth_openid'] = $identity; - - if (empty($form_state['values']['name']) && empty($form_state['values']['mail'])) { - drupal_set_message(t('Please complete the registration by filling out the form below. If you already have an account, you can log in now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'warning'); - $success = FALSE; - } - else { - $form = drupal_retrieve_form('user_register', $form_state); - drupal_prepare_form('user_register', $form, $form_state); - drupal_validate_form('user_register', $form, $form_state); - $success = !form_get_errors(); - if (!$success) { - drupal_set_message(t('Account registration using the information provided by your OpenID provider failed due to the reasons listed below. Please complete the registration by filling out the form below. If you already have an account, you can log in now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'warning'); - // Append form validation errors below the above warning. - $messages = drupal_get_messages('error'); - foreach ($messages['error'] as $message) { - drupal_set_message( $message, 'error'); - } - } - } - if (!$success) { - // We were unable to register a valid new user, redirect to standard - // user/register and prefill with the values we received. - $_SESSION['openid']['values'] = $form_state['values']; - // We'll want to redirect back to the same place. - $destination = drupal_get_destination(); - unset($_REQUEST['destination']); - drupal_goto('user/register', $destination); - } - else { - unset($form_state['values']['response']); - $account = user_save('', $form_state['values']); - // Terminate if an error occured during user_save(). - if (!$account) { - drupal_set_message(t("Error saving user account."), 'error'); - drupal_goto(); - } - user_external_login($account); - } - drupal_redirect_form($form, $form_state['redirect']); - } - else { - drupal_set_message(t('Only site administrators can create new user accounts.'), 'error'); - } - drupal_goto(); -} - -function openid_association_request($public) { - module_load_include('inc', 'openid'); - - $request = array( - 'openid.ns' => OPENID_NS_2_0, - 'openid.mode' => 'associate', - 'openid.session_type' => 'DH-SHA1', - 'openid.assoc_type' => 'HMAC-SHA1' - ); - - if ($request['openid.session_type'] == 'DH-SHA1' || $request['openid.session_type'] == 'DH-SHA256') { - $cpub = _openid_dh_long_to_base64($public); - $request['openid.dh_consumer_public'] = $cpub; - } - - return $request; -} - -function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $version = 2) { - global $base_url; - - module_load_include('inc', 'openid'); - - $ns = ($version == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0; - $request = array( - 'openid.ns' => $ns, - 'openid.mode' => 'checkid_setup', - 'openid.identity' => $identity, - 'openid.claimed_id' => $claimed_id, - 'openid.assoc_handle' => $assoc_handle, - 'openid.return_to' => $return_to, - ); - - if ($version == 2) { - $request['openid.realm'] = $base_url . '/'; - } - else { - $request['openid.trust_root'] = $base_url . '/'; - } - - // Simple Registration - $request['openid.sreg.required'] = 'nickname,email'; - $request['openid.ns.sreg'] = "http://openid.net/extensions/sreg/1.1"; - - $request = array_merge($request, module_invoke_all('openid', 'request', $request)); - - return $request; -} - -/** - * Attempt to verify the response received from the OpenID Provider. - * - * @param $service - * Array describing the OpenID provider. - * @param $response - * Array of response values from the provider. - * - * @return boolean - */ -function openid_verify_assertion($service, $response) { - module_load_include('inc', 'openid'); - - // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.3 - // Check the Nonce to protect against replay attacks. - if (!openid_verify_assertion_nonce($service, $response)) { - return FALSE; - } - - // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 - // Verify the signatures. - $valid = FALSE; - $association = db_fetch_object(db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = '%s' AND assoc_handle = '%s'", $service['uri'], $response['openid.assoc_handle'])); - if ($association && isset($association->session_type)) { - // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2 - // Verification using an association. - $valid = openid_verify_assertion_signature($service, $association, $response); - } - else { - // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.3 - // Direct verification. - $request = $response; - $request['openid.mode'] = 'check_authentication'; - $message = _openid_create_message($request); - $headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); - $result = drupal_http_request($service['uri'], $headers, 'POST', _openid_encode_message($message)); - if (!isset($result->error)) { - $response = _openid_parse_message($result->data); - if (strtolower(trim($response['is_valid'])) == 'true') { - $valid = TRUE; - } - else { - $valid = FALSE; - } - } - } - return $valid; -} - -/** - * Verify the signature of the response received from the OpenID provider. - * - * @param $service - * Array describing the OpenID provider. - * @param $association - * Information on the association with the OpenID provider. - * @param $response - * Array of response values from the provider. - * - * @return - * TRUE if the signature is valid and covers all fields required to be signed. - * @see http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 - */ -function openid_verify_assertion_signature($service, $association, $response) { - if ($service['version'] == 2) { - // OpenID Authentication 2.0, section 10.1: - // These keys must always be signed. - $mandatory_keys = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle'); - if (isset($response['openid.claimed_id'])) { - // If present, these two keys must also be signed. According to the spec, - // they are either both present or both absent. - $mandatory_keys[] = 'claimed_id'; - $mandatory_keys[] = 'identity'; - } - } - else { - // OpenID Authentication 1.1. section 4.3.3. - $mandatory_keys = array('identity', 'return_to'); - } - - $keys_to_sign = explode(',', $response['openid.signed']); - - if (count(array_diff($mandatory_keys, $keys_to_sign)) > 0) { - return FALSE; - } - - return _openid_signature($association, $response, $keys_to_sign) == $response['openid.sig']; -} - -/** - * Verify that the nonce has not been used in earlier assertions from the same OpenID provider. - * - * @param $service - * Array describing the OpenID provider. - * @param $response - * Array of response values from the provider. - * - * @return - * TRUE if the nonce has not expired and has not been used earlier. - */ -function openid_verify_assertion_nonce($service, $response) { - if ($service['version'] != 2) { - return TRUE; - } - - if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/', $response['openid.response_nonce'], $matches)) { - list(, $year, $month, $day, $hour, $minutes, $seconds) = $matches; - $nonce_timestamp = gmmktime($hour, $minutes, $seconds, $month, $day, $year); - } - else { - watchdog('openid', 'Nonce from @endpoint rejected because it is not correctly formatted, nonce: @nonce.', array('@endpoint' => $service['uri'], '@nonce' => $response['openid.response_nonce']), WATCHDOG_WARNING); - return FALSE; - } - - // A nonce with a timestamp to far in the past or future will already have - // been removed and cannot be checked for single use anymore. - $time = time(); - $expiry = 900; - if ($nonce_timestamp <= $time - $expiry || $nonce_timestamp >= $time + $expiry) { - watchdog('openid', 'Nonce received from @endpoint is out of range (time difference: @intervals). Check possible clock skew.', array('@endpoint' => $service['uri'], '@interval' => $time - $nonce_timestamp), WATCHDOG_WARNING); - return FALSE; - } - - // Record that this nonce was used. - db_query("INSERT INTO {openid_nonce} (idp_endpoint_uri, nonce, expires) VALUES ('%s', '%s', %d)", $service['uri'], $response['openid.response_nonce'], $nonce_timestamp + $expiry); - - // Count the number of times this nonce was used. - $count_used = db_result(db_query("SELECT COUNT(*) FROM {openid_nonce} WHERE nonce = '%s' AND idp_endpoint_uri = '%s'", $response['openid.response_nonce'], $service['uri'])); - - if ($count_used == 1) { - return TRUE; - } - else { - watchdog('openid', 'Nonce replay attempt blocked from @ip, nonce: @nonce.', array('@ip' => ip_address(), '@nonce' => $response['openid.response_nonce']), WATCHDOG_CRITICAL); - return FALSE; - } -} - -/** - * Remove expired nonces from the database. - * - * Implementation of hook_cron(). - */ -function openid_cron() { - db_query("DELETE FROM {openid_nonce} WHERE expires < %d", time()); -} diff --git a/modules/openid/openid.pages.inc b/modules/openid/openid.pages.inc deleted file mode 100644 index 5ac1671..0000000 --- a/modules/openid/openid.pages.inc +++ /dev/null @@ -1,113 +0,0 @@ -name)); - drupal_add_css(drupal_get_path('module', 'openid') .'/openid.css', 'module'); - - // Check to see if we got a response - $result = openid_complete(); - if ($result['status'] == 'success') { - $identity = $result['openid.claimed_id']; - db_query("INSERT INTO {authmap} (uid, authname, module) VALUES (%d, '%s','openid')", $account->uid, $identity); - drupal_set_message(t('Successfully added %identity', array('%identity' => $identity))); - } - - $header = array(t('OpenID'), t('Operations')); - $rows = array(); - - $result = db_query("SELECT * FROM {authmap} WHERE module='openid' AND uid=%d", $account->uid); - while ($identity = db_fetch_object($result)) { - $rows[] = array(check_plain($identity->authname), l(t('Delete'), 'user/'. $account->uid .'/openid/delete/'. $identity->aid)); - } - - $output = theme('table', $header, $rows); - $output .= drupal_get_form('openid_user_add'); - return $output; -} - -/** - * Form builder; Add an OpenID identity. - * - * @ingroup forms - * @see openid_user_add_validate() - */ -function openid_user_add() { - $form['openid_identifier'] = array( - '#type' => 'textfield', - '#title' => t('OpenID'), - ); - $form['submit'] = array('#type' => 'submit', '#value' => t('Add an OpenID')); - return $form; -} - -function openid_user_add_validate($form, &$form_state) { - // Check for existing entries. - $claimed_id = _openid_normalize($form_state['values']['openid_identifier']); - if (db_result(db_query("SELECT authname FROM {authmap} WHERE authname='%s'", $claimed_id))) { - form_set_error('openid_identifier', t('That OpenID is already in use on this site.')); - } -} - -function openid_user_add_submit($form, &$form_state) { - $return_to = url('user/'. arg(1) .'/openid', array('absolute' => TRUE)); - openid_begin($form_state['values']['openid_identifier'], $return_to); -} - - -/** - * Present a confirmation form to delete the specified OpenID identity from the system. - * - * @ingroup forms - * @see openid_user_delete_form_submit() - */ -function openid_user_delete_form($form_state, $account, $aid = 0) { - $authname = db_result(db_query('SELECT authname FROM {authmap} WHERE uid = %d AND aid = %d', $account->uid, $aid)); - - $form = array(); - - $form['uid'] = array( - '#type' => 'value', - '#value' => $account->uid, - ); - - $form['aid'] = array( - '#type' => 'value', - '#value' => $aid, - ); - - return confirm_form($form, t('Are you sure you want to delete the OpenID %authname for %user?', array('%authname' => $authname, '%user' => $account->name)), 'user/'. $account->uid .'/openid'); -} - -function openid_user_delete_form_submit($form, &$form_state) { - db_query("DELETE FROM {authmap} WHERE uid = %d AND aid = %d AND module = 'openid'", $form_state['values']['uid'], $form_state['values']['aid']); - if (db_affected_rows()) { - drupal_set_message(t('OpenID deleted.')); - } - $form_state['redirect'] = 'user/'. $form_state['values']['uid'] .'/openid'; -} diff --git a/modules/openid/xrds.inc b/modules/openid/xrds.inc deleted file mode 100644 index 7810b3c..0000000 --- a/modules/openid/xrds.inc +++ /dev/null @@ -1,97 +0,0 @@ - 2 * variable_get('openid_xrds_maximum_tag_count', 30000)) { - return array(); - } - - xml_parse($parser, $xml); - xml_parser_free($parser); - - return $xrds_services; -} - -/** - * Parser callback functions - */ -function _xrds_element_start(&$parser, $name, $attribs) { - global $xrds_open_elements; - - $xrds_open_elements[] = _xrds_strip_namespace($name); -} - -function _xrds_element_end(&$parser, $name) { - global $xrds_open_elements, $xrds_services, $xrds_current_service; - - $name = _xrds_strip_namespace($name); - if ($name == 'SERVICE') { - if (in_array(OPENID_NS_2_0 .'/signon', $xrds_current_service['types']) || - in_array(OPENID_NS_2_0 .'/server', $xrds_current_service['types'])) { - $xrds_current_service['version'] = 2; - } - elseif (in_array(OPENID_NS_1_1, $xrds_current_service['types']) || - in_array(OPENID_NS_1_0, $xrds_current_service['types'])) { - $xrds_current_service['version'] = 1; - } - if (!empty($xrds_current_service['version'])) { - $xrds_services[] = $xrds_current_service; - } - $xrds_current_service = array(); - } - array_pop($xrds_open_elements); -} - -function _xrds_cdata(&$parser, $data) { - global $xrds_open_elements, $xrds_services, $xrds_current_service; - $path = strtoupper(implode('/', $xrds_open_elements)); - switch ($path) { - case 'XRDS/XRD/SERVICE/TYPE': - $xrds_current_service['types'][] = $data; - break; - case 'XRDS/XRD/SERVICE/URI': - $xrds_current_service['uri'] = $data; - break; - case 'XRDS/XRD/SERVICE/DELEGATE': - $xrds_current_service['delegate'] = $data; - break; - case 'XRDS/XRD/SERVICE/LOCALID': - $xrds_current_service['localid'] = $data; - break; - } -} - -function _xrds_strip_namespace($name) { - // Strip namespacing. - $pos = strrpos($name, ':'); - if ($pos !== FALSE) { - $name = substr($name, $pos + 1, strlen($name)); - } - - return $name; -} \ No newline at end of file diff --git a/modules/php/php.info b/modules/php/php.info deleted file mode 100644 index b37a1d9..0000000 --- a/modules/php/php.info +++ /dev/null @@ -1,11 +0,0 @@ -name = PHP filter -description = Allows embedded PHP code/snippets to be evaluated. -package = Core - optional -version = VERSION -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/php/php.install b/modules/php/php.install deleted file mode 100644 index b41d054..0000000 --- a/modules/php/php.install +++ /dev/null @@ -1,28 +0,0 @@ - l('PHP code', 'admin/settings/filters/'. $format)))); - } -} - -/** - * Implementation of hook_disable(). - */ -function php_disable() { - drupal_set_message(t('The PHP module has been disabled. Please note that any existing content that was using the PHP filter will now be visible in plain text. This might pose a security risk by exposing sensitive information, if any, used in the PHP code.')); -} diff --git a/modules/php/php.module b/modules/php/php.module deleted file mode 100644 index 0533d43..0000000 --- a/modules/php/php.module +++ /dev/null @@ -1,88 +0,0 @@ -'. t('The PHP filter adds the ability to include PHP code in posts. PHP is a general-purpose scripting language widely-used for web development; the content management system used by this website has been developed using PHP.') .''; - $output .= ''. t('Through the PHP filter, users with the proper permission may include custom PHP code within a page of the site. While this is a powerful and flexible feature if used by a trusted user with PHP experience, it is a significant and dangerous security risk in the hands of a malicious user. Even a trusted user may accidentally compromise the site by entering malformed or incorrect PHP code. Only the most trusted users should be granted permission to use the PHP filter, and all PHP code added through the PHP filter should be carefully examined before use.') .'
'; - $output .= ''. t('Drupal.org offers some example PHP snippets, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/handbook/customization/php-snippets'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for PHP module.', array('@php' => 'http://drupal.org/handbook/modules/php/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_filter_tips(). - */ -function php_filter_tips($delta, $format, $long = false) { - global $base_url; - if ($delta == 0) { - switch ($long) { - case 0: - return t('You may post PHP code. You should include <?php ?> tags.'); - case 1: - $output = ''. t('Custom PHP code may be embedded in some types of site content, including posts and blocks. While embedding PHP code inside a post or block is a powerful and flexible feature when used by a trusted user with PHP experience, it is a significant and dangerous security risk when used improperly. Even a small mistake when posting PHP code may accidentally compromise your site.') .'
'; - $output .= ''. t('If you are unfamiliar with PHP, SQL, or Drupal, avoid using custom PHP code within posts. Experimenting with PHP may corrupt your database, render your site inoperable, or significantly compromise security.') .'
'; - $output .= ''. t('Notes:') .'
'; - $output .= 'register_globals
is turned off. If you need to use forms, understand and use the functions in the Drupal Form API.', array('@formapi' => url('http://api.drupal.org/api/group/form_api/6'))) .'print
or return
statement in your code to output content.') .'template.php
file rather than embedding it directly into a post or block.') .''. t('A basic example: Creating a "Welcome" block that greets visitors with a simple message.') .'
'; - $output .= 'Add a custom block to your site, named "Welcome". With its input format set to "PHP code" (or another format supporting PHP input), add the following in the Block body:
--print t(\'Welcome visitor! Thank you for visiting.\'); -') .'
To display the name of a registered user, use this instead:
--global $user; -if ($user->uid) { - print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => $user->name)); -} -else { - print t(\'Welcome visitor! Thank you for visiting.\'); -} -') .'
'. t('Drupal.org offers some example PHP snippets, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/handbook/customization/php-snippets'))) .'
'; - return $output; - } - } -} - -/** - * Implementation of hook_filter(). Contains a basic PHP evaluator. - * - * Executes PHP code. Use with care. - */ -function php_filter($op, $delta = 0, $format = -1, $text = '') { - switch ($op) { - case 'list': - return array(0 => t('PHP evaluator')); - case 'no cache': - // No caching for the PHP evaluator. - return $delta == 0; - case 'description': - return t('Executes a piece of PHP code. The usage of this filter should be restricted to administrators only!'); - case 'process': - return drupal_eval($text); - default: - return $text; - } -} - - - diff --git a/modules/ping/ping.info b/modules/ping/ping.info deleted file mode 100644 index f840c08..0000000 --- a/modules/ping/ping.info +++ /dev/null @@ -1,11 +0,0 @@ -name = Ping -description = Alerts other sites when your site has been updated. -package = Core - optional -version = VERSION -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/ping/ping.module b/modules/ping/ping.module deleted file mode 100644 index 6553960..0000000 --- a/modules/ping/ping.module +++ /dev/null @@ -1,60 +0,0 @@ -'. t('The ping module is useful for notifying interested sites that your site has changed. It automatically sends notifications, or "pings", to the pingomatic service about new or updated content. In turn, pingomatic notifies other popular services, including weblogs.com, Technorati, blo.gs, BlogRolling, Feedster.com, and Moreover.', array('@external-http-pingomatic-com' => 'http://pingomatic.com/')) .''; - $output .= ''. t('The ping module requires a correctly configured cron maintenance task.', array('@cron' => url('admin/reports/status'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Ping module.', array('@ping' => 'http://drupal.org/handbook/modules/ping/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_cron(). - * - * Fire off notifications of updates to remote sites. - */ -function ping_cron() { - global $base_url; - - if (variable_get('site_name', 0)) { - $cron_last = variable_get('cron_last', time()); - // Query changed first since usually changed >= created. - if (db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND changed > %d', $cron_last)) || db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND created > %d', $cron_last))) { - _ping_notify(variable_get('site_name', ''), $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', 'Failed to notify pingomatic.com (site).', array(), WATCHDOG_WARNING); - } -} - - diff --git a/modules/poll/poll-bar-block.tpl.php b/modules/poll/poll-bar-block.tpl.php deleted file mode 100644 index d5fbaee..0000000 --- a/modules/poll/poll-bar-block.tpl.php +++ /dev/null @@ -1,25 +0,0 @@ - - - - -'. t('When creating a poll, enter the question being posed, as well as the potential choices (and beginning vote counts for each choice). The status and duration (length of time the poll remains active for new votes) can also be specified. Use the poll menu item to view all current polls. To vote in or view the results of a specific poll, click on the poll itself.', array('@poll' => url('poll'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Poll module.', array('@poll' => 'http://drupal.org/handbook/modules/poll/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_init(). - */ -function poll_init() { - drupal_add_css(drupal_get_path('module', 'poll') .'/poll.css'); -} - -/** - * Implementation of hook_theme() - */ -function poll_theme() { - return array( - 'poll_vote' => array( - 'template' => 'poll-vote', - 'arguments' => array('form' => NULL), - ), - 'poll_choices' => array( - 'arguments' => array('form' => NULL), - ), - 'poll_results' => array( - 'template' => 'poll-results', - 'arguments' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL), - ), - 'poll_bar' => array( - 'template' => 'poll-bar', - 'arguments' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL), - ), - ); -} - -/** - * Implementation of hook_perm(). - */ -function poll_perm() { - return array('create poll content', 'delete own poll content', 'delete any poll content', 'edit any poll content', 'edit own poll content', 'vote on polls', 'cancel own vote', 'inspect all votes'); -} - -/** - * Implementation of hook_access(). - */ -function poll_access($op, $node, $account) { - switch ($op) { - case 'create': - return user_access('create poll content', $account) ? TRUE : NULL; - case 'update': - return user_access('edit any poll content', $account) || (user_access('edit own poll content', $account) && ($node->uid == $account->uid)) ? TRUE : NULL; - case 'delete': - return user_access('delete any poll content', $account) || (user_access('delete own poll content', $account) && ($node->uid == $account->uid)) ? TRUE : NULL; - } -} - -/** - * Implementation of hook_menu(). - */ -function poll_menu() { - $items['poll'] = array( - 'title' => 'Polls', - 'page callback' => 'poll_page', - 'access arguments' => array('access content'), - 'type' => MENU_SUGGESTED_ITEM, - 'file' => 'poll.pages.inc', - ); - - $items['node/%node/votes'] = array( - 'title' => 'Votes', - 'page callback' => 'poll_votes', - 'page arguments' => array(1), - 'access callback' => '_poll_menu_access', - 'access arguments' => array(1, 'inspect all votes', FALSE), - 'weight' => 3, - 'type' => MENU_LOCAL_TASK, - 'file' => 'poll.pages.inc', - ); - - $items['node/%node/results'] = array( - 'title' => 'Results', - 'page callback' => 'poll_results', - 'page arguments' => array(1), - 'access callback' => '_poll_menu_access', - 'access arguments' => array(1, 'access content', TRUE), - 'weight' => 3, - 'type' => MENU_LOCAL_TASK, - 'file' => 'poll.pages.inc', - ); - - $items['poll/js'] = array( - 'title' => 'Javascript Choice Form', - 'page callback' => 'poll_choice_js', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Callback function to see if a node is acceptable for poll menu items. - */ -function _poll_menu_access($node, $perm, $inspect_allowvotes) { - return user_access($perm) && ($node->type == 'poll') && ($node->allowvotes || !$inspect_allowvotes); -} - -/** - * Implementation of hook_block(). - * - * Generates a block containing the latest poll. - */ -function poll_block($op = 'list', $delta = 0) { - if ($op == 'list') { - $blocks[0]['info'] = t('Most recent poll'); - return $blocks; - } - else if ($op == 'view' && user_access('access content')) { - // 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"); - $timestamp = db_result(db_query($sql)); - if ($timestamp) { - $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'status' => 1)); - - if ($poll->nid) { - $poll = poll_view($poll, TRUE, FALSE, TRUE); - } - } - $block['subject'] = t('Poll'); - $block['content'] = drupal_render($poll->content); - 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_node_info(). - */ -function poll_node_info() { - return array( - 'poll' => array( - 'name' => t('Poll'), - 'module' => 'poll', - 'description' => t('A poll is a question with a set of possible responses. A poll, once created, automatically provides a simple running count of the number of votes received for each response.'), - 'title_label' => t('Question'), - 'has_body' => FALSE, - ) - ); -} - -/** - * Implementation of hook_form(). - */ -function poll_form(&$node, $form_state) { - global $user; - - $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid); - - $type = node_get_types('type', $node); - - $form = array( - '#cache' => TRUE, - ); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => check_plain($type->title_label), - '#required' => TRUE, - '#default_value' => $node->title, - '#weight' => -5, - ); - - if (isset($form_state['choice_count'])) { - $choice_count = $form_state['choice_count']; - } - else { - $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice)); - } - - // Add a wrapper for the choices and more button. - $form['choice_wrapper'] = array( - '#tree' => FALSE, - '#weight' => -4, - '#prefix' => ''. t('The statistics module provides:') .'
'; - $output .= ''. t('Configuring the statistics module') .'
'; - $output .= ''. t('For more information, see the online handbook entry for Statistics module.', array('@statistics' => 'http://drupal.org/handbook/modules/statistics/')) .'
'; - return $output; - case 'admin/reports/settings': - return ''. t('Settings for the statistical information that Drupal will keep about the site. See site statistics for the actual information.', array('@statistics' => url('admin/reports/hits'))) .'
'; - case 'admin/reports/hits': - return ''. t("This page displays the site's most recent hits.") .'
'; - case 'admin/reports/referrers': - return ''. t('This page displays all external referrers, or external references to your website.') .'
'; - case 'admin/reports/visitors': - return ''. t("When you ban a visitor, you prevent the visitor's IP address from accessing your site. Unlike blocking a user, banning a visitor works even for anonymous users. This is most commonly used to block resource-intensive bots or web crawlers.") .'
'; - } -} - -/** - * Implementation of hook_exit(). - * - * This is where statistics are gathered on page accesses. - */ -function statistics_exit() { - global $user, $recent_activity; - - drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH); - - if (variable_get('statistics_count_content_views', 0)) { - // We are counting content views. - if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') { - // A node has been viewed, so update the node's counters. - db_query('UPDATE {node_counter} SET daycount = daycount + 1, totalcount = totalcount + 1, timestamp = %d WHERE nid = %d', time(), arg(1)); - // If we affected 0 rows, this is the first time viewing the node. - if (!db_affected_rows()) { - // We must create a new row to store counters for the new node. - db_query('INSERT INTO {node_counter} (nid, daycount, totalcount, timestamp) VALUES (%d, 1, 1, %d)', arg(1), time()); - } - } - } - if ((variable_get('statistics_enable_access_log', 0)) && (module_invoke('throttle', 'status') == 0)) { - // Log this page access. - db_query("INSERT INTO {accesslog} (title, path, url, hostname, uid, sid, timer, timestamp) values('%s', '%s', '%s', '%s', %d, '%s', %d, %d)", strip_tags(drupal_get_title()), $_GET['q'], referer_uri(), ip_address(), $user->uid, session_id(), timer_read('page'), time()); - } -} - -/** - * Implementation of hook_perm(). - */ -function statistics_perm() { - return array('access statistics', 'view post access counter'); -} - -/** - * Implementation of hook_link(). - */ -function statistics_link($type, $node = NULL, $teaser = FALSE) { - global $id; - $links = array(); - - if ($type == 'node' && user_access('view post access counter')) { - $statistics = statistics_get($node->nid); - if ($statistics) { - $links['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads'); - } - } - - return $links; -} - -/** - * Implementation of hook_menu(). - */ -function statistics_menu() { - $items['admin/reports/hits'] = array( - 'title' => 'Recent hits', - 'description' => 'View pages that have recently been visited.', - 'page callback' => 'statistics_recent_hits', - 'access arguments' => array('access statistics'), - 'file' => 'statistics.admin.inc', - ); - $items['admin/reports/pages'] = array( - 'title' => 'Top pages', - 'description' => 'View pages that have been hit frequently.', - 'page callback' => 'statistics_top_pages', - 'access arguments' => array('access statistics'), - 'weight' => 1, - 'file' => 'statistics.admin.inc', - ); - $items['admin/reports/visitors'] = array( - 'title' => 'Top visitors', - 'description' => 'View visitors that hit many pages.', - 'page callback' => 'statistics_top_visitors', - 'access arguments' => array('access statistics'), - 'weight' => 2, - 'file' => 'statistics.admin.inc', - ); - $items['admin/reports/referrers'] = array( - 'title' => 'Top referrers', - 'description' => 'View top referrers.', - 'page callback' => 'statistics_top_referrers', - 'access arguments' => array('access statistics'), - 'file' => 'statistics.admin.inc', - ); - $items['admin/reports/access/%'] = array( - 'title' => 'Details', - 'description' => 'View access log.', - 'page callback' => 'statistics_access_log', - 'page arguments' => array(3), - 'access arguments' => array('access statistics'), - 'type' => MENU_CALLBACK, - 'file' => 'statistics.admin.inc', - ); - $items['admin/reports/settings'] = array( - 'title' => 'Access log settings', - 'description' => 'Control details about what and how your site logs.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('statistics_access_logging_settings'), - 'access arguments' => array('administer site configuration'), - 'type' => MENU_NORMAL_ITEM, - 'weight' => 3, - 'file' => 'statistics.admin.inc', - ); - $items['user/%user/track/navigation'] = array( - 'title' => 'Track page visits', - 'page callback' => 'statistics_user_tracker', - 'access callback' => 'user_access', - 'access arguments' => array('access statistics'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 2, - 'file' => 'statistics.pages.inc', - ); - $items['node/%node/track'] = array( - 'title' => 'Track', - 'page callback' => 'statistics_node_tracker', - 'access callback' => 'user_access', - 'access arguments' => array('access statistics'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 2, - 'file' => 'statistics.pages.inc', - ); - - return $items; -} - -/** - * Implementation of hook_user(). - */ -function statistics_user($op, &$edit, &$user) { - if ($op == 'delete') { - db_query('UPDATE {accesslog} SET uid = 0 WHERE uid = %d', $user->uid); - } -} - -/** - * Implementation of hook_cron(). - */ -function statistics_cron() { - $statistics_timestamp = variable_get('statistics_day_timestamp', ''); - - if ((time() - $statistics_timestamp) >= 86400) { - // Reset day counts. - db_query('UPDATE {node_counter} SET daycount = 0'); - variable_set('statistics_day_timestamp', time()); - } - - // Clean up expired access logs. - if (variable_get('statistics_flush_accesslog_timer', 259200) > 0) { - db_query('DELETE FROM {accesslog} WHERE timestamp < %d', time() - variable_get('statistics_flush_accesslog_timer', 259200)); - } -} - -/** - * Returns all time or today top or last viewed node(s). - * - * @param $dbfield - * one of - * - 'totalcount': top viewed content of all time. - * - 'daycount': top viewed content for today. - * - 'timestamp': last viewed node. - * - * @param $dbrows - * number of rows to be returned. - * - * @return - * A query result containing n.nid, n.title, u.uid, u.name of the selected node(s) - * or FALSE if the query could not be executed correctly. - */ -function statistics_title_list($dbfield, $dbrows) { - if (in_array($dbfield, array('totalcount', 'daycount', 'timestamp'))) { - return db_query_range(db_rewrite_sql("SELECT n.nid, n.title, u.uid, u.name FROM {node} n INNER JOIN {node_counter} s ON n.nid = s.nid INNER JOIN {users} u ON n.uid = u.uid WHERE s.". $dbfield ." != 0 AND n.status = 1 ORDER BY s.". $dbfield ." DESC"), 0, $dbrows); - } - return FALSE; -} - - -/** - * Retrieves a node's "view statistics". - * - * @param $nid - * node ID - * - * @return - * An array with three entries: [0]=totalcount, [1]=daycount, [2]=timestamp - * - totalcount: count of the total number of times that node has been viewed. - * - daycount: count of the total number of times that node has been viewed "today". - * For the daycount to be reset, cron must be enabled. - * - timestamp: timestamp of when that node was last viewed. - */ -function statistics_get($nid) { - - if ($nid > 0) { - // Retrieve an array with both totalcount and daycount. - $statistics = db_fetch_array(db_query('SELECT totalcount, daycount, timestamp FROM {node_counter} WHERE nid = %d', $nid)); - } - - return $statistics; -} - -/** - * Implementation of hook_block(). - */ -function statistics_block($op = 'list', $delta = 0, $edit = array()) { - switch ($op) { - case 'list': - if (variable_get('statistics_count_content_views', 0)) { - $blocks[0]['info'] = t('Popular content'); - // Too dynamic to cache. - $blocks[0]['cache'] = BLOCK_NO_CACHE; - return $blocks; - } - break; - - case 'configure': - // Popular content block settings - $numbers = array('0' => t('Disabled')) + drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40)); - $form['statistics_block_top_day_num'] = array('#type' => 'select', '#title' => t("Number of day's top views to display"), '#default_value' => variable_get('statistics_block_top_day_num', 0), '#options' => $numbers, '#description' => t('How many content items to display in "day" list.')); - $form['statistics_block_top_all_num'] = array('#type' => 'select', '#title' => t('Number of all time views to display'), '#default_value' => variable_get('statistics_block_top_all_num', 0), '#options' => $numbers, '#description' => t('How many content items to display in "all time" list.')); - $form['statistics_block_top_last_num'] = array('#type' => 'select', '#title' => t('Number of most recent views to display'), '#default_value' => variable_get('statistics_block_top_last_num', 0), '#options' => $numbers, '#description' => t('How many content items to display in "recently viewed" list.')); - return $form; - - case 'save': - variable_set('statistics_block_top_day_num', $edit['statistics_block_top_day_num']); - variable_set('statistics_block_top_all_num', $edit['statistics_block_top_all_num']); - variable_set('statistics_block_top_last_num', $edit['statistics_block_top_last_num']); - break; - - case 'view': - if (user_access('access content')) { - $content = array(); - - $daytop = variable_get('statistics_block_top_day_num', 0); - if ($daytop && ($result = statistics_title_list('daycount', $daytop)) && ($node_title_list = node_title_list($result, t("Today's:")))) { - $content[] = $node_title_list; - } - - $alltimetop = variable_get('statistics_block_top_all_num', 0); - if ($alltimetop && ($result = statistics_title_list('totalcount', $alltimetop)) && ($node_title_list = node_title_list($result, t('All time:')))) { - $content[] = $node_title_list; - } - - $lasttop = variable_get('statistics_block_top_last_num', 0); - if ($lasttop && ($result = statistics_title_list('timestamp', $lasttop)) && ($node_title_list = node_title_list($result, t('Last viewed:')))) { - $content[] = $node_title_list; - } - - if (count($content)) { - $block['content'] = implode(''. t('The throttle module provides a congestion control mechanism that automatically adjusts to a surge in incoming traffic. If your site is referenced by a popular website, or experiences a "Denial of Service" (DoS) attack, your webserver might become overwhelmed. The throttle mechanism is utilized by modules to temporarily disable CPU-intensive functionality, increasing performance. For instance, via the throttle module, modules may choose to disable resource-intensive blocks or the code within the site theme may temporarily disable user pictures in posts.') .'
'; - $output .= ''. t('The congestion control throttle can be automatically enabled when the number of anonymous or authenticated users currently visiting the site exceeds a specified threshold.') .'
'; - $output .= ''. t('For more information, see the online handbook entry for Throttle module.', array('@throttle' => 'http://drupal.org/handbook/modules/throttle/')) .'
'; - return $output; - case 'admin/settings/throttle': - return ''. t('The throttle module provides a congestion control mechanism that automatically adjusts to a surge in incoming traffic. If your site is referenced by a popular website, or experiences a "Denial of Service" (DoS) attack, your webserver might become overwhelmed. The throttle mechanism is utilized by modules to temporarily disable CPU-intensive functionality, increasing performance.') .'
'; - } -} diff --git a/modules/tracker/tracker.css b/modules/tracker/tracker.css deleted file mode 100644 index 138be88..0000000 --- a/modules/tracker/tracker.css +++ /dev/null @@ -1,7 +0,0 @@ - -#tracker td.replies { - text-align: center; -} -#tracker table { - width: 100%; -} diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info deleted file mode 100644 index a98b111..0000000 --- a/modules/tracker/tracker.info +++ /dev/null @@ -1,12 +0,0 @@ -name = Tracker -description = Enables tracking of recent posts for users. -dependencies[] = comment -package = Core - optional -version = VERSION -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/tracker/tracker.module b/modules/tracker/tracker.module deleted file mode 100644 index 18726c0..0000000 --- a/modules/tracker/tracker.module +++ /dev/null @@ -1,74 +0,0 @@ -'. t('The tracker module displays the most recently added or updated content on your site, and provides user-level tracking to follow the contributions of particular authors.') .''; - $output .= ''. t("The Recent posts page is available via a link in the navigation menu block and displays new and recently-updated content (including the content type, the title, the author's name, number of comments, and time of last update) in reverse chronological order. Posts are marked updated when changes occur in the text, or when new comments are added. To use the tracker module to follow a specific user's contributions, select the Track tab from the user's profile page.") .'
'; - $output .= ''. t('For more information, see the online handbook entry for Tracker module.', array('@tracker' => 'http://drupal.org/handbook/modules/tracker/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_menu(). - */ -function tracker_menu() { - $items['tracker'] = array( - 'title' => 'Recent posts', - 'page callback' => 'tracker_page', - 'access arguments' => array('access content'), - 'weight' => 1, - 'file' => 'tracker.pages.inc', - ); - $items['tracker/all'] = array( - 'title' => 'All recent posts', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['tracker/%user_uid_optional'] = array( - 'title' => 'My recent posts', - 'access callback' => '_tracker_myrecent_access', - 'access arguments' => array(1), - 'page arguments' => array(1), - 'type' => MENU_LOCAL_TASK, - ); - - $items['user/%user/track'] = array( - 'title' => 'Track', - 'page callback' => 'tracker_page', - 'page arguments' => array(1, TRUE), - 'access callback' => '_tracker_user_access', - 'access arguments' => array(1), - 'type' => MENU_LOCAL_TASK, - 'file' => 'tracker.pages.inc', - ); - $items['user/%user/track/posts'] = array( - 'title' => 'Track posts', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - return $items; -} - -/** - * Access callback for tracker/%user_uid_optional - */ -function _tracker_myrecent_access($account) { - // This path is only allowed for authenticated users looking at their own posts. - return $account->uid && ($GLOBALS['user']->uid == $account->uid) && user_access('access content'); -} - -/** - * Access callback for user/%user/track - */ -function _tracker_user_access($account) { - return user_view_access($account) && user_access('access content'); -} - diff --git a/modules/tracker/tracker.pages.inc b/modules/tracker/tracker.pages.inc deleted file mode 100644 index 64c9383..0000000 --- a/modules/tracker/tracker.pages.inc +++ /dev/null @@ -1,72 +0,0 @@ -name)); - } - // TODO: These queries are very expensive, see http://drupal.org/node/105639 - $sql = 'SELECT DISTINCT(n.nid), n.title, n.type, n.changed, n.uid, u.name, GREATEST(n.changed, l.last_comment_timestamp) AS last_updated, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {users} u ON n.uid = u.uid LEFT JOIN {comments} c ON n.nid = c.nid AND (c.status = %d OR c.status IS NULL) WHERE n.status = 1 AND (n.uid = %d OR c.uid = %d) ORDER BY last_updated DESC'; - $sql = db_rewrite_sql($sql); - $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n LEFT JOIN {comments} c ON n.nid = c.nid AND (c.status = %d OR c.status IS NULL) WHERE n.status = 1 AND (n.uid = %d OR c.uid = %d)'; - $sql_count = db_rewrite_sql($sql_count); - $result = pager_query($sql, 25, 0, $sql_count, COMMENT_PUBLISHED, $account->uid, $account->uid); - } - else { - $sql = 'SELECT DISTINCT(n.nid), n.title, n.type, n.changed, n.uid, u.name, GREATEST(n.changed, l.last_comment_timestamp) AS last_updated, l.comment_count FROM {node} n INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 ORDER BY last_updated DESC'; - $sql = db_rewrite_sql($sql); - $sql_count = 'SELECT COUNT(n.nid) FROM {node} n WHERE n.status = 1'; - $sql_count = db_rewrite_sql($sql_count); - $result = pager_query($sql, 25, 0, $sql_count); - } - - $rows = array(); - while ($node = db_fetch_object($result)) { - // Determine the number of comments: - $comments = 0; - if ($node->comment_count) { - $comments = $node->comment_count; - - if ($new = comment_num_new($node->nid)) { - $comments .= ''. t('Below you can assign actions to run when certain comment-related triggers happen. For example, you could promote a post to the front page when a comment is added.') .'
'; - case 'admin/build/trigger/node': - return $explanation .''. t('Below you can assign actions to run when certain content-related triggers happen. For example, you could send an e-mail to an administrator when a post is created or updated.') .'
'; - case 'admin/build/trigger/cron': - return $explanation .''. t('Below you can assign actions to run during each pass of a cron maintenance task.', array('@cron' => url('admin/reports/status'))) .'
'; - case 'admin/build/trigger/taxonomy': - return $explanation .''. t('Below you can assign actions to run when certain taxonomy-related triggers happen. For example, you could send an e-mail to an administrator when a term is deleted.') .'
'; - case 'admin/build/trigger/user': - return $explanation .''. t("Below you can assign actions to run when certain user-related triggers happen. For example, you could send an e-mail to an administrator when a user account is deleted.") .'
'; - case 'admin/help#trigger': - $output = ''. t('The Trigger module provides the ability to trigger actions upon system events, such as when new content is added or when a user logs in.', array('@actions' => url('admin/settings/actions'))) .'
'; - $output .= ''. t('The combination of actions and triggers can perform many useful tasks, such as e-mailing an administrator if a user account is deleted, or automatically unpublishing comments that contain certain words. By default, there are five "contexts" of events (Comments, Content, Cron, Taxonomy, and Users), but more may be added by additional modules.') .'
'; - $output .= ''. t('For more information, see the online handbook entry for Trigger module.', array('@trigger' => 'http://drupal.org/handbook/modules/trigger/')) .'
'; - return $output; - } -} - -/** - * Implementation of hook_menu(). - */ -function trigger_menu() { - $items['admin/build/trigger'] = array( - 'title' => 'Triggers', - 'description' => 'Tell Drupal when to execute actions.', - 'page callback' => 'trigger_assign', - 'access callback' => 'trigger_access_check', - 'access arguments' => array('node'), - 'file' => 'trigger.admin.inc', - ); - // We don't use a menu wildcard here because these are tabs, - // not invisible items. - $items['admin/build/trigger/node'] = array( - 'title' => 'Content', - 'page callback' => 'trigger_assign', - 'page arguments' => array('node'), - 'access callback' => 'trigger_access_check', - 'access arguments' => array('node'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - $items['admin/build/trigger/user'] = array( - 'title' => 'Users', - 'page callback' => 'trigger_assign', - 'page arguments' => array('user'), - 'access callback' => 'trigger_access_check', - 'access arguments' => array('user'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - $items['admin/build/trigger/comment'] = array( - 'title' => 'Comments', - 'page callback' => 'trigger_assign', - 'page arguments' => array('comment'), - 'access callback' => 'trigger_access_check', - 'access arguments' => array('comment'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - $items['admin/build/trigger/taxonomy'] = array( - 'title' => 'Taxonomy', - 'page callback' => 'trigger_assign', - 'page arguments' => array('taxonomy'), - 'access callback' => 'trigger_access_check', - 'access arguments' => array('taxonomy'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - $items['admin/build/trigger/cron'] = array( - 'title' => 'Cron', - 'page callback' => 'trigger_assign', - 'page arguments' => array('cron'), - 'access arguments' => array('administer actions'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - - // We want contributed modules to be able to describe - // their hooks and have actions assignable to them. - $hooks = module_invoke_all('hook_info'); - foreach ($hooks as $module => $hook) { - // We've already done these. - if (in_array($module, array('node', 'comment', 'user', 'system', 'taxonomy'))) { - continue; - } - $info = db_result(db_query("SELECT info FROM {system} WHERE name = '%s'", $module)); - $info = unserialize($info); - $nice_name = $info['name']; - $items["admin/build/trigger/$module"] = array( - 'title' => $nice_name, - 'page callback' => 'trigger_assign', - 'page arguments' => array($module), - 'access callback' => 'trigger_access_check', - 'access arguments' => array($module), - 'type' => MENU_LOCAL_TASK, - 'file' => 'trigger.admin.inc', - ); - } - $items['admin/build/trigger/unassign'] = array( - 'title' => 'Unassign', - 'description' => 'Unassign an action from a trigger.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('trigger_unassign'), - 'access arguments' => array('administer actions'), - 'type' => MENU_CALLBACK, - 'file' => 'trigger.admin.inc', - ); - - return $items; -} - -/** - * Access callback for menu system. - */ -function trigger_access_check($module) { - return (module_exists($module) && user_access('administer actions')); -} - -/** - * Get the aids of actions to be executed for a hook-op combination. - * - * @param $hook - * The name of the hook being fired. - * @param $op - * The name of the operation being executed. Defaults to an empty string - * because some hooks (e.g., hook_cron()) do not have operations. - * @return - * An array of action IDs. - */ -function _trigger_get_hook_aids($hook, $op = '') { - $aids = array(); - $result = db_query("SELECT aa.aid, a.type FROM {trigger_assignments} aa LEFT JOIN {actions} a ON aa.aid = a.aid WHERE aa.hook = '%s' AND aa.op = '%s' ORDER BY weight", $hook, $op); - while ($action = db_fetch_object($result)) { - $aids[$action->aid]['type'] = $action->type; - } - return $aids; -} - -/** - * Implementation of hook_theme(). - */ -function trigger_theme() { - return array( - 'trigger_display' => array( - 'arguments' => array('element'), - 'file' => 'trigger.admin.inc', - ), - ); -} - -/** - * Implementation of hook_forms(). We reuse code by using the - * same assignment form definition for each node-op combination. - */ -function trigger_forms() { - $hooks = module_invoke_all('hook_info'); - foreach ($hooks as $module => $info) { - foreach ($hooks[$module] as $hook => $ops) { - foreach ($ops as $op => $description) { - $forms['trigger_'. $hook .'_'. $op .'_assign_form'] = array('callback' => 'trigger_assign_form'); - } - } - } - - return $forms; -} - -/** - * When an action is called in a context that does not match its type, - * the object that the action expects must be retrieved. For example, when - * an action that works on users is called during the node hook, the - * user object is not available since the node hook doesn't pass it. - * So here we load the object the action expects. - * - * @param $type - * The type of action that is about to be called. - * @param $node - * The node that was passed via the nodeapi hook. - * @return - * The object expected by the action that is about to be called. - */ -function _trigger_normalize_node_context($type, $node) { - switch ($type) { - // If an action that works on comments is being called in a node context, - // the action is expecting a comment object. But we do not know which comment - // to give it. The first? The most recent? All of them? So comment actions - // in a node context are not supported. - - // An action that works on users is being called in a node context. - // Load the user object of the node's author. - case 'user': - return user_load(array('uid' => $node->uid)); - } -} - -/** - * Implementation of hook_nodeapi(). - */ -function trigger_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { - // Keep objects for reuse so that changes actions make to objects can persist. - static $objects; - // Prevent recursion by tracking which operations have already been called. - static $recursion; - // Support a subset of operations. - if (!in_array($op, array('view', 'update', 'presave', 'insert', 'delete')) || isset($recursion[$op])) { - return; - } - $recursion[$op] = TRUE; - - $aids = _trigger_get_hook_aids('nodeapi', $op); - if (!$aids) { - return; - } - $context = array( - 'hook' => 'nodeapi', - 'op' => $op, - ); - - // We need to get the expected object if the action's type is not 'node'. - // We keep the object in $objects so we can reuse it if we have multiple actions - // that make changes to an object. - foreach ($aids as $aid => $action_info) { - if ($action_info['type'] != 'node') { - if (!isset($objects[$action_info['type']])) { - $objects[$action_info['type']] = _trigger_normalize_node_context($action_info['type'], $node); - } - // Since we know about the node, we pass that info along to the action. - $context['node'] = $node; - $result = actions_do($aid, $objects[$action_info['type']], $context, $a3, $a4); - } - else { - actions_do($aid, $node, $context, $a3, $a4); - } - } -} - -/** - * When an action is called in a context that does not match its type, - * the object that the action expects must be retrieved. For example, when - * an action that works on nodes is called during the comment hook, the - * node object is not available since the comment hook doesn't pass it. - * So here we load the object the action expects. - * - * @param $type - * The type of action that is about to be called. - * @param $comment - * The comment that was passed via the comment hook. - * @return - * The object expected by the action that is about to be called. - */ -function _trigger_normalize_comment_context($type, $comment) { - switch ($type) { - // An action that works with nodes is being called in a comment context. - case 'node': - return node_load(is_array($comment) ? $comment['nid'] : $comment->nid); - - // An action that works on users is being called in a comment context. - case 'user': - return user_load(array('uid' => is_array($comment) ? $comment['uid'] : $comment->uid)); - } -} - -/** - * Implementation of hook_comment(). - */ -function trigger_comment($a1, $op) { - // Keep objects for reuse so that changes actions make to objects can persist. - static $objects; - // We support a subset of operations. - if (!in_array($op, array('insert', 'update', 'delete', 'view'))) { - return; - } - $aids = _trigger_get_hook_aids('comment', $op); - $context = array( - 'hook' => 'comment', - 'op' => $op, - ); - // We need to get the expected object if the action's type is not 'comment'. - // We keep the object in $objects so we can reuse it if we have multiple actions - // that make changes to an object. - foreach ($aids as $aid => $action_info) { - if ($action_info['type'] != 'comment') { - if (!isset($objects[$action_info['type']])) { - $objects[$action_info['type']] = _trigger_normalize_comment_context($action_info['type'], $a1); - } - // Since we know about the comment, we pass it along to the action - // in case it wants to peek at it. - $context['comment'] = (object) $a1; - actions_do($aid, $objects[$action_info['type']], $context); - } - else { - $comment = (object) $a1; - actions_do($aid, $comment, $context); - } - } -} - -/** - * Implementation of hook_cron(). - */ -function trigger_cron() { - $aids = _trigger_get_hook_aids('cron', 'run'); - $context = array( - 'hook' => 'cron', - 'op' => 'run', - ); - // Cron does not act on any specific object. - $object = NULL; - actions_do(array_keys($aids), $object, $context); -} - -/** - * When an action is called in a context that does not match its type, - * the object that the action expects must be retrieved. For example, when - * an action that works on nodes is called during the user hook, the - * node object is not available since the user hook doesn't pass it. - * So here we load the object the action expects. - * - * @param $type - * The type of action that is about to be called. - * @param $account - * The account object that was passed via the user hook. - * @return - * The object expected by the action that is about to be called. - */ -function _trigger_normalize_user_context($type, $account) { - switch ($type) { - // If an action that works on comments is being called in a user context, - // the action is expecting a comment object. But we have no way of - // determining the appropriate comment object to pass. So comment - // actions in a user context are not supported. - - // An action that works with nodes is being called in a user context. - // If a single node is being viewed, return the node. - case 'node': - // If we are viewing an individual node, return the node. - if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) { - return node_load(array('nid' => arg(1))); - } - } -} - -/** - * Implementation of hook_user(). - */ -function trigger_user($op, &$edit, &$account, $category = NULL) { - // Keep objects for reuse so that changes actions make to objects can persist. - static $objects; - // We support a subset of operations. - if (!in_array($op, array('login', 'logout', 'insert', 'update', 'delete', 'view'))) { - return; - } - $aids = _trigger_get_hook_aids('user', $op); - $context = array( - 'hook' => 'user', - 'op' => $op, - 'form_values' => &$edit, - ); - foreach ($aids as $aid => $action_info) { - if ($action_info['type'] != 'user') { - if (!isset($objects[$action_info['type']])) { - $objects[$action_info['type']] = _trigger_normalize_user_context($action_info['type'], $account); - } - $context['account'] = $account; - actions_do($aid, $objects[$action_info['type']], $context); - } - else { - actions_do($aid, $account, $context, $category); - } - } -} - -/** - * Implementation of hook_taxonomy(). - */ -function trigger_taxonomy($op, $type, $array) { - if ($type != 'term') { - return; - } - $aids = _trigger_get_hook_aids('taxonomy', $op); - $context = array( - 'hook' => 'taxonomy', - 'op' => $op - ); - foreach ($aids as $aid => $action_info) { - $taxonomy_object = (object) $array; - actions_do($aid, $taxonomy_object, $context); - } -} - -/** - * Often we generate a select field of all actions. This function - * generates the options for that select. - * - * @param $type - * One of 'node', 'user', 'comment'. - * @return - * Array keyed by action ID. - */ -function trigger_options($type = 'all') { - $options = array(t('Choose an action')); - foreach (actions_actions_map(actions_get_all_actions()) as $aid => $action) { - $options[$action['type']][$aid] = $action['description']; - } - - if ($type == 'all') { - return $options; - } - else { - return $options[$type]; - } -} - -/** - * Implementation of hook_actions_delete(). - * - * Remove all trigger entries for the given action, when deleted. - */ -function trigger_actions_delete($aid) { - db_query("DELETE FROM {trigger_assignments} WHERE aid = '%s'", $aid); -} diff --git a/modules/update/update-rtl.css b/modules/update/update-rtl.css deleted file mode 100644 index 5fc83d1..0000000 --- a/modules/update/update-rtl.css +++ /dev/null @@ -1,31 +0,0 @@ - -.update .project { - padding-right: .25em; -} - -.update .version-status { - float: left; - padding-left: 10px; -} - -.update .version-status .icon { - padding-right: .5em; -} - -.update table.version .version-title { - padding-left: 1em; -} - -.update table.version .version-details { - padding-left: .5em; - direction: ltr; -} - -.update table.version .version-links { - text-align: left; - padding-left: 1em; -} - -.update .check-manually { - padding-right: 1em; -} diff --git a/modules/update/update.compare.inc b/modules/update/update.compare.inc deleted file mode 100644 index f52f022..0000000 --- a/modules/update/update.compare.inc +++ /dev/null @@ -1,701 +0,0 @@ -status && !empty($file->sub_themes)) { - foreach ($file->sub_themes as $key => $name) { - // Build a list of enabled sub-themes. - if ($list[$key]->status) { - $file->enabled_sub_themes[$key] = $name; - } - } - // If there are no enabled subthemes, we should ingore this theme and go - // on to the next one. - if (empty($file->enabled_sub_themes)) { - continue; - } - } - elseif (empty($file->status)) { - // Skip disabled modules or themes. - continue; - } - - // Skip if the .info file is broken. - if (empty($file->info)) { - continue; - } - - // If the .info doesn't define the 'project', try to figure it out. - if (!isset($file->info['project'])) { - $file->info['project'] = update_get_project_name($file); - } - - // If we still don't know the 'project', give up. - if (empty($file->info['project'])) { - continue; - } - - // If we don't already know it, grab the change time on the .info file - // itself. Note: we need to use the ctime, not the mtime (modification - // time) since many (all?) tar implementations will go out of their way to - // set the mtime on the files it creates to the timestamps recorded in the - // tarball. We want to see the last time the file was changed on disk, - // which is left alone by tar and correctly set to the time the .info file - // was unpacked. - if (!isset($file->info['_info_file_ctime'])) { - $info_filename = dirname($file->filename) .'/'. $file->name .'.info'; - $file->info['_info_file_ctime'] = filectime($info_filename); - } - - if (!isset($file->info['datestamp'])) { - $file->info['datestamp'] = 0; - } - - $project_name = $file->info['project']; - - // Add a list of sub-themes that "depend on" the project and a list of base - // themes that are "required by" the project. - if ($project_name == 'drupal') { - // Drupal core is always required, so this extra info would be noise. - $sub_themes = array(); - $base_themes = array(); - } - else { - // Add list of enabled sub-themes. - $sub_themes = !empty($file->enabled_sub_themes) ? $file->enabled_sub_themes : array(); - // Add list of base themes. - $base_themes = !empty($file->base_themes) ? $file->base_themes : array(); - } - - if (!isset($projects[$project_name])) { - // Only process this if we haven't done this project, since a single - // project can have multiple modules or themes. - $projects[$project_name] = array( - 'name' => $project_name, - // Only save attributes from the .info file we care about so we do not - // bloat our RAM usage needlessly. - 'info' => update_filter_project_info($file->info), - 'datestamp' => $file->info['datestamp'], - 'includes' => array($file->name => $file->info['name']), - 'project_type' => $project_name == 'drupal' ? 'core' : $project_type, - 'sub_themes' => $sub_themes, - 'base_themes' => $base_themes, - ); - } - else { - $projects[$project_name]['includes'][$file->name] = $file->info['name']; - $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']); - $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']); - $projects[$project_name]['sub_themes'] = array_merge($projects[$project_name]['sub_themes'], $sub_themes); - $projects[$project_name]['base_themes'] = array_merge($projects[$project_name]['base_themes'], $base_themes); - } - } -} - -/** - * Given a $file object (as returned by system_get_files_database()), figure - * out what project it belongs to. - * - * @see system_get_files_database() - */ -function update_get_project_name($file) { - $project_name = ''; - if (isset($file->info['project'])) { - $project_name = $file->info['project']; - } - elseif (isset($file->info['package']) && (strpos($file->info['package'], 'Core -') !== FALSE)) { - $project_name = 'drupal'; - } - elseif (in_array($file->name, array('bluemarine', 'chameleon', 'garland', 'marvin', 'pushbutton'))) { - // Unfortunately, there's no way to tell if a theme is part of core, - // so we must hard-code a list here. - $project_name = 'drupal'; - } - return $project_name; -} - -/** - * Process the list of projects on the system to figure out the currently - * installed versions, and other information that is required before we can - * compare against the available releases to produce the status report. - * - * @param $projects - * Array of project information from update_get_projects(). - */ -function update_process_project_info(&$projects) { - foreach ($projects as $key => $project) { - // Assume an official release until we see otherwise. - $install_type = 'official'; - - $info = $project['info']; - - if (isset($info['version'])) { - // Check for development snapshots - if (preg_match('@(dev|HEAD)@', $info['version'])) { - $install_type = 'dev'; - } - - // Figure out what the currently installed major version is. We need - // to handle both contribution (e.g. "5.x-1.3", major = 1) and core - // (e.g. "5.1", major = 5) version strings. - $matches = array(); - if (preg_match('/^(\d+\.x-)?(\d+)\..*$/', $info['version'], $matches)) { - $info['major'] = $matches[2]; - } - elseif (!isset($info['major'])) { - // This would only happen for version strings that don't follow the - // drupal.org convention. We let contribs define "major" in their - // .info in this case, and only if that's missing would we hit this. - $info['major'] = -1; - } - } - else { - // No version info available at all. - $install_type = 'unknown'; - $info['version'] = t('Unknown'); - $info['major'] = -1; - } - - // Finally, save the results we care about into the $projects array. - $projects[$key]['existing_version'] = $info['version']; - $projects[$key]['existing_major'] = $info['major']; - $projects[$key]['install_type'] = $install_type; - } -} - -/** - * Given the installed projects and the available release data retrieved from - * remote servers, calculate the current status. - * - * This function is the heart of the update status feature. It iterates over - * every currently installed project. For each one, it first checks if the - * project has been flagged with a special status like "unsupported" or - * "insecure", or if the project node itself has been unpublished. In any of - * those cases, the project is marked with an error and the next project is - * considered. - * - * If the project itself is valid, the function decides what major release - * series to consider. The project defines what the currently supported major - * versions are for each version of core, so the first step is to make sure - * the current version is still supported. If so, that's the target version. - * If the current version is unsupported, the project maintainer's recommended - * major version is used. There's also a check to make sure that this function - * never recommends an earlier release than the currently installed major - * version. - * - * Given a target major version, it scans the available releases looking for - * the specific release to recommend (avoiding beta releases and development - * snapshots if possible). This is complicated to describe, but an example - * will help clarify. For the target major version, find the highest patch - * level. If there is a release at that patch level with no extra ("beta", - * etc), then we recommend the release at that patch level with the most - * recent release date. If every release at that patch level has extra (only - * betas), then recommend the latest release from the previous patch - * level. For example: - * - * 1.6-bugfix <-- recommended version because 1.6 already exists. - * 1.6 - * - * or - * - * 1.6-beta - * 1.5 <-- recommended version because no 1.6 exists. - * 1.4 - * - * It also looks for the latest release from the same major version, even a - * beta release, to display to the user as the "Latest version" option. - * Additionally, it finds the latest official release from any higher major - * versions that have been released to provide a set of "Also available" - * options. - * - * Finally, and most importantly, it keeps scanning the release history until - * it gets to the currently installed release, searching for anything marked - * as a security update. If any security updates have been found between the - * recommended release and the installed version, all of the releases that - * included a security fix are recorded so that the site administrator can be - * warned their site is insecure, and links pointing to the release notes for - * each security update can be included (which, in turn, will link to the - * official security announcements for each vulnerability). - * - * This function relies on the fact that the .xml release history data comes - * sorted based on major version and patch level, then finally by release date - * if there are multiple releases such as betas from the same major.patch - * version (e.g. 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development - * snapshots for a given major version are always listed last. - * - * The results of this function are expensive to compute, especially on sites - * with lots of modules or themes, since it involves a lot of comparisons and - * other operations. Therefore, we cache the results into the {cache_update} - * table using the 'update_project_data' cache ID. However, since this is not - * the data about available updates fetched from the network, it is ok to - * invalidate it somewhat quickly. If we keep this data for very long, site - * administrators are more likely to see incorrect results if they upgrade to - * a newer version of a module or theme but do not visit certain pages that - * automatically clear this cache. - * - * @param $available - * Array of data about available project releases. - * - * @see update_get_available() - * @see update_get_projects() - * @see update_process_project_info() - * @see update_project_cache() - */ -function update_calculate_project_data($available) { - // Retrieve the projects from cache, if present. - $projects = update_project_cache('update_project_data'); - // If $projects is empty, then the cache must be rebuilt. - // Otherwise, return the cached data and skip the rest of the function. - if (!empty($projects)) { - return $projects; - } - $projects = update_get_projects(); - update_process_project_info($projects); - foreach ($projects as $project => $project_info) { - if (isset($available[$project])) { - - // If the project status is marked as something bad, there's nothing - // else to consider. - if (isset($available[$project]['project_status'])) { - switch ($available[$project]['project_status']) { - case 'insecure': - $projects[$project]['status'] = UPDATE_NOT_SECURE; - if (empty($projects[$project]['extra'])) { - $projects[$project]['extra'] = array(); - } - $projects[$project]['extra'][] = array( - 'class' => 'project-not-secure', - 'label' => t('Project not secure'), - 'data' => t('This project has been labeled insecure by the Drupal security team, and is no longer available for download. Immediately disabling everything included by this project is strongly recommended!'), - ); - break; - case 'unpublished': - case 'revoked': - $projects[$project]['status'] = UPDATE_REVOKED; - if (empty($projects[$project]['extra'])) { - $projects[$project]['extra'] = array(); - } - $projects[$project]['extra'][] = array( - 'class' => 'project-revoked', - 'label' => t('Project revoked'), - 'data' => t('This project has been revoked, and is no longer available for download. Disabling everything included by this project is strongly recommended!'), - ); - break; - case 'unsupported': - $projects[$project]['status'] = UPDATE_NOT_SUPPORTED; - if (empty($projects[$project]['extra'])) { - $projects[$project]['extra'] = array(); - } - $projects[$project]['extra'][] = array( - 'class' => 'project-not-supported', - 'label' => t('Project not supported'), - 'data' => t('This project is no longer supported, and is no longer available for download. Disabling everything included by this project is strongly recommended!'), - ); - break; - case 'not-fetched': - $projects[$project]['status'] = UPDATE_NOT_FETCHED; - $projects[$project]['reason'] = t('Failed to fetch available update data'); - break; - - default: - // Assume anything else (e.g. 'published') is valid and we should - // perform the rest of the logic in this function. - break; - } - } - - if (!empty($projects[$project]['status'])) { - // We already know the status for this project, so there's nothing - // else to compute. Just record everything else we fetched from the - // XML file into our projects array and move to the next project. - $projects[$project] += $available[$project]; - continue; - } - - // Figure out the target major version. - $existing_major = $project_info['existing_major']; - $supported_majors = array(); - if (isset($available[$project]['supported_majors'])) { - $supported_majors = explode(',', $available[$project]['supported_majors']); - } - elseif (isset($available[$project]['default_major'])) { - // Older release history XML file without supported or recommended. - $supported_majors[] = $available[$project]['default_major']; - } - - if (in_array($existing_major, $supported_majors)) { - // Still supported, stay at the current major version. - $target_major = $existing_major; - } - elseif (isset($available[$project]['recommended_major'])) { - // Since 'recommended_major' is defined, we know this is the new XML - // format. Therefore, we know the current release is unsupported since - // its major version was not in the 'supported_majors' list. We should - // find the best release from the recommended major version. - $target_major = $available[$project]['recommended_major']; - $projects[$project]['status'] = UPDATE_NOT_SUPPORTED; - } - elseif (isset($available[$project]['default_major'])) { - // Older release history XML file without recommended, so recommend - // the currently defined "default_major" version. - $target_major = $available[$project]['default_major']; - } - else { - // Malformed XML file? Stick with the current version. - $target_major = $existing_major; - } - - // Make sure we never tell the admin to downgrade. If we recommended an - // earlier version than the one they're running, they'd face an - // impossible data migration problem, since Drupal never supports a DB - // downgrade path. In the unfortunate case that what they're running is - // unsupported, and there's nothing newer for them to upgrade to, we - // can't print out a "Recommended version", but just have to tell them - // what they have is unsupported and let them figure it out. - $target_major = max($existing_major, $target_major); - - $version_patch_changed = ''; - $patch = ''; - - // Defend ourselves from XML history files that contain no releases. - if (empty($available[$project]['releases'])) { - $projects[$project]['status'] = UPDATE_UNKNOWN; - $projects[$project]['reason'] = t('No available releases found'); - continue; - } - foreach ($available[$project]['releases'] as $version => $release) { - // First, if this is the existing release, check a few conditions. - if ($projects[$project]['existing_version'] === $version) { - if (isset($release['terms']['Release type']) && - in_array('Insecure', $release['terms']['Release type'])) { - $projects[$project]['status'] = UPDATE_NOT_SECURE; - } - elseif ($release['status'] == 'unpublished') { - $projects[$project]['status'] = UPDATE_REVOKED; - if (empty($projects[$project]['extra'])) { - $projects[$project]['extra'] = array(); - } - $projects[$project]['extra'][] = array( - 'class' => 'release-revoked', - 'label' => t('Release revoked'), - 'data' => t('Your currently installed release has been revoked, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'), - ); - } - elseif (isset($release['terms']['Release type']) && - in_array('Unsupported', $release['terms']['Release type'])) { - $projects[$project]['status'] = UPDATE_NOT_SUPPORTED; - if (empty($projects[$project]['extra'])) { - $projects[$project]['extra'] = array(); - } - $projects[$project]['extra'][] = array( - 'class' => 'release-not-supported', - 'label' => t('Release not supported'), - 'data' => t('Your currently installed release is now unsupported, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'), - ); - } - } - - // Otherwise, ignore unpublished, insecure, or unsupported releases. - if ($release['status'] == 'unpublished' || - (isset($release['terms']['Release type']) && - (in_array('Insecure', $release['terms']['Release type']) || - in_array('Unsupported', $release['terms']['Release type'])))) { - continue; - } - - // See if this is a higher major version than our target and yet still - // supported. If so, record it as an "Also available" release. - if ($release['version_major'] > $target_major) { - if (in_array($release['version_major'], $supported_majors)) { - if (!isset($available[$project]['also'])) { - $available[$project]['also'] = array(); - } - if (!isset($available[$project]['also'][$release['version_major']])) { - $available[$project]['also'][$release['version_major']] = $version; - } - } - // Otherwise, this release can't matter to us, since it's neither - // from the release series we're currently using nor the recommended - // release. We don't even care about security updates for this - // branch, since if a project maintainer puts out a security release - // at a higher major version and not at the lower major version, - // they must remove the lower version from the supported major - // versions at the same time, in which case we won't hit this code. - continue; - } - - // Look for the 'latest version' if we haven't found it yet. Latest is - // defined as the most recent version for the target major version. - if (!isset($available[$project]['latest_version']) - && $release['version_major'] == $target_major) { - $available[$project]['latest_version'] = $version; - } - - // Look for the development snapshot release for this branch. - if (!isset($available[$project]['dev_version']) - && $release['version_major'] == $target_major - && isset($release['version_extra']) - && $release['version_extra'] == 'dev') { - $available[$project]['dev_version'] = $version; - } - - // Look for the 'recommended' version if we haven't found it yet (see - // phpdoc at the top of this function for the definition). - if (!isset($available[$project]['recommended']) - && $release['version_major'] == $target_major - && isset($release['version_patch'])) { - if ($patch != $release['version_patch']) { - $patch = $release['version_patch']; - $version_patch_changed = $release['version']; - } - if (empty($release['version_extra']) && $patch == $release['version_patch']) { - $available[$project]['recommended'] = $version_patch_changed; - } - } - - // Stop searching once we hit the currently installed version. - if ($projects[$project]['existing_version'] === $version) { - break; - } - - // If we're running a dev snapshot and have a timestamp, stop - // searching for security updates once we hit an official release - // older than what we've got. Allow 100 seconds of leeway to handle - // differences between the datestamp in the .info file and the - // timestamp of the tarball itself (which are usually off by 1 or 2 - // seconds) so that we don't flag that as a new release. - if ($projects[$project]['install_type'] == 'dev') { - if (empty($projects[$project]['datestamp'])) { - // We don't have current timestamp info, so we can't know. - continue; - } - elseif (isset($release['date']) && ($projects[$project]['datestamp'] + 100 > $release['date'])) { - // We're newer than this, so we can skip it. - continue; - } - } - - // See if this release is a security update. - if (isset($release['terms']['Release type']) - && in_array('Security update', $release['terms']['Release type'])) { - $projects[$project]['security updates'][] = $release; - } - } - - // If we were unable to find a recommended version, then make the latest - // version the recommended version if possible. - if (!isset($available[$project]['recommended']) && isset($available[$project]['latest_version'])) { - $available[$project]['recommended'] = $available[$project]['latest_version']; - } - - // Stash the info about available releases into our $projects array. - $projects[$project] += $available[$project]; - - // - // Check to see if we need an update or not. - // - - if (!empty($projects[$project]['security updates'])) { - // If we found security updates, that always trumps any other status. - $projects[$project]['status'] = UPDATE_NOT_SECURE; - } - - if (isset($projects[$project]['status'])) { - // If we already know the status, we're done. - continue; - } - - // If we don't know what to recommend, there's nothing we can report. - // Bail out early. - if (!isset($projects[$project]['recommended'])) { - $projects[$project]['status'] = UPDATE_UNKNOWN; - $projects[$project]['reason'] = t('No available releases found'); - continue; - } - - // If we're running a dev snapshot, compare the date of the dev snapshot - // with the latest official version, and record the absolute latest in - // 'latest_dev' so we can correctly decide if there's a newer release - // than our current snapshot. - if ($projects[$project]['install_type'] == 'dev') { - if (isset($available[$project]['dev_version']) && $available[$project]['releases'][$available[$project]['dev_version']]['date'] > $available[$project]['releases'][$available[$project]['latest_version']]['date']) { - $projects[$project]['latest_dev'] = $available[$project]['dev_version']; - } - else { - $projects[$project]['latest_dev'] = $available[$project]['latest_version']; - } - } - - // Figure out the status, based on what we've seen and the install type. - switch ($projects[$project]['install_type']) { - case 'official': - if ($projects[$project]['existing_version'] === $projects[$project]['recommended'] || $projects[$project]['existing_version'] === $projects[$project]['latest_version']) { - $projects[$project]['status'] = UPDATE_CURRENT; - } - else { - $projects[$project]['status'] = UPDATE_NOT_CURRENT; - } - break; - - case 'dev': - $latest = $available[$project]['releases'][$projects[$project]['latest_dev']]; - if (empty($projects[$project]['datestamp'])) { - $projects[$project]['status'] = UPDATE_NOT_CHECKED; - $projects[$project]['reason'] = t('Unknown release date'); - } - elseif (($projects[$project]['datestamp'] + 100 > $latest['date'])) { - $projects[$project]['status'] = UPDATE_CURRENT; - } - else { - $projects[$project]['status'] = UPDATE_NOT_CURRENT; - } - break; - - default: - $projects[$project]['status'] = UPDATE_UNKNOWN; - $projects[$project]['reason'] = t('Invalid info'); - } - } - else { - $projects[$project]['status'] = UPDATE_UNKNOWN; - $projects[$project]['reason'] = t('No available releases found'); - } - } - // Give other modules a chance to alter the status (for example, to allow a - // contrib module to provide fine-grained settings to ignore specific - // projects or releases). - drupal_alter('update_status', $projects); - - // Cache the site's update status for at most 1 hour. - _update_cache_set('update_project_data', $projects, time() + 3600); - return $projects; -} - -/** - * Retrieve data from {cache_update} or empty the cache when necessary. - * - * Two very expensive arrays computed by this module are the list of all - * installed modules and themes (and .info data, project associations, etc), - * and the current status of the site relative to the currently available - * releases. These two arrays are cached in the {cache_update} table and used - * whenever possible. The cache is cleared whenever the administrator visits - * the status report, available updates report, or the module or theme - * administration pages, since we should always recompute the most current - * values on any of those pages. - * - * Note: while both of these arrays are expensive to compute (in terms of disk - * I/O and some fairly heavy CPU processing), neither of these is the actual - * data about available updates that we have to fetch over the network from - * updates.drupal.org. That information is stored with the - * 'update_available_releases' cache ID -- it needs to persist longer than 1 - * hour and never get invalidated just by visiting a page on the site. - * - * @param $cid - * The cache id of data to return from the cache. Valid options are - * 'update_project_data' and 'update_project_projects'. - * - * @return - * The cached value of the $projects array generated by - * update_calculate_project_data() or update_get_projects(), or an empty - * array when the cache is cleared. - */ -function update_project_cache($cid) { - $projects = array(); - - // On certain paths, we should clear the cache and recompute the projects or - // update status of the site to avoid presenting stale information. - $q = $_GET['q']; - $paths = array('admin/build/modules', 'admin/build/themes', 'admin/reports', 'admin/reports/updates', 'admin/reports/status', 'admin/reports/updates/check'); - if (in_array($q, $paths)) { - _update_cache_clear($cid); - } - else { - $cache = _update_cache_get($cid); - if (!empty($cache->data) && $cache->expire > time()) { - $projects = $cache->data; - } - } - return $projects; -} - -/** - * Filter the project .info data to only save attributes we need. - * - * @param array $info - * Array of .info file data as returned by drupal_parse_info_file(). - * - * @return - * Array of .info file data we need for the Update manager. - * - * @see _update_process_info_list() - */ -function update_filter_project_info($info) { - $whitelist = array( - '_info_file_ctime', - 'datestamp', - 'major', - 'name', - 'package', - 'project', - 'project status url', - 'version', - ); - $whitelist = array_flip($whitelist); - foreach ($info as $key => $value) { - if (!isset($whitelist[$key])) { - unset($info[$key]); - } - } - return $info; -} diff --git a/modules/update/update.css b/modules/update/update.css deleted file mode 100644 index 57cef84..0000000 --- a/modules/update/update.css +++ /dev/null @@ -1,109 +0,0 @@ - -.update .project { - font-weight: bold; - font-size: 110%; - padding-left: .25em; /* LTR */ - height: 22px; -} - -.update .version-status { - float: right; /* LTR */ - padding-right: 10px; /* LTR */ - font-size: 110%; - height: 20px; -} - -.update .version-status .icon { - padding-left: .5em; /* LTR */ -} - -.update .version-date { - white-space: nowrap; -} - -.update .info { - margin: 0; - padding: 1em 1em .25em 1em; -} - -.update tr td { - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -.update tr.error { - background: #fcc; -} - -.update tr.error .version-recommended { - background: #fdd; -} - -.update tr.ok { - background: #dfd; -} - -.update tr.warning { - background: #ffd; -} - -.update tr.warning .version-recommended { - background: #ffe; -} - -.current-version, .new-version { - direction: ltr; /* Note: version numbers should always be LTR. */ -} - -.update tr.unknown { - background: #ddd; -} - -table.update, -.update table.version { - width: 100%; - margin-top: .5em; -} - -.update table.version tbody { - border: none; -} - -.update table.version tr, -.update table.version td { - line-height: .9em; - padding: 0; - margin: 0; - border: none; -} - -.update table.version .version-title { - padding-left: 1em; /* LTR */ - width: 14em; -} - -.update table.version .version-details { - padding-right: .5em; /* LTR */ -} - -.update table.version .version-links { - text-align: right; /* LTR */ - padding-right: 1em; /* LTR */ -} - -.update table.version-security .version-title { - color: #970F00; -} - -.update table.version-recommended-strong .version-title { - font-weight: bold; -} - -.update .security-error { - font-weight: bold; - color: #970F00; -} - -.update .check-manually { - padding-left: 1em; /* LTR */ -} diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc deleted file mode 100644 index d1e3812..0000000 --- a/modules/update/update.fetch.inc +++ /dev/null @@ -1,284 +0,0 @@ - $project) { - $url = _update_build_fetch_url($project, $site_key); - $fetch_url_base = _update_get_fetch_url_base($project); - if (empty($fail[$fetch_url_base]) || count($fail[$fetch_url_base]) < $max_fetch_attempts) { - $xml = drupal_http_request($url); - if (isset($xml->data)) { - $data[] = $xml->data; - } - else { - // Connection likely broken; prepare to give up. - $fail[$fetch_url_base][$key] = 1; - } - } - else { - // Didn't bother trying to fetch. - $fail[$fetch_url_base][$key] = 1; - } - } - - if ($data) { - $parser = new update_xml_parser; - $available = $parser->parse($data); - } - if (!empty($available) && is_array($available)) { - // Record the projects where we failed to fetch data. - foreach ($fail as $fetch_url_base => $failures) { - foreach ($failures as $key => $value) { - $available[$key]['project_status'] = 'not-fetched'; - } - } - $frequency = variable_get('update_check_frequency', 1); - _update_cache_set('update_available_releases', $available, time() + (60 * 60 * 24 * $frequency)); - watchdog('update', 'Attempted to fetch information about all available new releases and updates.', array(), WATCHDOG_NOTICE, l(t('view'), 'admin/reports/updates')); - } - else { - watchdog('update', 'Unable to fetch any information about available new releases and updates.', array(), WATCHDOG_ERROR, l(t('view'), 'admin/reports/updates')); - } - // Whether this worked or not, we did just (try to) check for updates. - variable_set('update_last_check', time()); - return $available; -} - -/** - * Generates the URL to fetch information about project updates. - * - * This figures out the right URL to use, based on the project's .info file - * and the global defaults. Appends optional query arguments when the site is - * configured to report usage stats. - * - * @param $project - * The array of project information from update_get_projects(). - * @param $site_key - * The anonymous site key hash (optional). - * - * @see update_refresh() - * @see update_get_projects() - */ -function _update_build_fetch_url($project, $site_key = '') { - $name = $project['name']; - $url = _update_get_fetch_url_base($project); - $url .= '/'. $name .'/'. DRUPAL_CORE_COMPATIBILITY; - // Only append a site_key and the version information if we have a site_key - // in the first place, and if this is not a disabled module or theme. We do - // not want to record usage statistics for disabled code. - if (!empty($site_key) && (strpos($project['project_type'], 'disabled') === FALSE)) { - $url .= (strpos($url, '?') === TRUE) ? '&' : '?'; - $url .= 'site_key='; - $url .= rawurlencode($site_key); - if (!empty($project['info']['version'])) { - $url .= '&version='; - $url .= rawurlencode($project['info']['version']); - } - } - return $url; -} - -/** - * Return the base of the URL to fetch available update data for a project. - * - * @param $project - * The array of project information from update_get_projects(). - * @return - * The base of the URL used for fetching available update data. This does - * not include the path elements to specify a particular project, version, - * site_key, etc. - * - * @see _update_build_fetch_url() - */ -function _update_get_fetch_url_base($project) { - return isset($project['info']['project status url']) ? $project['info']['project status url'] : variable_get('update_fetch_url', UPDATE_DEFAULT_URL); -} - -/** - * Perform any notifications that should be done once cron fetches new data. - * - * This method checks the status of the site using the new data and depending - * on the configuration of the site, notifies administrators via email if there - * are new releases or missing security updates. - * - * @see update_requirements() - */ -function _update_cron_notify() { - include_once './includes/install.inc'; - $status = update_requirements('runtime'); - $params = array(); - $notify_all = (variable_get('update_notification_threshold', 'all') == 'all'); - foreach (array('core', 'contrib') as $report_type) { - $type = 'update_'. $report_type; - if (isset($status[$type]['severity']) - && ($status[$type]['severity'] == REQUIREMENT_ERROR || ($notify_all && $status[$type]['reason'] == UPDATE_NOT_CURRENT))) { - $params[$report_type] = $status[$type]['reason']; - } - } - if (!empty($params)) { - $notify_list = variable_get('update_notify_emails', ''); - if (!empty($notify_list)) { - $default_language = language_default(); - foreach ($notify_list as $target) { - if ($target_user = user_load(array('mail' => $target))) { - $target_language = user_preferred_language($target_user); - } - else { - $target_language = $default_language; - } - drupal_mail('update', 'status_notify', $target, $target_language, $params); - } - } - } -} - -/** - * XML Parser object to read Drupal's release history info files. - * This uses PHP4's lame XML parsing, but it works. - */ -class update_xml_parser { - var $projects = array(); - var $current_project; - var $current_release; - var $current_term; - var $current_tag; - var $current_object; - - /** - * Parse an array of XML data files. - */ - function parse($data) { - foreach ($data as $datum) { - $parser = xml_parser_create(); - xml_set_object($parser, $this); - xml_set_element_handler($parser, 'start', 'end'); - xml_set_character_data_handler($parser, "data"); - xml_parse($parser, $datum); - xml_parser_free($parser); - } - return $this->projects; - } - - function start($parser, $name, $attr) { - $this->current_tag = $name; - switch ($name) { - case 'PROJECT': - unset($this->current_object); - $this->current_project = array(); - $this->current_object = &$this->current_project; - break; - case 'RELEASE': - unset($this->current_object); - $this->current_release = array(); - $this->current_object = &$this->current_release; - break; - case 'TERM': - unset($this->current_object); - $this->current_term = array(); - $this->current_object = &$this->current_term; - break; - case 'FILE': - unset($this->current_object); - $this->current_file = array(); - $this->current_object = &$this->current_file; - break; - } - } - - function end($parser, $name) { - switch ($name) { - case 'PROJECT': - unset($this->current_object); - $this->projects[$this->current_project['short_name']] = $this->current_project; - $this->current_project = array(); - break; - case 'RELEASE': - unset($this->current_object); - $this->current_project['releases'][$this->current_release['version']] = $this->current_release; - break; - case 'RELEASES': - $this->current_object = &$this->current_project; - break; - case 'TERM': - unset($this->current_object); - $term_name = $this->current_term['name']; - if (!isset($this->current_release['terms'])) { - $this->current_release['terms'] = array(); - } - if (!isset($this->current_release['terms'][$term_name])) { - $this->current_release['terms'][$term_name] = array(); - } - $this->current_release['terms'][$term_name][] = $this->current_term['value']; - break; - case 'TERMS': - $this->current_object = &$this->current_release; - break; - case 'FILE': - unset($this->current_object); - $this->current_release['files'][] = $this->current_file; - break; - case 'FILES': - $this->current_object = &$this->current_release; - break; - default: - $this->current_object[strtolower($this->current_tag)] = trim($this->current_object[strtolower($this->current_tag)]); - $this->current_tag = ''; - } - } - - function data($parser, $data) { - if ($this->current_tag && !in_array($this->current_tag, array('PROJECT', 'RELEASE', 'RELEASES', 'TERM', 'TERMS', 'FILE', 'FILES'))) { - $tag = strtolower($this->current_tag); - if (isset($this->current_object[$tag])) { - $this->current_object[$tag] .= $data; - } - else { - $this->current_object[$tag] = $data; - } - } - } -} diff --git a/modules/update/update.info b/modules/update/update.info deleted file mode 100644 index 6c0397f..0000000 --- a/modules/update/update.info +++ /dev/null @@ -1,11 +0,0 @@ -name = Update status -description = Checks the status of available updates for Drupal and your installed modules and themes. -version = VERSION -package = Core - optional -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/update/update.install b/modules/update/update.install deleted file mode 100644 index 8aa86f1..0000000 --- a/modules/update/update.install +++ /dev/null @@ -1,62 +0,0 @@ -'. t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') .''; - $output .= ''. t('To extend the functionality or to change the look of your site, a number of contributed modules and themes are available.', array('@modules' => 'http://drupal.org/project/modules', '@themes' => 'http://drupal.org/project/themes')) .'
'; - return $output; - case 'admin/build/themes': - case 'admin/build/modules': - include_once './includes/install.inc'; - $status = update_requirements('runtime'); - foreach (array('core', 'contrib') as $report_type) { - $type = 'update_'. $report_type; - if (isset($status[$type]['severity'])) { - if ($status[$type]['severity'] == REQUIREMENT_ERROR) { - drupal_set_message($status[$type]['description'], 'error'); - } - elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) { - drupal_set_message($status[$type]['description'], 'warning'); - } - } - } - return ''. t('See the available updates page for information on installed modules and themes with new versions released.', array('@available_updates' => url('admin/reports/updates'))) .'
'; - - case 'admin/reports/updates/settings': - case 'admin/reports/status': - // These two pages don't need additional nagging. - break; - - case 'admin/help#update': - $output = ''. t("The Update status module periodically checks for new versions of your site's software (including contributed modules and themes), and alerts you to available updates.") .'
'; - $output .= ''. t('The report of available updates will alert you when new releases are available for download. You may configure options for update checking frequency and notifications at the Update status module settings page.', array('@update-report' => url('admin/reports/updates'), '@update-settings' => url('admin/reports/updates/settings'))) .'
'; - $output .= ''. t('Please note that in order to provide this information, anonymous usage statistics are sent to drupal.org. If desired, you may disable the Update status module from the module administration page.', array('@modules' => url('admin/build/modules'))) .'
'; - $output .= ''. t('For more information, see the online handbook entry for Update status module.', array('@update' => 'http://drupal.org/handbook/modules/update')) .'
'; - return $output; - - default: - // Otherwise, if we're on *any* admin page and there's a security - // update missing, print an error message about it. - if (arg(0) == 'admin' && strpos($path, '#') === FALSE - && user_access('administer site configuration')) { - include_once './includes/install.inc'; - $status = update_requirements('runtime'); - foreach (array('core', 'contrib') as $report_type) { - $type = 'update_'. $report_type; - if (isset($status[$type]) - && isset($status[$type]['reason']) - && $status[$type]['reason'] === UPDATE_NOT_SECURE) { - drupal_set_message($status[$type]['description'], 'error'); - } - } - } - - } -} - -/** - * Implementation of hook_menu(). - */ -function update_menu() { - $items = array(); - - $items['admin/reports/updates'] = array( - 'title' => 'Available updates', - 'description' => 'Get a status report about available updates for your installed modules and themes.', - 'page callback' => 'update_status', - 'access arguments' => array('administer site configuration'), - 'file' => 'update.report.inc', - 'weight' => 10, - ); - $items['admin/reports/updates/list'] = array( - 'title' => 'List', - 'page callback' => 'update_status', - 'access arguments' => array('administer site configuration'), - 'file' => 'update.report.inc', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['admin/reports/updates/settings'] = array( - 'title' => 'Settings', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('update_settings'), - 'access arguments' => array('administer site configuration'), - 'file' => 'update.settings.inc', - 'type' => MENU_LOCAL_TASK, - ); - $items['admin/reports/updates/check'] = array( - 'title' => 'Manual update check', - 'page callback' => 'update_manual_status', - 'access arguments' => array('administer site configuration'), - 'file' => 'update.fetch.inc', - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Implementation of the hook_theme() registry. - */ -function update_theme() { - return array( - 'update_settings' => array( - 'arguments' => array('form' => NULL), - ), - 'update_report' => array( - 'arguments' => array('data' => NULL), - ), - 'update_version' => array( - 'arguments' => array('version' => NULL, 'tag' => NULL, 'class' => NULL), - ), - ); -} - -/** - * Implementation of hook_requirements(). - * - * @return - * An array describing the status of the site regarding available updates. - * If there is no update data, only one record will be returned, indicating - * that the status of core can't be determined. If data is available, there - * will be two records: one for core, and another for all of contrib - * (assuming there are any contributed modules or themes enabled on the - * site). In addition to the fields expected by hook_requirements ('value', - * 'severity', and optionally 'description'), this array will contain a - * 'reason' attribute, which is an integer constant to indicate why the - * given status is being returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or - * UPDATE_UNKNOWN). This is used for generating the appropriate e-mail - * notification messages during update_cron(), and might be useful for other - * modules that invoke update_requirements() to find out if the site is up - * to date or not. - * - * @see _update_message_text() - * @see _update_cron_notify() - */ -function update_requirements($phase) { - if ($phase == 'runtime') { - if ($available = update_get_available(FALSE)) { - module_load_include('inc', 'update', 'update.compare'); - $data = update_calculate_project_data($available); - // First, populate the requirements for core: - $requirements['update_core'] = _update_requirement_check($data['drupal'], 'core'); - // We don't want to check drupal a second time. - unset($data['drupal']); - if (!empty($data)) { - // Now, sort our $data array based on each project's status. The - // status constants are numbered in the right order of precedence, so - // we just need to make sure the projects are sorted in ascending - // order of status, and we can look at the first project we find. - uasort($data, '_update_project_status_sort'); - $first_project = reset($data); - $requirements['update_contrib'] = _update_requirement_check($first_project, 'contrib'); - } - } - else { - $requirements['update_core']['title'] = t('Drupal core update status'); - $requirements['update_core']['value'] = t('No update data available'); - $requirements['update_core']['severity'] = REQUIREMENT_WARNING; - $requirements['update_core']['reason'] = UPDATE_UNKNOWN; - $requirements['update_core']['description'] = _update_no_data(); - } - return $requirements; - } -} - -/** - * Private helper method to fill in the requirements array. - * - * This is shared for both core and contrib to generate the right elements in - * the array for hook_requirements(). - * - * @param $project - * Array of information about the project we're testing as returned by - * update_calculate_project_data(). - * @param $type - * What kind of project is this ('core' or 'contrib'). - * - * @return - * An array to be included in the nested $requirements array. - * - * @see hook_requirements() - * @see update_requirements() - * @see update_calculate_project_data() - */ -function _update_requirement_check($project, $type) { - $requirement = array(); - if ($type == 'core') { - $requirement['title'] = t('Drupal core update status'); - } - else { - $requirement['title'] = t('Module and theme update status'); - } - $status = $project['status']; - if ($status != UPDATE_CURRENT) { - $requirement['reason'] = $status; - $requirement['description'] = _update_message_text($type, $status, TRUE); - $requirement['severity'] = REQUIREMENT_ERROR; - } - switch ($status) { - case UPDATE_NOT_SECURE: - $requirement_label = t('Not secure!'); - break; - case UPDATE_REVOKED: - $requirement_label = t('Revoked!'); - break; - case UPDATE_NOT_SUPPORTED: - $requirement_label = t('Unsupported release'); - break; - case UPDATE_NOT_CURRENT: - $requirement_label = t('Out of date'); - $requirement['severity'] = REQUIREMENT_WARNING; - break; - case UPDATE_UNKNOWN: - case UPDATE_NOT_CHECKED: - case UPDATE_NOT_FETCHED: - $requirement_label = isset($project['reason']) ? $project['reason'] : t('Can not determine status'); - $requirement['severity'] = REQUIREMENT_WARNING; - break; - default: - $requirement_label = t('Up to date'); - } - if ($status != UPDATE_CURRENT && $type == 'core' && isset($project['recommended'])) { - $requirement_label .= ' '. t('(version @version available)', array('@version' => $project['recommended'])); - } - $requirement['value'] = l($requirement_label, 'admin/reports/updates'); - return $requirement; -} - -/** - * Implementation of hook_cron(). - */ -function update_cron() { - $frequency = variable_get('update_check_frequency', 1); - $interval = 60 * 60 * 24 * $frequency; - // Cron should check for updates if there is no update data cached or if the - // configured update interval has elapsed. - if (!_update_cache_get('update_available_releases') || ((time() - variable_get('update_last_check', 0)) > $interval)) { - update_refresh(); - _update_cron_notify(); - } -} - -/** - * Implementation of hook_form_alter(). - * - * Adds a submit handler to the system modules and themes forms, so that if a - * site admin saves either form, we invalidate the cache of available updates. - * - * @see update_invalidate_cache() - */ -function update_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'system_modules' || $form_id == 'system_themes_form' ) { - $form['#submit'][] = 'update_invalidate_cache'; - } -} - -/** - * Prints a warning message when there is no data about available updates. - */ -function _update_no_data() { - $destination = drupal_get_destination(); - return t('No information is available about potential new releases for currently installed modules and themes. To check for updates, you may need to run cron or you can check manually. Please note that checking for available updates can take a long time, so please be patient.', array( - '@run_cron' => url('admin/reports/status/run-cron', array('query' => $destination)), - '@check_manually' => url('admin/reports/updates/check', array('query' => $destination)), - )); -} - -/** - * Internal helper to try to get the update information from the cache - * if possible, and to refresh the cache when necessary. - * - * In addition to checking the cache lifetime, this function also ensures that - * there are no .info files for enabled modules or themes that have a newer - * modification timestamp than the last time we checked for available update - * data. If any .info file was modified, it almost certainly means a new - * version of something was installed. Without fresh available update data, - * the logic in update_calculate_project_data() will be wrong and produce - * confusing, bogus results. - * - * @param $refresh - * Boolean to indicate if this method should refresh the cache automatically - * if there's no data. - * - * @see update_refresh() - * @see update_get_projects() - */ -function update_get_available($refresh = FALSE) { - module_load_include('inc', 'update', 'update.compare'); - $available = array(); - - // First, make sure that none of the .info files have a change time - // newer than the last time we checked for available updates. - $needs_refresh = FALSE; - $last_check = variable_get('update_last_check', 0); - $projects = update_get_projects(); - foreach ($projects as $key => $project) { - if ($project['info']['_info_file_ctime'] > $last_check) { - $needs_refresh = TRUE; - break; - } - } - if (!$needs_refresh && ($cache = _update_cache_get('update_available_releases')) && $cache->expire > time()) { - $available = $cache->data; - } - elseif ($needs_refresh || $refresh) { - // If we need to refresh due to a newer .info file, ignore the argument - // and force the refresh (e.g., even for update_requirements()) to prevent - // bogus results. - $available = update_refresh(); - } - return $available; -} - -/** - * Wrapper to load the include file and then refresh the release data. - */ -function update_refresh() { - module_load_include('inc', 'update', 'update.fetch'); - return _update_refresh(); -} - -/** - * Implementation of hook_mail(). - * - * Constructs the email notification message when the site is out of date. - * - * @param $key - * Unique key to indicate what message to build, always 'status_notify'. - * @param $message - * Reference to the message array being built. - * @param $params - * Array of parameters to indicate what kind of text to include in the - * message body. This is a keyed array of message type ('core' or 'contrib') - * as the keys, and the status reason constant (UPDATE_NOT_SECURE, etc) for - * the values. - * - * @see drupal_mail() - * @see _update_cron_notify() - * @see _update_message_text() - */ -function update_mail($key, &$message, $params) { - $language = $message['language']; - $langcode = $language->language; - $message['subject'] .= t('New release(s) available for !site_name', array('!site_name' => variable_get('site_name', 'Drupal')), $langcode); - foreach ($params as $msg_type => $msg_reason) { - $message['body'][] = _update_message_text($msg_type, $msg_reason, FALSE, $language); - } - $message['body'][] = t('See the available updates page for more information:', array(), $langcode) ."\n". url('admin/reports/updates', array('absolute' => TRUE, 'language' => $language)); -} - -/** - * Helper function to return the appropriate message text when the site is out - * of date or missing a security update. - * - * These error messages are shared by both update_requirements() for the - * site-wide status report at admin/reports/status and in the body of the - * notification emails generated by update_cron(). - * - * @param $msg_type - * String to indicate what kind of message to generate. Can be either - * 'core' or 'contrib'. - * @param $msg_reason - * Integer constant specifying why message is generated. - * @param $report_link - * Boolean that controls if a link to the updates report should be added. - * @param $language - * An optional language object to use. - * @return - * The properly translated error message for the given key. - */ -function _update_message_text($msg_type, $msg_reason, $report_link = FALSE, $language = NULL) { - $langcode = isset($language) ? $language->language : NULL; - $text = ''; - switch ($msg_reason) { - case UPDATE_NOT_SECURE: - if ($msg_type == 'core') { - $text = t('There is a security update available for your version of Drupal. To ensure the security of your server, you should update immediately!', array(), $langcode); - } - else { - $text = t('There are security updates available for one or more of your modules or themes. To ensure the security of your server, you should update immediately!', array(), $langcode); - } - break; - - case UPDATE_REVOKED: - if ($msg_type == 'core') { - $text = t('Your version of Drupal has been revoked and is no longer available for download. Upgrading is strongly recommended!', array(), $langcode); - } - else { - $text = t('The installed version of at least one of your modules or themes has been revoked and is no longer available for download. Upgrading or disabling is strongly recommended!', array(), $langcode); - } - break; - - case UPDATE_NOT_SUPPORTED: - if ($msg_type == 'core') { - $text = t('Your version of Drupal is no longer supported. Upgrading is strongly recommended!', array(), $langcode); - } - else { - $text = t('The installed version of at least one of your modules or themes is no longer supported. Upgrading or disabling is strongly recommended! Please see the project homepage for more details.', array(), $langcode); - } - break; - - case UPDATE_NOT_CURRENT: - if ($msg_type == 'core') { - $text = t('There are updates available for your version of Drupal. To ensure the proper functioning of your site, you should update as soon as possible.', array(), $langcode); - } - else { - $text = t('There are updates available for one or more of your modules or themes. To ensure the proper functioning of your site, you should update as soon as possible.', array(), $langcode); - } - break; - - case UPDATE_UNKNOWN: - case UPDATE_NOT_CHECKED: - case UPDATE_NOT_FETCHED: - if ($msg_type == 'core') { - $text = t('There was a problem determining the status of available updates for your version of Drupal.', array(), $langcode); - } - else { - $text = t('There was a problem determining the status of available updates for one or more of your modules or themes.', array(), $langcode); - } - break; - } - - if ($report_link) { - $text .= ' '. t('See the available updates page for more information.', array('@available_updates' => url('admin/reports/updates', array('language' => $language))), $langcode); - } - - return $text; -} - -/** - * Private sort function to order projects based on their status. - * - * @see update_requirements() - * @see uasort() - */ -function _update_project_status_sort($a, $b) { - // The status constants are numerically in the right order, so we can - // usually subtract the two to compare in the order we want. However, - // negative status values should be treated as if they are huge, since we - // always want them at the bottom of the list. - $a_status = $a['status'] > 0 ? $a['status'] : (-10 * $a['status']); - $b_status = $b['status'] > 0 ? $b['status'] : (-10 * $b['status']); - return $a_status - $b_status; -} - -/** - * @defgroup update_status_cache Private update status cache system - * @{ - * - * We specifically do NOT use the core cache API for saving the fetched data - * about available updates. It is vitally important that this cache is only - * cleared when we're populating it after successfully fetching new available - * update data. Usage of the core cache API results in all sorts of potential - * problems that would result in attempting to fetch available update data all - * the time, including if a site has a "minimum cache lifetime" (which is both - * a minimum and a maximum) defined, or if a site uses memcache or another - * plug-able cache system that assumes volatile caches. - * - * Update module still uses the {cache_update} table, but instead of using - * cache_set(), cache_get(), and cache_clear_all(), there are private helper - * functions that implement these same basic tasks but ensure that the cache - * is not prematurely cleared, and that the data is always stored in the - * database, even if memcache or another cache backend is in use. - */ - -/** - * Store data in the private update status cache table. - * - * Note: this function completely ignores the {cache_update}.headers field - * since that is meaningless for the kinds of data we're caching. - * - * @param $cid - * The cache ID to save the data with. - * @param $data - * The data to store. - * @param $expire - * One of the following values: - * - CACHE_PERMANENT: Indicates that the item should never be removed except - * by explicitly using _update_cache_clear() or update_invalidate_cache(). - * - A Unix timestamp: Indicates that the item should be kept at least until - * the given time, after which it will be invalidated. - */ -function _update_cache_set($cid, $data, $expire) { - $serialized = 0; - if (is_object($data) || is_array($data)) { - $data = serialize($data); - $serialized = 1; - } - $created = time(); - db_query("UPDATE {cache_update} SET data = %b, created = %d, expire = %d, serialized = %d WHERE cid = '%s'", $data, $created, $expire, $serialized, $cid); - if (!db_affected_rows()) { - @db_query("INSERT INTO {cache_update} (cid, data, created, expire, serialized) VALUES ('%s', %b, %d, %d, %d)", $cid, $data, $created, $expire, $serialized); - } -} - -/** - * Retrieve data from the private update status cache table. - * - * @param $cid - * The cache ID to retrieve. - * @return - * The data for the given cache ID, or NULL if the ID was not found. - */ -function _update_cache_get($cid) { - $cache = db_fetch_object(db_query("SELECT data, created, expire, serialized FROM {cache_update} WHERE cid = '%s'", $cid)); - if (isset($cache->data)) { - $cache->data = db_decode_blob($cache->data); - if ($cache->serialized) { - $cache->data = unserialize($cache->data); - } - } - return $cache; -} - -/** - * Invalidates specific cached data relating to update status. - * - * @param $cid - * Optional cache ID of the record to clear from the private update module - * cache. If empty, all records will be cleared from the table. - */ -function _update_cache_clear($cid = NULL) { - if (empty($cid)) { - db_query("TRUNCATE TABLE {cache_update}"); - } - else { - db_query("DELETE FROM {cache_update} WHERE cid = '%s'", $cid); - } -} - -/** - * Implementation of hook_flush_caches(). - * - * Called from update.php (among others) to flush the caches. - * Since we're running update.php, we are likely to install a new version of - * something, in which case, we want to check for available update data again. - * However, because we have our own caching system, we need to directly clear - * the database table ourselves at this point and return nothing, for example, - * on sites that use memcache where cache_clear_all() won't know how to purge - * this data. - * - * However, we only want to do this from update.php, since otherwise, we'd - * lose all the available update data on every cron run. So, we specifically - * check if the site is in MAINTENANCE_MODE == 'update' (which indicates - * update.php is running, not update module... alas for overloaded names). - */ -function update_flush_caches() { - if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { - _update_cache_clear(); - } - return array(); -} - -/** - * Invalidates all cached data relating to update status. - */ -function update_invalidate_cache() { - _update_cache_clear(); -} - -/** - * @} End of "defgroup update_status_cache". - */ diff --git a/modules/update/update.report.inc b/modules/update/update.report.inc deleted file mode 100644 index 5565563..0000000 --- a/modules/update/update.report.inc +++ /dev/null @@ -1,264 +0,0 @@ -'. ($last ? t('Last checked: @time ago', array('@time' => format_interval(time() - $last))) : t('Last checked: never')); - $output .= ' ('. l(t('Check manually'), 'admin/reports/updates/check') .')'; - $output .= "\n"; - - if (!is_array($data)) { - $output .= ''. $data .'
'; - return $output; - } - - $header = array(); - $rows = array(); - - $notification_level = variable_get('update_notification_threshold', 'all'); - - foreach ($data as $project) { - switch ($project['status']) { - case UPDATE_CURRENT: - $class = 'ok'; - $icon = theme('image', 'misc/watchdog-ok.png', t('ok'), t('ok')); - break; - case UPDATE_UNKNOWN: - case UPDATE_NOT_FETCHED: - $class = 'unknown'; - $icon = theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning')); - break; - case UPDATE_NOT_SECURE: - case UPDATE_REVOKED: - case UPDATE_NOT_SUPPORTED: - $class = 'error'; - $icon = theme('image', 'misc/watchdog-error.png', t('error'), t('error')); - break; - case UPDATE_NOT_CHECKED: - case UPDATE_NOT_CURRENT: - default: - $class = 'warning'; - $icon = theme('image', 'misc/watchdog-warning.png', t('warning'), t('warning')); - break; - } - - $row = ''. $tag ." | \n"; - $output .= ''; - $output .= l($version['version'], $version['release_link']); - $output .= ' ('. format_date($version['date'], 'custom', 'Y-M-d') .')'; - $output .= " | \n"; - $output .= ''; - $links = array(); - $links['update-download'] = array( - 'title' => t('Download'), - 'href' => $version['download_link'], - ); - $links['update-release-notes'] = array( - 'title' => t('Release notes'), - 'href' => $version['release_link'], - ); - $output .= theme('links', $links); - $output .= ' | '; - $output .= '
'. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))) .'
'); - - $roles = user_roles(FALSE, 'upload files'); - $form['roles'] = array('#type' => 'value', '#value' => $roles); - - foreach ($roles as $rid => $role) { - $form['settings_role_'. $rid] = array( - '#type' => 'fieldset', - '#title' => t('Settings for @role', array('@role' => $role)), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $form['settings_role_'. $rid]['upload_extensions_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Permitted file extensions'), - '#default_value' => variable_get('upload_extensions_'. $rid, $upload_extensions_default), - '#maxlength' => 255, - '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'), - ); - $form['settings_role_'. $rid]['upload_uploadsize_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Maximum file size per upload'), - '#default_value' => variable_get('upload_uploadsize_'. $rid, $upload_uploadsize_default), - '#size' => 5, - '#maxlength' => 5, - '#description' => t('The maximum size of a file a user can upload. If an image is uploaded and a maximum resolution is set, the size will be checked after the file has been resized.'), - '#field_suffix' => t('MB'), - ); - $form['settings_role_'. $rid]['upload_usersize_'. $rid] = array( - '#type' => 'textfield', - '#title' => t('Total file size per user'), - '#default_value' => variable_get('upload_usersize_'. $rid, $upload_usersize_default), - '#size' => 5, - '#maxlength' => 5, - '#description' => t('The maximum size of all files a user can have on the site.'), - '#field_suffix' => t('MB'), - ); - } - - $form['#validate'] = array('upload_admin_settings_validate'); - - return system_settings_form($form); -} diff --git a/modules/upload/upload.info b/modules/upload/upload.info deleted file mode 100644 index 5e9677f..0000000 --- a/modules/upload/upload.info +++ /dev/null @@ -1,11 +0,0 @@ -name = Upload -description = Allows users to upload and attach files to content. -package = Core - optional -version = VERSION -core = 6.x - -; Information added by Drupal.org packaging script on 2016-02-24 -version = "6.38" -project = "drupal" -datestamp = "1456343372" - diff --git a/modules/upload/upload.install b/modules/upload/upload.install deleted file mode 100644 index 421883b..0000000 --- a/modules/upload/upload.install +++ /dev/null @@ -1,84 +0,0 @@ - 'Stores uploaded file information and table associations.', - 'fields' => array( - 'fid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: The {files}.fid.', - ), - 'nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {node}.nid associated with the uploaded file.', - ), - 'vid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: The {node}.vid associated with the uploaded file.', - ), - 'description' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Description of the uploaded file.', - ), - 'list' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Whether the file should be visibly listed on the node: yes(1) or no(0).', - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Weight of this upload in relation to other uploads in this node.', - ), - ), - 'primary key' => array('vid', 'fid'), - 'indexes' => array( - 'fid' => array('fid'), - 'nid' => array('nid'), - ), - ); - - return $schema; -} - - diff --git a/modules/upload/upload.module b/modules/upload/upload.module deleted file mode 100644 index ee5b127..0000000 --- a/modules/upload/upload.module +++ /dev/null @@ -1,642 +0,0 @@ -'. t('The upload module allows users to upload files to the site. The ability to upload files is important for members of a community who want to share work. It is also useful to administrators who want to keep uploaded files connected to posts.') .''; - $output .= ''. t('Users with the upload files permission can upload attachments to posts. Uploads may be enabled for specific content types on the content types settings page. Each user role can be customized to limit or control the file size of uploads, or the maximum dimension of image files.') .'
'; - $output .= ''. t('For more information, see the online handbook entry for Upload module.', array('@upload' => 'http://drupal.org/handbook/modules/upload/')) .'
'; - return $output; - case 'admin/settings/uploads': - return ''. t('Users with the upload files permission can upload attachments. Users with the view uploaded files permission can view uploaded attachments. You can choose which post types can take attachments on the content types settings page.', array('@permissions' => url('admin/user/permissions', array('fragment' => 'module-upload')), '@types' => url('admin/content/types'))) .'
'; - } -} - -/** - * Implementation of hook_theme() - */ -function upload_theme() { - return array( - 'upload_attachments' => array( - 'arguments' => array('files' => NULL), - ), - 'upload_form_current' => array( - 'arguments' => array('form' => NULL), - ), - 'upload_form_new' => array( - 'arguments' => array('form' => NULL), - ), - ); -} - -/** - * Implementation of hook_perm(). - */ -function upload_perm() { - return array('upload files', 'view uploaded files'); -} - -/** - * Implementation of hook_link(). - */ -function upload_link($type, $node = NULL, $teaser = FALSE) { - $links = array(); - - // Display a link with the number of attachments - if ($teaser && $type == 'node' && isset($node->files) && user_access('view uploaded files')) { - $num_files = 0; - foreach ($node->files as $file) { - if ($file->list) { - $num_files++; - } - } - if ($num_files) { - $links['upload_attachments'] = array( - 'title' => format_plural($num_files, '1 attachment', '@count attachments'), - 'href' => "node/$node->nid", - 'attributes' => array('title' => t('Read full article to view attachments.')), - 'fragment' => 'attachments' - ); - } - } - - return $links; -} - -/** - * Implementation of hook_menu(). - */ -function upload_menu() { - $items['upload/js'] = array( - 'page callback' => 'upload_js', - 'access arguments' => array('upload files'), - 'type' => MENU_CALLBACK, - ); - $items['admin/settings/uploads'] = array( - 'title' => 'File uploads', - 'description' => 'Control how files may be attached to content.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('upload_admin_settings'), - 'access arguments' => array('administer site configuration'), - 'type' => MENU_NORMAL_ITEM, - 'file' => 'upload.admin.inc', - ); - return $items; -} - -function upload_menu_alter(&$items) { - $items['system/files']['access arguments'] = array('view uploaded files'); -} - -/** - * Determine the limitations on files that a given user may upload. The user - * may be in multiple roles so we select the most permissive limitations from - * all of their roles. - * - * @param $user - * A Drupal user object. - * @return - * An associative array with the following keys: - * 'extensions' - * A white space separated string containing all the file extensions this - * user may upload. - * 'file_size' - * The maximum size of a file upload in bytes. - * 'user_size' - * The total number of bytes for all for a user's files. - * 'resolution' - * A string specifying the maximum resolution of images. - */ -function _upload_file_limits($user) { - $file_limit = variable_get('upload_uploadsize_default', 1); - $user_limit = variable_get('upload_usersize_default', 1); - $all_extensions = explode(' ', variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp')); - foreach ($user->roles as $rid => $name) { - $extensions = variable_get("upload_extensions_$rid", variable_get('upload_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp')); - $all_extensions = array_merge($all_extensions, explode(' ', $extensions)); - - // A zero value indicates no limit, take the least restrictive limit. - $file_size = variable_get("upload_uploadsize_$rid", variable_get('upload_uploadsize_default', 1)) * 1024 * 1024; - $file_limit = ($file_limit && $file_size) ? max($file_limit, $file_size) : 0; - - $user_size = variable_get("upload_usersize_$rid", variable_get('upload_usersize_default', 1)) * 1024 * 1024; - $user_limit = ($user_limit && $user_size) ? max($user_limit, $user_size) : 0; - } - $all_extensions = implode(' ', array_unique($all_extensions)); - return array( - 'extensions' => $all_extensions, - 'file_size' => $file_limit, - 'user_size' => $user_limit, - 'resolution' => variable_get('upload_max_resolution', 0), - ); -} - -/** - * Implementation of hook_file_download(). - */ -function upload_file_download($filepath) { - $filepath = file_create_path($filepath); - $result = db_query("SELECT f.*, u.nid FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = '%s'", $filepath); - while ($file = db_fetch_object($result)) { - if ($filepath !== $file->filepath) { - // Since some database servers sometimes use a case-insensitive - // comparison by default, double check that the filename is an exact - // match. - continue; - } - if (user_access('view uploaded files') && ($node = node_load($file->nid)) && node_access('view', $node)) { - return array( - 'Content-Type: ' . $file->filemime, - 'Content-Length: ' . $file->filesize, - ); - } - else { - return -1; - } - } -} - -/** - * Save new uploads and store them in the session to be associated to the node - * on upload_save. - * - * @param $node - * A node object to associate with uploaded files. - */ -function upload_node_form_submit(&$form, &$form_state) { - global $user; - - $limits = _upload_file_limits($user); - $validators = array( - 'file_validate_extensions' => array($limits['extensions']), - 'file_validate_image_resolution' => array($limits['resolution']), - 'file_validate_size' => array($limits['file_size'], $limits['user_size']), - ); - - // Save new file uploads. - if (user_access('upload files') && ($file = file_save_upload('upload', $validators, file_directory_path()))) { - $file->list = variable_get('upload_list_default', 1); - $file->description = $file->filename; - $file->weight = 0; - $file->new = TRUE; - $form['#node']->files[$file->fid] = $file; - $form_state['values']['files'][$file->fid] = (array)$file; - } - - if (isset($form_state['values']['files'])) { - foreach ($form_state['values']['files'] as $fid => $file) { - // If the node was previewed prior to saving, $form['#node']->files[$fid] - // is an array instead of an object. Convert file to object for compatibility. - $form['#node']->files[$fid] = (object) $form['#node']->files[$fid]; - $form_state['values']['files'][$fid]['new'] = !empty($form['#node']->files[$fid]->new); - } - } - - // Order the form according to the set file weight values. - if (!empty($form_state['values']['files'])) { - $microweight = 0.001; - foreach ($form_state['values']['files'] as $fid => $file) { - if (is_numeric($fid)) { - $form_state['values']['files'][$fid]['#weight'] = $file['weight'] + $microweight; - $microweight += 0.001; - } - } - uasort($form_state['values']['files'], 'element_sort'); - } -} - -function upload_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { - $form['workflow']['upload'] = array( - '#type' => 'radios', - '#title' => t('Attachments'), - '#default_value' => variable_get('upload_'. $form['#node_type']->type, 1), - '#options' => array(t('Disabled'), t('Enabled')), - ); - } - - if (isset($form['type']) && isset($form['#node'])) { - $node = $form['#node']; - if ($form['type']['#value'] .'_node_form' == $form_id && variable_get("upload_$node->type", TRUE)) { - // Attachments fieldset - $form['attachments'] = array( - '#type' => 'fieldset', - '#access' => user_access('upload files'), - '#title' => t('File attachments'), - '#collapsible' => TRUE, - '#collapsed' => empty($node->files), - '#description' => t('Changes made to the attachments are not permanent until you save this post. The first "listed" file will be included in RSS feeds.'), - '#prefix' => ' ', - '#weight' => 30, - ); - - // Wrapper for fieldset contents (used by ahah.js). - $form['attachments']['wrapper'] = array( - '#prefix' => '