system.module 63.1 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
2
// $Id$
Dries's avatar
 
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Configuration system that lets administrators modify the workings of the site.
 */

9
define('VERSION', '6.0-dev');
10
define('DRUPAL_CORE_COMPATIBILITY', '6.x');
11

12
13
14
15
16
define('DRUPAL_MINIMUM_PHP',    '4.3.3');
define('DRUPAL_MINIMUM_MYSQL',  '4.1.0'); // If using MySQL
define('DRUPAL_MINIMUM_PGSQL',  '7.4');   // If using PostgreSQL
define('DRUPAL_MINIMUM_APACHE', '1.3');   // If using Apache

17
18
19
// Maximum age of temporary files in seconds.
define('DRUPAL_MAXIMUM_TEMP_FILE_AGE', 1440);

Dries's avatar
   
Dries committed
20
21
22
/**
 * Implementation of hook_help().
 */
23
function system_help($path, $arg) {
24
25
  global $base_url;

26
  switch ($path) {
27
    case 'admin/help#system':
28
      $output = '<p>'. t('The system module provides system-wide defaults such as running jobs at a particular time, and storing web pages to improve efficiency. The ability to run scheduled jobs makes administering the website more usable, as administrators do not have to manually start jobs. The storing of web pages, or caching, allows the site to efficiently re-use web pages and improve website performance. The system module provides control over preferences, behaviours including visual and operational settings.') .'</p>';
29
      $output .= '<p>'. t('Some modules require regularly scheduled actions, such as cleaning up logfiles. Cron, which stands for chronograph, is a periodic command scheduler executing commands at intervals specified in seconds. It can be used to control the execution of daily, weekly and monthly jobs (or anything with a period measured in seconds). The aggregator module periodically updates feeds using cron. Ping periodically notifies services of new content on your site. Search periodically indexes the content on your site. Automating tasks is one of the best ways to keep a system running smoothly, and if most of your administration does not require your direct involvement, cron is an ideal solution. Cron can, if necessary, also be run manually.') .'</p>';
30
      $output .= '<p>'. t("There is a caching mechanism which stores dynamically generated web pages in a database. By caching a web page, the system module does not have to create the page each time someone wants to view it, instead it takes only one SQL query to display it, reducing response time and the server's load. Only pages requested by <em>anonymous</em> users are cached. In order to reduce server load and save bandwidth, the system module stores and sends cached pages compressed.") .'</p>';
31
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@system">System page</a>.', array('@system' => 'http://drupal.org/handbook/modules/system/')) .'</p>';
32
      return $output;
33
    case 'admin':
34
      return '<p>'. t('Welcome to the administration section. Here you may control how your site functions.') .'</p>';
35
    case 'admin/by-module':
36
      return '<p>'. t('This page shows you all available administration tasks for each module.') .'</p>';
37
    case 'admin/build/themes':
38
      return '<p>'. t('Select which themes are available to your users and specify the default theme. To configure site-wide display settings, click the "configure" task above. Alternately, to override these settings in a specific theme, click the "configure" link for the corresponding theme. Note that different themes may have different regions available for rendering content like blocks. If you want consistency in what your users see, you may wish to enable only one theme.') .'</p>';
39
40
    case 'admin/build/themes/settings/'. $arg[4]:
      $reference = explode('.', $arg[4], 2);
41
      $theme = array_pop($reference);
42
      return '<p>'. t('These options control the display settings for the <code>%template</code> theme. When your site is displayed using this theme, these settings will be used. By clicking "Reset to defaults," you can choose to use the <a href="@global">global settings</a> for this theme.', array('%template' => $theme, '@global' => url('admin/build/themes/settings'))) .'</p>';
43
44
    case 'admin/build/themes/settings':
      return '<p>'. t('These options control the default display settings for your entire site, across all themes. Unless they have been overridden by a specific theme, these settings will be used.') .'</p>';
45
    case 'admin/build/modules':
46
47
48
49
50
51
52
      $output = '<p>'. t('Modules are plugins for Drupal that extend its core functionality. Here you can select which modules are enabled. Once a module is enabled, new <a href="@permissions">permissions</a> might be made available. Modules can automatically be temporarily disabled to reduce server load when your site becomes extremely busy by enabling the throttle.module and checking throttle.', array('@permissions' => url('admin/user/access')));
      if (module_exists('throttle')) {
        $output .= ' '. t('The auto-throttle functionality must be enabled on the <a href="@throttle">throttle configuration page</a> after having enabled the throttle module.', array('@throttle' => url('admin/settings/throttle')));
      }
      $output .= '</p>';
      $output .= t('<p>It is important that <a href="@update-php">update.php</a> is run every time a module is updated to a newer version.</p><p>You can find all administration tasks belonging to a particular module on the <a href="@by-module">administration by module page</a>.</p>', array('@update-php' => $base_url .'/update.php', '@by-module' => url('admin/by-module')));
      return $output;
53
    case 'admin/build/modules/uninstall':
54
      return '<p>'. t('The uninstall process removes all data related to a module. To uninstall a module, you must first disable it. Not all modules support this feature.') .'</p>';
55
56
57
58
59
60
61
    case 'admin/settings/actions':
    case 'admin/settings/actions/manage':
      $output = '<p>'. t('Actions are individual tasks that the system can do, such as unpublishing a piece of content or banning a user. Modules, such as the trigger module, can fire these actions when certain system events happen; for example, when a new post is added or when a user logs in. Modules may also provide additional actions.') .'</p>';
      $output .= '<p>'. t('There are two types of actions: simple and advanced. Simple actions do not require any additional configuration, and are listed here automatically. Advanced actions can do more than simple actions; for example, send an e-mail to a specified address, or check for certain words within a piece of content. These actions need to be created and configured first before they may be used. To create an advanced action, select the action from the drop-down below and click the <em>Create</em> button.') .'</p>';
      if (module_exists('trigger')) {
        $output .= '<p>'. t('You may proceed to the <a href="@url">Triggers</a> page to assign these actions to system events.', array('@url' => url('admin/build/trigger'))) .'</p>';
      }
62
      return $output;
63
64
    case 'admin/settings/actions/configure':
      return t('An advanced action offers additional configuration options which may be filled out below. Changing the <em>Description</em> field is recommended, in order to better identify the precise action taking place. This description will be displayed in modules such as the trigger module when assigning actions to system events, so it is best if it is as descriptive as possible (for example, "Send e-mail to Moderation Team" rather than simply "Send e-mail").');
65
    case 'admin/logs/status':
66
      return '<p>'. t("Here you can find a short overview of your Drupal site's parameters as well as any problems detected with your installation. It is useful to copy/paste this information when you need support.") .'</p>';
67
  }
Dries's avatar
   
Dries committed
68
69
}

