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

241
242
243
  foreach (list_themes() as $theme) {
    if ($theme->status) {
      $items['admin/build/themes/settings/'. $theme->name] = array(
244
        'title' => $theme->info['name'],
245
246
247
        'page arguments' => array('system_theme_settings', $theme->name),
        'type' => MENU_LOCAL_TASK,
      );
Dries's avatar
   
Dries committed
248
    }
Dries's avatar
   
Dries committed
249
  }
drumm's avatar
drumm committed
250

251
252
  // Modules:
  $items['admin/build/modules'] = array(
253
254
    'title' => 'Modules',
    'description' => 'Enable or disable add-on modules for your site.',
255
256
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_modules'),
257
    'file' => 'system.admin.inc',
258
259
  );
  $items['admin/build/modules/list'] = array(
260
    'title' => 'List',
261
262
263
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/build/modules/list/confirm'] = array(
264
    'title' => 'List',
265
266
267
    'type' => MENU_CALLBACK,
  );
  $items['admin/build/modules/uninstall'] = array(
268
    'title' => 'Uninstall',
269
270
271
272
    'page arguments' => array('system_modules_uninstall'),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/build/modules/uninstall/confirm'] = array(
273
    'title' => 'Uninstall',
274
275
    'type' => MENU_CALLBACK,
  );
Dries's avatar
   
Dries committed
276

277
  // Actions:
278
  $items['admin/settings/actions'] = array(
279
280
281
282
283
    'title' => 'Actions',
    'description' => 'Manage the actions defined for your site.',
    'access arguments' => array('administer actions'),
    'page callback' => 'system_actions_manage'
  );
284
  $items['admin/settings/actions/manage'] = array(
285
286
287
288
289
290
    'title' => 'Manage actions',
    'description' => 'Manage the actions defined for your site.',
    'page callback' => 'system_actions_manage',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -2,
  );
291
292
  $items['admin/settings/actions/configure'] = array(
    'title' => 'Configure an advanced action',
293
294
295
296
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_actions_configure'),
    'type' => MENU_CALLBACK,
  );
297
  $items['admin/settings/actions/delete/%actions'] = array(
298
299
300
301
302
303
    'title' => 'Delete action',
    'description' => 'Delete an action.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('system_actions_delete_form', 4),
    'type' => MENU_CALLBACK,
  );
304
  $items['admin/settings/actions/orphan'] = array(
305
306
307
308
309
    'title' => 'Remove orphans',
    'page callback' => 'system_actions_remove_orphans',
    'type' => MENU_CALLBACK,
  );

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

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

433
434
function system_init() {
  // Use the administrative theme if the user is looking at a page in the admin/* path.
435
  if (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit'))) {
436
437
438
439
440
441
442
443
444
445
    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
446
447
448
449
450
/**
 * Implementation of hook_user().
 *
 * Allows users to individually set their theme and time zone.
 */
451
function system_user($type, $edit, &$user, $category = NULL) {
Dries's avatar
Dries committed
452
  if ($type == 'form' && $category == 'account') {
453
    $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
454

455
    if (variable_get('configurable_timezones', 1)) {
Steven Wittens's avatar
Steven Wittens committed
456
      $zones = _system_zonelist();
457
      $form['timezone'] = array(
458
        '#type' => 'fieldset',
459
460
461
462
        '#title' => t('Locale settings'),
        '#weight' => 6,
        '#collapsible' => TRUE,
      );
463
      $form['timezone']['timezone'] = array(
464
465
466
467
468
        '#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.'),
469
      );
Dries's avatar
   
Dries committed
470
    }
Dries's avatar
Dries committed
471

472
    return $form;
Dries's avatar
   
Dries committed
473
  }
Dries's avatar
   
Dries committed
474
475
}

476
477
478
/**
 * Provide a single block on the administration overview page.
 */
479
function system_admin_menu_block($item) {
480
  $content = array();
481
  if (!isset($item['mlid'])) {
482
    $item += db_fetch_array(db_query("SELECT mlid, menu_name FROM {menu_links} ml WHERE ml.router_path = '%s' AND module = 'system'", $item['path']));
483
  }
484
  $result = db_query("
485
    SELECT m.*, ml.*
486
487
    FROM {menu_links} ml
    INNER JOIN {menu_router} m ON ml.router_path = m.path
488
    WHERE ml.plid = %d AND ml.menu_name = '%s' AND hidden = 0", $item['mlid'], $item['menu_name']);
489
  while ($item = db_fetch_array($result)) {
490
    _menu_link_translate($item);
491
    if (!$item['access']) {
492
      continue;
493
    }
494
495
496
497
498
499
500
501
    // 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;
502
  }
503
  ksort($content);
504
505
506
507
  return $content;
}


508
function system_admin_theme_submit($form, &$form_state) {
509
  // If we're changing themes, make sure the theme has its blocks initialized.
510
  if ($form_state['values']['admin_theme'] != variable_get('admin_theme', '0')) {
511
512
    $result = db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $form_state['values']['admin_theme']));
    if (!$result) {
513
      system_initialize_theme_blocks($form_state['values']['admin_theme']);
514
515
516
517
    }
  }
}

