Project

General

Profile

Paste
Download (23.8 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / aggregator / aggregator.admin.inc @ b0dc3a2e

1
<?php
2

    
3
/**
4
 * @file
5
 * Administration page callbacks for the Aggregator module.
6
 */
7

    
8
/**
9
 * Page callback: Displays the Aggregator module administration page.
10
 */
11
function aggregator_admin_overview() {
12
  return aggregator_view();
13
}
14

    
15
/**
16
 * Displays the aggregator administration page.
17
 *
18
 * @return
19
 *   A HTML-formatted string with administration page content.
20
 */
21
function aggregator_view() {
22
  $result = db_query('SELECT f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.hash, f.etag, f.modified, f.image, f.block, COUNT(i.iid) AS items FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.hash, f.etag, f.modified, f.image, f.block ORDER BY f.title');
23

    
24
  $output = '<h3>' . t('Feed overview') . '</h3>';
25

    
26
  $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), array('data' => t('Operations'), 'colspan' => '3'));
27
  $rows = array();
28
  foreach ($result as $feed) {
29
    $rows[] = array(
30
      l($feed->title, "aggregator/sources/$feed->fid"),
31
      format_plural($feed->items, '1 item', '@count items'),
32
      ($feed->checked ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $feed->checked))) : t('never')),
33
      ($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')),
34
      l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"),
35
      l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"),
36
      l(t('update items'), "admin/config/services/aggregator/update/$feed->fid", array('query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")))),
37
    );
38
  }
39
  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed')))));
40

    
41
  $result = db_query('SELECT c.cid, c.title, COUNT(ci.iid) as items FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title');
42

    
43
  $output .= '<h3>' . t('Category overview') . '</h3>';
44

    
45
  $header = array(t('Title'), t('Items'), t('Operations'));
46
  $rows = array();
47
  foreach ($result as $category) {
48
    $rows[] = array(l($category->title, "aggregator/categories/$category->cid"), format_plural($category->items, '1 item', '@count items'), l(t('edit'), "admin/config/services/aggregator/edit/category/$category->cid"));
49
  }
50
  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No categories available. <a href="@link">Add category</a>.', array('@link' => url('admin/config/services/aggregator/add/category')))));
51

    
52
  return $output;
53
}
54

    
55
/**
56
 * Form constructor for adding and editing feed sources.
57
 *
58
 * @param $feed
59
 *   (optional) If editing a feed, the feed to edit as a PHP stdClass value; if
60
 *   adding a new feed, NULL. Defaults to NULL.
61
 *
62
 * @ingroup forms
63
 * @see aggregator_form_feed_validate()
64
 * @see aggregator_form_feed_submit()
65
 */
66
function aggregator_form_feed($form, &$form_state, stdClass $feed = NULL) {
67
  $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
68
  $period[AGGREGATOR_CLEAR_NEVER] = t('Never');
69

    
70
  $form['title'] = array('#type' => 'textfield',
71
    '#title' => t('Title'),
72
    '#default_value' => isset($feed->title) ? $feed->title : '',
73
    '#maxlength' => 255,
74
    '#description' => t('The name of the feed (or the name of the website providing the feed).'),
75
    '#required' => TRUE,
76
  );
77
  $form['url'] = array('#type' => 'textfield',
78
    '#title' => t('URL'),
79
    '#default_value' => isset($feed->url) ? $feed->url : '',
80
    '#maxlength' => NULL,
81
    '#description' => t('The fully-qualified URL of the feed.'),
82
    '#required' => TRUE,
83
  );
84
  $form['refresh'] = array('#type' => 'select',
85
    '#title' => t('Update interval'),
86
    '#default_value' => isset($feed->refresh) ? $feed->refresh : 3600,
87
    '#options' => $period,
88
    '#description' => t('The length of time between feed updates. Requires a correctly configured <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))),
89
  );
90
  $form['block'] = array('#type' => 'select',
91
    '#title' => t('News items in block'),
92
    '#default_value' => isset($feed->block) ? $feed->block : 5,
93
    '#options' => drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)),
94
    '#description' => t("Drupal can make a block with the most recent news items of this feed. You can <a href=\"@block-admin\">configure blocks</a> to be displayed in the sidebar of your page. This setting lets you configure the number of news items to show in this feed's block. If you choose '0' this feed's block will be disabled.", array('@block-admin' => url('admin/structure/block'))),
95
  );