70
71
72
73
function system_theme() {
  return array_merge(drupal_common_themes(), array(
    'system_theme_select_form' => array(
      'arguments' => array('form' => NULL),
74
      'file' => 'system.admin.inc',
75
76
77
    ),
    'system_themes_form' => array(
      'arguments' => array('form' => NULL),
78
      'file' => 'system.admin.inc',
79
80
81
    ),
    'system_modules' => array(
      'arguments' => array('form' => NULL),
82
      'file' => 'system.admin.inc',
83
84
85
    ),
    'system_modules_uninstall' => array(
      'arguments' => array('form' => NULL),
86
      'file' => 'system.admin.inc',
87
88
89
    ),
    'status_report' => array(
      'arguments' => array('requirements' => NULL),
90
      'file' => 'system.admin.inc',
91
92
93
    ),
    'admin_page' => array(
      'arguments' => array('blocks' => NULL),
94
      'file' => 'system.admin.inc',
95
96
97
    ),
    'admin_block' => array(
      'arguments' => array('block' => NULL),
98
      'file' => 'system.admin.inc',
99
100
101
    ),
    'admin_block_content' => array(
      'arguments' => array('content' => NULL),
102
      'file' => 'system.admin.inc',
103
104
105
    ),
    'system_admin_by_module' => array(
      'arguments' => array('menu_items' => NULL),
106
      'file' => 'system.admin.inc',
107
108
109
    ),
  ));
}
Dries's avatar
   
Dries committed
110
111
112
/**
 * Implementation of hook_perm().
 */
Dries's avatar
 
Dries committed
113
function system_perm() {
114
  return array('administer site configuration', 'access administration pages', 'administer actions', 'select different theme', 'administer files');
Dries's avatar
   
Dries committed
115
116
}

117
118
119
120
121
/**
 * Implementation of hook_elements().
 */
function system_elements() {
  // Top level form
122
  $type['form'] = array('#method' => 'post', '#action' => request_uri());
123
124

  // Inputs
125
126
  $type['submit'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'));
  $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'));
127
  $type['image_button'] = array('#input' => TRUE, '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'), '#return_value' => TRUE, '#has_garbage_value' => TRUE, '#src' => NULL);
128
  $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE);
129
  $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128);
130
  $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm'));
131
  $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE);
132
  $type['radios'] = array('#input' => TRUE, '#process' => array('expand_radios'));
133
  $type['radio'] = array('#input' => TRUE, '#default_value' => NULL);
134
  $type['checkboxes'] = array('#input' => TRUE, '#process' => array('expand_checkboxes'), '#tree' => TRUE);
135
  $type['checkbox'] = array('#input' => TRUE, '#return_value' => 1);
136
  $type['select'] = array('#input' => TRUE, '#size' => 0, '#multiple' => FALSE);
137
  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight'));
138
  $type['date'] = array('#input' => TRUE, '#process' => array('expand_date'), '#element_validate' => array('date_validate'));
139
  $type['file'] = array('#input' => TRUE, '#size' => 60);
140
141

  // Form structure
142
  $type['item'] = array('#value' => '');
143
144
145
  $type['hidden'] = array('#input' => TRUE);
  $type['value'] = array('#input' => TRUE);
  $type['markup'] = array('#prefix' => '', '#suffix' => '');
146
  $type['fieldset'] = array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL);
Dries's avatar
Dries committed
147
  $type['token'] = array('#input' => TRUE);
148
149
150
  return $type;
}