518
/**
519
520
521
522
523
524
525
526
527
528
529
 * 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
 */
530
function system_theme_select_form(&$form_state, $description = '', $default_value = '', $weight = 0) {
531
  if (user_access('select different theme')) {
532
    $enabled = array();
533
534
535
    $themes = list_themes();

    foreach ($themes as $theme) {
536
537
538
539
540
541
542
543
544
      if ($theme->status) {
        $enabled[] = $theme;
      }
    }

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

      $form['themes'] = array(
Dries's avatar
Dries committed
545
546
547
548
        '#type' => 'fieldset',
        '#title' => t('Theme configuration'),
        '#description' => $description,
        '#collapsible' => TRUE,
549
550
551
552
553
        '#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.
554
        $info->key = $info->name == variable_get('theme_default', 'garland') ? '' : $info->name;
555

556
557
558
559
560
561
562
563
564
565
566
        $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');
567

568
        $form['themes'][$info->key]['screenshot'] = array('#value' => $screenshot);
569
        $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>' : ''));
570
571
572
573
574
575
576
577
578
579
        $options[$info->key] = '';
      }

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

580
581
/**
 * Checks the existence of the directory specified in $form_element. This
Dries's avatar
Dries committed
582
 * function is called from the system_settings form to check both the
583
584
585
586
587
588
589
 * 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.
 */
590
function system_check_directory($form_element) {
591
592
593
594
  file_check_directory($form_element['#value'], FILE_CREATE_DIRECTORY, $form_element['#parents'][0]);
  return $form_element;
}

Dries's avatar
   
Dries committed
595
596
597
598
/**
 * 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
599
  // Extract current files from database.
600
  $result = db_query("SELECT filename, name, type, status, throttle, schema_version FROM {system} WHERE type = '%s'", $type);
Dries's avatar
   
Dries committed
601
  while ($file = db_fetch_object($result)) {
602
    if (isset($files[$file->name]) && is_object($files[$file->name])) {
603
      $file->old_filename = $file->filename;
Dries's avatar
   
Dries committed
604
      foreach ($file as $key => $value) {
605
        if (!isset($files[$file->name]) || !isset($files[$file->name]->$key)) {
Dries's avatar
Dries committed
606
607
          $files[$file->name]->$key = $value;
        }
Dries's avatar
   
Dries committed
608
      }
609
    }
Dries's avatar
   
Dries committed
610
  }
Dries's avatar
   
Dries committed
611
}
Dries's avatar
   
Dries committed
612

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
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',
632
633
634
      'slogan',
      'primary_links',
      'secondary_links',
635
    ),
636
637
638
639
    'stylesheets' => array(
      'all' => array('style.css')
    ),
    'scripts' => array('script.js'),
640
    'screenshot' => 'screenshot.png',
641
    'php' => DRUPAL_MINIMUM_PHP,
642
643
644
  );
}

Dries's avatar
   
Dries committed
645
646
647
/**
 * Collect data about all currently available themes
 */
Dries's avatar
Dries committed
648
649
function system_theme_data() {
  // Find themes
650
  $themes = drupal_system_listing('\.info$', 'themes');
Dries's avatar
Dries committed
651

Dries's avatar
   
Dries committed
652
  // Find theme engines
653
  $engines = drupal_system_listing('\.engine$', 'themes/engines');
Dries's avatar
Dries committed
654
655

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

  foreach ($engines as $engine) {
Dries's avatar
   
Dries committed
659
    // Insert theme engine into system table
Dries's avatar
Dries committed
660
661
662
    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);
663
  }
Dries's avatar
Dries committed
664

665
  $defaults = system_theme_default();
666

667
668
669
670
  $sub_themes = array();
  // Read info files for each theme
  foreach ($themes as $key => $theme) {
    $themes[$key]->info = drupal_parse_info_file($theme->filename) + $defaults;
671
672
673
674
675

    // 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]);

676
677
678
679
680
681
682
683
    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
684
      }
Dries's avatar
   
Dries committed
685
    }
686
687
688
689
690
691
692
693
694
    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;
      }
    }

695
696
697
698
699
700
    // 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;
      }
701
    }
702
    $themes[$key]->info['stylesheets'] = $pathed_stylesheets;
Dries's avatar
Dries committed
703

704
705
706
707
    // Give the scripts proper path information.
    $scripts = array();
    foreach ($themes[$key]->info['scripts'] as $script) {
      $scripts[$script] = dirname($themes[$key]->filename) .'/'. $script;
708
    }
709
    $themes[$key]->info['scripts'] = $scripts;
710
711
712
713
    // 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
714
715
  }

716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
  // 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