96

    
97
  // Handling of categories.
98
  $options = array();
99
  $values = array();
100
  $categories = db_query('SELECT c.cid, c.title, f.fid FROM {aggregator_category} c LEFT JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => isset($feed->fid) ? $feed->fid : NULL));
101
  foreach ($categories as $category) {
102
    $options[$category->cid] = check_plain($category->title);
103
    if ($category->fid) $values[] = $category->cid;
104
  }
105

    
106
  if ($options) {
107
    $form['category'] = array(
108
      '#type' => 'checkboxes',
109
      '#title' => t('Categorize news items'),
110
      '#default_value' => $values,
111
      '#options' => $options,
112
      '#description' => t('New feed items are automatically filed in the checked categories.'),
113
    );
114
  }
115

    
116
  $form['actions'] = array('#type' => 'actions');
117
  $form['actions']['submit'] = array(
118
    '#type' => 'submit',
119
    '#value' => t('Save'),
120
  );
121
  if (!empty($feed->fid)) {
122
    $form['actions']['delete'] = array(
123
      '#type' => 'submit',
124
      '#value' => t('Delete'),
125
    );
126
    $form['fid'] = array(
127
      '#type' => 'hidden',
128
      '#value' => $feed->fid,
129
    );
130
  }
131

    
132
  return $form;
133
}
134

    
135
/**
136
 * Form validation handler for aggregator_form_feed().
137
 *
138
 * @see aggregator_form_feed_submit()
139
 */
140
function aggregator_form_feed_validate($form, &$form_state) {
141
  if ($form_state['values']['op'] == t('Save')) {
142
    // Ensure URL is valid.
143
    if (!valid_url($form_state['values']['url'], TRUE)) {
144
      form_set_error('url', t('The URL %url is invalid. Enter a fully-qualified URL, such as http://www.example.com/feed.xml.', array('%url' => $form_state['values']['url'])));
145
    }
146
    // Check for duplicate titles.
147
    if (isset($form_state['values']['fid'])) {
148
      $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE (title = :title OR url = :url) AND fid <> :fid", array(':title' => $form_state['values']['title'], ':url' => $form_state['values']['url'], ':fid' => $form_state['values']['fid']));
149
    }
150
    else {
151
      $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = :title OR url = :url", array(':title' => $form_state['values']['title'], ':url' => $form_state['values']['url']));
152
    }
153
    foreach ($result as $feed) {
154
      if (strcasecmp($feed->title, $form_state['values']['title']) == 0) {
155
        form_set_error('title', t('A feed named %feed already exists. Enter a unique title.', array('%feed' => $form_state['values']['title'])));
156
      }
157
      if (strcasecmp($feed->url, $form_state['values']['url']) == 0) {
158
        form_set_error('url', t('A feed with this URL %url already exists. Enter a unique URL.', array('%url' => $form_state['values']['url'])));
159
      }
160
    }
161
  }
162
}
163

    
164
/**
165
 * Form submission handler for aggregator_form_feed().
166
 *
167
 * @see aggregator_form_feed_validate()
168
 *
169
 * @todo Add delete confirmation dialog.
170
 */