Dries's avatar
   
Dries committed
151
/**
Dries's avatar
   
Dries committed
152
 * Implementation of hook_menu().
Dries's avatar
   
Dries committed
153
 */
154
155
function system_menu() {
  $items['system/files'] = array(
156
    'title' => 'File download',
157
158
159
160
161
    'page callback' => 'file_download',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['admin'] = array(
162
    'title' => 'Administer',
163
164
165
    'access arguments' => array('access administration pages'),
    'page callback' => 'system_main_admin_page',
    'weight' => 9,
166
    'file' => 'system.admin.inc',
167
168
  );
  $items['admin/compact'] = array(
169
    'title' => 'Compact mode',
170
171
    'page callback' => 'system_admin_compact_page',
    'type' => MENU_CALLBACK,
172
    'file' => 'system.admin.inc',
173
174
  );
  $items['admin/by-task'] = array(
175
    'title' => 'By task',
176
    'page callback' => 'system_main_admin_page',
177
    'file' => 'system.admin.inc',
178
179
180
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/by-module'] = array(
181
    'title' => 'By module',
182
    'page callback' => 'system_admin_by_module',
183
    'file' => 'system.admin.inc',
184
185
186
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
187
188
189
190
191
192
193
194
195
196
  $items['admin/content'] = array(
    'title' => 'Content management',
    'description' => "Manage your site's content.",
    'position' => 'left',
    'weight' => -10,
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array('access administration pages'),
    'file' => 'system.admin.inc',
  );

197
198
  // menu items that are basically just menu blocks
  $items['admin/settings'] = array(
199
200
    'title' => 'Site configuration',
    'description' => 'Adjust basic site configuration options.',
201
202
203
204
    'position' => 'right',
    'weight' => -5,
    'page callback' => 'system_settings_overview',
    'access arguments' => array('administer site configuration'),
205
    'file' => 'system.admin.inc',
206
207
  );
  $items['admin/build'] = array(
208
209
    'title' => 'Site building',
    'description' => 'Control how your site looks and feels.',
210
211
212
213
    'position' => 'right',
    'weight' => -10,
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array('administer site configuration'),
214
    'file' => 'system.admin.inc',
215
216
  );
  $items['admin/settings/admin'] = array(
217
218
    'title' => 'Administration theme',
    'description' => 'Settings for how your administrative pages should look.',
219
220
221
222
    'position' => 'left',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_admin_theme_settings'),
    'block callback' => 'system_admin_theme_settings',
223
    'file' => 'system.admin.inc',
224
225
226
  );
  // Themes:
  $items['admin/build/themes'] = array(
227
228
    'title' => 'Themes',
    'description' => 'Change which theme your site uses or allows users to set.',
229
    'page callback' => 'drupal_get_form',
230
    'page arguments' => array('system_themes_form', NULL),
231
    'file' => 'system.admin.inc',
232
233
  );
  $items['admin/build/themes/select'] = array(
234
235
    'title' => 'List',
    'description' => 'Select the default theme.',
236
237
238
239
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );
  $items['admin/build/themes/settings'] = array(
240
    'title' => 'Configure',
241
    'page arguments' => array('system_theme_settings'),
242
243
244
245
    'type' => MENU_LOCAL_TASK,
  );
  // Theme configuration subtabs
  $items['admin/build/themes/settings/global'] = array(
246
    'title' => 'Global settings',
247
248
249
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );
Dries's avatar
   
Dries committed
250

251
252
253
  foreach (list_themes() as $theme) {
    if ($theme->status) {
      $items['admin/build/themes/settings/'. $theme->name] = array(
254
        'title' => $theme->info['name'],
255
256
257
        'page arguments' => array('system_theme_settings', $theme->name),
        'type' => MENU_LOCAL_TASK,
      );
Dries's avatar
   
Dries committed
258
    }
Dries's avatar
   
Dries committed
259
  }
drumm's avatar
drumm committed
260

261
262
  // Modules:
  $items['admin/build/modules'] = array(
263
264
    'title' => 'Modules',
    'description' => 'Enable or disable add-on modules for your site.',
265
266
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_modules'),
267
    'file' => 'system.admin.inc',
268
269
  );
  $items['admin/build/modules/list'] = array(
270
    'title' => 'List',
271
272
273
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/build/modules/list/confirm'] = array(
274
    'title' => 'List',
275
276
277
    'type' => MENU_CALLBACK,
  );
  $items['admin/build/modules/uninstall'] = array(
278
    'title' => 'Uninstall',
279
280
281
282
    'page arguments' => array('system_modules_uninstall'),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/build/modules/uninstall/confirm'] = array(
283
    'title' => 'Uninstall',
284
285
    'type' => MENU_CALLBACK,
  );
Dries's avatar
   
Dries committed
286

287
  // Actions:
288
  $items['admin/settings/actions'] = array(
289
290
291
292
293
    'title' => 'Actions',
    'description' => 'Manage the actions defined for your site.',
    'access arguments' => array('administer actions'),
    'page callback' => 'system_actions_manage'
  );
294
  $items['admin/settings/actions/manage'] = array(
295
296
297
298
299
300
    'title' => 'Manage actions',
    'description' => 'Manage the actions defined for your site.',
    'page callback' => 'system_actions_manage',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -2,
  );
301
302
  $items['admin/settings/actions/configure'] = array(
    'title' => 'Configure an advanced action',
303
304
305
306
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_actions_configure'),
    'type' => MENU_CALLBACK,
  );
307
  $items['admin/settings/actions/delete/%actions'] = array(
308
309
310
311
312
313
    'title' => 'Delete action',
    'description' => 'Delete an action.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_actions_delete_form', 4),
    'type' => MENU_CALLBACK,
  );
314
  $items['admin/settings/actions/orphan'] = array(
315
316
317
318
319
    'title' => 'Remove orphans',
    'page callback' => 'system_actions_remove_orphans',
    'type' => MENU_CALLBACK,
  );

320
321
  // Settings:
  $items['admin/settings/site-information'] = array(
322
323
    'title' => 'Site information',
    'description' => 'Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.',
324
325
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_site_information_settings'),
326
    'file' => 'system.admin.inc',
327
328
  );
  $items['admin/settings/error-reporting'] = array(
329
330
    'title' => 'Error reporting',
    'description' => 'Control how Drupal deals with errors including 403/404 errors as well as PHP error reporting.',
331
332
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_error_reporting_settings'),
333
    'file' => 'system.admin.inc',
334
  );
335
  $items['admin/settings/logging'] = array(
336
337
    'title' => 'Logging and alerts',
    'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destination, such as syslog, database, email, ...etc.",
338
    'page callback' => 'system_logging_overview',
339
    'file' => 'system.admin.inc',
340
  );
341
  $items['admin/settings/performance'] = array(
342
    'title' => 'Performance',
343
    'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.',
344
345
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_performance_settings'),
346
    'file' => 'system.admin.inc',
347
348
  );
  $items['admin/settings/file-system'] = array(
349
350
    'title' => 'File system',
    'description' => 'Tell Drupal where to store uploaded files and how they are accessed.',
351
352
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_file_system_settings'),
353
    'file' => 'system.admin.inc',
354
355
  );
  $items['admin/settings/image-toolkit'] = array(
356
357
    'title' => 'Image toolkit',
    'description' => 'Choose which image toolkit to use if you have installed optional toolkits.',
358
359
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_image_toolkit_settings'),
360
    'file' => 'system.admin.inc',
361
362
  );
  $items['admin/content/rss-publishing'] = array(
363
364
    'title' => 'RSS publishing',
    'description' => 'Configure the number of items per feed and whether feeds should be titles/teasers/full-text.',
365
366
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_rss_feeds_settings'),
367
    'access arguments' => array('administer site configuration'),
368
    'file' => 'system.admin.inc',
369
370
  );
  $items['admin/settings/date-time'] = array(
371
372
    'title' => 'Date and time',
    'description' => "Settings for how Drupal displays date and time, as well as the system's default timezone.",
373
374
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_date_time_settings'),
375
    'file' => 'system.admin.inc',
376
  );
377
378
379
380
  $items['admin/settings/date-time/lookup'] = array(
    'title' => t('Date and time lookup'),
    'type' => MENU_CALLBACK,
    'page callback' => 'system_date_time_lookup',
381
    'file' => 'system.admin.inc',
382
  );
383
  $items['admin/settings/site-maintenance'] = array(
384
385
    'title' => 'Site maintenance',
    'description' => 'Take the site off-line for maintenance or bring it back online.',
386
387
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_site_maintenance_settings'),
388
    'file' => 'system.admin.inc',
389
390
  );
  $items['admin/settings/clean-urls'] = array(
391
392
    'title' => 'Clean URLs',
    'description' => 'Enable or disable clean URLs for your site.',
393
394
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_clean_url_settings'),
395
    'file' => 'system.admin.inc',
396
397
398
399
  );

  // Logs:
  $items['admin/logs'] = array(
400
401
    'title' => 'Logs',
    'description' => 'View system logs and other status information.',
402
403
404
    'page callback' => 'system_admin_menu_block_page',
    'weight' => 5,
    'position' => 'left',
405
    'file' => 'system.admin.inc',
406
407
  );
  $items['admin/logs/status'] = array(
408
409
    'title' => 'Status report',
    'description' => "Get a status report about your site's operation and any detected problems.",
410
411
412
    'page callback' => 'system_status',
    'weight' => 10,
    'access arguments' => array('administer site configuration'),
413
    'file' => 'system.admin.inc',
414
415
  );
  $items['admin/logs/status/run-cron'] = array(
416
    'title' => 'Run cron',
417
418
    'page callback' => 'system_run_cron',
    'type' => MENU_CALLBACK,
419
    'file' => 'system.admin.inc',
420
421
  );
  $items['admin/logs/status/php'] = array(
422
    'title' => 'PHP',
423
424
    'page callback' => 'system_php',
    'type' => MENU_CALLBACK,
425
    'file' => 'system.admin.inc',
426
427
  );
  $items['admin/logs/status/sql'] = array(
428
    'title' => 'SQL',
429
430
    'page callback' => 'system_sql',
    'type' => MENU_CALLBACK,
431
    'file' => 'system.admin.inc',
432
  );
433
434
435
436
437
  // Default page for batch operations
  $items['batch'] = array(
    'page callback' => 'system_batch_page',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
438
    'file' => 'system.admin.inc',
439
  );
Dries's avatar
   
Dries committed
440
  return $items;
Dries's avatar
 
Dries committed
441
442
}

443
444
function system_init() {
  // Use the administrative theme if the user is looking at a page in the admin/* path.
445
  if (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit'))) {
446
447
448
449
450
451
452
453
454
455
    global $custom_theme;
    $custom_theme = variable_get('admin_theme', '0');
    drupal_add_css(drupal_get_path('module', 'system') .'/admin.css', 'module');
  }

  // Add the CSS for this module.
  drupal_add_css(drupal_get_path('module', 'system') .'/defaults.css', 'module');
  drupal_add_css(drupal_get_path('module', 'system') .'/system.css', 'module');
}

Dries's avatar
   
Dries committed
456
457
458
459
460
/**
 * Implementation of hook_user().
 *
 * Allows users to individually set their theme and time zone.
 */
461
function system_user($type, $edit, &$user, $category = NULL) {
Dries's avatar
Dries committed
462
  if ($type == 'form' && $category == 'account') {
463
    $form['theme_select'] = system_theme_select_form(t('Selecting a different theme will change the look and feel of the site.'), isset($edit['theme']) ? $edit['theme'] : NULL, 2);
Dries's avatar
Dries committed
464

465
    if (variable_get('configurable_timezones', 1)) {
Steven Wittens's avatar
Steven Wittens committed
466
      $zones = _system_zonelist();
467
      $form['timezone'] = array(
468
        '#type' => 'fieldset',
469
470
471
472
        '#title' => t('Locale settings'),
        '#weight' => 6,
        '#collapsible' => TRUE,
      );
473
      $form['timezone']['timezone'] = array(
474
475
476
477
478
        '#type' => 'select',
        '#title' => t('Time zone'),
        '#default_value' => strlen($edit['timezone']) ? $edit['timezone'] : variable_get('date_default_timezone', 0),
        '#options' => $zones,
        '#description' => t('Select your current local time. Dates and times throughout this site will be displayed using this time zone.'),
479
      );
Dries's avatar
   
Dries committed
480
    }
Dries's avatar
Dries committed
481

482
    return $form;
Dries's avatar
   
Dries committed
483
  }
Dries's avatar
   
Dries committed
484
485
}

486
487
488
/**
 * Provide a single block on the administration overview page.
 */
489
function system_admin_menu_block($item) {
490
  $content = array();
491
  if (!isset($item['mlid'])) {
492
    $item += db_fetch_array(db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = '%s' AND module = 'system'", $item['path']));
493
  }
494
  $result = db_query("
495
    SELECT m.*, ml.*
496
497
    FROM {menu_links} ml
    INNER JOIN {menu_router} m ON ml.router_path = m.path
498
    WHERE ml.plid = %d AND ml.menu_name = '%s' AND hidden = 0", $item['mlid'], $item['menu_name']);
499
  while ($item = db_fetch_array($result)) {
500
    _menu_link_translate($item);
501
    if (!$item['access']) {
502
      continue;
503
    }
504
505
506
507
508
509
510
511
    // The link 'description' either derived from the hook_menu 'description' or
    // entered by the user via menu module is saved as the title attribute.
    if (!empty($item['options']['attributes']['title'])) {
      $item['description'] = $item['options']['attributes']['title'];
    }
    // Prepare for sorting as in function _menu_tree_check_access().
    // The weight is offset so it is always positive, with a uniform 5-digits.
    $content[(50000 + $item['weight']) .' '. $item['title'] .' '. $item['mlid']] = $item;
512
  }
513
  ksort($content);
514
515
516
517
  return $content;
}


518
function system_admin_theme_submit($form, &$form_state) {
519
  // If we're changing themes, make sure the theme has its blocks initialized.
520
  if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] != variable_get('admin_theme', '0')) {
521
522
    $result = db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $form_state['values']['admin_theme']));
    if (!$result) {
523
      system_initialize_theme_blocks($form_state['values']['admin_theme']);
524
525
526
527
    }
  }
}

