Now all modules are in core modules folder

This commit is contained in:
Manuel Cillero 2017-08-08 12:14:45 +02:00
parent 5ba1cdfa0b
commit 05b6a91b0c
1907 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,367 @@
<?php
/**
* @file
* Administrative page callbacks for the linkchecker module.
*/
function linkchecker_admin_settings_form(&$form_state) {
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('General settings'),
'#collapsible' => FALSE,
);
$form['settings']['linkchecker_scan_nodetypes'] = array(
'#type' => 'checkboxes',
'#title' => t('Scan node types for links'),
'#default_value' => variable_get('linkchecker_scan_nodetypes', array()),
'#options' => array_map('check_plain', node_get_types('names')),
'#description' => t('Enable link checking for the selected node type(s).'),
);
$comment_dependencies = '<div class="admin-dependencies">';
$comment_dependencies .= t('Depends on: !dependencies', array('!dependencies' => (module_exists('comment') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Comment')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Comment')))));
$comment_dependencies .= '</div>';
$form['settings']['linkchecker_scan_comments'] = array(
'#default_value' => variable_get('linkchecker_scan_comments', 0),
'#type' => 'checkbox',
'#title' => t('Scan comments for links'),
'#description' => t('Enable this checkbox if links in comments of the above selected node type(s) should be checked.') . $comment_dependencies,
'#disabled' => module_exists('comment') ? FALSE : TRUE,
);
$form['settings']['linkchecker_scan_blocks'] = array(
'#default_value' => variable_get('linkchecker_scan_blocks', 0),
'#type' => 'checkbox',
'#title' => t('Scan blocks for links'),
'#description' => t('Enable this checkbox if links in blocks should be checked.'),
);
$form['settings']['linkchecker_fqdn_only'] = array(
'#type' => 'select',
'#title' => t('What type of links should be checked?'),
'#description' => t('A full qualified link (http://example.com/foo/bar) to a page is considered external, whereas an absolute (/foo/bar) or relative link (node/123) without a domain is considered internal.'),
'#default_value' => variable_get('linkchecker_fqdn_only', 1),
'#options' => array(
'0' => t('Internal and external'),
'1' => t('External only (http://example.com/foo/bar)'),
'2' => t('Internal only (node/123)'),
),
);
$form['tag'] = array(
'#type' => 'fieldset',
'#title' => t('Link extraction'),
'#collapsible' => FALSE,
);
$form['tag']['linkchecker_extract_from_a'] = array(
'#default_value' => variable_get('linkchecker_extract_from_a', 1),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;a&gt;</code> and <code>&lt;area&gt;</code> tags'),
'#description' => t('Enable this checkbox if normal hyperlinks should be extracted. The anchor element defines a hyperlink, the named target destination for a hyperlink, or both. The area element defines a hot-spot region on an image, and associates it with a hypertext link.'),
);
$form['tag']['linkchecker_extract_from_audio'] = array(
'#default_value' => variable_get('linkchecker_extract_from_audio', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;audio&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in audio tags should be extracted. The audio element is used to embed audio content.'),
);
$form['tag']['linkchecker_extract_from_embed'] = array(
'#default_value' => variable_get('linkchecker_extract_from_embed', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;embed&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in embed tags should be extracted. This is an obsolete and non-standard element that was used for embedding plugins in past and should no longer used in modern websites.'),
);
$form['tag']['linkchecker_extract_from_iframe'] = array(
'#default_value' => variable_get('linkchecker_extract_from_iframe', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;iframe&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in iframe tags should be extracted. The iframe element is used to embed another HTML page into a page.'),
);
$form['tag']['linkchecker_extract_from_img'] = array(
'#default_value' => variable_get('linkchecker_extract_from_img', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;img&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in image tags should be extracted. The img element is used to add images to the content.'),
);
$form['tag']['linkchecker_extract_from_object'] = array(
'#default_value' => variable_get('linkchecker_extract_from_object', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;object&gt;</code> and <code>&lt;param&gt;</code> tags'),
'#description' => t('Enable this checkbox if multimedia and other links in object and their param tags should be extracted. The object tag is used for flash, java, quicktime and other applets.'),
);
$form['tag']['linkchecker_extract_from_source'] = array(
'#default_value' => variable_get('linkchecker_extract_from_source', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;source&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in source tags should be extracted. The source element is used to specify multiple media resources for audio and video elements.'),
);
$form['tag']['linkchecker_extract_from_video'] = array(
'#default_value' => variable_get('linkchecker_extract_from_video', 0),
'#type' => 'checkbox',
'#title' => t('Extract links in <code>&lt;video&gt;</code> tags'),
'#description' => t('Enable this checkbox if links in video tags should be extracted. The video element is used to embed video content.'),
);
// Get all filters available on the system.
$filters = filter_list_all();
$filter_options = array();
foreach ($filters as $filter) {
$name = $filter->module . '/' . $filter->delta;
if (in_array($name, explode('|', LINKCHECKER_DEFAULT_FILTER_BLACKLIST))) {
$filter_options[$name] = t('!title <span class="form-required">(Recommended)</span>', array('!title' => $filter->name));
}
else {
$filter_options[$name] = $filter->name;
}
}
$form['tag']['linkchecker_filter_blacklist'] = array(
'#type' => 'checkboxes',
'#title' => t('Filters disabled for link extraction'),
'#default_value' => variable_get('linkchecker_filter_blacklist', explode('|', LINKCHECKER_DEFAULT_FILTER_BLACKLIST)),
'#options' => $filter_options,
'#description' => t('If a filter has been enabled for an input format it runs first and afterwards the link extraction. This helps the link checker module to find all links normally created by custom filters (e.g. Markdown filter, Bbcode). All filters used as inline references (e.g. Weblink filter <code>[link: id]</code>) to other content and filters only wasting processing time (e.g. Line break converter) should be disabled. This setting does not have any effect on how content is shown on a page. This feature optimizes the internal link extraction process for link checker and prevents false alarms about broken links in content not having the real data of a link.'),
);
$count_lids_enabled = db_result(db_query("SELECT count(lid) FROM {linkchecker_links} WHERE status = %d", 1));
$count_lids_disabled = db_result(db_query("SELECT count(lid) FROM {linkchecker_links} WHERE status = %d", 0));
$form['check'] = array(
'#type' => 'fieldset',
'#title' => t('Check settings'),
'#description' => t('For simultaneous link checks it is recommended to install the <a href="@httprl">HTTP Parallel Request & Threading Library</a>. This may be <strong>necessary</strong> on larger sites with very many links (30.000+), but will also improve overall link check duration on smaller sites. Currently the site has @count links (@count_enabled enabled / @count_disabled disabled).', array('@httprl' => 'http://drupal.org/project/httprl', '@count' => $count_lids_enabled+$count_lids_disabled, '@count_enabled' => $count_lids_enabled, '@count_disabled' => $count_lids_disabled)),
'#collapsible' => FALSE,
);
$form['check']['linkchecker_check_library'] = array(
'#type' => 'select',
'#title' => t('Check library'),
'#description' => t('Defines the library that is used for checking links.'),
'#default_value' => variable_get('linkchecker_check_library', 'core'),
'#options' => array(
'core' => t('Drupal core'),
'httprl' => t('HTTP Parallel Request & Threading Library'),
),
);
$form['check']['linkchecker_check_connections_max'] = array(
'#type' => 'select',
'#title' => t('Number of simultaneous connections'),
'#description' => t('Defines the maximum number of simultaneous connections that can be opened by the server. <em>HTTP Parallel Request & Threading Library</em> make sure that a single domain is not overloaded beyond RFC limits. For small hosting plans with very limited CPU and RAM it may be required to reduce the default limit.'),
'#default_value' => variable_get('linkchecker_check_connections_max', 8),
'#options' => drupal_map_assoc(array(2, 4, 8, 16, 24, 32, 48, 64, 96, 128)),
);
$form['check']['linkchecker_check_useragent'] = array(
'#type' => 'select',
'#title' => t('User-Agent'),
'#description' => t('Defines the user agent that will be used for checking links on remote sites. If someone blocks the standard Drupal user agent you can try with a more common browser.'),
'#default_value' => variable_get('linkchecker_check_useragent', 'Drupal (+http://drupal.org/)'),
'#options' => array(
'Drupal (+http://drupal.org/)' => 'Drupal (+http://drupal.org/)',
'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko' => 'Windows 8.1 (x64), Internet Explorer 11.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586' => 'Windows 10 (x64), Edge',
'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' => 'Windows 8.1 (x64), Mozilla Firefox 47.0',
'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' => 'Windows 10 (x64), Mozilla Firefox 47.0',
),
);
$form['check']['linkchecker_check_links_interval'] = array(
'#type' => 'select',
'#title' => t('Check interval for links'),
'#description' => t('This interval setting defines how often cron will re-check the status of links.'),
'#default_value' => variable_get('linkchecker_check_links_interval', 2419200),
'#options' => drupal_map_assoc(array(86400, 172800, 259200, 604800, 1209600, 2419200, 4838400), 'format_interval'),
);
$form['check']['linkchecker_disable_link_check_for_urls'] = array(
'#default_value' => variable_get('linkchecker_disable_link_check_for_urls', LINKCHECKER_RESERVED_DOCUMENTATION_DOMAINS),
'#type' => 'textarea',
'#title' => t('Do not check the link status of links containing these URLs'),
'#description' => t('By default this list contains the domain names reserved for use in documentation and not available for registration. See <a href="@rfc-2606">RFC 2606</a>, Section 3 for more information. URLs on this list are still extracted, but the link setting <em>Check link status</em> becomes automatically disabled to prevent false alarms. If you change this list you need to clear all link data and re-analyze your content. Otherwise this setting will only affect new links added after the configuration change.', array('@rfc-2606' => 'http://www.rfc-editor.org/rfc/rfc2606.txt')),
'#wysiwyg' => FALSE,
);
$form['error'] = array(
'#type' => 'fieldset',
'#title' => t('Error handling'),
'#description' => t('Defines error handling and custom actions to be executed if specific HTTP requests are failing.'),
'#collapsible' => FALSE,
);
$linkchecker_default_impersonate_user = user_load(1);
$form['error']['linkchecker_impersonate_user'] = array(
'#type' => 'textfield',
'#title' => t('Impersonate user account'),
'#description' => t('If below error handling actions are executed they can be impersonated with a custom user account. By default this is user %name, but you are able to assign a custom user to allow easier identification of these automatic revision updates. Make sure you select a user with <em>full</em> permissions on your site or the user may not able to access and save all content.', array('%name' => $linkchecker_default_impersonate_user->name)),
'#size' => 30,
'#maxlength' => 60,
'#autocomplete_path' => 'user/autocomplete',
'#default_value' => variable_get('linkchecker_impersonate_user', ''),
);
$form['error']['linkchecker_action_status_code_301'] = array(
'#title' => t('Update permanently moved links'),
'#description' => t('If enabled, outdated links in content providing a status <em>Moved Permanently</em> (status code 301) are automatically updated to the most recent URL. If used, it is recommended to use a value of <em>three</em> to make sure this is not only a temporarily change. This feature trust sites to provide a valid permanent redirect. A new node revision is automatically created on link updates if <em>create new revision</em> is enabled in the <a href="@node_types">node type</a> workflow settings. It is recommended to create new revisions for all link checker enabled node types. Link updates are nevertheless always logged in <a href="@dblog">recent log entries</a>.', array('@dblog' => url('admin/reports/dblog'), '@node_types' => url('admin/content/types'))),
'#type' => 'select',
'#default_value' => variable_get('linkchecker_action_status_code_301', 0),
'#options' => array(
0 => t('Disabled'),
1 => t('After one failed check'),
2 => t('After two failed checks'),
3 => t('After three failed checks'),
5 => t('After five failed checks'),
10 => t('After ten failed checks'),
),
);
$form['error']['linkchecker_action_status_code_404'] = array(
'#title' => t('Unpublish node on file not found error'),
'#description' => t('If enabled, a node with one or more broken links (status code 404) will be unpublished and moved to moderation queue for review after the number of specified checks failed. If used, it is recommended to use a value of <em>three</em> to make sure this is not only a temporarily error.'),
'#type' => 'select',
'#default_value' => variable_get('linkchecker_action_status_code_404', 0),
'#options' => array(
0 => t('Disabled'),
1 => t('After one file not found error'),
2 => t('After two file not found errors'),
3 => t('After three file not found errors'),
5 => t('After five file not found errors'),
10 => t('After ten file not found errors'),
),
);
$form['error']['linkchecker_ignore_response_codes'] = array(
'#default_value' => variable_get('linkchecker_ignore_response_codes', "200\n206\n302\n304\n401\n403"),
'#type' => 'textarea',
'#title' => t("Don't treat these response codes as errors"),
'#description' => t('One HTTP status code per line, e.g. 403.'),
'#wysiwyg' => FALSE,
);
// Buttons are only required for testing and debugging reasons.
$description = '<p>' . t('These actions will either clear all link checker tables in the database and/or analyze all selected node types, blocks and cck fields (see settings above) for new/updated/removed links. Normally there is no need to press one of these buttons. Use this only for immediate cleanup tasks and to force a full re-build of the links to be checked in the linkchecker tables. Keep in mind that all custom link settings will be lost if you clear link data!') . '</p>';
$description .= '<p>' . t('<strong>Note</strong>: These functions ONLY collect the links, they do not evaluate the HTTP response codes, this will be done during normal cron runs.') . '</p>';
$form['clear'] = array(
'#type' => 'fieldset',
'#title' => t('Maintenance'),
'#description' => $description,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['clear']['linkchecker_analyze'] = array(
'#type' => 'submit',
'#value' => t('Reanalyze content for links'),
'#submit' => array('linkchecker_analyze_links_submit'),
);
$form['clear']['linkchecker_clear_analyze'] = array(
'#type' => 'submit',
'#value' => t('Clear link data and analyze content for links'),
'#submit' => array('linkchecker_clear_analyze_links_submit'),
);
$form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
$form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
return $form;
}
function linkchecker_admin_settings_form_validate($form, &$form_state) {
$form_state['values']['linkchecker_disable_link_check_for_urls'] = trim($form_state['values']['linkchecker_disable_link_check_for_urls']);
$form_state['values']['linkchecker_ignore_response_codes'] = trim($form_state['values']['linkchecker_ignore_response_codes']);
$ignore_response_codes = preg_split('/(\r\n?|\n)/', $form_state['values']['linkchecker_ignore_response_codes']);
foreach ($ignore_response_codes as $ignore_response_code) {
if (!_linkchecker_isvalid_response_code($ignore_response_code)) {
form_set_error('linkchecker_ignore_response_codes', t('Invalid response code %code found.', array('%code' => $ignore_response_code)));
}
}
// Prevent the removal of RFC documentation domains. This are the official and
// reserved documentation domains and not "example" hostnames!
$linkchecker_disable_link_check_for_urls = array_filter(preg_split('/(\r\n?|\n)/', $form_state['values']['linkchecker_disable_link_check_for_urls']));
$form_state['values']['linkchecker_disable_link_check_for_urls'] = implode("\n", array_unique(array_merge(explode("\n", LINKCHECKER_RESERVED_DOCUMENTATION_DOMAINS), $linkchecker_disable_link_check_for_urls)));
// Validate impersonation user name.
$linkchecker_impersonate_user = user_load(array('name' => $form_state['values']['linkchecker_impersonate_user']));
if (empty($linkchecker_impersonate_user->uid)) {
form_set_error('linkchecker_impersonate_user', t('User account %name cannot found.', array('%name' => $form_state['values']['linkchecker_impersonate_user'])));
}
}
function linkchecker_admin_settings_form_submit($form, &$form_state) {
// Exclude unnecessary elements.
unset($form_state['values']['linkchecker_analyze'], $form_state['values']['linkchecker_clear_analyze']);
// Save form settings.
system_settings_form_submit($form, $form_state);
// Re-scan items, if node types or comment or block selection have been changed.
$additional_nodetypes_selected = array_diff($form_state['values']['linkchecker_scan_nodetypes'], $form['settings']['linkchecker_scan_nodetypes']['#default_value']);
if (!empty($additional_nodetypes_selected) || $form_state['values']['linkchecker_scan_comments'] > $form['settings']['linkchecker_scan_comments']['#default_value']) {
$node_types = array_keys(array_filter($form_state['values']['linkchecker_scan_nodetypes']));
// If one or more node types have been selected.
if (!empty($node_types)) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_nodes($node_types));
// If comment scanning of node types has been selected.
if ($form_state['values']['linkchecker_scan_comments'] > $form['settings']['linkchecker_scan_comments']['#default_value']) {
batch_set(_linkchecker_batch_import_comments($node_types));
}
}
}
// If block scanning has been selected.
if ($form_state['values']['linkchecker_scan_blocks'] > $form['settings']['linkchecker_scan_blocks']['#default_value']) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_boxes());
}
}
/**
* Submit callback; Analyze all node types, boxes and cck fields.
*/
function linkchecker_analyze_links_submit($form, &$form_state) {
// Exclude unnecessary elements.
unset($form_state['values']['linkchecker_analyze'], $form_state['values']['linkchecker_clear_analyze']);
// Save form settings.
system_settings_form_submit($form, $form_state);
// Start batch and analyze all nodes.
$node_types = array_keys(array_filter(variable_get('linkchecker_scan_nodetypes', array())));
if (!empty($node_types)) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_nodes($node_types));
if (variable_get('linkchecker_scan_comments', 0)) {
batch_set(_linkchecker_batch_import_comments($node_types));
}
}
if (variable_get('linkchecker_scan_blocks', 0)) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_boxes());
}
}
/**
* Submit callback; Clear link data and analyze all node types, boxes and cck fields.
*/
function linkchecker_clear_analyze_links_submit($form, &$form_state) {
// Exclude unnecessary elements.
unset($form_state['values']['linkchecker_analyze'], $form_state['values']['linkchecker_clear_analyze']);
// Save form settings.
system_settings_form_submit($form, $form_state);
db_query("TRUNCATE TABLE {linkchecker_nodes}");
db_query("TRUNCATE TABLE {linkchecker_comments}");
db_query("TRUNCATE TABLE {linkchecker_boxes}");
db_query("TRUNCATE TABLE {linkchecker_links}");
// Start batch and analyze all nodes.
$node_types = array_keys(array_filter(variable_get('linkchecker_scan_nodetypes', array())));
if (!empty($node_types)) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_nodes($node_types));
if (variable_get('linkchecker_scan_comments', 0)) {
batch_set(_linkchecker_batch_import_comments($node_types));
}
}
if (variable_get('linkchecker_scan_blocks', 0)) {
module_load_include('inc', 'linkchecker', '/includes/linkchecker.batch');
batch_set(_linkchecker_batch_import_boxes());
}
}