171
function aggregator_form_feed_submit($form, &$form_state) {
172
  if ($form_state['values']['op'] == t('Delete')) {
173
    $title = $form_state['values']['title'];
174
    // Unset the title.
175
    unset($form_state['values']['title']);
176
  }
177
  aggregator_save_feed($form_state['values']);
178
  if (isset($form_state['values']['fid'])) {
179
    if (isset($form_state['values']['title'])) {
180
      drupal_set_message(t('The feed %feed has been updated.', array('%feed' => $form_state['values']['title'])));
181
      if (arg(0) == 'admin') {
182
        $form_state['redirect'] = 'admin/config/services/aggregator/';
183
        return;
184
      }
185
      else {
186
        $form_state['redirect'] = 'aggregator/sources/' . $form_state['values']['fid'];
187
        return;
188
      }
189
    }
190
    else {
191
      watchdog('aggregator', 'Feed %feed deleted.', array('%feed' => $title));
192
      drupal_set_message(t('The feed %feed has been deleted.', array('%feed' => $title)));
193
      if (arg(0) == 'admin') {
194
        $form_state['redirect'] = 'admin/config/services/aggregator/';
195
        return;
196
      }
197
      else {
198
        $form_state['redirect'] = 'aggregator/sources/';
199
        return;
200
      }
201
    }
202
  }
203
  else {
204
    watchdog('aggregator', 'Feed %feed added.', array('%feed' => $form_state['values']['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/services/aggregator'));
205
    drupal_set_message(t('The feed %feed has been added.', array('%feed' => $form_state['values']['title'])));
206
  }
207
}
208

    
209
/**
210
 * Deletes a feed.
211
 *
212
 * @param $feed
213
 *   An associative array describing the feed to be cleared.
214
 *
215
 * @see aggregator_admin_remove_feed_submit()
216
 */
217
function aggregator_admin_remove_feed($form, $form_state, $feed) {
218
  return confirm_form(
219
    array(
220
      'feed' => array(
221
        '#type' => 'value',
222
        '#value' => $feed,
223
      ),
224
    ),
225
    t('Are you sure you want to remove all items from the feed %feed?', array('%feed' => $feed->title)),
226
    'admin/config/services/aggregator',
227
    t('This action cannot be undone.'),
228
    t('Remove items'),
229
    t('Cancel')
230
  );
231
}
232

    
233
/**
234
 * Form submission handler for aggregator_admin_remove_feed().
235
 *
236
 * Removes all items from a feed and redirects to the overview page.
237
 */
238
function aggregator_admin_remove_feed_submit($form, &$form_state) {
239
  aggregator_remove($form_state['values']['feed']);
240
  $form_state['redirect'] = 'admin/config/services/aggregator';
241
}
242

    
243
/**
244
 * Form constructor for importing feeds from OPML.
245
 *
246
 * @ingroup forms
247
 * @see aggregator_form_opml_validate()
248
 * @see aggregator_form_opml_submit()
249
 */
250
function aggregator_form_opml($form, &$form_state) {
251
  $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
252

    
253
  $form['upload'] = array(
254
    '#type' => 'file',
255
    '#title' => t('OPML File'),
256
    '#description' => t('Upload an OPML file containing a list of feeds to be imported.'),
257
  );
258
  $form['remote'] = array(
259
    '#type' => 'textfield',
260
    '#title' => t('OPML Remote URL'),
261
    '#maxlength' => 1024,
262
    '#description' => t('Enter the URL of an OPML file. This file will be downloaded and processed only once on submission of the form.'),
263
  );
264
  $form['refresh'] = array(
265
    '#type' => 'select',
266
    '#title' => t('Update interval'),
267
    '#default_value' => 3600,
268
    '#options' => $period,
269
    '#description' => t('The length of time between feed updates. Requires a correctly configured <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))),
270
  );
271
  $form['block'] = array('#type' => 'select',
272
    '#title' => t('News items in block'),
273
    '#default_value' => 5,
274
    '#options' => drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)),
275
    '#description' => t("Drupal can make a block with the most recent news items of a feed. You can <a href=\"@block-admin\">configure blocks</a> to be displayed in the sidebar of your page. This setting lets you configure the number of news items to show in a feed's block. If you choose '0' these feeds' blocks will be disabled.", array('@block-admin' => url('admin/structure/block'))),
276
  );
277

    
278
  // Handling of categories.
279
  $options = array_map('check_plain', db_query("SELECT cid, title FROM {aggregator_category} ORDER BY title")->fetchAllKeyed());
280
  if ($options) {
281
    $form['category'] = array(
282
      '#type' => 'checkboxes',
283
      '#title' => t('Categorize news items'),
284
      '#options' => $options,
285
      '#description' => t('New feed items are automatically filed in the checked categories.'),
286
    );
287
  }
288
  $form['actions'] = array('#type' => 'actions');
289
  $form['actions']['submit'] = array(
290
    '#type' => 'submit',
291
    '#value' => t('Import')
292
  );
293

    
294
  return $form;
295
}
296

    
297
/**
298
 * Form validation handler for aggregator_form_opml().
299
 *
300
 * @see aggregator_form_opml_submit()
301
 */