528
/**
529
530
531
532
533
534
535
536
537
538
539
 * Returns a fieldset containing the theme select form.
 *
 * @param $description
 *    description of the fieldset
 * @param $default_value
 *    default value of theme radios
 * @param $weight
 *    weight of the fieldset
 * @return
 *    a form array
 */
540
function system_theme_select_form($description = '', $default_value = '', $weight = 0) {
541
  if (user_access('select different theme')) {
542
    $enabled = array();
543
544
545
    $themes = list_themes();

    foreach ($themes as $theme) {
546
547
548
549
550
551
552
553
554
      if ($theme->status) {
        $enabled[] = $theme;
      }
    }

    if (count($enabled) > 1) {
      ksort($enabled);

      $form['themes'] = array(
Dries's avatar
Dries committed
555
556
557
558
        '#type' => 'fieldset',
        '#title' => t('Theme configuration'),
        '#description' => $description,
        '#collapsible' => TRUE,
559
560
561
562
563
        '#theme' => 'system_theme_select_form'
      );

      foreach ($enabled as $info) {
        // For the default theme, revert to an empty string so the user's theme updates when the site theme is changed.
564
        $info->key = $info->name == variable_get('theme_default', 'garland') ? '' : $info->name;
565

566
567
568
569
570
571
572
573
574
575
576
        $screenshot = NULL;
        $theme_key = $info->name;
        while ($theme_key) {
          if (file_exists($themes[$theme_key]->info['screenshot'])) {
            $screenshot = $themes[$theme_key]->info['screenshot'];
            break;
          }
          $theme_key = isset($themes[$theme_key]->info['base theme']) ? $themes[$theme_key]->info['base theme'] : NULL;
        }

        $screenshot = $screenshot ? theme('image', $screenshot, t('Screenshot for %theme theme', array('%theme' => $info->name)), '', array('class' => 'screenshot'), FALSE) : t('no screenshot');
577

578
        $form['themes'][$info->key]['screenshot'] = array('#value' => $screenshot);
579
        $form['themes'][$info->key]['description'] = array('#type' => 'item', '#title' => $info->name,  '#value' => dirname($info->filename) . ($info->name == variable_get('theme_default', 'garland') ? '<br /> <em>'. t('(site default theme)') .'</em>' : ''));
580
581
582
583
584
585
586
587
588
589
        $options[$info->key] = '';
      }

      $form['themes']['theme'] = array('#type' => 'radios', '#options' => $options, '#default_value' => $default_value ? $default_value : '');
      $form['#weight'] = $weight;
      return $form;
    }
  }
}