View file

@ -0,0 +1,280 @@
<?php
/**
* @file
* Batch API callbacks for the linkchecker module.
*
* TODO: Test with 500.000+ nodes/comments/blocks and review memory consumption.
*/
/**
* Batch: Scan nodes for links.
*/
function _linkchecker_batch_import_nodes($node_types = array()) {
// Get all active {node}.nid's.
$result = db_query('SELECT n.nid FROM {node} n WHERE n.status = %d AND n.type IN (' . db_placeholders($node_types, 'varchar') . ') ORDER BY n.nid', array_merge(array(1), $node_types));
$operations = array();
while ($row = db_fetch_array($result)) {
$operations[] = array('_linkchecker_batch_node_import_op', array($row['nid']));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_node_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
);
return $batch;
}
/**
* Batch operation: Scan ony by one node for links.
*/
function _linkchecker_batch_node_import_op($nid, &$context) {
// Load the node and scan for links.
$node = node_load($nid, NULL, TRUE);
_linkchecker_add_node_links($node);
// Store results for post-processing in the finished callback.
$context['results'][] = $node->nid;
$context['message'] = t('Node: @title', array('@title' => $node->title));
}
function _linkchecker_batch_node_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'One node has been scanned.', '@count nodes have been scanned.');
}
else {
$message = t('Scanning for links in nodes have failed with an error.');
}
drupal_set_message($message);
}
/**
* Batch: Scan comments for links.
*/
function _linkchecker_batch_import_comments($node_types = array()) {
// Get all active {comments}.cid's.
$result = db_query('SELECT c.cid FROM {comments} c INNER JOIN {node} n ON c.nid = n.nid WHERE c.status = %d AND n.status = %d AND n.type IN (' . db_placeholders($node_types, 'varchar') . ') ORDER BY c.cid', array_merge(array(COMMENT_PUBLISHED, 1), $node_types));
$operations = array();
while ($row = db_fetch_array($result)) {
$operations[] = array('_linkchecker_batch_comments_import_op', array($row['cid']));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_comments_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
);
return $batch;
}
/**
* Batch operation: Scan ony by one comment for links.
*/
function _linkchecker_batch_comments_import_op($cid, &$context) {
// Load the comment and scan for links.
$comment = _linkchecker_comment_load($cid);
_linkchecker_add_comment_links($comment);
// Store results for post-processing in the finished callback.
$context['results'][] = $comment['cid'];
$context['message'] = t('Comment: @title', array('@title' => $comment['subject']));
}
function _linkchecker_batch_comments_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'One comment has been scanned.', '@count comments have been scanned.');
}
else {
$message = t('Scanning for links in comments have failed with an error.');
}
drupal_set_message($message);
}
/**
* Batch: Scan blocks for links.
*/
function _linkchecker_batch_import_boxes() {
// Get all {boxes}.bid's as block module suxxx and has no usable hooks.
$result = db_query('SELECT b.bid FROM {boxes} b ORDER BY b.bid');
$operations = array();
while ($row = db_fetch_array($result)) {
$operations[] = array('_linkchecker_batch_import_boxes_op', array($row['bid']));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_box_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
);
return $batch;
}
/**
* Batch operation: Scan ony by one block for links.
*/
function _linkchecker_batch_import_boxes_op($bid, &$context) {
// Load the box and scan for links.
$box = block_box_get($bid);
_linkchecker_add_box_links($box, $box['bid']);
// Store some result for post-processing in the finished callback.
$context['results'][] = $box['bid'];
$context['message'] = t('Block: @title', array('@title' => $box['info']));
}
function _linkchecker_batch_box_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'One block has been scanned.', '@count blocks have been scanned.');
}
else {
$message = t('Scanning for links in blocks have failed with an error.');
}
drupal_set_message($message);
}
/**
* Recurring scans of a single node via batch API.
*
* @param int $nid
* The unique node id to scan for links.
* @param int $missing_links_count
* The number of links not yet added to linkchecker_links table. By this
* number the re-scan rounds are calulated.
* @return
* The batch task definition.
*/
function _linkchecker_batch_import_single_node($nid, $missing_links_count) {
$operations = array();
for ($i = 0; $i <= $missing_links_count; $i = $i+LINKCHECKER_SCAN_MAX_LINKS_PER_RUN) {
$operations[] = array('_linkchecker_batch_single_node_import_op', array($nid));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_single_node_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
'progress_message' => t('Remaining @remaining of @total scans.'),
);
return $batch;
}
function _linkchecker_batch_single_node_import_op($nid, &$context) {
// Load the node and scan for links.
$node = node_load($nid, NULL, TRUE);
_linkchecker_add_node_links($node, TRUE);
// Store results for post-processing in the finished callback.
$context['results'][] = $node->nid;
$context['message'] = t('Node: @title', array('@title' => $node->title));
}
function _linkchecker_batch_single_node_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'Node @nid has been re-scanned once to collect all links.', 'Node @nid has been re-scanned @count times to collect all links.', array('@nid' => $results[0]));
}
else {
$message = t('Recurring scanning for links in node @nid have failed with an error.', array('@nid' => $results[0]));
}
drupal_set_message($message);
}
/**
* Recurring scans of a single comment via batch API.
*
* @param int $cid
* The unique comment id to scan for links.
* @param int $missing_links_count
* The number of links not yet added to linkchecker_links table. By this
* number the re-scan rounds are calulated.
* @return
* The batch task definition.
*/
function _linkchecker_batch_import_single_comment($cid, $missing_links_count) {
$operations = array();
for ($i = 0; $i <= $missing_links_count; $i = $i+LINKCHECKER_SCAN_MAX_LINKS_PER_RUN) {
$operations[] = array('_linkchecker_batch_single_comment_import_op', array($cid));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_single_comment_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
'progress_message' => t('Remaining @remaining of @total scans.'),
);
return $batch;
}
function _linkchecker_batch_single_comment_import_op($cid, &$context) {
$comment = _linkchecker_comment_load($cid);
_linkchecker_add_comment_links($comment, TRUE);
// Store results for post-processing in the finished callback.
$context['results'][] = $comment['cid'];
$context['message'] = t('Comment: @title', array('@title' => $comment['subject']));
}
function _linkchecker_batch_single_comment_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'Comment @cid has been re-scanned once to collect all links.', 'Comment @cid has been re-scanned @count times to collect all links.', array('@cid' => $results[0]));
}
else {
$message = t('Recurring scanning for links in comment @cid have failed with an error.', array('@cid' => $results[0]));
}
drupal_set_message($message);
}
/**
* Recurring scans of a single block via batch API.
*
* @param int $bid
* The unique block id to scan for links.
* @param int $missing_links_count
* The number of links not yet added to linkchecker_links table. By this
* number the re-scan rounds are calulated.
* @return
* The batch task definition.
*/
function _linkchecker_batch_import_single_box($bid, $missing_links_count) {
$operations = array();
for ($i = 0; $i <= $missing_links_count; $i = $i+LINKCHECKER_SCAN_MAX_LINKS_PER_RUN) {
$operations[] = array('_linkchecker_batch_single_box_import_op', array($bid));
}
$batch = array(
'file' => drupal_get_path('module', 'linkchecker') .'/includes/linkchecker.batch.inc',
'finished' => '_linkchecker_batch_single_box_import_finished',
'operations' => $operations,
'title' => t('Scanning for links'),
'progress_message' => t('Remaining @remaining of @total scans.'),
);
return $batch;
}
function _linkchecker_batch_single_box_import_op($bid, &$context) {
// Load the box and scan for links.
$box = block_box_get($bid);
_linkchecker_add_box_links($box, $box['bid'], TRUE);
// Store some result for post-processing in the finished callback.
$context['results'][] = $box['bid'];
$context['message'] = t('Block: @title', array('@title' => $box['info']));
}
function _linkchecker_batch_single_box_import_finished($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'Block @bid has been re-scanned once to collect all links.', 'Block @bid has been re-scanned @count times to collect all links.', array('@bid' => $results[0]));
}
else {
$message = t('Recurring scanning for links in block @bid have failed with an error.', array('@bid' => $results[0]));
}
drupal_set_message($message);
}