302
function aggregator_form_opml_validate($form, &$form_state) {
303
  // If both fields are empty or filled, cancel.
304
  if (empty($form_state['values']['remote']) == empty($_FILES['files']['name']['upload'])) {
305
    form_set_error('remote', t('You must <em>either</em> upload a file or enter a URL.'));
306
  }
307

    
308
  // Validate the URL, if one was entered.
309
  if (!empty($form_state['values']['remote']) && !valid_url($form_state['values']['remote'], TRUE)) {
310
    form_set_error('remote', t('This URL is not valid.'));
311
  }
312
}
313

    
314
/**
315
 * Form submission handler for aggregator_form_opml().
316
 *
317
 * @see aggregator_form_opml_validate()
318
 */
319
function aggregator_form_opml_submit($form, &$form_state) {
320
  $data = '';
321
  $validators = array('file_validate_extensions' => array('opml xml'));
322
  if ($file = file_save_upload('upload', $validators)) {
323
    $data = file_get_contents($file->uri);
324
  }
325
  else {
326
    $response = drupal_http_request($form_state['values']['remote']);
327
    if (!isset($response->error)) {
328
      $data = $response->data;
329
    }
330
  }
331

    
332
  $feeds = _aggregator_parse_opml($data);
333
  if (empty($feeds)) {
334
    drupal_set_message(t('No new feed has been added.'));
335
    return;
336
  }
337

    
338
  $form_state['values']['op'] = t('Save');
339

    
340
  foreach ($feeds as $feed) {
341
    // Ensure URL is valid.
342
    if (!valid_url($feed['url'], TRUE)) {
343
      drupal_set_message(t('The URL %url is invalid.', array('%url' => $feed['url'])), 'warning');
344
      continue;
345
    }
346

    
347
    // Check for duplicate titles or URLs.
348
    $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = :title OR url = :url", array(':title' => $feed['title'], ':url' => $feed['url']));
349
    foreach ($result as $old) {
350
      if (strcasecmp($old->title, $feed['title']) == 0) {
351
        drupal_set_message(t('A feed named %title already exists.', array('%title' => $old->title)), 'warning');
352
        continue 2;
353
      }
354
      if (strcasecmp($old->url, $feed['url']) == 0) {
355
        drupal_set_message(t('A feed with the URL %url already exists.', array('%url' => $old->url)), 'warning');
356
        continue 2;
357
      }
358
    }
359

    
360
    $form_state['values']['title'] = $feed['title'];
361
    $form_state['values']['url'] = $feed['url'];
362
    drupal_form_submit('aggregator_form_feed', $form_state);
363
  }
364

    
365
  $form_state['redirect'] = 'admin/config/services/aggregator';
366
}
367

    
368
/**
369
 * Parses an OPML file.
370
 *
371
 * Feeds are recognized as <outline> elements with the attributes "text" and
372
 * "xmlurl" set.
373
 *
374
 * @param $opml
375
 *   The complete contents of an OPML document.
376
 *
377
 * @return
378
 *   An array of feeds, each an associative array with a "title" and a "url"
379
 *   element, or NULL if the OPML document failed to be parsed. An empty array
380
 *   will be returned if the document is valid but contains no feeds, as some
381
 *   OPML documents do.
382
 */
383
function _aggregator_parse_opml($opml) {
384
  $feeds = array();
385
  $xml_parser = drupal_xml_parser_create($opml);
386
  if (xml_parse_into_struct($xml_parser, $opml, $values)) {
387
    foreach ($values as $entry) {
388
      if ($entry['tag'] == 'OUTLINE' && isset($entry['attributes'])) {
389
        $item = $entry['attributes'];
390
        if (!empty($item['XMLURL']) && !empty($item['TEXT'])) {
391
          $feeds[] = array('title' => $item['TEXT'], 'url' => $item['XMLURL']);
392
        }
393
      }
394
    }
395
  }
396
  xml_parser_free($xml_parser);
397

    
398
  return $feeds;
399
}
400

    
401
/**
402
 * Page callback: Refreshes a feed, then redirects to the overview page.
403
 *
404
 * @param $feed
405
 *   An object describing the feed to be refreshed.
406
 */
407
function aggregator_admin_refresh_feed($feed) {
408
  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'aggregator/update/' . $feed->fid)) {
409
    return MENU_ACCESS_DENIED;
410
  }
411
  aggregator_refresh($feed);
412
  drupal_goto('admin/config/services/aggregator');
413
}
414

    
415
/**
416
 * Form constructor for the aggregator system settings.
417
 *
418
 * @see aggregator_admin_form_submit()
419
 * @ingroup forms
420
 */