733
734
      }
    }
735
    // Add any stylesheets from the base theme, unless the names match in which case
Dries's avatar
Dries committed
736
    // the sub-theme wins. Note that we slip the base theme's stylesheets in at the
737
738
739
740
741
742
743
744
745
746
747
748
749
    // 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
750
    // the sub-theme wins. Note that we slip the base theme's scripts in at the
751
752
753
754
755
756
    // 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
757
  }
Dries's avatar
   
Dries committed
758
759

  // Extract current files from database.
Dries's avatar
Dries committed
760
761
762
  system_get_files_database($themes, 'theme');

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

Dries's avatar
Dries committed
764
  foreach ($themes as $theme) {
765
766
767
768
    if (!isset($theme->owner)) {
      $theme->owner = '';
    }

769
    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
770
771
772
773
774
  }

  return $themes;
}

775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
/**
 * 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;
}

801
802
803
/**
 * Get a list of available regions from a specified theme.
 *
804
 * @param $theme_key
805
806
807
808
 *   The name of a theme.
 * @return
 *   An array of regions in the form $region['name'] = 'description'.
 */
809
function system_region_list($theme_key) {
810
811
  static $list = array();

812
  if (!array_key_exists($theme_key, $list)) {
813
814
    $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']);
815
816
  }

817
  return $list[$theme_key];
818
819
820
821
822
823
824
825
826
827
828
829
}

/**
 * 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));
830
  return isset($regions[0]) ? $regions[0] : '';
831
832
}

Dries's avatar
   
Dries committed
833
/**
834
835
 * Assign an initial, default set of blocks for a theme.
 *
836
 * This function is called the first time a new theme is enabled. The new theme
837
838
839
840
841
842
843
844
845
 * 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.
846
  if (!(db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $theme)))) {
847
    $default_theme = variable_get('theme_default', 'garland');
848
849
    $regions = system_region_list($theme);
    $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s'", $default_theme);
850
    while ($block = db_fetch_array($result)) {
851
852
853
854
      // 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);
      }
855
856
      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']);
857
858
859
860
    }
  }
}

861
862
863
864
/**
 * Add default buttons to a form and set its prefix
 */
function system_settings_form($form) {
865
866
  $form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
  $form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
867

868
  if (!empty($_POST) && form_get_errors()) {
869
    drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
870
  }
871
872
  $form['#submit'][] = 'system_settings_form_submit';
  $form['#validate'][] = 'system_settings_form_validate';
873
  $form['#theme'] = 'system_settings_form';
874
  return $form;
875
876
}

877

Steven Wittens's avatar
Steven Wittens committed
878

879
880
881
/**
 * Execute the system_settings_form.
 *
882
883
884
 * If you want node type configure style handling of your checkboxes,
 * add an array_filter value to your form.
 *
885
 */
886
887
function system_settings_form_submit($form, &$form_state) {
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
888

889
  // Exclude unnecessary elements.
890
  unset($form_state['values']['submit'], $form_state['values']['reset'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token']);
891

892
  foreach ($form_state['values'] as $key => $value) {
893
894
895
896
    if ($op == t('Reset to defaults')) {
      variable_del($key);
    }
    else {
897
      if (is_array($value) && isset($form_state['values']['array_filter'])) {
898
899
900
901
902
903
904
        $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.'));
905
906
  }
  else {
907
908
    drupal_set_message(t('The configuration options have been saved.'));
  }
909

910
  drupal_rebuild_theme_registry();
911
912
913
}

/**
914
 * Helper function to sort requirements.
915
 */
916
917
918
919
function _system_sort_requirements($a, $b) {
  if (!isset($a['weight'])) {
    if (!isset($b['weight'])) {
      return strcmp($a['title'], $b['title']);
920
    }
921
    return -$b['weight'];
922
  }
923
  return isset($b['weight']) ? $a['weight'] - $b['weight'] : $a['weight'];
924
925
}

926
927
928
929
930
931
932
933
934
/**
 * Implementation of hook_node_type().
 *
 * Updates theme settings after a node type change.
 */
function system_node_type($op, $info) {
  if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
    $old = 'toggle_node_info_'. $info->old_type;
    $new = 'toggle_node_info_'. $info->type;
935

936
937
938
939
940
    $theme_settings = variable_get('theme_settings', array());
    if (isset($theme_settings[$old])) {
      $theme_settings[$new] = $theme_settings[$old];
      unset($theme_settings[$old]);
      variable_set('theme_settings', $theme_settings);
941
942
943
944
    }
  }
}

945
946
947
/**
 * Output a confirmation form
 *
948
 * This function returns a complete form for confirming an action. A link is
949
950
951
 * offered to go back to the item that is being changed in case the user changes
 * his/her mind.
 *
952
 * You can check for the existence of $_POST[$name] (where $name
953
954
 * is usually 'confirm') to check if the confirmation was successful or
 * use the regular submit model.