/[drupal]/contributions/modules/links/links_admin.module
ViewVC logotype

Contents of /contributions/modules/links/links_admin.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.11 - (show annotations) (download) (as text)
Tue Dec 30 03:46:09 2008 UTC (10 months, 3 weeks ago) by syscrusher
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-BETA5, HEAD
Branch point for: DRUPAL-6--1
Changes since 1.10: +181 -65 lines
File MIME type: text/x-php
        links_admin.module is now fully functional and ready
        for a beta release.

        Added feature to allow deleting node references from
        a given link on the sub-screen.

        Added filter capabilities to the main links management
        screen.

        Added links_admin.css stylesheet to format the filter
        criteria form.

        General cleanup of code and moving functions into more
        appropriate locations within the file.
1 <?php
2 /** @file
3 * $Id: links_admin.module,v 1.10 2008/12/29 23:09:33 syscrusher Exp $
4 *
5 * links_admin provides a user interface to manage all links cataloged for
6 * the site, regardless of which module(s) is/are using each link.
7 *
8 * Author: Scott Courtney (drupal.org user "syscrusher")
9 *
10 * RELEASE STATUS: This is BETA code; do not use in production sites.
11 */
12
13 require_once "links.inc";
14
15 /**
16 * Implements hook_init()
17 */
18 function links_admin_init() {
19 $links_path = drupal_get_path('module','links');
20 drupal_add_css($links_path . '/links_admin.css', 'module', 'all', TRUE);
21 }
22
23 /**
24 * Implements hook_help().
25 */
26 function links_admin_help($section="") {
27 switch ($section) {
28 case 'admin/help#links_admin': return t('Administer the database of links');
29 }
30 }
31
32 /**
33 * Returns an array of the permission names that apply to this module
34 */
35 function links_admin_perm() {
36 return array('administer links','change url globally','access links statistics');
37 }
38
39 /**
40 * Implements hook_menu
41 */
42 function links_admin_menu() {
43 $items = array();
44 $items['admin/settings/links/links_admin'] = array(
45 'title' => t('links admin module'),
46 'description' => t('Configure settings for the links administration module.'),
47 'page callback' => 'drupal_get_form',
48 'page arguments' => array('links_admin_settings_form'),
49 'access callback' => 'user_access',
50 'access arguments' => array('administer site configuration'),
51 'type' => MENU_LOCAL_TASK,
52 'weight' => 5,
53 );
54 $items['admin/content/links'] = array(
55 'title' => t('Links database'),
56 'description' => t('Update individual links, and mark links for scheduled validation.'),
57 'page callback' => 'drupal_get_form',
58 'page arguments' => array('links_admin_list_form'),
59 'access callback' => 'user_access',
60 'access arguments' => array('administer links'),
61 );
62 $items['admin/content/links/%links_admin_ref'] = array(
63 'type' => MENU_CALLBACK,
64 'access callback' => 'user_access',
65 'access arguments' => array('administer links'),
66 'page callback' => 'drupal_get_form',
67 'page arguments' => array('links_admin_edit_form', 3),
68 );
69 $items['admin/content/links/list'] = array(
70 'title' => t('administer links database'),
71 'page callback' => 'links_admin_list_page',
72 'access callback' => 'user_access',
73 'access arguments' => array('administer links'),
74 'type' => MENU_CALLBACK,
75 'weight' => -10,
76 );
77 return $items;
78 }
79
80 /**
81 * Implements hook_theme to register our theme functions
82 */
83 function links_admin_theme($existing, $type, $theme, $path) {
84 return array(
85 'links_admin_list_form' => array(
86 'arguments' => array('element' => NULL),
87 ),
88 'links_admin_edit_form' => array(
89 'arguments' => array('element' => NULL),
90 ),
91 'links_admin_filter' => array(
92 'arguments' => array('element' => NULL),
93 ),
94 /*
95 'links_admin_list_row' => array(
96 'arguments' => array('element' => NULL),
97 ),
98 */
99 );
100 }
101
102 // Menu callback functions
103
104 /**
105 * Manages the global settings for the module
106 */
107 function links_admin_settings_form() {
108 $form = array();
109
110 $form['description'] = array(
111 '#type' => 'markup',
112 '#value' => '<p>' . t('These settings control the behavior of the link-management services.'),
113 );
114
115 $form['display_settings'] = array(
116 '#type' => 'fieldset',
117 '#title' => t('Display settings'),
118 );
119
120 $form['display_settings']['links_admin_pagelines'] = array(
121 '#type' => 'select',
122 '#title' => t("Links per page in administrative screens"),
123 '#options' => drupal_map_assoc(array(5,10,15,20,25,30,40,50)),
124 '#default_value' => variable_get('links_admin_pagelines',10),
125 '#description' => t("This setting controls how many links will be displayed per page in link administration pages."),
126 );
127
128 $form['display_settings']['links_admin_collapse_filter_form'] = array(
129 '#type' => 'checkbox',
130 '#title' => t("Always collapse filter criteria form"),
131 '#default_value' => variable_get('links_admin_collapse_filter_form', FALSE),
132 '#description' => t("If selected, this option always hides the sorting criteria form unless you expand it. Otherwise, the form is expanded when there are currently active filter criteria, and collapsed if there are none."),
133 );
134
135 return system_settings_form($form);
136 }
137
138 function _links_admin_filter_to_html($filter, $match_fields, $count_fields) {
139 $match_sense = array('match_yes'=>t('matches'), 'match_no'=>t('does not match'));
140 $html = '';
141 switch ($filter['type']) {
142 case 'match':
143 $html .= $match_fields[$filter['match_field']] . ' ' . $match_sense[$filter['match_sense']] . ' ';
144 $html .= '&quot;' . check_plain($filter['match_string']) . '&quot;';
145 break;
146 case 'count':
147 $html .= $count_fields[$filter['count_field']] . ' ' . htmlspecialchars($filter['count_op']) . ' ' . $filter['count_value'];
148 break;
149 }
150 return $html;
151 }
152
153 function _links_admin_filters_to_html() {
154 $match_fields = _links_admin_get_filter_match_fields();
155 $count_fields = _links_admin_get_filter_count_fields();
156 $filters = $_SESSION['links_admin_filter'];
157 $html = t('No filter currently set.');
158 if (is_array($filters) && count($filters)) {
159 $html = t('Current filter displays links matching all of the following:');
160 $html .= '<ul>';
161 foreach ($filters as $filter) {
162 $html .= '<li>' . _links_admin_filter_to_html($filter, $match_fields, $count_fields) . '</li>';
163 }
164 $html .= '</ul>';
165 }
166 return $html;
167 }
168
169 function _links_admin_filter_to_sql($filter, $match_fields, $count_fields) {
170 $sql = '';
171 switch ($filter['type']) {
172 case 'match':
173 // Respect ^ and $ metacharacters from regexp by simulating with % placement in LIKE
174 $match_string = check_plain($filter['match_string']);
175 if (preg_match('/^\^/', $match_string)) {
176 $match_string = substr($match_string, 1);
177 } else {
178 // Double the % because something in the Drupal stack uses it as a format
179 // string in an sprintf() call.
180 $match_string = '%%' . $match_string;
181 }
182 if (preg_match('/\$$/', $match_string)) {
183 $match_string = substr($match_string, 0, strlen($match_string)-1);
184 } else {
185 // Double the % because something in the Drupal stack uses it as a format
186 // string in an sprintf() call.
187 $match_string .= '%%';
188 }
189 $sql = " (" . $filter['match_field'] . ($filter['match_sense'] == 'match_yes' ? " LIKE '" : " NOT LIKE '") . $match_string . "') ";
190 break;
191 case 'count':
192 $sql = " (" . $filter['count_field'] . $filter['count_op'] . intval($filter['count_value']) . ") ";
193 break;
194 }
195 return $sql;
196 }
197
198 function _links_admin_filters_to_sql() {
199 $match_fields = _links_admin_get_filter_match_fields();
200 $count_fields = _links_admin_get_filter_count_fields();
201 $filters = $_SESSION['links_admin_filter'];
202 $sql = '';
203 $sql_conditions = array();
204 if (is_array($filters) && count($filters)) {
205 foreach ($filters as $filter) {
206 $sql_cond = _links_admin_filter_to_sql($filter, $match_fields, $count_fields);
207 if (! empty($sql_cond)) {
208 $sql_conditions[] = $sql_cond;
209 }
210 }
211 }
212 if (count($sql_conditions)) {
213 $sql = ' HAVING (' . implode(' AND ', $sql_conditions) . ') ';
214 }
215 return $sql;
216 }
217
218 function _links_admin_get_filter_match_fields() {
219 $match_fields = array(
220 'l.url' => t('Link URL'),
221 'master_title' => t('Link Title'),
222 );
223 return $match_fields;
224 }
225
226 function _links_admin_get_filter_count_fields() {
227 $count_fields = array(
228 'total_clicks' => t('Total clicks'),
229 'node_count' => t('Referring nodes'),
230 );
231 return $count_fields;
232 }
233
234 /**
235 * Returns a form to filter which links are displayed.
236 */
237 function links_admin_filter_form() {
238 $match_fields = _links_admin_get_filter_match_fields();
239 $count_fields = _links_admin_get_filter_count_fields();
240 $filters = $_SESSION['links_admin_filter'];
241 $match_sense = array(
242 'match_yes' => t('matches'),
243 'match_no' => t('does not match'),
244 );
245 if (variable_get('links_admin_collapse_filter_form', FALSE)) {
246 $collapsed = TRUE;
247 } else {
248 $collapsed = ! (is_array($filters) && count($filters));
249 }
250 $fieldset = array(
251 '#type' => 'fieldset',
252 '#title' => t('Filter criteria'),
253 '#tree' => TRUE,
254 '#collapsible' => TRUE,
255 '#collapsed' => $collapsed,
256 '#weight' => -8,
257 '#theme' => 'links_admin_filter',
258 'filter_current' => array(
259 '#type' => 'markup',
260 '#value' => '<p>' . _links_admin_filters_to_html() . '</p>', // . '<p>SQL: ' . _links_admin_filters_to_sql() . '</p>',
261 '#weight' => -10,
262 ),
263 'match_field' => array(
264 '#type' => 'select',
265 '#title' => t('Show only links where'),
266 '#default_value' => '',
267 '#options' => $match_fields,
268 ),
269 'match_sense' => array(
270 '#type' => 'radios',
271 '#default_value' => 'match_yes',
272 '#options' => $match_sense,
273 ),
274 'match_string' => array(
275 '#type' => 'textfield',
276 '#default_value' => '',
277 '#size' => 60,
278 '#maxlength' => 200,
279 '#description' => t('Specify a string to match. Metacharacter ^ matches the start of the string, and $ matches the end of the string, so for instance you can use &quot;.com$&quot; to match URLs like &quot;example.com&quot; but not &quot;www.common.example.net&quot;.'),
280 ),
281 'count_field' => array(
282 '#type' => 'select',
283 '#title' => t('Show only links where'),
284 '#default_value' => '',
285 '#options' => $count_fields,
286 ),
287 'count_op' => array(
288 '#type' => 'select',
289 '#default_value' => '',
290 '#options' => drupal_map_assoc(array('=', '>', '<', '>=', '<=')),
291 ),
292 'count_value' => array(
293 '#type' => 'textfield',
294 '#default_value' => '',
295 '#size' => 10,
296 '#maxlength' => 10,
297 '#description' => t('Specify an integer value to match against the selected numeric field.'),
298 ),
299 'filter_submit' => array(
300 '#type' => 'submit',
301 '#value' => t('Add filter'),
302 '#submit' => array('links_admin_links_filter_add_submit'),
303 '#weight' => 9,
304 ),
305 'filter_reset' => array(
306 '#type' => 'submit',
307 '#value' => t('Reset filters'),
308 '#submit' => array('links_admin_links_filter_reset_submit'),
309 '#weight' => 10,
310 ),
311 );
312 $form = array(
313 'filter' => $fieldset,
314 );
315 return $form;
316 }
317
318 /**
319 * Returns a list of the available operations from this module.
320 * Other modules can add their own by implementing a similar
321 * function named [your_module]_links_admin_operations().
322 *
323 * In our particular case, the name looks strange because
324 * we are invoking our own module from module_invoke_all().
325 *
326 * The $section parameter is one of the following:
327 * 'links' Master links management screen
328 * 'links_node' Node-specific references screen
329 */
330 function links_admin_links_admin_operations($section) {
331 switch ($section) {
332 case 'links':
333 $operations = array(
334 'update' => array(
335 'label' => t('Update master table only (selection checkboxes ignored)'),
336 // This one is done directly by the form submit function
337 'callback' => NULL,
338 ),
339 'update_node_titles' => array(
340 'label' => t('Use title from master table for all references to selected links'),
341 'callback' => 'links_admin_force_master_title',
342 ),
343 'delete' => array(
344 'label' => t('Delete selected links'),
345 'callback' => NULL,
346 ),
347 );
348 break;
349 case 'links_node':
350 $operations = array(
351 'update' => array(
352 'label' => t('Update titles only (selection checkboxes ignored)'),
353 // This one is done directly by the form submit function
354 'callback' => NULL,
355 ),
356 'update_node_titles' => array(
357 'label' => t('Use title from master table for the selected node references'),
358 'callback' => 'links_admin_force_master_title_nodes',
359 ),
360 'delete' => array(
361 'label' => t('Delete this link from the selected nodes'),
362 'callback' => NULL,
363 ),
364 );
365 break;
366 default:
367 $operations = NULL;
368 }
369 return $operations;
370 }
371
372 /*
373 * Returns the header array for the main links management page
374 */
375 function _links_admin_form_header() {
376 $header = array(
377 t('Select'),
378 array('data'=>t('Link ID'), 'field'=>'l.lid'),
379 array('data'=>t('Title'), 'field'=>'l.link_title'),
380 array('data'=>t('Link URL'), 'field'=>'l.url'),
381 t('Statistics'),
382 );
383 return $header;
384 }
385
386 /**
387 * Returns a links administration form for links and the nodes that refer to them
388 */
389 function links_admin_list_form($form_state) {
390 if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
391 $selected = _links_admin_get_selected_lids($form_state['values']);
392 $form_state['rebuild'] = TRUE;
393 $form = links_admin_multiple_delete_confirm($selected);
394 return $form;
395 }
396
397 $form = links_admin_filter_form();
398
399 $header = _links_admin_form_header();
400
401 $sort_sql = tablesort_sql($header);
402 if (empty($sort_sql)) {
403 $sort_sql = "ORDER BY l.lid";
404 }
405
406 $filter_sql = _links_admin_filters_to_sql();
407
408 $links_per_page = variable_get('links_admin_pagelines',10);
409 // STUB STUB STUB
410 $links_per_page = 5;
411
412 $sql = "SELECT l.lid, l.url, l.link_title AS master_title, COUNT(ln.nid) AS node_count, SUM(ln.clicks) AS total_clicks FROM {links} l LEFT JOIN {links_node} ln ON l.lid=ln.lid GROUP BY l.lid " . $filter_sql . $sort_sql;
413
414 // The count SQL gets a little fugly here...
415 $count_sql = "SELECT COUNT(*) FROM (" . $sql . ") AS count_derived_table";
416
417 $result = pager_query($sql, $links_per_page, 0, $count_sql, NULL);
418
419 while ($row = db_fetch_array($result)) {
420 $lid = $row['lid'];
421 $form[$lid] = array(
422 '#type' => 'markup',
423 '#tree' => TRUE,
424 );
425 $form[$lid]['select'] = array(
426 '#type' => 'checkbox',
427 '#return_value' => 1,
428 );
429 $form[$lid]['lid_display'] = array(
430 '#type' => 'markup',
431 '#value' => $lid,
432 );
433 $form[$lid]['title'] = array(
434 '#type' => 'textfield',
435 '#default_value' => $row['master_title'],
436 '#size' => 30,
437 );
438 $form[$lid]['url'] = array(
439 '#type' => 'textfield',
440 '#default_value' => $row['url'],
441 '#size' => 40,
442 );
443 $form[$lid]['stats'] = array(
444 '#type' => 'markup',
445 '#value' => t('Used by %nodes nodes.<br/>%clicks total clicks.', array('%nodes'=>$row['node_count'], '%clicks'=>$row['total_clicks'])),
446 );
447 $form[$lid]['lid'] = array(
448 '#type' => 'hidden',
449 '#value' => $lid,
450 );
451 $form[$lid]['#theme'] = 'links_admin_list_row';
452 }
453
454 $form['pager'] = array('#value' => theme('pager', NULL, $links_per_page, 0));
455
456 $form['update_options'] = array(
457 '#type' => 'fieldset',
458 '#title' => t('Update options'),
459 '#tree' => FALSE,
460 '#collapsible' => TRUE,
461 '#collapsed' => FALSE,
462 );
463
464 $options = array();
465 foreach (module_invoke_all('links_admin_operations', 'links') as $operation => $array) {
466 $options[$operation] = $array['label'];
467 }
468
469 $form['update_options']['operation'] = array(
470 '#type' => 'select',
471 '#options' => $options,
472 );
473 $form['update_options']['submit'] = array(
474 '#type' => 'submit',
475 '#value' => t('Update'),
476 );
477
478 $form['instructions'] = array(
479 '#type' => 'markup',
480 '#value' => t('<p>The <em>Links Package</em> keeps all URLs in a master table, regardless of how many nodes refer to the same URL. This screen allows you to conveniently update the master table, or you can link to sub-pages that let you manage the references to a given link. Refer to the detailed instructions below if you are unfamiliar with this page.</p>'),
481 '#weight' => -10,
482 );
483
484 $form['help'] = array(
485 '#type' => 'fieldset',
486 '#title' => t('Detailed instructions'),
487 '#tree' => FALSE,
488 '#collapsible' => TRUE,
489 '#collapsed' => TRUE,
490 '#weight' => -9,
491 );
492
493 $form['help']['details'] = array(
494 '#type' => 'markup',
495 '#value' => t('<p>If you change a URL here, it will be reflected immediately in all nodes that share that link, unless it has been overridden at the node level.</p><p>Any changes you make to titles in this list will update the master table, but by default any node-specific title overrides are not affected. Select the <strong>Use master link title</strong> update option and select one or more links if you want to force all referring nodes to use the title from the master table, for the selected links. Any unselected links will still change only the master table.</p><p>If you <strong>delete</strong> any links from this screen, they will be removed from all nodes that currently use them.</p>'),
496 );
497
498 return $form;
499 }
500
501 /**
502 * Returns a confirmation dialog for deletes that may affect multiple links.
503 */
504 function links_admin_multiple_delete_confirm($selected) {
505 $link_required_list = module_invoke_all('links_admin_link_required');
506 $link_required_types = array_keys($link_required_list);
507 $link_required_modules = array_values($link_required_list);
508 $form = array();
509 $form['links'] = array(
510 '#type' => 'markup',
511 '#prefix' => '<ul>',
512 '#suffix' => '</ul>',
513 '#tree' => TRUE,
514 );
515 $in_list = implode(',', $selected);
516 $sql = "SELECT l.lid, ln.nid, l.url, l.link_title AS master_title, ln.link_title AS local_title, ln.clicks, ln.module, n.title AS node_title, n.type AS node_type, nt.name AS node_type_name FROM {links} l LEFT JOIN {links_node} ln ON l.lid=ln.lid LEFT JOIN {node} n ON ln.nid=n.nid LEFT JOIN {node_type} nt ON n.type=nt.type WHERE l.lid IN (" . $in_list . ") ORDER BY l.lid, ln.nid";
517 $result = db_query($sql);
518 while ($row = db_fetch_array($result)) {
519 $lid = $row['lid'];
520 $form['links'][$lid] = array(
521 '#type' => 'markup',
522 '#tree' => TRUE,
523 'select' => array(
524 '#type' => 'hidden',
525 '#value' => $lid,
526 ),
527 'text' => array(
528 '#type' => 'markup',
529 '#prefix' => '<li>',
530 '#suffix' => '</li>',
531 '#value' => ($row['nid'] > 0 ? t('Link #!lid (&quot;!link_title&quot;) is used in !type_name node #!nid. (&quot;!title&quot;)', array('!lid'=>$lid, '!link_title'=>$row['master_title'], '!nid'=>$row['nid'], '!title'=>$row['node_title'], '!type_name'=>$row['node_type_name'])) : t('Link #!lid (&quot;!link_title&quot;) is not used in any nodes.', array('!lid'=>$lid, '!link_title'=>$row['master_title']))),
532 ),
533 );
534 if (in_array($row['node_type'], $link_required_types)) {
535 $form['links'][$lid]['text']['#value'] .= t('<br/><strong>The link is required for this content type, so deleting the link will also delete this !type_name.</strong>', array('!type_name'=>$row['node_type_name']));
536 }
537 }
538 $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
539 $form['#submit'][] = 'links_admin_multiple_delete_confirm_submit';
540 return confirm_form($form,
541 t('Are you sure you want to delete these links?'),
542 'admin/content/links', t('This action cannot be undone.'),
543 t('Delete all'), t('Cancel'));
544 }
545
546 /*
547 * Called by the wildcard %links_admin_ref from a menu path.
548 */
549 function links_admin_ref_load($lid) {
550 if (! is_numeric($lid)) {
551 return FALSE;
552 }
553 $link = links_get_link($lid);
554 if (is_array($link)) {
555 return $link;
556 } else {
557 return FALSE;
558 }
559 }
560
561 /*
562 * Displays the links node reference edit page. $link is a {links}
563 * record as returned from links_admin_ref_load() or links_get_link().
564 */
565 function links_admin_edit_form($form_state, $link) {
566 drupal_set_title(t('Edit references for link #!lid (!link)', array('!lid'=>$link['lid'], '!link'=>$link['link_title'])));
567 $lid = intval($link['lid']);
568
569 if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
570 $selected = _links_admin_get_selected_nids($form_state['values']);
571 $form_state['rebuild'] = TRUE;
572 $form = links_admin_multiple_delete_confirm_node($lid, $selected);
573 return $form;
574 }
575
576 $links_per_page = variable_get('links_admin_pagelines',10);
577
578 $link_required_list = module_invoke_all('links_admin_link_required');
579 $link_required_types = array_keys($link_required_list);
580 $link_required_modules = array_values($link_required_list);
581 $form = array();
582 $form['lid'] = array(
583 '#type' => 'hidden',
584 '#value' => $lid,
585 );
586 $form['link'] = array(
587 '#type' => 'markup',
588 '#value' => t('<p>These are the node(s) that refer to link #!lid (!link). From this screen you can change the title by which the link is known at the specific node where it is used, which may or may not be the same as its main title in the master links table. You can also delete references to this link in any of the nodes that now refer to it. Be aware that for some node types (such as &quot;weblink&quot;) the link is essential to the node, so deleting the link will also delete the node. You will be warned about this before the nodes are deleted.</p><p><strong>Any records with empty title below are using the title from the master table.</strong>', array('!lid'=>$link['lid'], '!link'=>l($link['link_title'], $link['url']))),
589 );
590 // Obtain the detail records
591 $sql = "SELECT n.nid, ln.link_title AS local_title, ln.clicks, ln.module, n.title AS node_title, n.type AS node_type, nt.name AS node_type_name FROM {links_node} ln LEFT JOIN {node} n ON ln.nid=n.nid LEFT JOIN {node_type} nt ON n.type=nt.type WHERE ln.lid=$lid ORDER BY node_title";
592 $result = pager_query($sql, $links_per_page, 0, "select count(*) from {links_node} where lid=$lid");
593 while ($row = db_fetch_array($result)) {
594 $nid = $row['nid'];
595 $module = $row['module'];
596 $node_data = '<p>' . t('!node_link<br/>Type: !type_name, Total clicks: !clicks', array('!node_link'=>l($row['node_title'], 'node/'.$nid), '!type_name'=>check_plain($row['node_type_name']), '!clicks'=>$row['clicks']));
597 if (in_array($row['module'], $link_required_modules)) {
598 $node_data .= t('<br/><em>This link is required for the node, so deleting the link will also delete the node.</em>');
599 }
600 $node_data .= '</p>';
601 $form[$nid] = array(
602 '#type' => 'markup',
603 '#tree' => TRUE,
604 );
605 $form[$nid]['nid'] = array(
606 '#type' => 'hidden',
607 '#value' => $nid,
608 );
609 $form[$nid]['module'] = array(
610 '#type' => 'hidden',
611 '#value' => $module,
612 );
613 $form[$nid]['select'] = array(
614 '#type' => 'checkbox',
615 '#return_value' => 1,
616 );
617 $form[$nid]['node_data'] = array(
618 '#type' => 'markup',
619 '#value' => $node_data,
620 );
621 $form[$nid]['module_data'] = array(
622 '#type' => 'markup',
623 '#value' => $module,
624 );
625 $form[$nid]['local_title'] = array(
626 '#type' => 'textfield',
627 '#default_value' => $row['local_title'],
628 '#size' => 40,
629 '#maxlength' => 255,
630 );
631 }
632
633 $form['pager'] = array('#value' => theme('pager', NULL, $links_per_page, 0));
634
635 $form['update_options'] = array(
636 '#type' => 'fieldset',
637 '#title' => t('Update options'),
638 '#tree' => FALSE,
639 '#collapsible' => TRUE,
640 '#collapsed' => FALSE,
641 );
642
643 $options = array();
644 foreach (module_invoke_all('links_admin_operations', 'links_node') as $operation => $array) {
645 $options[$operation] = $array['label'];
646 }
647
648 $form['update_options']['operation'] = array(
649 '#type' => 'select',
650 '#options' => $options,
651 );
652 $form['update_options']['submit'] = array(
653 '#type' => 'submit',
654 '#value' => t('Update'),
655 );
656 return $form;
657 }
658
659 //*********** FORM VALIDATE AND SUBMIT FUNCTIONS *************
660
661 /*
662 * Scans the form values to return a simple array of
663 * the selected link IDs (the primary key to the {links}
664 * table).
665 */
666 function _links_admin_get_selected_lids($form_values) {
667 $selected = array();
668 foreach($form_values as $key=>$child) {
669 if (is_int($key)) {
670 $sel = intval($child['select']);
671 if ($sel) {
672 $selected[] = intval($child['lid']);
673 }
674 }
675 }
676 return $selected;
677 }
678
679 /*
680 * Scans the form values for the links_node edit form
681 * and returns the selected node IDs and modules for
682 * the links_node table. The return values are an
683 * associative array inside a simple array. Each of
684 * the inner arrays has keys 'nid' and 'module'. The
685 * 'lid' is not returned from here, because it is common
686 * to all of the records and is available directly in
687 * the calling context.
688 */
689 function _links_admin_get_selected_nids($form_values) {
690 $selected = array();
691 foreach($form_values as $key=>$child) {
692 if (is_int($key)) {
693 $sel = intval($child['select']);
694 if ($sel) {
695 $nid = intval($child['nid']);
696 $module = $child['module'];
697 $selected[] = array('nid'=>$nid, 'module'=>$module);
698 }
699 }
700 }
701 return $selected;
702 }
703
704 /*
705 * Validate the main links form.
706 */
707 function links_admin_list_form_validate($form_id, $form) {
708 $form_values = $form['values'];
709 foreach($form_values as $key=>$child) {
710 if (is_int($key)) {
711 $lid = intval($child['lid']);
712 if (empty($child['url'])) {
713 form_set_error("$key][url", t('Link URL may not be empty (ID=!lid)', array('!lid'=>$lid)));
714 }
715 if (empty($child['title'])) {
716 form_set_error("$key][title", t('Link title may not be empty in the master table (ID=!lid)', array('!lid'=>$lid)));
717 }
718 }
719 }
720 }
721
722 /*
723 * Use the title from the master table for all nodes referring to
724 * the specified links.
725 */
726 function links_admin_force_master_title($selected) {
727 foreach ($selected as $lid) {
728 drupal_set_message(t("Forcing master title: !lid", array("!lid"=>$lid)));
729 links_force_master_title($lid);
730 }
731 }
732
733 /*
734 * Use the title from the master table on the specified links references
735 */
736 function links_admin_force_master_title_nodes($lid, $selected) {
737 foreach ($selected as $key) {
738 $nid = $key['nid'];
739 $module = $key['module'];
740 drupal_set_message(t("Forcing master title (lid:nid:module): !lid:!nid:!module", array('!lid'=>$lid, '!nid'=>$nid, '!module'=>$module)));
741 links_force_master_title($lid, $nid, $module);
742 }
743 }
744
745 /*
746 * This is called from the main list form's submit
747 * to perform the common update that is always done
748 * regardless of other functions. It's based on
749 * the data in the title and URL fields.
750 */
751 function _links_admin_update_links(&$form_state) {
752 $form_values = $form_state['values'];
753 foreach ($form_values as $key=>$value) {
754 if (is_int($key)) {
755 $lid = intval($value['lid']);
756 $new_url = $value['url'];
757 $new_title = $value['title'];
758 if ($lid) {
759 $new_lid = links_update_link($lid, $new_url, $new_title);
760 if ($new_lid) {
761 drupal_set_message(t("Updated link !old_lid (new ID is !new_lid)", array('!old_lid'=>$lid, '!new_lid'=>$new_lid)));
762 }
763 }
764 }
765 }
766 }
767
768 /*
769 * Called when the user selects the button to add a filter
770 */
771 function links_admin_links_filter_add_submit($form, &$form_state) {
772 $form_values = $form_state['values']['filter'];
773 $match_fields = _links_admin_get_filter_match_fields();
774 $count_fields = _links_admin_get_filter_count_fields();
775 if (is_array($_SESSION['links_admin_filter'])) {
776 $filters = $_SESSION['links_admin_filter'];
777 } else {
778 $filters = array();
779 }
780 if (! empty($form_values['match_string'])) {
781 $newfilter = array(
782 'type' => 'match',
783 'match_field' => $form_values['match_field'],
784 'match_sense' => $form_values['match_sense'],
785 'match_string' => $form_values['match_string'],
786 );
787 $filters[] = $newfilter;
788 drupal_set_message(t("Adding filter: !filter", array('!filter'=>_links_admin_filter_to_html($newfilter, $match_fields, $count_fields))));
789 }
790 $count_value_string = trim($form_values['count_value']);
791 if (strlen($count_value_string) > 0) {
792 $newfilter = array(
793 'type' => 'count',
794 'count_field' => $form_values['count_field'],
795 'count_op' => $form_values['count_op'],
796 'count_value' => intval($count_value_string),
797 );
798 $filters[] = $newfilter;
799 drupal_set_message(t("Adding filter: !filter", array('!filter'=>_links_admin_filter_to_html($newfilter, $match_fields, $count_fields))));
800 }
801 $_SESSION['links_admin_filter'] = $filters;
802 }
803
804 /*
805 * Called when the user clicks the button to clear the filters
806 */
807 function links_admin_links_filter_reset_submit($form, &$form_state) {
808 $_SESSION['links_admin_filter'] = array();
809 drupal_set_message(t('Filters have been removed.'));
810 }
811
812 /*
813 * Called when the main links management form is submitted for a
814 * normal update.
815 */
816 function links_admin_list_form_submit($form, &$form_state) {
817 // Do the common update first
818 _links_admin_update_links($form_state);
819
820 $operations = module_invoke_all('links_admin_operations', 'links');
821 $operation = $operations[$form_state['values']['operation']];
822 $form_values = $form_state['values'];
823 $selected = _links_admin_get_selected_lids($form_values);
824
825 if ($function = $operation['callback']) {
826 // Add in callback arguments if present.
827 if (isset($operation['callback arguments'])) {
828 $args = array_merge(array($selected), $operation['callback arguments']);
829 }
830 else {
831 $args = array($selected);
832 }
833 call_user_func_array($function, $args);
834
835 cache_clear_all();
836 }
837
838 // Necessary to make the delete confirm form work
839 $form_state['rebuild'] = TRUE;
840 }
841
842 /*
843 * Called when the user accepts a delete confirmation from
844 * the main links management screen.
845 */
846 function links_admin_multiple_delete_confirm_submit($form_id, &$form_state) {
847 $form_values = $form_state['values'];
848 $selected = array_keys($form_values['links']);
849 foreach ($selected as $lid) {
850 if (links_delete_link($lid)) {
851 drupal_set_message(t('Deleted link !lid', array('!lid'=>$lid)));
852 } else {
853 drupal_set_message(t('Failed to delete link !lid', array('!lid'=>$lid)), 'error');
854 }
855 }
856 }
857
858 /**
859 * Returns a confirmation dialog for deletes of references to a specific link.
860 */
861 function links_admin_multiple_delete_confirm_node($lid, $selected) {
862 $link_required_list = module_invoke_all('links_admin_link_required');
863 $link_required_types = array_keys($link_required_list);
864 $link_required_modules = array_values($link_required_list);
865 $master = links_get_link($lid);
866 $form = array();
867 $form['links'] = array(
868 '#type' => 'markup',
869 '#prefix' => '<ul>',
870 '#suffix' => '</ul>',
871 '#tree' => TRUE,
872 );
873 $form['lid'] = array(
874 '#type' => 'hidden',
875 '#value' => $lid,
876 );
877 foreach ($selected as $linkref) {
878 $nid = $linkref['nid'];
879 $module = $linkref['module'];
880 $result = db_query("SELECT ln.*, n.title AS node_title, n.type AS node_type, nt.name AS node_type_name FROM {links_node} ln LEFT JOIN {node} n ON n.nid=ln.nid LEFT JOIN {node_type} nt ON nt.type=n.type WHERE lid=%d AND ln.nid=%d AND ln.module='%s'", $lid, $nid, $module);
881 $row = db_fetch_array($result);
882 $link_title = empty($row['link_title']) ? $master['link_title'] : $row['link_title'];
883 $form['nodes'][$nid] = array(
884 '#type' => 'markup',
885 '#tree' => TRUE,
886 'nid' => array(
887 '#type' => 'hidden',
888 '#value' => $nid,
889 ),
890 'module' => array(
891 '#type' => 'hidden',
892 '#value' => $module,
893 ),
894 'text' => array(
895 '#type' => 'markup',
896 '#prefix' => '<li>',
897 '#suffix' => '</li>',
898 '#value' => t('Link #!lid (&quot;!link_title&quot;) is used in !type_name node #!nid. (&quot;!title&quot;)', array('!lid'=>$lid, '!link_title'=>$link_title, '!nid'=>$row['nid'], '!title'=>$row['node_title'], '!type_name'=>$row['node_type_name'])),
899 ),
900 );
901 if (in_array($row['node_type'], $link_required_types) && in_array($module, $link_required_modules)) {
902 $form['nodes'][$nid]['text']['#value'] .= t('<br/><strong>The link is required for this content type, so deleting the link will also delete this !type_name.</strong>', array('!type_name'=>$row['node_type_name']));
903 }
904 }
905 $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
906 $form['#submit'][] = 'links_admin_multiple_delete_node_confirm_submit';
907 return confirm_form($form,
908 t('Are you sure you want to delete these link references?'),
909 'admin/content/links/'.$lid, t('This action cannot be undone.'),
910 t('Delete all'), t('Cancel'));
911 }
912
913 // Called for the update of the node-specific link titles, which is
914 // done regardless of the dropdown-select operation (if any).
915 function _links_admin_update_links_node(&$form_state) {
916 $form_values = $form_state['values'];
917 $lid = $form_values['lid'];
918 foreach ($form_values as $key=>$value) {
919 if (is_int($key)) {
920 $nid = intval($value['nid']);
921 $module = $value['module'];
922 $ident = $lid . ':' . $nid . ':' . $module;
923 $new_title = trim($value['local_title']);
924 links_set_local_title($lid, $nid, $module, $new_title);
925 // We need to rebuild the form to go to a second step.
926 // In this case, it forces the rebuild in case we have
927 // changed the master title for the link, which without
928 // this would display with its old value.
929 $form_state['rebuild'] = TRUE;
930 }
931 }
932 }
933
934 /*
935 * Called when the user accepts a delete confirmation from
936 * the node reference management screen.
937 */
938 function links_admin_multiple_delete_node_confirm_submit($form_id, &$form_state) {
939 $form_values = $form_state['values'];
940 $link_required_list = module_invoke_all('links_admin_link_required');
941 $link_required_types = array_keys($link_required_list);
942 $link_required_modules = array_values($link_required_list);
943 $lid = intval($form_values['lid']);
944 if (! $lid) {
945 watchdog(t('Missing link ID in request to delete link references'), array(), WATCHDOG_ERROR);
946 return;
947 }
948 foreach ($form_values as $key=>$child) {
949 if (is_int($key)) {
950 $nid = $child['nid'];
951 $result = db_query("SELECT title, type AS node_type FROM {node} WHERE nid=%d", $nid);
952 $row = db_fetch_array($result);
953 $module = $child['module'];
954 links_delete_links_for_node($nid, $module);
955 drupal_set_message(t('Deleted reference from link !lid to !type node !nid via module !module', array('!lid'=>$lid, '!type'=>$row['node_type'], '!nid'=>$nid, '!module'=>$module)));
956 if (in_array($row['node_type'], $link_required_types) && in_array($module, $link_required_modules)) {
957 node_delete($nid);
958 drupal_set_message(t('Deleted node !nid (&quot;!title&quot;)', array('!nid'=>$nid, '!title'=>$row['title'])));
959 }
960 }
961 }
962 }
963
964 /*
965 * Called when the user submits an update to the node references
966 * for a given link.
967 */
968 function links_admin_edit_form_submit($form_id, &$form_state) {
969 // Always do the common update
970 _links_admin_update_links_node($form_state);
971
972 $operations = module_invoke_all('links_admin_operations', 'links_node');
973 $operation = $operations[$form_state['values']['operation']];
974 $form_values = $form_state['values'];
975 $selected = _links_admin_get_selected_nids($form_values);
976 $lid = intval($form_values['lid']);
977
978 if ($function = $operation['callback']) {
979 // Add in callback arguments if present.
980 if (isset($operation['callback arguments'])) {
981 $args = array_merge(array($lid, $selected), $operation['callback arguments']);
982 }
983 else {
984 $args = array($lid, $selected);
985 }
986 call_user_func_array($function, $args);
987
988 cache_clear_all();
989 }
990 else {
991 // We need to rebuild the form to go to a second step.
992 $form_state['rebuild'] = TRUE;
993 }
994 }
995
996 //******************* THEME FUNCTIONS ********************
997
998 function theme_links_admin_list_form($form) {
999 $html = drupal_render($form['instructions']);
1000 $html .= drupal_render($form['help']);
1001 $html .= drupal_render($form['filter']);
1002 $header = _links_admin_form_header();
1003 $rows = array();
1004 $columns = array('select', 'lid_display', 'title', 'url', 'stats');
1005 $i = 0;
1006 reset ($form);
1007 foreach (array_keys($form) as $key) {
1008 if (is_int($key)) {
1009 $rows[$i] = array();
1010 $j = 0;
1011 foreach ($columns as $col) {
1012 $rows[$i][$j] = drupal_render($form[$key][$col]);
1013 $lid = intval($form[$key]['lid']['#value']);
1014 switch($col) {
1015 case 'title':
1016 $rows[$i][$j] .= l(t('Edit references from nodes'), 'admin/content/links/'.$lid);
1017 break;
1018 case 'url':
1019 $rows[$i][$j] .= l(t('Open link'), 'links/goto/'.$lid);
1020 $rows[$i][$j] .= ' | ';
1021 $rows[$i][$j] .= l(t('Open link in new window'), 'links/goto/'.$lid, array('attributes'=>array('target'=>'_blank')));
1022 break;
1023 }
1024 $j++;
1025 }
1026 $i++;
1027 }
1028 }
1029 $html .= theme('table', $header, $rows);
1030 $html .= drupal_render($form);
1031 return $html;
1032 }
1033
1034 /*
1035 * Themes the filter criteria form
1036 */
1037 function theme_links_admin_filter(&$element) {
1038 $html = drupal_render($element['filter_current']);
1039 $html .= '<table id="links_admin_filter">';
1040 $html .= '<tr><td>' . drupal_render($element['match_field']) . '</td><td>' . drupal_render($element['match_sense']);
1041 $html .= '</td><td>' . drupal_render($element['match_string']) . '</td></tr>';
1042 $html .= '<tr><td>' . drupal_render($element['count_field']) . '</td><td>' . drupal_render($element['count_op']);
1043 $html .= '</td><td>' . drupal_render($element['count_value']) . '</td></tr>';
1044 $html .= '</table>';
1045 $html .= '<p>' . drupal_render($element);
1046 return $html;
1047 }
1048
1049 /*
1050 * Themes the form to edit the node references to a link
1051 */
1052 function theme_links_admin_edit_form(&$element) {
1053 $columns = array('select', 'node_data', 'module_data', 'local_title');
1054 $header = array(t('Select'), t('Referring Node'), t('Used by Module'), t('Node-Specific Title'));
1055 $html = drupal_render($element['link']);
1056 reset($element);
1057 $rows = array();
1058 $i = 0;
1059 while (list($key, $child) = each($element)) {
1060 if (is_int($key)) {
1061 $j = 0;
1062 $rows[$i] = array();
1063 foreach ($columns as $col) {
1064 $rows[$i][$j++] = drupal_render($element[$key][$col]);
1065 }
1066 // Special case to cleanup the hidden field(s)
1067 $rows[$i++][0] .= drupal_render($element[$key]);
1068 }
1069 }
1070 $html .= theme('table', $header, $rows);
1071 $html .= drupal_render($element);
1072 return $html;
1073 }

  ViewVC Help
Powered by ViewVC 1.1.2