421
function aggregator_admin_form($form, $form_state) {
422
  // Global aggregator settings.
423
  $form['aggregator_allowed_html_tags'] = array(
424
    '#type' => 'textfield',
425
    '#title' => t('Allowed HTML tags'),
426
    '#size' => 80,
427
    '#maxlength' => 255,
428
    '#default_value' => variable_get('aggregator_allowed_html_tags', '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'),
429
    '#description' => t('A space-separated list of HTML tags allowed in the content of feed items. Disallowed tags are stripped from the content.'),
430
  );
431

    
432
  // Make sure configuration is sane.
433
  aggregator_sanitize_configuration();
434

    
435
  // Get all available fetchers.
436
  $fetchers = module_implements('aggregator_fetch');
437
  foreach ($fetchers as $k => $module) {
438
    if ($info = module_invoke($module, 'aggregator_fetch_info')) {
439
      $label = $info['title'] . ' <span class="description">' . $info['description'] . '</span>';
440
    }
441
    else {
442
      $label = $module;
443
    }
444
    unset($fetchers[$k]);
445
    $fetchers[$module] = $label;
446
  }
447

    
448
  // Get all available parsers.
449
  $parsers = module_implements('aggregator_parse');
450
  foreach ($parsers as $k => $module) {
451
    if ($info = module_invoke($module, 'aggregator_parse_info')) {
452
      $label = $info['title'] . ' <span class="description">' . $info['description'] . '</span>';
453
    }
454
    else {
455
      $label = $module;
456
    }
457
    unset($parsers[$k]);
458
    $parsers[$module] = $label;
459
  }
460

    
461
  // Get all available processors.
462
  $processors = module_implements('aggregator_process');
463
  foreach ($processors as $k => $module) {
464
    if ($info = module_invoke($module, 'aggregator_process_info')) {
465
      $label = $info['title'] . ' <span class="description">' . $info['description'] . '</span>';
466
    }
467
    else {
468
      $label = $module;
469
    }
470
    unset($processors[$k]);
471
    $processors[$module] = $label;
472
  }
473

    
474
  // Only show basic configuration if there are actually options.
475
  $basic_conf = array();
476
  if (count($fetchers) > 1) {
477
    $basic_conf['aggregator_fetcher'] = array(
478
      '#type' => 'radios',
479
      '#title' => t('Fetcher'),
480
      '#description' => t('Fetchers download data from an external source. Choose a fetcher suitable for the external source you would like to download from.'),
481
      '#options' => $fetchers,
482
      '#default_value' => variable_get('aggregator_fetcher', 'aggregator'),
483
    );
484
  }
485
  if (count($parsers) > 1) {
486
    $basic_conf['aggregator_parser'] = array(
487
      '#type' => 'radios',
488
      '#title' => t('Parser'),
489
      '#description' => t('Parsers transform downloaded data into standard structures. Choose a parser suitable for the type of feeds you would like to aggregate.'),
490
      '#options' => $parsers,
491
      '#default_value' => variable_get('aggregator_parser', 'aggregator'),
492
    );
493
  }
494
  if (count($processors) > 1) {
495
    $basic_conf['aggregator_processors'] = array(
496
      '#type' => 'checkboxes',
497
      '#title' => t('Processors'),
498
      '#description' => t('Processors act on parsed feed data, for example they store feed items. Choose the processors suitable for your task.'),
499
      '#options' => $processors,
500
      '#default_value' => variable_get('aggregator_processors', array('aggregator')),
501
    );
502
  }
503
  if (count($basic_conf)) {
504
    $form['basic_conf'] = array(
505
      '#type' => 'fieldset',
506
      '#title' => t('Basic configuration'),
507
      '#description' => t('For most aggregation tasks, the default settings are fine.'),
508
      '#collapsible' => TRUE,
509
      '#collapsed' => FALSE,
510
      );
511
    $form['basic_conf'] += $basic_conf;
512
  }
513

    
514
  // Implementing modules will expect an array at $form['modules'].
515
  $form['modules'] = array();
516

    
517
  $form['actions'] = array('#type' => 'actions');
518
  $form['actions']['submit'] = array(
519
    '#type' => 'submit',
520
    '#value' => t('Save configuration'),
521
  );
522

    
523
  return $form;
524
}
525

    
526
/**
527
 * Form submission handler for aggregator_admin_form().
528
 */