View file

@ -0,0 +1,256 @@
<?php
/**
* @file
* User page callbacks for the linkchecker module.
*/
/**
* Menu callback for general reporting.
*/
function linkchecker_admin_report_page() {
$ignore_response_codes = preg_split('/(\r\n?|\n)/', variable_get('linkchecker_ignore_response_codes', "200\n206\n302\n304\n401\n403"));
// Search for broken links in nodes and comments and blocks of all users.
// TODO: Try to make subselect result smaller.
$links_report_sql = "SELECT ll.*
FROM {linkchecker_links} ll
INNER JOIN (
SELECT lid FROM (
SELECT DISTINCT lid FROM {linkchecker_boxes}
UNION
SELECT DISTINCT lid FROM {linkchecker_comments}
UNION
SELECT DISTINCT lid FROM {linkchecker_nodes}
) q1
) q2 ON q2.lid = ll.lid
WHERE ll.last_checked <> %d AND ll.status = %d AND ll.code NOT IN (" . db_placeholders($ignore_response_codes, 'int') . ")";
// Build the array variable with all parameters for the pager_query.
$links_report_parameters = array_merge(array(0, 1), $ignore_response_codes);
return _linkchecker_report_page($links_report_sql, $links_report_parameters);
}
/**
* Menu callback for author specific reporting.
*/
function linkchecker_user_report_page($account) {
drupal_set_title(check_plain($account->name));
$ignore_response_codes = preg_split('/(\r\n?|\n)/', variable_get('linkchecker_ignore_response_codes', "200\n206\n302\n304\n401\n403"));
// Search for broken links in nodes and comments of the current user.
if (module_exists('comment') && variable_get('linkchecker_scan_comments', 0)) {
$links_report_sql = "SELECT ll.*
FROM {linkchecker_links} ll
INNER JOIN (
SELECT lid FROM (
SELECT DISTINCT ll.lid
FROM {node} n
INNER JOIN {node_revisions} r ON r.vid = n.vid
INNER JOIN {linkchecker_nodes} ln ON ln.nid = n.nid
INNER JOIN {linkchecker_links} ll ON ll.lid = ln.lid AND ll.last_checked <> %d AND ll.status = %d AND ll.code NOT IN (" . db_placeholders($ignore_response_codes, 'int') . ")
WHERE n.uid = %d OR r.uid = %d
UNION
SELECT DISTINCT ll.lid
FROM {comments} c
INNER JOIN {linkchecker_comments} lc ON lc.cid = c.cid
INNER JOIN {linkchecker_links} ll ON ll.lid = lc.lid AND ll.last_checked <> %d AND ll.status = %d AND ll.code NOT IN (" . db_placeholders($ignore_response_codes, 'int') . ")
WHERE c.uid = %d
) q1
) q2 ON q2.lid = ll.lid";
// Build the array variable with all parameters for the pager_query with comment module enabled.
$links_report_parameters = array_merge(array(0, 1), $ignore_response_codes, array($account->uid, $account->uid, 0, 1), $ignore_response_codes, array($account->uid));
}
else {
// Search for broken links in nodes of the current user.
$links_report_sql = "SELECT ll.*
FROM {linkchecker_links} ll
INNER JOIN (
SELECT lid FROM (
SELECT DISTINCT ll.lid
FROM {node} n
INNER JOIN {node_revisions} r ON r.vid = n.vid
INNER JOIN {linkchecker_nodes} ln ON ln.nid = n.nid
INNER JOIN {linkchecker_links} ll ON ll.lid = ln.lid AND ll.last_checked <> %d AND ll.status = %d AND ll.code NOT IN (" . db_placeholders($ignore_response_codes, 'int') . ")
WHERE n.uid = %d OR r.uid = %d
) q1
) q2 ON q2.lid = ll.lid";
// Build the array variable with all parameters for the pager_query with comment module disabled.
$links_report_parameters = array_merge(array(0, 1), $ignore_response_codes, array($account->uid, $account->uid));
}
return _linkchecker_report_page($links_report_sql, $links_report_parameters, $account);
}
function _linkchecker_report_page($links_report_sql, $links_report_parameters = NULL, $account = NULL) {
$links_unchecked = db_result(db_query('SELECT COUNT(1) FROM {linkchecker_links} WHERE last_checked = %d AND status = %d', 0, 1));
if ($links_unchecked > 0) {
$links_all = db_result(db_query('SELECT COUNT(1) FROM {linkchecker_links} WHERE status = %d', 1));
drupal_set_message(format_plural($links_unchecked,
'There is 1 unchecked link of about @links_all links in the database. Please be patient until all links have been checked via cron.',
'There are @count unchecked links of about @links_all links in the database. Please be patient until all links have been checked via cron.',
array('@links_all' => $links_all)), 'warning');
}
$header = array(
array('data' => t('URL'), 'field' => 'url', 'sort' => 'desc'),
array('data' => t('Response'), 'field' => 'code', 'sort' => 'desc'),
array('data' => t('Error'), 'field' => 'error'),
array('data' => t('Operations')),
);
$result = pager_query($links_report_sql . tablesort_sql($header), 50, 0, NULL, $links_report_parameters);
// Evaluate permission once for performance reasons.
$access_edit_link_settings = user_access('edit link settings');
$access_administer_blocks = user_access('administer blocks');
$access_administer_redirects = user_access('administer redirects');
$rows = array();
while ($link = db_fetch_object($result)) {
// Get the node, block and comment IDs that refer to this broken link and
// that the current user has access to.
$nids = _linkchecker_link_node_ids($link, $account);
$cids = _linkchecker_link_comment_ids($link, $account);
$bids = _linkchecker_link_block_ids($link);
// If the user does not have access to see this link anywhere, do not
// display it, for reasons explained in _linkchecker_link_access(). We
// still need to fill the table row, though, so as not to throw off the
// number of items in the pager.
if (empty($nids) && empty($cids) && empty($bids)) {
$rows[] = array(array('data' => t('Permission restrictions deny you access to this broken link.'), 'colspan' => count($header)));
continue;
}
$links = array();
// Show links to link settings.
if ($access_edit_link_settings) {
$links[] = l(t('Edit link settings'), 'linkchecker/' . $link->lid . '/edit', array('query' => drupal_get_destination()));
}
// Show link to nodes having this broken link.
foreach ($nids as $nid) {
$links[] = l(t('Edit node @node', array('@node' => $nid)), 'node/' . $nid . '/edit', array('query' => drupal_get_destination()));
}
// Show link to comments having this broken link.
if (module_exists('comment') && variable_get('linkchecker_scan_comments', 0)) {
foreach ($cids as $cid) {
$links[] = l(t('Edit comment @comment', array('@comment' => $cid)), 'comment/edit/' . $cid, array('query' => drupal_get_destination()));
}
}
// Show link to blocks having this broken link.
if ($access_administer_blocks) {
foreach ($bids as $bid) {
$links[] = l(t('Edit block @block', array('@block' => $bid)), 'admin/build/block/configure/block/' . $bid, array('query' => drupal_get_destination()));
}
}
// Show link to redirect this broken internal link.
if (module_exists('path_redirect') && $access_administer_redirects && _linkchecker_is_internal_url($link)) {
$links[] = l(t('Create redirection'), 'admin/build/path-redirect/add', array('query' => array('source' => $link->internal)));
}
// Create table data for output. Use inline styles to prevent extra CSS file.
$rows[] = array(
l(_filter_url_trim($link->url, 40), $link->url),
$link->code,
check_plain($link->error),
theme('item_list', $links),
);
}
if (empty($rows)) {
$rows[] = array(array('data' => t('No broken links have been found.'), 'colspan' => count($header)));
}
$output = theme('table', $header, $rows);
$output .= theme('pager', NULL, 3000, 0);
return $output;
}
function linkchecker_link_edit_form(&$form_state, $link) {
$form['settings'] = array(
'#type' => 'fieldset',
'#title' => t('Settings'),
'#collapsible' => FALSE,
'#description' => t('The link <a href="@url">@url</a> was last checked on @last_checked and failed @fail_count times.', array('@url' => $link['url'], '@fail_count' => $link['fail_count'], '@last_checked' => format_date($link['last_checked'])))
);
$form['settings']['lid'] = array('#type' => 'hidden', '#value' => $link['lid']);
$form['settings']['url'] = array('#type' => 'hidden', '#value' => $link['url']);
$form['settings']['method'] = array(
'#type' => 'select',
'#title' => t('Select request method'),
'#default_value' => $link['method'],
'#options' => array(
'HEAD' => t('HEAD'),
'GET' => t('GET'),
),
'#description' => t('Select the request method used for link checks of this link. If you encounter issues like status code 500 errors with the HEAD request method you should try the GET request method before ignoring a link.'),
);
$form['settings']['status'] = array(
'#default_value' => $link['status'],
'#type' => 'checkbox',
'#title' => t('Check link status'),
'#description' => t("Uncheck if you wish to ignore this link. Use this setting only as a last resort if there is no other way to solve a failed link check."),
);
$form['maintenance'] = array(
'#type' => 'fieldset',
'#title' => t('Maintenance'),
'#collapsible' => FALSE,
);
$form['maintenance']['recheck'] = array(
'#default_value' => 0,
'#type' => 'checkbox',
'#title' => t('Re-check link status on next cron run'),
'#description' => t('Enable this checkbox if you want to re-check the link during the next cron job rather than wait for the next scheduled check on @date.', array('@date' => format_date($link['last_checked'] + variable_get('linkchecker_check_links_interval', 2419200)))),
);
$form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
$form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults'));
return $form;
}
function linkchecker_link_edit_form_submit($form, &$form_state) {
// Force asap link re-check.
if ($form_state['values']['recheck']) {
db_query("UPDATE {linkchecker_links} SET last_checked = %d WHERE lid = %d", 0, $form_state['values']['lid']);
drupal_set_message(t('The link %url will be checked again on the next cron run.', array('%url' => $form_state['values']['url'])));
}
if ($form_state['values']['method'] != $form['settings']['method']['#default_value']) {
// Update settings and reset statistics for a quick re-check.
db_query("UPDATE {linkchecker_links} SET method = '%s', fail_count = %d, last_checked = %d, status = %d WHERE lid = %d", $form_state['values']['method'], 0, 0, $form_state['values']['status'], $form_state['values']['lid']);
drupal_set_message(t('The link settings for %url have been saved and the fail counter has been reset.', array('%url' => $form_state['values']['url'])));
}
else {
// Update setting only.
db_query("UPDATE {linkchecker_links} SET method = '%s', status = %d WHERE lid = %d", $form_state['values']['method'], $form_state['values']['status'], $form_state['values']['lid']);
drupal_set_message(t('The link settings for %url have been saved.', array('%url' => $form_state['values']['url'])));
}
}
function _linkchecker_is_internal_url(&$link) {
global $base_url;
if (strpos($link->url, $base_url) === 0) {
$link->internal = trim(substr($link->url, strlen($base_url)), " \t\r\n\0\\/");
return TRUE;
}
}