590
591
/**
 * Checks the existence of the directory specified in $form_element. This
Dries's avatar
Dries committed
592
 * function is called from the system_settings form to check both the
593
594
595
596
597
598
599
 * file_directory_path and file_directory_temp directories. If validation
 * fails, the form element is flagged with an error from within the
 * file_check_directory function.
 *
 * @param $form_element
 *   The form element containing the name of the directory to check.
 */
600
function system_check_directory($form_element) {
601
602
603
604
  file_check_directory($form_element['#value'], FILE_CREATE_DIRECTORY, $form_element['#parents'][0]);
  return $form_element;
}

Dries's avatar
   
Dries committed
605
606
607
608
/**
 * Retrieves the current status of an array of files in the system table.
 */
function system_get_files_database(&$files, $type) {
Dries's avatar
   
Dries committed
609
  // Extract current files from database.
610
  $result = db_query("SELECT filename, name, type, status, throttle, schema_version FROM {system} WHERE type = '%s'", $type);
Dries's avatar
   
Dries committed
611
  while ($file = db_fetch_object($result)) {
612
    if (isset($files[$file->name]) && is_object($files[$file->name])) {
613
      $file->old_filename = $file->filename;
Dries's avatar
   
Dries committed
614
      foreach ($file as $key => $value) {
615
        if (!isset($files[$file->name]) || !isset($files[$file->name]->$key)) {
Dries's avatar
Dries committed
616
617
          $files[$file->name]->$key = $value;
        }
Dries's avatar
   
Dries committed
618
      }
619
    }
Dries's avatar
 
Dries committed
620
  }
Dries's avatar
   
Dries committed
621
}
Dries's avatar
 