529
function aggregator_admin_form_submit($form, &$form_state) {
530
  if (isset($form_state['values']['aggregator_processors'])) {
531
    $form_state['values']['aggregator_processors'] = array_filter($form_state['values']['aggregator_processors']);
532
  }
533
  system_settings_form_submit($form, $form_state);
534
}
535

    
536
/**
537
 * Form constructor to add/edit/delete aggregator categories.
538
 *
539
 * @param $edit
540
 *   An associative array containing:
541
 *   - title: A string to use for the category title.
542
 *   - description: A string to use for the category description.
543
 *   - cid: The category ID.
544
 *
545
 * @ingroup forms
546
 * @see aggregator_form_category_validate()
547
 * @see aggregator_form_category_submit()
548
 */
549
function aggregator_form_category($form, &$form_state, $edit = array('title' => '', 'description' => '', 'cid' => NULL)) {
550
  $form['title'] = array('#type' => 'textfield',
551
    '#title' => t('Title'),
552
    '#default_value' => $edit['title'],
553
    '#maxlength' => 64,
554
    '#required' => TRUE,
555
  );
556
  $form['description'] = array('#type' => 'textarea',
557
    '#title' => t('Description'),
558
    '#default_value' => $edit['description'],
559
  );
560
  $form['actions'] = array('#type' => 'actions');
561
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
562
  if ($edit['cid']) {
563
    $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
564
    $form['cid'] = array('#type' => 'hidden', '#value' => $edit['cid']);
565
  }
566

    
567
  return $form;
568
}
569

    
570
/**
571
 * Form validation handler for aggregator_form_category().
572
 *
573
 * @see aggregator_form_category_submit()
574
 */
575
function aggregator_form_category_validate($form, &$form_state) {
576
  if ($form_state['values']['op'] == t('Save')) {
577
    // Check for duplicate titles
578
    if (isset($form_state['values']['cid'])) {
579
      $category = db_query("SELECT cid FROM {aggregator_category} WHERE title = :title AND cid <> :cid", array(':title' => $form_state['values']['title'], ':cid' => $form_state['values']['cid']))->fetchObject();
580
    }
581
    else {
582
      $category = db_query("SELECT cid FROM {aggregator_category} WHERE title = :title", array(':title' => $form_state['values']['title']))->fetchObject();
583
    }
584
    if ($category) {
585
      form_set_error('title', t('A category named %category already exists. Enter a unique title.', array('%category' => $form_state['values']['title'])));
586
    }
587
  }
588
}
589

    
590
/**
591
 * Form submission handler for aggregator_form_category().
592
 *
593
 * @see aggregator_form_category_validate()
594
 *
595
 * @todo Add delete confirmation dialog.
596
 */
597
function aggregator_form_category_submit($form, &$form_state) {
598
  if ($form_state['values']['op'] == t('Delete')) {
599
    $title = $form_state['values']['title'];
600
    // Unset the title.
601
    unset($form_state['values']['title']);
602
  }
603
  aggregator_save_category($form_state['values']);
604
  if (isset($form_state['values']['cid'])) {
605
    if (isset($form_state['values']['title'])) {
606
      drupal_set_message(t('The category %category has been updated.', array('%category' => $form_state['values']['title'])));
607
      if (arg(0) == 'admin') {
608
        $form_state['redirect'] = 'admin/config/services/aggregator/';
609
        return;
610
      }
611
      else {
612
        $form_state['redirect'] = 'aggregator/categories/' . $form_state['values']['cid'];
613
        return;
614
      }
615
    }
616
    else {
617
      watchdog('aggregator', 'Category %category deleted.', array('%category' => $title));
618
      drupal_set_message(t('The category %category has been deleted.', array('%category' => $title)));
619
      if (arg(0) == 'admin') {
620
        $form_state['redirect'] = 'admin/config/services/aggregator/';
621
        return;
622
      }
623
      else {
624
        $form_state['redirect'] = 'aggregator/categories/';
625
        return;
626
      }
627
    }
628
  }
629
  else {
630
    watchdog('aggregator', 'Category %category added.', array('%category' => $form_state['values']['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/services/aggregator'));
631
    drupal_set_message(t('The category %category has been added.', array('%category' => $form_state['values']['title'])));
632
  }
633
}