Dries committed
622

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
function system_theme_default() {
  // Prepare defaults for themes.
  return array(
    'regions' => array(
      'left' => 'Left sidebar',
      'right' => 'Right sidebar',
      'content' => 'Content',
      'header' => 'Header',
      'footer' => 'Footer',
    ),
    'description' => '',
    'features' => array(
      'comment_user_picture',
      'favicon',
      'mission',
      'logo',
      'name',
      'node_user_picture',
      'search',
642
643
644
      'slogan',
      'primary_links',
      'secondary_links',
645
    ),
646
647
648
649
    'stylesheets' => array(
      'all' => array('style.css')
    ),
    'scripts' => array('script.js'),
650
    'screenshot' => 'screenshot.png',
651
    'php' => DRUPAL_MINIMUM_PHP,
652
653
654
  );
}

Dries's avatar
   
Dries committed
655
656
657
/**
 * Collect data about all currently available themes
 */
Dries's avatar
Dries committed
658
659
function system_theme_data() {
  // Find themes
660
  $themes = drupal_system_listing('\.info$', 'themes');
Dries's avatar
Dries committed
661

Dries's avatar
   
Dries committed
662
  // Find theme engines
663
  $engines = drupal_system_listing('\.engine$', 'themes/engines');
Dries's avatar
Dries committed
664
665

  // Remove all theme engines from the system table
666
  db_query("DELETE FROM {system} WHERE type = '%s'", 'theme_engine');
Dries's avatar
   
Dries committed
667
668

  foreach ($engines as $engine) {
Dries's avatar
   
Dries committed
669
    // Insert theme engine into system table
Dries's avatar
Dries committed
670
671
672
    drupal_get_filename('theme_engine', $engine->name, $engine->filename);
    drupal_load('theme_engine', $engine->name);
    db_query("INSERT INTO {system} (name, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', %d, %d, %d)", $engine->name, 'theme_engine', $engine->filename, 1, 0, 0);
673
  }
Dries's avatar
Dries committed
674

675
  $defaults = system_theme_default();
676

677
678
679
680
  $sub_themes = array();
  // Read info files for each theme
  foreach ($themes as $key => $theme) {
    $themes[$key]->info = drupal_parse_info_file($theme->filename) + $defaults;
681
682
683
684
685

    // Invoke hook_system_info_alter() to give installed modules a chance to
    // modify the data in the .info files if necessary.
    drupal_alter('system_info', $themes[$key]->info, $themes[$key]);

686
687
688
689
690
691
692
693
    if (!empty($themes[$key]->info['base theme'])) {
      $sub_themes[] = $key;
    }
    if (empty($themes[$key]->info['engine'])) {
      $filename = dirname($themes[$key]->filename) .'/'. $themes[$key]->name .'.theme';
      if (file_exists($filename)) {
        $themes[$key]->owner = $filename;
        $themes[$key]->prefix = $key;
Dries's avatar
   
Dries committed
694
      }
Dries's avatar
   
Dries committed
695
    }
696
697
698
699
700
701
702
703
704
    else {
      $engine = $themes[$key]->info['engine'];
      if (isset($engines[$engine])) {
        $themes[$key]->owner = $engines[$engine]->filename;
        $themes[$key]->prefix = $engines[$engine]->name;
        $themes[$key]->template = TRUE;
      }
    }

705
706
707
708
709
710
    // Give the stylesheets proper path information.
    $pathed_stylesheets = array();
    foreach ($themes[$key]->info['stylesheets'] as $media => $stylesheets) {
      foreach ($stylesheets as $stylesheet) {
        $pathed_stylesheets[$media][$stylesheet] = dirname($themes[$key]->filename) .'/'. $stylesheet;
      }
711
    }
712
    $themes[$key]->info['stylesheets'] = $pathed_stylesheets;
Dries's avatar
Dries committed
713

714
715
716
717
    // Give the scripts proper path information.
    $scripts = array();
    foreach ($themes[$key]->info['scripts'] as $script) {
      $scripts[$script] = dirname($themes[$key]->filename) .'/'. $script;
718
    }
719
    $themes[$key]->info['scripts'] = $scripts;
720
721
722
723
    // Give the screenshot proper path information.
    if (!empty($themes[$key]->info['screenshot'])) {
      $themes[$key]->info['screenshot'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['screenshot'];
    }
Dries's avatar
   
Dries committed
724
725
  }

726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
  // Now that we've established all our master themes, go back and fill in
  // data for subthemes.
  foreach ($sub_themes as $key) {
    $base_key = system_find_base_theme($themes, $key);
    if (!$base_key) {
      continue;
    }
    // Copy the 'owner' and 'engine' over if the top level theme uses a
    // theme engine.
    if (isset($themes[$base_key]->owner)) {
      if (isset($themes[$base_key]->info['engine'])) {
        $themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
        $themes[$key]->owner = $themes[$base_key]->owner;
        $themes[$key]->prefix = $themes[$base_key]->prefix;
      }
      else {
        $themes[$key]->prefix = $key;
Dries's avatar
   
Dries committed
743
744
      }
    }
745
    // Add any stylesheets from the base theme, unless the names match in which case
Dries's avatar
Dries committed
746
    // the sub-theme wins. Note that we slip the base theme's stylesheets in at the
747
748
749
750
751
752
753
754
755
756
757
758
759
    // beginning of the array so that they get added to the page in the correct order.
    foreach ($themes[$base_key]->info['stylesheets'] as $media => $stylesheets) {
      foreach ($stylesheets as $stylesheet => $path) {
        if (!isset($themes[$key]->info['stylesheets'][$media][$stylesheet])) {
          // We need to ensure the media array exists, or the array addition below doesn't work.
          if (!isset($themes[$key]->info['stylesheets'][$media])) {
            $themes[$key]->info['stylesheets'][$media] = array();
          }
          $themes[$key]->info['stylesheets'][$media] = array($stylesheet => $path) + $themes[$key]->info['stylesheets'][$media];
        }
      }
    }
    // Add any scripts from the base theme, unless the names match in which case
Dries's avatar
Dries committed
760
    // the sub-theme wins. Note that we slip the base theme's scripts in at the
761
762
763
764
765
766
    // beginning of the array so that they get added to the page in the correct order.
    foreach ($themes[$base_key]->info['scripts'] as $script => $path) {
      if (!isset($themes[$key]->info['scripts'][$script])) {
        $themes[$key]->info['scripts'] = array($script => $path) + $themes[$key]->info['scripts'];
      }
    }
Dries's avatar
   
Dries committed
767
  }
Dries's avatar
   
Dries committed
768
769

  // Extract current files from database.
Dries's avatar
Dries committed
770
771
772
  system_get_files_database($themes, 'theme');

  db_query("DELETE FROM {system} WHERE type = 'theme'");
Dries's avatar
   
Dries committed
773

Dries's avatar
Dries committed
774
  foreach ($themes as $theme) {
775
776
777
778
    if (!isset($theme->owner)) {
      $theme->owner = '';
    }

779
    db_query("INSERT INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)", $theme->name, $theme->owner, serialize($theme->info), 'theme', $theme->filename, isset($theme->status) ? $theme->status : 0, 0, 0);
Dries's avatar
Dries committed
780
781
782
783
784
  }

  return $themes;
}

785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
/**
 * Recursive function to find the top level base theme. Themes can inherit
 * templates and function implementations from earlier themes; this function
 * finds the top level parent that has no ancestor, or returns NULL if there
 * isn't a valid parent.
 */
function system_find_base_theme($themes, $key, $used_keys = array()) {
  $base_key = $themes[$key]->info['base theme'];
  // Does the base theme exist?
  if (!isset($themes[$base_key])) {
    return NULL;
  }

  // Is the base theme itself a child of another theme?
  if (isset($themes[$base_key]->info['base theme'])) {
    // Prevent loops.
    if ($used_keys[$base_key]) {
      return NULL;
    }
    $used_keys[$base_key] = TRUE;
    return system_find_base_theme($themes, $base_key, $used_keys);
  }
  // If we get here, then this is our parent theme.
  return $base_key;
}

811
812
813
/**
 * Get a list of available regions from a specified theme.
 *
814
 * @param $theme_key
815
816
817
818
 *   The name of a theme.
 * @return
 *   An array of regions in the form $region['name'] = 'description'.
 */
819
function system_region_list($theme_key) {
820
821
  static $list = array();

822
  if (!array_key_exists($theme_key, $list)) {
823
824
    $info = unserialize(db_result(db_query("SELECT info FROM {system} WHERE type = 'theme' AND name = '%s'", $theme_key)));
    $list[$theme_key] = array_map('t', $info['regions']);
825
826
  }

827
  return $list[$theme_key];
828
829
830
831
832
833
834
835
836
837
838
839
}

/**
 * Get the name of the default region for a given theme.
 *
 * @param $theme
 *   The name of a theme.
 * @return
 *   A string that is the region name.
 */
function system_default_region($theme) {
  $regions = array_keys(system_region_list($theme));
840
  return isset($regions[0]) ? $regions[0] : '';
841
842
}

Dries's avatar
   
Dries committed
843
/**
844
845
 * Assign an initial, default set of blocks for a theme.
 *
846
 * This function is called the first time a new theme is enabled. The new theme
847
848
849
850
851
852
853
854
855
 * gets a copy of the default theme's blocks, with the difference that if a
 * particular region isn't available in the new theme, the block is assigned
 * to the new theme's default region.
 *
 * @param $theme
 *   The name of a theme.
 */
function system_initialize_theme_blocks($theme) {
  // Initialize theme's blocks if none already registered.
856
  if (!(db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $theme)))) {
857
    $default_theme = variable_get('theme_default', 'garland');
858
859
    $regions = system_region_list($theme);
    $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s'", $default_theme);
860
    while ($block = db_fetch_array($result)) {
861
862
863
864
      // If the region isn't supported by the theme, assign the block to the theme's default region.
      if (!array_key_exists($block['region'], $regions)) {
        $block['region'] = system_default_region($theme);
      }
865
866
      db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d, %d)",
          $block['module'], $block['delta'], $theme, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle'], $block['cache']);
867
868
869
870
    }
  }
}

871
872
873
874
/**
 * Add default buttons to a form and set its prefix
 */
function system_settings_form($form) {
875
876
  $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
  $form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
877

878
  if (!empty($_POST) && form_get_errors()) {
879
    drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
880
  }
881
882
  $form['#submit'][] = 'system_settings_form_submit';
  $form['#validate'][] = 'system_settings_form_validate';
883
  $form['#theme'] = 'system_settings_form';
884
  return $form;
885
886
}

887

Steven Wittens's avatar
Steven Wittens committed
888

889
890
891
/**
 * Execute the system_settings_form.
 *
892
893
894
 * If you want node type configure style handling of your checkboxes,
 * add an array_filter value to your form.
 *
895
 */
896
897
function system_settings_form_submit($form, &$form_state) {
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
898

899
  // Exclude unnecessary elements.
900
  unset($form_state['values']['submit'], $form_state['values']['reset'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token']);
901

902
  foreach ($form_state['values'] as $key => $value) {
903
904
905
906
    if ($op == t('Reset to defaults')) {
      variable_del($key);
    }
    else {
907
      if (is_array($value) && isset($form_state['values']['array_filter'])) {
908
909
910
911
912
913
914
        $value = array_keys(array_filter($value));
      }
      variable_set($key, $value);
    }
  }
  if ($op == t('Reset to defaults')) {
    drupal_set_message(t('The configuration options have been reset to their default values.'));
915
916
  }
  else {
917
918
    drupal_set_message(t('The configuration options have been saved.'));
  }
919

920
  drupal_rebuild_theme_registry();
921
922
923
}

/**
924
 * Helper function to sort requirements.
925
 */
926
927
928