summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.htaccess131
-rw-r--r--CHANGELOG.txt1402
-rw-r--r--COPYRIGHT.txt25
-rw-r--r--INSTALL.mysql.txt42
-rw-r--r--INSTALL.pgsql.txt44
-rw-r--r--INSTALL.sqlite.txt31
-rw-r--r--INSTALL.txt398
-rw-r--r--LICENSE.txt339
-rw-r--r--MAINTAINERS.txt294
-rw-r--r--README.txt88
-rw-r--r--UPGRADE.txt232
-rw-r--r--authorize.php174
-rw-r--r--cron.php26
-rw-r--r--includes/actions.inc383
-rw-r--r--includes/ajax.inc1213
-rw-r--r--includes/archiver.inc66
-rw-r--r--includes/authorize.inc326
-rw-r--r--includes/batch.inc534
-rw-r--r--includes/batch.queue.inc71
-rw-r--r--includes/bootstrap.inc3276
-rw-r--r--includes/cache-install.inc58
-rw-r--r--includes/cache.inc508
-rw-r--r--includes/common.inc7869
-rw-r--r--includes/database/database.inc3006
-rw-r--r--includes/database/log.inc159
-rw-r--r--includes/database/mysql/database.inc187
-rw-r--r--includes/database/mysql/install.inc33
-rw-r--r--includes/database/mysql/query.inc107
-rw-r--r--includes/database/mysql/schema.inc531
-rw-r--r--includes/database/pgsql/database.inc203
-rw-r--r--includes/database/pgsql/install.inc197
-rw-r--r--includes/database/pgsql/query.inc209
-rw-r--r--includes/database/pgsql/schema.inc617
-rw-r--r--includes/database/pgsql/select.inc108
-rw-r--r--includes/database/prefetch.inc507
-rw-r--r--includes/database/query.inc1953
-rw-r--r--includes/database/schema.inc722
-rw-r--r--includes/database/select.inc1630
-rw-r--r--includes/database/sqlite/database.inc511
-rw-r--r--includes/database/sqlite/install.inc51
-rw-r--r--includes/database/sqlite/query.inc160
-rw-r--r--includes/database/sqlite/schema.inc683
-rw-r--r--includes/database/sqlite/select.inc27
-rw-r--r--includes/date.inc196
-rw-r--r--includes/entity.inc1351
-rw-r--r--includes/errors.inc295
-rw-r--r--includes/file.inc2461
-rw-r--r--includes/file.mimetypes.inc859
-rw-r--r--includes/filetransfer/filetransfer.inc417
-rw-r--r--includes/filetransfer/ftp.inc144
-rw-r--r--includes/filetransfer/local.inc76
-rw-r--r--includes/filetransfer/ssh.inc108
-rw-r--r--includes/form.inc4458
-rw-r--r--includes/graph.inc146
-rw-r--r--includes/image.inc442
-rw-r--r--includes/install.core.inc1831
-rw-r--r--includes/install.inc1246
-rw-r--r--includes/iso.inc482
-rw-r--r--includes/language.inc467
-rw-r--r--includes/locale.inc2372
-rw-r--r--includes/lock.inc274
-rw-r--r--includes/mail.inc586
-rw-r--r--includes/menu.inc3822
-rw-r--r--includes/module.inc1006
-rw-r--r--includes/pager.inc658
-rw-r--r--includes/password.inc288
-rw-r--r--includes/path.inc581
-rw-r--r--includes/registry.inc186
-rw-r--r--includes/session.inc493
-rw-r--r--includes/stream_wrappers.inc836
-rw-r--r--includes/tablesort.inc252
-rw-r--r--includes/theme.inc2629
-rw-r--r--includes/theme.maintenance.inc211
-rw-r--r--includes/token.inc257
-rw-r--r--includes/unicode.entities.inc265
-rw-r--r--includes/unicode.inc601
-rw-r--r--includes/update.inc1490
-rw-r--r--includes/updater.inc427
-rw-r--r--includes/utility.inc58
-rw-r--r--includes/xmlrpc.inc625
-rw-r--r--includes/xmlrpcs.inc385
-rw-r--r--index.php21
-rw-r--r--install.php26
-rw-r--r--misc/ajax.js622
-rw-r--r--misc/arrow-asc.pngbin0 -> 118 bytes
-rw-r--r--misc/arrow-desc.pngbin0 -> 118 bytes
-rw-r--r--misc/authorize.js28
-rw-r--r--misc/autocomplete.js321
-rw-r--r--misc/batch.js32
-rw-r--r--misc/collapse.js103
-rw-r--r--misc/configure.pngbin0 -> 248 bytes
-rw-r--r--misc/draggable.pngbin0 -> 268 bytes
-rw-r--r--misc/drupal.js402
-rw-r--r--misc/druplicon.pngbin0 -> 3905 bytes
-rw-r--r--misc/farbtastic/farbtastic.css36
-rw-r--r--misc/farbtastic/farbtastic.js8
-rw-r--r--misc/farbtastic/marker.pngbin0 -> 437 bytes
-rw-r--r--misc/farbtastic/mask.pngbin0 -> 2001 bytes
-rw-r--r--misc/farbtastic/wheel.pngbin0 -> 11589 bytes
-rw-r--r--misc/favicon.icobin0 -> 1150 bytes
-rw-r--r--misc/feed.pngbin0 -> 656 bytes
-rw-r--r--misc/form.js78
-rw-r--r--misc/forum-icons.pngbin0 -> 1765 bytes
-rw-r--r--misc/grippie.pngbin0 -> 106 bytes
-rw-r--r--misc/help.pngbin0 -> 294 bytes
-rw-r--r--misc/jquery.ba-bbq.js19
-rw-r--r--misc/jquery.cookie.js11
-rw-r--r--misc/jquery.form.js12
-rw-r--r--misc/jquery.js168
-rw-r--r--misc/jquery.once.js79
-rw-r--r--misc/machine-name.js119
-rw-r--r--misc/menu-collapsed-rtl.pngbin0 -> 107 bytes
-rw-r--r--misc/menu-collapsed.pngbin0 -> 105 bytes
-rw-r--r--misc/menu-expanded.pngbin0 -> 106 bytes
-rw-r--r--misc/menu-leaf.pngbin0 -> 126 bytes
-rw-r--r--misc/message-16-error.pngbin0 -> 519 bytes
-rw-r--r--misc/message-16-help.pngbin0 -> 668 bytes
-rw-r--r--misc/message-16-info.pngbin0 -> 733 bytes
-rw-r--r--misc/message-16-ok.pngbin0 -> 639 bytes
-rw-r--r--misc/message-16-warning.pngbin0 -> 442 bytes
-rw-r--r--misc/message-24-error.pngbin0 -> 733 bytes
-rw-r--r--misc/message-24-help.pngbin0 -> 1088 bytes
-rw-r--r--misc/message-24-info.pngbin0 -> 1011 bytes
-rw-r--r--misc/message-24-ok.pngbin0 -> 1058 bytes
-rw-r--r--misc/message-24-warning.pngbin0 -> 753 bytes
-rw-r--r--misc/permissions.pngbin0 -> 242 bytes
-rw-r--r--misc/powered-black-135x42.pngbin0 -> 2699 bytes
-rw-r--r--misc/powered-black-80x15.pngbin0 -> 1448 bytes
-rw-r--r--misc/powered-black-88x31.pngbin0 -> 2005 bytes
-rw-r--r--misc/powered-blue-135x42.pngbin0 -> 2879 bytes
-rw-r--r--misc/powered-blue-80x15.pngbin0 -> 943 bytes
-rw-r--r--misc/powered-blue-88x31.pngbin0 -> 2009 bytes
-rw-r--r--misc/powered-gray-135x42.pngbin0 -> 2594 bytes
-rw-r--r--misc/powered-gray-80x15.pngbin0 -> 698 bytes
-rw-r--r--misc/powered-gray-88x31.pngbin0 -> 1968 bytes
-rw-r--r--misc/print-rtl.css7
-rw-r--r--misc/print.css25
-rw-r--r--misc/progress.gifbin0 -> 5872 bytes
-rw-r--r--misc/progress.js106
-rw-r--r--misc/states.js423
-rw-r--r--misc/tabledrag.js1172
-rw-r--r--misc/tableheader.js111
-rw-r--r--misc/tableselect.js90
-rw-r--r--misc/textarea.js32
-rw-r--r--misc/throbber.gifbin0 -> 1336 bytes
-rw-r--r--misc/timezone.js66
-rw-r--r--misc/tree-bottom.pngbin0 -> 129 bytes
-rw-r--r--misc/tree.pngbin0 -> 130 bytes
-rw-r--r--misc/ui/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--misc/ui/images/ui-bg_flat_75_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--misc/ui/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--misc/ui/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--misc/ui/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--misc/ui/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--misc/ui/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--misc/ui/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--misc/ui/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--misc/ui/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--misc/ui/images/ui-icons_454545_256x240.pngbin0 -> 4369 bytes
-rw-r--r--misc/ui/images/ui-icons_888888_256x240.pngbin0 -> 4369 bytes
-rw-r--r--misc/ui/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--misc/ui/jquery.effects.blind.min.js15
-rw-r--r--misc/ui/jquery.effects.bounce.min.js16
-rw-r--r--misc/ui/jquery.effects.clip.min.js15
-rw-r--r--misc/ui/jquery.effects.core.min.js31
-rw-r--r--misc/ui/jquery.effects.drop.min.js15
-rw-r--r--misc/ui/jquery.effects.explode.min.js16
-rw-r--r--misc/ui/jquery.effects.fade.min.js14
-rw-r--r--misc/ui/jquery.effects.fold.min.js15
-rw-r--r--misc/ui/jquery.effects.highlight.min.js15
-rw-r--r--misc/ui/jquery.effects.pulsate.min.js15
-rw-r--r--misc/ui/jquery.effects.scale.min.js21
-rw-r--r--misc/ui/jquery.effects.shake.min.js15
-rw-r--r--misc/ui/jquery.effects.slide.min.js15
-rw-r--r--misc/ui/jquery.effects.transfer.min.js15
-rw-r--r--misc/ui/jquery.ui.accordion.css20
-rw-r--r--misc/ui/jquery.ui.accordion.min.js31
-rw-r--r--misc/ui/jquery.ui.autocomplete.css54
-rw-r--r--misc/ui/jquery.ui.autocomplete.min.js32
-rw-r--r--misc/ui/jquery.ui.button.css39
-rw-r--r--misc/ui/jquery.ui.button.min.js26
-rw-r--r--misc/ui/jquery.ui.core.css42
-rw-r--r--misc/ui/jquery.ui.core.min.js18
-rw-r--r--misc/ui/jquery.ui.datepicker.css69
-rw-r--r--misc/ui/jquery.ui.datepicker.min.js82
-rw-r--r--misc/ui/jquery.ui.dialog.css22
-rw-r--r--misc/ui/jquery.ui.dialog.min.js41
-rw-r--r--misc/ui/jquery.ui.draggable.min.js51
-rw-r--r--misc/ui/jquery.ui.droppable.min.js27
-rw-r--r--misc/ui/jquery.ui.mouse.min.js18
-rw-r--r--misc/ui/jquery.ui.position.min.js17
-rw-r--r--misc/ui/jquery.ui.progressbar.css12
-rw-r--r--misc/ui/jquery.ui.progressbar.min.js17
-rw-r--r--misc/ui/jquery.ui.resizable.css21
-rw-r--r--misc/ui/jquery.ui.resizable.min.js48
-rw-r--r--misc/ui/jquery.ui.selectable.css11
-rw-r--r--misc/ui/jquery.ui.selectable.min.js23
-rw-r--r--misc/ui/jquery.ui.slider.css25
-rw-r--r--misc/ui/jquery.ui.slider.min.js34
-rw-r--r--misc/ui/jquery.ui.sortable.min.js61
-rw-r--r--misc/ui/jquery.ui.tabs.css19
-rw-r--r--misc/ui/jquery.ui.tabs.min.js36
-rw-r--r--misc/ui/jquery.ui.theme.css253
-rw-r--r--misc/ui/jquery.ui.widget.min.js16
-rw-r--r--misc/vertical-tabs-rtl.css14
-rw-r--r--misc/vertical-tabs.css77
-rw-r--r--misc/vertical-tabs.js205
-rw-r--r--misc/watchdog-error.pngbin0 -> 780 bytes
-rw-r--r--misc/watchdog-ok.pngbin0 -> 375 bytes
-rw-r--r--misc/watchdog-warning.pngbin0 -> 318 bytes
-rw-r--r--modules/README.txt9
-rw-r--r--modules/aggregator/aggregator-feed-source.tpl.php34
-rw-r--r--modules/aggregator/aggregator-item.tpl.php45
-rw-r--r--modules/aggregator/aggregator-rtl.css4
-rw-r--r--modules/aggregator/aggregator-summary-item.tpl.php23
-rw-r--r--modules/aggregator/aggregator-summary-items.tpl.php23
-rw-r--r--modules/aggregator/aggregator-wrapper.tpl.php18
-rw-r--r--modules/aggregator/aggregator.admin.inc597
-rw-r--r--modules/aggregator/aggregator.api.php231
-rw-r--r--modules/aggregator/aggregator.css38
-rw-r--r--modules/aggregator/aggregator.fetcher.inc61
-rw-r--r--modules/aggregator/aggregator.info14
-rw-r--r--modules/aggregator/aggregator.install308
-rw-r--r--modules/aggregator/aggregator.module763
-rw-r--r--modules/aggregator/aggregator.pages.inc562
-rw-r--r--modules/aggregator/aggregator.parser.inc321
-rw-r--r--modules/aggregator/aggregator.processor.inc203
-rw-r--r--modules/aggregator/aggregator.test887
-rw-r--r--modules/aggregator/tests/aggregator_test.info12
-rw-r--r--modules/aggregator/tests/aggregator_test.module58
-rw-r--r--modules/aggregator/tests/aggregator_test_atom.xml20
-rw-r--r--modules/aggregator/tests/aggregator_test_rss091.xml30
-rw-r--r--modules/block/block-admin-display-form.tpl.php67
-rw-r--r--modules/block/block.admin.inc701
-rw-r--r--modules/block/block.api.php356
-rw-r--r--modules/block/block.css36
-rw-r--r--modules/block/block.info13
-rw-r--r--modules/block/block.install468
-rw-r--r--modules/block/block.js168
-rw-r--r--modules/block/block.module1031
-rw-r--r--modules/block/block.test753
-rw-r--r--modules/block/block.tpl.php55
-rw-r--r--modules/block/tests/block_test.info12
-rw-r--r--modules/block/tests/block_test.module28
-rw-r--r--modules/blog/blog.info12
-rw-r--r--modules/blog/blog.install23
-rw-r--r--modules/blog/blog.module272
-rw-r--r--modules/blog/blog.pages.inc127
-rw-r--r--modules/blog/blog.test213
-rw-r--r--modules/book/book-all-books-block.tpl.php18
-rw-r--r--modules/book/book-export-html.tpl.php50
-rw-r--r--modules/book/book-navigation.tpl.php51
-rw-r--r--modules/book/book-node-export-html.tpl.php24
-rw-r--r--modules/book/book-rtl.css11
-rw-r--r--modules/book/book.admin.inc264
-rw-r--r--modules/book/book.css54
-rw-r--r--modules/book/book.info14
-rw-r--r--modules/book/book.install92
-rw-r--r--modules/book/book.js22
-rw-r--r--modules/book/book.module1315
-rw-r--r--modules/book/book.pages.inc220
-rw-r--r--modules/book/book.test335
-rw-r--r--modules/color/color-rtl.css44
-rw-r--r--modules/color/color.css81
-rw-r--r--modules/color/color.info12
-rw-r--r--modules/color/color.install57
-rw-r--r--modules/color/color.js243
-rw-r--r--modules/color/color.module740
-rw-r--r--modules/color/color.test133
-rw-r--r--modules/color/images/hook-rtl.pngbin0 -> 116 bytes
-rw-r--r--modules/color/images/hook.pngbin0 -> 116 bytes
-rw-r--r--modules/color/images/lock.pngbin0 -> 230 bytes
-rw-r--r--modules/color/preview.html7
-rw-r--r--modules/color/preview.js34
-rw-r--r--modules/comment/comment-node-form.js32
-rw-r--r--modules/comment/comment-rtl.css5
-rw-r--r--modules/comment/comment-wrapper.tpl.php51
-rw-r--r--modules/comment/comment.admin.inc283
-rw-r--r--modules/comment/comment.api.php145
-rw-r--r--modules/comment/comment.css13
-rw-r--r--modules/comment/comment.info16
-rw-r--r--modules/comment/comment.install573
-rw-r--r--modules/comment/comment.module2707
-rw-r--r--modules/comment/comment.pages.inc119
-rw-r--r--modules/comment/comment.test1989
-rw-r--r--modules/comment/comment.tokens.inc243
-rw-r--r--modules/comment/comment.tpl.php90
-rw-r--r--modules/contact/contact.admin.inc206
-rw-r--r--modules/contact/contact.info13
-rw-r--r--modules/contact/contact.install168
-rw-r--r--modules/contact/contact.module257
-rw-r--r--modules/contact/contact.pages.inc291
-rw-r--r--modules/contact/contact.test416
-rw-r--r--modules/contextual/contextual-rtl.css16
-rw-r--r--modules/contextual/contextual.api.php40
-rw-r--r--modules/contextual/contextual.css99
-rw-r--r--modules/contextual/contextual.info11
-rw-r--r--modules/contextual/contextual.js43
-rw-r--r--modules/contextual/contextual.module172
-rw-r--r--modules/contextual/images/gear-select.pngbin0 -> 506 bytes
-rw-r--r--modules/dashboard/dashboard-rtl.css25
-rw-r--r--modules/dashboard/dashboard.api.php42
-rw-r--r--modules/dashboard/dashboard.css130
-rw-r--r--modules/dashboard/dashboard.info14
-rw-r--r--modules/dashboard/dashboard.install78
-rw-r--r--modules/dashboard/dashboard.js218
-rw-r--r--modules/dashboard/dashboard.module677
-rw-r--r--modules/dashboard/dashboard.test140
-rw-r--r--modules/dblog/dblog-rtl.css7
-rw-r--r--modules/dblog/dblog.admin.inc383
-rw-r--r--modules/dblog/dblog.css59
-rw-r--r--modules/dblog/dblog.info12
-rw-r--r--modules/dblog/dblog.install142
-rw-r--r--modules/dblog/dblog.module184
-rw-r--r--modules/dblog/dblog.test591
-rw-r--r--modules/field/field.api.php2635
-rw-r--r--modules/field/field.attach.inc1364
-rw-r--r--modules/field/field.crud.inc971
-rw-r--r--modules/field/field.default.inc268
-rw-r--r--modules/field/field.form.inc570
-rw-r--r--modules/field/field.info17
-rw-r--r--modules/field/field.info.inc889
-rw-r--r--modules/field/field.install439
-rw-r--r--modules/field/field.module1246
-rw-r--r--modules/field/field.multilingual.inc298
-rw-r--r--modules/field/modules/field_sql_storage/field_sql_storage.info14
-rw-r--r--modules/field/modules/field_sql_storage/field_sql_storage.install215
-rw-r--r--modules/field/modules/field_sql_storage/field_sql_storage.module750
-rw-r--r--modules/field/modules/field_sql_storage/field_sql_storage.test427
-rw-r--r--modules/field/modules/list/list.info14
-rw-r--r--modules/field/modules/list/list.install129
-rw-r--r--modules/field/modules/list/list.module471
-rw-r--r--modules/field/modules/list/tests/list.test374
-rw-r--r--modules/field/modules/list/tests/list_test.info12
-rw-r--r--modules/field/modules/list/tests/list_test.module23
-rw-r--r--modules/field/modules/number/number.info13
-rw-r--r--modules/field/modules/number/number.install45
-rw-r--r--modules/field/modules/number/number.module419
-rw-r--r--modules/field/modules/number/number.test133
-rw-r--r--modules/field/modules/options/options.api.php69
-rw-r--r--modules/field/modules/options/options.info13
-rw-r--r--modules/field/modules/options/options.module406
-rw-r--r--modules/field/modules/options/options.test518
-rw-r--r--modules/field/modules/text/text.info14
-rw-r--r--modules/field/modules/text/text.install95
-rw-r--r--modules/field/modules/text/text.js49
-rw-r--r--modules/field/modules/text/text.module611
-rw-r--r--modules/field/modules/text/text.test517
-rw-r--r--modules/field/tests/field.test3253
-rw-r--r--modules/field/tests/field_test.entity.inc494
-rw-r--r--modules/field/tests/field_test.field.inc379
-rw-r--r--modules/field/tests/field_test.info13
-rw-r--r--modules/field/tests/field_test.install150
-rw-r--r--modules/field/tests/field_test.module250
-rw-r--r--modules/field/tests/field_test.storage.inc473
-rw-r--r--modules/field/theme/field-rtl.css14
-rw-r--r--modules/field/theme/field.css28
-rw-r--r--modules/field/theme/field.tpl.php60
-rw-r--r--modules/field_ui/field_ui-rtl.css5
-rw-r--r--modules/field_ui/field_ui.admin.inc2053
-rw-r--r--modules/field_ui/field_ui.api.php204
-rw-r--r--modules/field_ui/field_ui.css59
-rw-r--r--modules/field_ui/field_ui.info13
-rw-r--r--modules/field_ui/field_ui.js330
-rw-r--r--modules/field_ui/field_ui.module375
-rw-r--r--modules/field_ui/field_ui.test645
-rw-r--r--modules/file/file.api.php66
-rw-r--r--modules/file/file.css35
-rw-r--r--modules/file/file.field.inc1003
-rw-r--r--modules/file/file.info13
-rw-r--r--modules/file/file.install98
-rw-r--r--modules/file/file.js149
-rw-r--r--modules/file/file.module996
-rw-r--r--modules/file/icons/application-octet-stream.pngbin0 -> 189 bytes
-rw-r--r--modules/file/icons/application-pdf.pngbin0 -> 346 bytes
-rw-r--r--modules/file/icons/application-x-executable.pngbin0 -> 189 bytes
-rw-r--r--modules/file/icons/audio-x-generic.pngbin0 -> 314 bytes
-rw-r--r--modules/file/icons/image-x-generic.pngbin0 -> 385 bytes
-rw-r--r--modules/file/icons/package-x-generic.pngbin0 -> 260 bytes
-rw-r--r--modules/file/icons/text-html.pngbin0 -> 265 bytes
-rw-r--r--modules/file/icons/text-plain.pngbin0 -> 220 bytes
-rw-r--r--modules/file/icons/text-x-generic.pngbin0 -> 220 bytes
-rw-r--r--modules/file/icons/text-x-script.pngbin0 -> 276 bytes
-rw-r--r--modules/file/icons/video-x-generic.pngbin0 -> 214 bytes
-rw-r--r--modules/file/icons/x-office-document.pngbin0 -> 196 bytes
-rw-r--r--modules/file/icons/x-office-presentation.pngbin0 -> 181 bytes
-rw-r--r--modules/file/icons/x-office-spreadsheet.pngbin0 -> 183 bytes
-rw-r--r--modules/file/tests/file.test1138
-rw-r--r--modules/file/tests/file_module_test.info12
-rw-r--r--modules/file/tests/file_module_test.module65
-rw-r--r--modules/filter/filter.admin.inc365
-rw-r--r--modules/filter/filter.admin.js44
-rw-r--r--modules/filter/filter.api.php323
-rw-r--r--modules/filter/filter.css53
-rw-r--r--modules/filter/filter.info14
-rw-r--r--modules/filter/filter.install494
-rw-r--r--modules/filter/filter.js20
-rw-r--r--modules/filter/filter.module1682
-rw-r--r--modules/filter/filter.pages.inc88
-rw-r--r--modules/filter/filter.test1752
-rw-r--r--modules/filter/tests/filter.url-input.txt36
-rw-r--r--modules/filter/tests/filter.url-output.txt36
-rw-r--r--modules/forum/forum-icon.tpl.php24
-rw-r--r--modules/forum/forum-list.tpl.php76
-rw-r--r--modules/forum/forum-rtl.css16
-rw-r--r--modules/forum/forum-submitted.tpl.php28
-rw-r--r--modules/forum/forum-topic-list.tpl.php68
-rw-r--r--modules/forum/forum.admin.inc313
-rw-r--r--modules/forum/forum.css50
-rw-r--r--modules/forum/forum.info16
-rw-r--r--modules/forum/forum.install444
-rw-r--r--modules/forum/forum.module1294
-rw-r--r--modules/forum/forum.pages.inc28
-rw-r--r--modules/forum/forum.test550
-rw-r--r--modules/forum/forums.tpl.php22
-rw-r--r--modules/help/help-rtl.css10
-rw-r--r--modules/help/help.admin.inc77
-rw-r--r--modules/help/help.api.php62
-rw-r--r--modules/help/help.css9
-rw-r--r--modules/help/help.info12
-rw-r--r--modules/help/help.module63
-rw-r--r--modules/help/help.test125
-rw-r--r--modules/image/image-rtl.css11
-rw-r--r--modules/image/image.admin.css60
-rw-r--r--modules/image/image.admin.inc907
-rw-r--r--modules/image/image.api.php199
-rw-r--r--modules/image/image.css14
-rw-r--r--modules/image/image.effects.inc316
-rw-r--r--modules/image/image.field.inc610
-rw-r--r--modules/image/image.info14
-rw-r--r--modules/image/image.install424
-rw-r--r--modules/image/image.module1231
-rw-r--r--modules/image/image.test1131
-rw-r--r--modules/image/sample.pngbin0 -> 168110 bytes
-rw-r--r--modules/image/tests/image_module_test.info13
-rw-r--r--modules/image/tests/image_module_test.module41
-rw-r--r--modules/locale/locale-rtl.css12
-rw-r--r--modules/locale/locale.admin.inc1439
-rw-r--r--modules/locale/locale.api.php196
-rw-r--r--modules/locale/locale.css32
-rw-r--r--modules/locale/locale.datepicker.js69
-rw-r--r--modules/locale/locale.info13
-rw-r--r--modules/locale/locale.install376
-rw-r--r--modules/locale/locale.module1038
-rw-r--r--modules/locale/locale.test2724
-rw-r--r--modules/locale/tests/locale_test.info12
-rw-r--r--modules/locale/tests/locale_test.js35
-rw-r--r--modules/locale/tests/locale_test.module115
-rw-r--r--modules/locale/tests/translations/test.xx.po28
-rw-r--r--modules/menu/menu.admin.inc686
-rw-r--r--modules/menu/menu.admin.js47
-rw-r--r--modules/menu/menu.api.php87
-rw-r--r--modules/menu/menu.css12
-rw-r--r--modules/menu/menu.info13
-rw-r--r--modules/menu/menu.install188
-rw-r--r--modules/menu/menu.js66
-rw-r--r--modules/menu/menu.module777
-rw-r--r--modules/menu/menu.test722
-rw-r--r--modules/node/content_types.inc447
-rw-r--r--modules/node/content_types.js34
-rw-r--r--modules/node/node.admin.inc596
-rw-r--r--modules/node/node.api.php1275
-rw-r--r--modules/node/node.css10
-rw-r--r--modules/node/node.info16
-rw-r--r--modules/node/node.install904
-rw-r--r--modules/node/node.js43
-rw-r--r--modules/node/node.module3943
-rw-r--r--modules/node/node.pages.inc583
-rw-r--r--modules/node/node.test2299
-rw-r--r--modules/node/node.tokens.inc190
-rw-r--r--modules/node/node.tpl.php110
-rw-r--r--modules/node/tests/node_access_test.info12
-rw-r--r--modules/node/tests/node_access_test.install42
-rw-r--r--modules/node/tests/node_access_test.module218
-rw-r--r--modules/node/tests/node_test.info12
-rw-r--r--modules/node/tests/node_test.module151
-rw-r--r--modules/node/tests/node_test_exception.info12
-rw-r--r--modules/node/tests/node_test_exception.module16
-rw-r--r--modules/openid/login-bg.pngbin0 -> 205 bytes
-rw-r--r--modules/openid/openid-rtl.css18
-rw-r--r--modules/openid/openid.api.php116
-rw-r--r--modules/openid/openid.css46
-rw-r--r--modules/openid/openid.inc869
-rw-r--r--modules/openid/openid.info12
-rw-r--r--modules/openid/openid.install160
-rw-r--r--modules/openid/openid.js49
-rw-r--r--modules/openid/openid.module1014
-rw-r--r--modules/openid/openid.pages.inc115
-rw-r--r--modules/openid/openid.test731
-rw-r--r--modules/openid/tests/openid_test.info13
-rw-r--r--modules/openid/tests/openid_test.install17
-rw-r--r--modules/openid/tests/openid_test.module352
-rw-r--r--modules/overlay/images/background.pngbin0 -> 76 bytes
-rw-r--r--modules/overlay/images/close-rtl.pngbin0 -> 368 bytes
-rw-r--r--modules/overlay/images/close.pngbin0 -> 344 bytes
-rw-r--r--modules/overlay/overlay-child-rtl.css35
-rw-r--r--modules/overlay/overlay-child.css167
-rw-r--r--modules/overlay/overlay-child.js192
-rw-r--r--modules/overlay/overlay-parent.css50
-rw-r--r--modules/overlay/overlay-parent.js991
-rw-r--r--modules/overlay/overlay.api.php40
-rw-r--r--modules/overlay/overlay.info11
-rw-r--r--modules/overlay/overlay.install19
-rw-r--r--modules/overlay/overlay.module975
-rw-r--r--modules/overlay/overlay.tpl.php37
-rw-r--r--modules/path/path.admin.inc283
-rw-r--r--modules/path/path.api.php74
-rw-r--r--modules/path/path.info13
-rw-r--r--modules/path/path.js16
-rw-r--r--modules/path/path.module306
-rw-r--r--modules/path/path.test505
-rw-r--r--modules/php/php.info12
-rw-r--r--modules/php/php.install45
-rw-r--r--modules/php/php.module140
-rw-r--r--modules/php/php.test120
-rw-r--r--modules/poll/poll-bar--block.tpl.php26
-rw-r--r--modules/poll/poll-bar.tpl.php26
-rw-r--r--modules/poll/poll-results--block.tpl.php28
-rw-r--r--modules/poll/poll-results.tpl.php28
-rw-r--r--modules/poll/poll-rtl.css10
-rw-r--r--modules/poll/poll-vote.tpl.php29
-rw-r--r--modules/poll/poll.css51
-rw-r--r--modules/poll/poll.info13
-rw-r--r--modules/poll/poll.install206
-rw-r--r--modules/poll/poll.module1014
-rw-r--r--modules/poll/poll.pages.inc97
-rw-r--r--modules/poll/poll.test784
-rw-r--r--modules/poll/poll.tokens.inc107
-rw-r--r--modules/profile/profile-block.tpl.php41
-rw-r--r--modules/profile/profile-listing.tpl.php52
-rw-r--r--modules/profile/profile-wrapper.tpl.php24
-rw-r--r--modules/profile/profile.admin.inc446
-rw-r--r--modules/profile/profile.css10
-rw-r--r--modules/profile/profile.info18
-rw-r--r--modules/profile/profile.install172
-rw-r--r--modules/profile/profile.js58
-rw-r--r--modules/profile/profile.module628
-rw-r--r--modules/profile/profile.pages.inc139
-rw-r--r--modules/profile/profile.test490
-rw-r--r--modules/rdf/rdf.api.php106
-rw-r--r--modules/rdf/rdf.info12
-rw-r--r--modules/rdf/rdf.install49
-rw-r--r--modules/rdf/rdf.module870
-rw-r--r--modules/rdf/rdf.test715
-rw-r--r--modules/rdf/tests/rdf_test.info12
-rw-r--r--modules/rdf/tests/rdf_test.install25
-rw-r--r--modules/rdf/tests/rdf_test.module66
-rw-r--r--modules/search/search-block-form.tpl.php37
-rw-r--r--modules/search/search-result.tpl.php79
-rw-r--r--modules/search/search-results.tpl.php33
-rw-r--r--modules/search/search-rtl.css13
-rw-r--r--modules/search/search.admin.inc185
-rw-r--r--modules/search/search.api.php368
-rw-r--r--modules/search/search.css34
-rw-r--r--modules/search/search.extender.inc483
-rw-r--r--modules/search/search.info15
-rw-r--r--modules/search/search.install182
-rw-r--r--modules/search/search.module1351
-rw-r--r--modules/search/search.pages.inc160
-rw-r--r--modules/search/search.test1992
-rw-r--r--modules/search/tests/UnicodeTest.txt333
-rw-r--r--modules/search/tests/search_embedded_form.info12
-rw-r--r--modules/search/tests/search_embedded_form.module70
-rw-r--r--modules/search/tests/search_extra_type.info12
-rw-r--r--modules/search/tests/search_extra_type.module69
-rw-r--r--modules/shortcut/shortcut-rtl.css48
-rw-r--r--modules/shortcut/shortcut.admin.css8
-rw-r--r--modules/shortcut/shortcut.admin.inc781
-rw-r--r--modules/shortcut/shortcut.admin.js99
-rw-r--r--modules/shortcut/shortcut.api.php42
-rw-r--r--modules/shortcut/shortcut.css106
-rw-r--r--modules/shortcut/shortcut.info13
-rw-r--r--modules/shortcut/shortcut.install115
-rw-r--r--modules/shortcut/shortcut.module749
-rw-r--r--modules/shortcut/shortcut.pngbin0 -> 558 bytes
-rw-r--r--modules/shortcut/shortcut.test369
-rw-r--r--modules/simpletest/drupal_web_test_case.php3428
-rw-r--r--modules/simpletest/files/README.txt4
-rw-r--r--modules/simpletest/files/css_test_files/comment_hacks.css80
-rw-r--r--modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css1
-rw-r--r--modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css80
-rw-r--r--modules/simpletest/files/css_test_files/css_input_with_import.css30
-rw-r--r--modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css6
-rw-r--r--modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css30
-rw-r--r--modules/simpletest/files/css_test_files/css_input_without_import.css69
-rw-r--r--modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css4
-rw-r--r--modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css69
-rw-r--r--modules/simpletest/files/css_test_files/import1.css6
-rw-r--r--modules/simpletest/files/css_test_files/import2.css5
-rw-r--r--modules/simpletest/files/html-1.txt1
-rw-r--r--modules/simpletest/files/html-2.html1
-rw-r--r--modules/simpletest/files/image-1.pngbin0 -> 39325 bytes
-rw-r--r--modules/simpletest/files/image-2.jpgbin0 -> 1831 bytes
-rw-r--r--modules/simpletest/files/image-test.gifbin0 -> 183 bytes
-rw-r--r--modules/simpletest/files/image-test.jpgbin0 -> 1901 bytes
-rw-r--r--modules/simpletest/files/image-test.pngbin0 -> 125 bytes
-rw-r--r--modules/simpletest/files/javascript-1.txt3
-rw-r--r--modules/simpletest/files/javascript-2.script3
-rw-r--r--modules/simpletest/files/php-1.txt3
-rw-r--r--modules/simpletest/files/php-2.php2
-rw-r--r--modules/simpletest/files/sql-1.txt1
-rw-r--r--modules/simpletest/files/sql-2.sql1
-rw-r--r--modules/simpletest/simpletest.api.php60
-rw-r--r--modules/simpletest/simpletest.css89
-rw-r--r--modules/simpletest/simpletest.info56
-rw-r--r--modules/simpletest/simpletest.install182
-rw-r--r--modules/simpletest/simpletest.js103
-rw-r--r--modules/simpletest/simpletest.module506
-rw-r--r--modules/simpletest/simpletest.pages.inc510
-rw-r--r--modules/simpletest/simpletest.test505
-rw-r--r--modules/simpletest/tests/actions.test126
-rw-r--r--modules/simpletest/tests/actions_loop_test.info12
-rw-r--r--modules/simpletest/tests/actions_loop_test.install11
-rw-r--r--modules/simpletest/tests/actions_loop_test.module94
-rw-r--r--modules/simpletest/tests/ajax.test430
-rw-r--r--modules/simpletest/tests/ajax_forms_test.info12
-rw-r--r--modules/simpletest/tests/ajax_forms_test.module459
-rw-r--r--modules/simpletest/tests/ajax_test.info12
-rw-r--r--modules/simpletest/tests/ajax_test.module71
-rw-r--r--modules/simpletest/tests/batch.test405
-rw-r--r--modules/simpletest/tests/batch_test.callbacks.inc141
-rw-r--r--modules/simpletest/tests/batch_test.info12
-rw-r--r--modules/simpletest/tests/batch_test.module513
-rw-r--r--modules/simpletest/tests/bootstrap.test499
-rw-r--r--modules/simpletest/tests/cache.test379
-rw-r--r--modules/simpletest/tests/common.test2482
-rw-r--r--modules/simpletest/tests/common_test.css2
-rw-r--r--modules/simpletest/tests/common_test.info14
-rw-r--r--modules/simpletest/tests/common_test.module240
-rw-r--r--modules/simpletest/tests/common_test.print.css2
-rw-r--r--modules/simpletest/tests/common_test_cron_helper.info12
-rw-r--r--modules/simpletest/tests/common_test_cron_helper.module17
-rw-r--r--modules/simpletest/tests/common_test_info.txt9
-rw-r--r--modules/simpletest/tests/database_test.info12
-rw-r--r--modules/simpletest/tests/database_test.install217
-rw-r--r--modules/simpletest/tests/database_test.module241
-rw-r--r--modules/simpletest/tests/database_test.test3691
-rw-r--r--modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info12
-rw-r--r--modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module1
-rw-r--r--modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info12
-rw-r--r--modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module1
-rw-r--r--modules/simpletest/tests/entity_cache_test.info13
-rw-r--r--modules/simpletest/tests/entity_cache_test.module27
-rw-r--r--modules/simpletest/tests/entity_cache_test_dependency.info12
-rw-r--r--modules/simpletest/tests/entity_cache_test_dependency.module17
-rw-r--r--modules/simpletest/tests/entity_crud_hook_test.info12
-rw-r--r--modules/simpletest/tests/entity_crud_hook_test.module266
-rw-r--r--modules/simpletest/tests/entity_crud_hook_test.test332
-rw-r--r--modules/simpletest/tests/entity_query.test1623
-rw-r--r--modules/simpletest/tests/error.test116
-rw-r--r--modules/simpletest/tests/error_test.info12
-rw-r--r--modules/simpletest/tests/error_test.module65
-rw-r--r--modules/simpletest/tests/file.test2748
-rw-r--r--modules/simpletest/tests/file_test.info13
-rw-r--r--modules/simpletest/tests/file_test.module461
-rw-r--r--modules/simpletest/tests/filetransfer.test168
-rw-r--r--modules/simpletest/tests/filter_test.info12
-rw-r--r--modules/simpletest/tests/filter_test.module62
-rw-r--r--modules/simpletest/tests/form.test1550
-rw-r--r--modules/simpletest/tests/form_test.file.inc48
-rw-r--r--modules/simpletest/tests/form_test.info12
-rw-r--r--modules/simpletest/tests/form_test.module1627
-rw-r--r--modules/simpletest/tests/graph.test195
-rw-r--r--modules/simpletest/tests/http.php32
-rw-r--r--modules/simpletest/tests/https.php31
-rw-r--r--modules/simpletest/tests/image.test460
-rw-r--r--modules/simpletest/tests/image_test.info12
-rw-r--r--modules/simpletest/tests/image_test.module138
-rw-r--r--modules/simpletest/tests/lock.test57
-rw-r--r--modules/simpletest/tests/mail.test418
-rw-r--r--modules/simpletest/tests/menu.test1646
-rw-r--r--modules/simpletest/tests/menu_test.info12
-rw-r--r--modules/simpletest/tests/menu_test.module527
-rw-r--r--modules/simpletest/tests/module.test304
-rw-r--r--modules/simpletest/tests/module_test.file.inc13
-rw-r--r--modules/simpletest/tests/module_test.info12
-rw-r--r--modules/simpletest/tests/module_test.install42
-rw-r--r--modules/simpletest/tests/module_test.module131
-rw-r--r--modules/simpletest/tests/password.test60
-rw-r--r--modules/simpletest/tests/path.test327
-rw-r--r--modules/simpletest/tests/registry.test142
-rw-r--r--modules/simpletest/tests/requirements1_test.info12
-rw-r--r--modules/simpletest/tests/requirements1_test.install21
-rw-r--r--modules/simpletest/tests/requirements1_test.module7
-rw-r--r--modules/simpletest/tests/requirements2_test.info14
-rw-r--r--modules/simpletest/tests/requirements2_test.module7
-rw-r--r--modules/simpletest/tests/schema.test384
-rw-r--r--modules/simpletest/tests/session.test536
-rw-r--r--modules/simpletest/tests/session_test.info12
-rw-r--r--modules/simpletest/tests/session_test.module192
-rw-r--r--modules/simpletest/tests/system.base.css6
-rw-r--r--modules/simpletest/tests/system_dependencies_test.info13
-rw-r--r--modules/simpletest/tests/system_dependencies_test.module1
-rw-r--r--modules/simpletest/tests/system_test.info13
-rw-r--r--modules/simpletest/tests/system_test.module399
-rw-r--r--modules/simpletest/tests/tablesort.test166
-rw-r--r--modules/simpletest/tests/taxonomy_test.info13
-rw-r--r--modules/simpletest/tests/taxonomy_test.install34
-rw-r--r--modules/simpletest/tests/taxonomy_test.module81
-rw-r--r--modules/simpletest/tests/theme.test382
-rw-r--r--modules/simpletest/tests/theme_test.info12
-rw-r--r--modules/simpletest/tests/theme_test.module106
-rw-r--r--modules/simpletest/tests/unicode.test305
-rw-r--r--modules/simpletest/tests/update.test115
-rw-r--r--modules/simpletest/tests/update_test_1.info12
-rw-r--r--modules/simpletest/tests/update_test_1.install53
-rw-r--r--modules/simpletest/tests/update_test_1.module1
-rw-r--r--modules/simpletest/tests/update_test_2.info12
-rw-r--r--modules/simpletest/tests/update_test_2.install53
-rw-r--r--modules/simpletest/tests/update_test_2.module1
-rw-r--r--modules/simpletest/tests/update_test_3.info12
-rw-r--r--modules/simpletest/tests/update_test_3.install24
-rw-r--r--modules/simpletest/tests/update_test_3.module1
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.bare.database.php8131
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.comments.database.php40
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.filled.database.php20384
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.forum.database.php260
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.locale.database.php276
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.menu.database.php175
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.translatable.database.php125
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.upload.database.php397
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.user-no-password-token.database.php10
-rw-r--r--modules/simpletest/tests/upgrade/drupal-6.user-password-token.database.php55
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.comment.test32
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.filter.test55
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.forum.test60
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.locale.test143
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.menu.test83
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.node.test116
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.poll.test66
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.taxonomy.test196
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.test410
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.translatable.test51
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.upload.test77
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.user.test63
-rw-r--r--modules/simpletest/tests/url_alter_test.info12
-rw-r--r--modules/simpletest/tests/url_alter_test.install12
-rw-r--r--modules/simpletest/tests/url_alter_test.module67
-rw-r--r--modules/simpletest/tests/xmlrpc.test244
-rw-r--r--modules/simpletest/tests/xmlrpc_test.info12
-rw-r--r--modules/simpletest/tests/xmlrpc_test.module111
-rw-r--r--modules/statistics/statistics.admin.inc274
-rw-r--r--modules/statistics/statistics.info13
-rw-r--r--modules/statistics/statistics.install158
-rw-r--r--modules/statistics/statistics.module427
-rw-r--r--modules/statistics/statistics.pages.inc91
-rw-r--r--modules/statistics/statistics.test444
-rw-r--r--modules/statistics/statistics.tokens.inc63
-rw-r--r--modules/syslog/syslog.info12
-rw-r--r--modules/syslog/syslog.install15
-rw-r--r--modules/syslog/syslog.module109
-rw-r--r--modules/syslog/syslog.test41
-rw-r--r--modules/system/html.tpl.php61
-rw-r--r--modules/system/image.gd.inc367
-rw-r--r--modules/system/maintenance-page.tpl.php93
-rw-r--r--modules/system/page.tpl.php150
-rw-r--r--modules/system/region.tpl.php33
-rw-r--r--modules/system/system.admin-rtl.css86
-rw-r--r--modules/system/system.admin.css271
-rw-r--r--modules/system/system.admin.inc3173
-rw-r--r--modules/system/system.api.php4516
-rw-r--r--modules/system/system.archiver.inc139
-rw-r--r--modules/system/system.base-rtl.css54
-rw-r--r--modules/system/system.base.css264
-rw-r--r--modules/system/system.cron.js19
-rw-r--r--modules/system/system.info19
-rw-r--r--modules/system/system.install2989
-rw-r--r--modules/system/system.js137
-rw-r--r--modules/system/system.mail.inc112
-rw-r--r--modules/system/system.maintenance.css55
-rw-r--r--modules/system/system.menus-rtl.css37
-rw-r--r--modules/system/system.menus.css116
-rw-r--r--modules/system/system.messages-rtl.css13
-rw-r--r--modules/system/system.messages.css63
-rw-r--r--modules/system/system.module3988
-rw-r--r--modules/system/system.queue.inc371
-rw-r--r--modules/system/system.tar.inc1892
-rw-r--r--modules/system/system.test2452
-rw-r--r--modules/system/system.theme-rtl.css53
-rw-r--r--modules/system/system.theme.css239
-rw-r--r--modules/system/system.tokens.inc269
-rw-r--r--modules/system/system.updater.inc146
-rw-r--r--modules/system/theme.api.php230
-rw-r--r--modules/taxonomy/taxonomy-term.tpl.php51
-rw-r--r--modules/taxonomy/taxonomy.admin.inc983
-rw-r--r--modules/taxonomy/taxonomy.api.php214
-rw-r--r--modules/taxonomy/taxonomy.css13
-rw-r--r--modules/taxonomy/taxonomy.info15
-rw-r--r--modules/taxonomy/taxonomy.install812
-rw-r--r--modules/taxonomy/taxonomy.js40
-rw-r--r--modules/taxonomy/taxonomy.module1797
-rw-r--r--modules/taxonomy/taxonomy.pages.inc130
-rw-r--r--modules/taxonomy/taxonomy.test1286
-rw-r--r--modules/taxonomy/taxonomy.tokens.inc189
-rw-r--r--modules/toolbar/toolbar-rtl.css41
-rw-r--r--modules/toolbar/toolbar.css150
-rw-r--r--modules/toolbar/toolbar.info11
-rw-r--r--modules/toolbar/toolbar.js106
-rw-r--r--modules/toolbar/toolbar.module350
-rw-r--r--modules/toolbar/toolbar.pngbin0 -> 558 bytes
-rw-r--r--modules/toolbar/toolbar.tpl.php37
-rw-r--r--modules/tracker/tracker.css7
-rw-r--r--modules/tracker/tracker.info13
-rw-r--r--modules/tracker/tracker.install220
-rw-r--r--modules/tracker/tracker.module370
-rw-r--r--modules/tracker/tracker.pages.inc126
-rw-r--r--modules/tracker/tracker.test254
-rw-r--r--modules/translation/tests/translation_test.info12
-rw-r--r--modules/translation/tests/translation_test.module13
-rw-r--r--modules/translation/translation.info13
-rw-r--r--modules/translation/translation.module535
-rw-r--r--modules/translation/translation.pages.inc77
-rw-r--r--modules/translation/translation.test468
-rw-r--r--modules/trigger/tests/trigger_test.info11
-rw-r--r--modules/trigger/tests/trigger_test.module133
-rw-r--r--modules/trigger/trigger.admin.inc309
-rw-r--r--modules/trigger/trigger.api.php78
-rw-r--r--modules/trigger/trigger.info13
-rw-r--r--modules/trigger/trigger.install70
-rw-r--r--modules/trigger/trigger.module631
-rw-r--r--modules/trigger/trigger.test740
-rw-r--r--modules/update/tests/aaa_update_test.1_0.xml34
-rw-r--r--modules/update/tests/aaa_update_test.info11
-rw-r--r--modules/update/tests/aaa_update_test.module6
-rw-r--r--modules/update/tests/aaa_update_test.no-releases.xml2
-rw-r--r--modules/update/tests/aaa_update_test.tar.gzbin0 -> 383 bytes
-rw-r--r--modules/update/tests/bbb_update_test.1_0.xml34
-rw-r--r--modules/update/tests/bbb_update_test.info11
-rw-r--r--modules/update/tests/bbb_update_test.module6
-rw-r--r--modules/update/tests/ccc_update_test.1_0.xml34
-rw-r--r--modules/update/tests/ccc_update_test.info11
-rw-r--r--modules/update/tests/ccc_update_test.module6
-rw-r--r--modules/update/tests/drupal.0.xml34
-rw-r--r--modules/update/tests/drupal.1.xml51
-rw-r--r--modules/update/tests/drupal.2-sec.xml69
-rw-r--r--modules/update/tests/drupal.dev.xml50
-rw-r--r--modules/update/tests/update_test.info12
-rw-r--r--modules/update/tests/update_test.module164
-rw-r--r--modules/update/tests/update_test_basetheme.1_1-sec.xml52
-rw-r--r--modules/update/tests/update_test_subtheme.1_0.xml34
-rw-r--r--modules/update/update-rtl.css31
-rw-r--r--modules/update/update.api.php133
-rw-r--r--modules/update/update.authorize.inc314
-rw-r--r--modules/update/update.compare.inc789
-rw-r--r--modules/update/update.css131
-rw-r--r--modules/update/update.fetch.inc388
-rw-r--r--modules/update/update.info13
-rw-r--r--modules/update/update.install190
-rw-r--r--modules/update/update.manager.inc923
-rw-r--r--modules/update/update.module958
-rw-r--r--modules/update/update.report.inc324
-rw-r--r--modules/update/update.settings.inc120
-rw-r--r--modules/update/update.test699
-rw-r--r--modules/user/tests/user_form_test.info12
-rw-r--r--modules/user/tests/user_form_test.module64
-rw-r--r--modules/user/user-picture.tpl.php21
-rw-r--r--modules/user/user-profile-category.tpl.php33
-rw-r--r--modules/user/user-profile-item.tpl.php26
-rw-r--r--modules/user/user-profile.tpl.php37
-rw-r--r--modules/user/user-rtl.css34
-rw-r--r--modules/user/user.admin.inc1037
-rw-r--r--modules/user/user.api.php450
-rw-r--r--modules/user/user.css102
-rw-r--r--modules/user/user.info16
-rw-r--r--modules/user/user.install892
-rw-r--r--modules/user/user.js196
-rw-r--r--modules/user/user.module3935
-rw-r--r--modules/user/user.pages.inc552
-rw-r--r--modules/user/user.permissions.js69
-rw-r--r--modules/user/user.test2171
-rw-r--r--modules/user/user.tokens.inc131
-rw-r--r--profiles/minimal/minimal.info13
-rw-r--r--profiles/minimal/minimal.install75
-rw-r--r--profiles/minimal/minimal.profile11
-rw-r--r--profiles/minimal/translations/README.txt3
-rw-r--r--profiles/standard/standard.info32
-rw-r--r--profiles/standard/standard.install428
-rw-r--r--profiles/standard/standard.profile11
-rw-r--r--profiles/standard/translations/README.txt3
-rw-r--r--profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info12
-rw-r--r--profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module1
-rw-r--r--profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info15
-rw-r--r--profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module1
-rw-r--r--profiles/testing/testing.info11
-rw-r--r--profiles/testing/testing.install15
-rw-r--r--profiles/testing/testing.profile2
-rw-r--r--robots.txt60
-rw-r--r--scripts/code-clean.sh9
-rw-r--r--scripts/cron-curl.sh3
-rw-r--r--scripts/cron-lynx.sh3
-rwxr-xr-xscripts/drupal.sh144
-rw-r--r--scripts/dump-database-d6.sh101
-rw-r--r--scripts/generate-d6-content.sh206
-rwxr-xr-xscripts/password-hash.sh90
-rwxr-xr-xscripts/run-tests.sh671
-rw-r--r--sites/all/README.txt7
-rw-r--r--sites/all/modules/README.txt4
-rw-r--r--sites/all/themes/README.txt4
-rw-r--r--sites/default/default.settings.php489
-rw-r--r--sites/example.sites.php43
-rw-r--r--themes/README.txt9
-rw-r--r--themes/bartik/bartik.info41
-rw-r--r--themes/bartik/color/base.pngbin0 -> 106 bytes
-rw-r--r--themes/bartik/color/color.inc132
-rw-r--r--themes/bartik/color/preview.css200
-rw-r--r--themes/bartik/color/preview.html65
-rw-r--r--themes/bartik/color/preview.js39
-rw-r--r--themes/bartik/color/preview.pngbin0 -> 106 bytes
-rw-r--r--themes/bartik/css/colors.css58
-rw-r--r--themes/bartik/css/ie-rtl.css48
-rw-r--r--themes/bartik/css/ie.css63
-rw-r--r--themes/bartik/css/ie6.css18
-rw-r--r--themes/bartik/css/layout-rtl.css22
-rw-r--r--themes/bartik/css/layout.css100
-rw-r--r--themes/bartik/css/maintenance-page.css67
-rw-r--r--themes/bartik/css/print.css46
-rw-r--r--themes/bartik/css/style-rtl.css271
-rw-r--r--themes/bartik/css/style.css1650
-rw-r--r--themes/bartik/images/add.pngbin0 -> 94 bytes
-rw-r--r--themes/bartik/images/buttons.pngbin0 -> 831 bytes
-rw-r--r--themes/bartik/images/comment-arrow-rtl.gifbin0 -> 97 bytes
-rw-r--r--themes/bartik/images/comment-arrow.gifbin0 -> 97 bytes
-rw-r--r--themes/bartik/images/search-button.pngbin0 -> 725 bytes
-rw-r--r--themes/bartik/images/tabs-border.pngbin0 -> 83 bytes
-rw-r--r--themes/bartik/logo.pngbin0 -> 3479 bytes
-rw-r--r--themes/bartik/screenshot.pngbin0 -> 19658 bytes
-rw-r--r--themes/bartik/template.php152
-rw-r--r--themes/bartik/templates/comment-wrapper.tpl.php51
-rw-r--r--themes/bartik/templates/comment.tpl.php105
-rw-r--r--themes/bartik/templates/maintenance-page.tpl.php66
-rw-r--r--themes/bartik/templates/node.tpl.php124
-rw-r--r--themes/bartik/templates/page.tpl.php246
-rw-r--r--themes/engines/phptemplate/phptemplate.engine25
-rw-r--r--themes/garland/color/base.pngbin0 -> 20894 bytes
-rw-r--r--themes/garland/color/color.inc220
-rw-r--r--themes/garland/color/preview.css60
-rw-r--r--themes/garland/color/preview.pngbin0 -> 9965 bytes
-rw-r--r--themes/garland/comment.tpl.php31
-rw-r--r--themes/garland/fix-ie-rtl.css61
-rw-r--r--themes/garland/fix-ie.css65
-rw-r--r--themes/garland/garland.info14
-rw-r--r--themes/garland/images/bg-bar-white.pngbin0 -> 103 bytes
-rw-r--r--themes/garland/images/bg-bar.pngbin0 -> 125 bytes
-rw-r--r--themes/garland/images/bg-content-left.pngbin0 -> 2889 bytes
-rw-r--r--themes/garland/images/bg-content-right.pngbin0 -> 2819 bytes
-rw-r--r--themes/garland/images/bg-content.pngbin0 -> 485 bytes
-rw-r--r--themes/garland/images/bg-navigation-item-hover.pngbin0 -> 441 bytes
-rw-r--r--themes/garland/images/bg-navigation-item.pngbin0 -> 499 bytes
-rw-r--r--themes/garland/images/bg-navigation.pngbin0 -> 104 bytes
-rw-r--r--themes/garland/images/bg-tab.pngbin0 -> 115 bytes
-rw-r--r--themes/garland/images/body.pngbin0 -> 680 bytes
-rw-r--r--themes/garland/images/gradient-inner.pngbin0 -> 188 bytes
-rw-r--r--themes/garland/images/menu-collapsed-rtl.gifbin0 -> 176 bytes
-rw-r--r--themes/garland/images/menu-collapsed.gifbin0 -> 176 bytes
-rw-r--r--themes/garland/images/menu-expanded.gifbin0 -> 183 bytes
-rw-r--r--themes/garland/images/menu-leaf.gifbin0 -> 174 bytes
-rw-r--r--themes/garland/images/task-list.pngbin0 -> 128 bytes
-rw-r--r--themes/garland/logo.pngbin0 -> 5116 bytes
-rw-r--r--themes/garland/maintenance-page.tpl.php87
-rw-r--r--themes/garland/node.tpl.php34
-rw-r--r--themes/garland/page.tpl.php68
-rw-r--r--themes/garland/print.css61
-rw-r--r--themes/garland/screenshot.pngbin0 -> 10950 bytes
-rw-r--r--themes/garland/style-rtl.css344
-rw-r--r--themes/garland/style.css1255
-rw-r--r--themes/garland/template.php160
-rw-r--r--themes/garland/theme-settings.php30
-rw-r--r--themes/seven/ie.css18
-rw-r--r--themes/seven/ie6.css17
-rw-r--r--themes/seven/ie7.css23
-rw-r--r--themes/seven/images/add.pngbin0 -> 160 bytes
-rw-r--r--themes/seven/images/arrow-asc.pngbin0 -> 88 bytes
-rw-r--r--themes/seven/images/arrow-desc.pngbin0 -> 95 bytes
-rw-r--r--themes/seven/images/arrow-next.pngbin0 -> 118 bytes
-rw-r--r--themes/seven/images/arrow-prev.pngbin0 -> 115 bytes
-rw-r--r--themes/seven/images/buttons.pngbin0 -> 786 bytes
-rw-r--r--themes/seven/images/fc-rtl.pngbin0 -> 76 bytes
-rw-r--r--themes/seven/images/fc.pngbin0 -> 82 bytes
-rw-r--r--themes/seven/images/list-item-rtl.pngbin0 -> 225 bytes
-rw-r--r--themes/seven/images/list-item.pngbin0 -> 195 bytes
-rw-r--r--themes/seven/images/task-check.pngbin0 -> 261 bytes
-rw-r--r--themes/seven/images/task-item-rtl.pngbin0 -> 178 bytes
-rw-r--r--themes/seven/images/task-item.pngbin0 -> 105 bytes
-rw-r--r--themes/seven/images/ui-icons-222222-256x240.pngbin0 -> 3702 bytes
-rw-r--r--themes/seven/images/ui-icons-454545-256x240.pngbin0 -> 3702 bytes
-rw-r--r--themes/seven/images/ui-icons-800000-256x240.pngbin0 -> 3702 bytes
-rw-r--r--themes/seven/images/ui-icons-888888-256x240.pngbin0 -> 3702 bytes
-rw-r--r--themes/seven/images/ui-icons-ffffff-256x240.pngbin0 -> 3702 bytes
-rw-r--r--themes/seven/jquery.ui.theme.css436
-rw-r--r--themes/seven/logo.pngbin0 -> 3905 bytes
-rw-r--r--themes/seven/maintenance-page.tpl.php47
-rw-r--r--themes/seven/page.tpl.php36
-rw-r--r--themes/seven/reset.css216
-rw-r--r--themes/seven/screenshot.pngbin0 -> 12298 bytes
-rw-r--r--themes/seven/seven.info20
-rw-r--r--themes/seven/style-rtl.css241
-rw-r--r--themes/seven/style.css989
-rw-r--r--themes/seven/template.php115
-rw-r--r--themes/seven/vertical-tabs-rtl.css21
-rw-r--r--themes/seven/vertical-tabs.css93
-rw-r--r--themes/stark/README.txt25
-rw-r--r--themes/stark/layout.css57
-rw-r--r--themes/stark/logo.pngbin0 -> 2326 bytes
-rw-r--r--themes/stark/screenshot.pngbin0 -> 11662 bytes
-rw-r--r--themes/stark/stark.info12
-rw-r--r--themes/tests/README.txt4
-rw-r--r--themes/tests/test_theme/template.php21
-rw-r--r--themes/tests/test_theme/test_theme.info22
-rw-r--r--themes/tests/update_test_basetheme/update_test_basetheme.info10
-rw-r--r--themes/tests/update_test_subtheme/update_test_subtheme.info11
-rw-r--r--update.php464
-rw-r--r--web.config43
-rw-r--r--xmlrpc.php18
1014 files changed, 297860 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..baff153
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# Ignore configuration files that may contain sensitive information.
+sites/*/settings*.php
+
+# Ignore paths that contain user-generated content.
+sites/*/files
+sites/*/private
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..e59cd99
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,131 @@
+#
+# Apache/PHP/Drupal settings:
+#
+
+# Protect files and directories from prying eyes.
+<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$">
+ Order allow,deny
+</FilesMatch>
+
+# Don't show directory listings for URLs which map to a directory.
+Options -Indexes
+
+# Follow symbolic links in this directory.
+Options +FollowSymLinks
+
+# Make Drupal handle any 404 errors.
+ErrorDocument 404 /index.php
+
+# Set the default handler.
+DirectoryIndex index.php index.html index.htm
+
+# Override PHP settings that cannot be changed at runtime. See
+# sites/default/default.settings.php and drupal_initialize_variables() in
+# includes/bootstrap.inc for settings that can be changed at runtime.
+
+# PHP 5, Apache 1 and 2.
+<IfModule mod_php5.c>
+ php_flag magic_quotes_gpc off
+ php_flag magic_quotes_sybase off
+ php_flag register_globals off
+ php_flag session.auto_start off
+ php_value mbstring.http_input pass
+ php_value mbstring.http_output pass
+ php_flag mbstring.encoding_translation off
+</IfModule>
+
+# Requires mod_expires to be enabled.
+<IfModule mod_expires.c>
+ # Enable expirations.
+ ExpiresActive On
+
+ # Cache all files for 2 weeks after access (A).
+ ExpiresDefault A1209600
+
+ <FilesMatch \.php$>
+ # Do not allow PHP scripts to be cached unless they explicitly send cache
+ # headers themselves. Otherwise all scripts would have to overwrite the
+ # headers set by mod_expires if they want another caching behavior. This may
+ # fail if an error occurs early in the bootstrap process, and it may cause
+ # problems if a non-Drupal PHP file is installed in a subdirectory.
+ ExpiresActive Off
+ </FilesMatch>
+</IfModule>
+
+# Various rewrite rules.
+<IfModule mod_rewrite.c>
+ RewriteEngine on
+
+ # Block access to "hidden" directories whose names begin with a period. This
+ # includes directories used by version control systems such as Subversion or
+ # Git to store control files. Files whose names begin with a period, as well
+ # as the control files used by CVS, are protected by the FilesMatch directive
+ # above.
+ #
+ # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
+ # not possible to block access to entire directories from .htaccess, because
+ # <DirectoryMatch> is not allowed here.
+ #
+ # If you do not have mod_rewrite installed, you should remove these
+ # directories from your webroot or otherwise protect them from being
+ # downloaded.
+ RewriteRule "(^|/)\." - [F]
+
+ # If your site can be accessed both with and without the 'www.' prefix, you
+ # can use one of the following settings to redirect users to your preferred
+ # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
+ #
+ # To redirect all users to access the site WITH the 'www.' prefix,
+ # (http://example.com/... will be redirected to http://www.example.com/...)
+ # uncomment the following:
+ # RewriteCond %{HTTP_HOST} !^www\. [NC]
+ # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+ #
+ # To redirect all users to access the site WITHOUT the 'www.' prefix,
+ # (http://www.example.com/... will be redirected to http://example.com/...)
+ # uncomment the following:
+ # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+ # RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301]
+
+ # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
+ # VirtualDocumentRoot and the rewrite rules are not working properly.
+ # For example if your site is at http://example.com/drupal uncomment and
+ # modify the following line:
+ # RewriteBase /drupal
+ #
+ # If your site is running in a VirtualDocumentRoot at http://example.com/,
+ # uncomment the following line:
+ # RewriteBase /
+
+ # Pass all requests not referring directly to files in the filesystem to
+ # index.php. Clean URLs are handled in drupal_environment_initialize().
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_URI} !=/favicon.ico
+ RewriteRule ^ index.php [L]
+
+ # Rules to correctly serve gzip compressed CSS and JS files.
+ # Requires both mod_rewrite and mod_headers to be enabled.
+ <IfModule mod_headers.c>
+ # Serve gzip compressed CSS files if they exist and the client accepts gzip.
+ RewriteCond %{HTTP:Accept-encoding} gzip
+ RewriteCond %{REQUEST_FILENAME}\.gz -s
+ RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
+
+ # Serve gzip compressed JS files if they exist and the client accepts gzip.
+ RewriteCond %{HTTP:Accept-encoding} gzip
+ RewriteCond %{REQUEST_FILENAME}\.gz -s
+ RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
+
+ # Serve correct content types, and prevent mod_deflate double gzip.
+ RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
+ RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1]
+
+ <FilesMatch "(\.js\.gz|\.css\.gz)$">
+ # Serve correct encoding type.
+ Header set Content-Encoding gzip
+ # Force proxies to cache gzipped & non-gzipped css/js files separately.
+ Header append Vary Accept-Encoding
+ </FilesMatch>
+ </IfModule>
+</IfModule>
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 0000000..de4095d
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,1402 @@
+
+Drupal 7.9, 2011-10-26
+----------------------
+- Critical fixes to OpenID to spec violations that could allow for
+ impersonation in certain scenarios. Existing OpenID users should see
+ http://drupal.org/node/1120290#comment-5092796 for more information on
+ transitioning.
+- Fixed files getting lost when adding multiple files to multiple file fields
+ at the same time.
+- Improved usability of the clean URL test screens.
+- Restored height/width attributes on images run through the theme system.
+- Fixed usability bug with first password field being pre-filled by certain
+ browser plugins.
+- Fixed file_usage_list() so that it can return more than one result.
+- Fixed bug preventing preview of private images on node form.
+- Fixed PDO error when inserting an aggregator title longer than 255 characters.
+- Spelled out what TRADITIONAL means in MySQL sql_mode.
+- Deprecated "!=" operator for DBTNG; should be "<>".
+- Added two new API functions (menu_tree_set_path()/menu_tree_get_path()) were
+ added in order to enable setting the active menu trail for dynamically
+ generated menu paths.
+- Added new "fast 404" capability in settings.php to bypass Drupal bootstrap
+ when serving 404 pages for certain file types.
+- Added format_string() function which can perform string munging ala the t()
+ function without the overhead of the translation system.
+- Numerous #states system fixes.
+- Numerous EntityFieldQuery, DBTNG, and SQLite fixes.
+- Numerous Shortcut module fixes.
+- Numerous language system fixes.
+- Numerous token fixes.
+- Numerous CSS fixes.
+- Numerous upgrade path fixes.
+- Numerous minor string fixes.
+- Numerous notice fixes.
+
+Drupal 7.8, 2011-08-31
+----------------------
+- Fixed critical upgrade path issue with multilingual sites, leading to lost
+ content.
+- Numerous fixes to upgrade path, preventing fatal errors due to incorrect
+ dependencies.
+- Fixed issue with saving files on hosts with open_basedir restrictions.
+- Fixed Update manger error when used with Overlay.
+- Fixed RTL support in Seven administration theme and Overlay.
+- Fixes to nested transaction support.
+- Introduced performance pattern to reduce Drupal core's RAM usage.
+- Added support for HTML 5 tags to filter_xss_admin().
+- Added exception handling to cron.
+- Added new hook hook_field_widget_form_alter() for contribtued modules.
+- element_validate_*() functions now available to contrib.
+- Added new maintainers for several subsystems.
+- Numerous testing system improvements.
+- Numerous markup and CSS fixes.
+- Numerous poll module fixes.
+- Numerous notice/warning fixes.
+- Numerous documentation fixes.
+- Numerous token fixes.
+
+Drupal 7.7, 2011-07-27
+----------------------
+- Fixed VERSION string.
+
+Drupal 7.6, 2011-07-27
+----------------------
+- Fixed support for remote streamwrappers.
+- AJAX now binds to 'click' instead of 'mousedown'.
+- 'Translatable' flag on fields created in UI now defaults to FALSE, to match those created via the API.
+- Performance enhancement to permissions page on large numbers of permissions.
+- More secure password generation.
+- Fix for temporary directory on Windows servers.
+- run-tests.sh now uses proc_open() instead of pcntl_fork() for better Windows support.
+- Numerous upgrade path fixes.
+- Numerous documentation fixes.
+- Numerous notice fixes.
+- Numerous fixes to improve PHP 5.4 support.
+- Numerous RTL improvements.
+
+Drupal 7.5, 2011-07-27
+----------------------
+- Fixed security issue (Access bypass), see SA-CORE-2011-003.
+
+Drupal 7.4, 2011-06-29
+----------------------
+- Rolled back patch that caused fatal errors in CTools, Feeds, and other modules using the class registry.
+- Fixed critical bug with saving default images.
+- Fixed fatal errors when uninstalling some modules.
+- Added workaround for MySQL transaction support breaking on DDL statments.
+- Improved page caching with external caching systems.
+- Fix to Batch API, which was terminating too early.
+- Numerous upgrade path fixes.
+- Performance fixes.
+- Additional test coverage.
+- Numerous documentation fixes.
+
+Drupal 7.3, 2011-06-29
+----------------------
+- Fixed security issue (Access bypass), see SA-CORE-2011-002.
+
+Drupal 7.2, 2011-05-25
+----------------------
+- Added a default .gitignore file.
+- Improved PostgreSQL and SQLite support.
+- Numerous critical performance improvements.
+- Numerous critical fixes to the upgrade path.
+- Numerous fixes to language and translation systems.
+- Numerous fixes to AJAX and #states systems.
+- Improvements to the locking system.
+- Numerous documentation fixes.
+- Numerous styling and theme system fixes.
+- Numerous fixes for schema mis-matches between Drupal 6 and 7.
+- Minor internal API clean-ups.
+
+Drupal 7.1, 2011-05-25
+----------------------
+- Fixed security issues (Cross site scripting, File access bypass), see SA-CORE-2011-001.
+
+Drupal 7.0, 2011-01-05
+----------------------
+- Database:
+ * Fully rewritten database layer utilizing PHP 5's PDO abstraction layer.
+ * Drupal now requires MySQL >= 5.0.15 or PostgreSQL >= 8.3.
+ * Added query builders for INSERT, UPDATE, DELETE, MERGE, and SELECT queries.
+ * Support for master/slave replication, transactions, multi-insert queries,
+ and other features.
+ * Added support for the SQLite database engine.
+ * Default to InnoDB engine, rather than MyISAM, on MySQL when available.
+ This offers increased scalability and data integrity.
+- Security:
+ * Protected cron.php -- cron will only run if the proper key is provided.
+ * Implemented a pluggable password system and much stronger password hashes
+ that are compatible with the Portable PHP password hashing framework.
+ * Rate limited login attempts to prevent brute-force password guessing, and
+ improved the flood control API to allow variable time windows and
+ identifiers for limiting user access to resources.
+ * Transformed the "Update status" module into the "Update manager" which
+ can securely install or update modules and themes via a web interface.
+- Usability:
+ * Added contextual links (a.k.a. local tasks) to page elements, such as
+ blocks, nodes, or comments, which allows to perform the most common tasks
+ with a single click only.
+ * Improved installer requirements check.
+ * Improved support for integration of WYSIWYG editors.
+ * Implemented drag-and-drop positioning for input format listings.
+ * Implemented drag-and-drop positioning for language listing.
+ * Implemented drag-and-drop positioning for poll options.
+ * Provided descriptions and human-readable names for user permissions.
+ * Removed comment controls for users.
+ * Removed display order settings for comment module. Comment display
+ order can now be customized using the Views module.
+ * Removed the 'related terms' feature from taxonomy module since this can
+ now be achieved with Field API.
+ * Added additional features to the default install profile, and implemented
+ a "slimmed down" install profile designed for developers.
+ * Added a built-in, automated cron run feature, which is triggered by site
+ visitors.
+ * Added an administrator role which is assigned all permissions for
+ installed modules automatically.
+ * Image toolkits are now provided by modules (rather than requiring a
+ manual file copy to the includes directory).
+ * Added an edit tab to taxonomy term pages.
+ * Redesigned password strength validator.
+ * Redesigned the add content type screen.
+ * Highlight duplicate URL aliases.
+ * Renamed "input formats" to "text formats".
+ * Moved text format permissions to the main permissions page.
+ * Added configurable ability for users to cancel their own accounts.
+ * Added "vertical tabs", a reusable interface component that features
+ automatic summaries and increases usability.
+ * Replaced fieldsets on node edit and add pages with vertical tabs.
+- Performance:
+ * Improved performance on uncached page views by loading multiple core
+ objects in a single database query.
+ * Improved performance for logged-in users by reducing queries for path
+ alias lookups.
+ * Improved support for HTTP proxies (including reverse proxies), allowing
+ anonymous page views to be served entirely from the proxy.
+- Documentation:
+ * Hook API documentation now included in Drupal core.
+- News aggregator:
+ * Added OPML import functionality for RSS feeds.
+ * Optionally, RSS feeds may be configured to not automatically generate feed blocks.
+- Search:
+ * Added support for language-aware searches.
+- Aggregator:
+ * Introduced architecture that allows pluggable parsers and processors for
+ syndicating RSS and Atom feeds.
+ * Added options to suspend updating specific feeds and never discard feeds
+ items.
+- Testing:
+ * Added test framework and tests.
+- Improved time zone support:
+ * Drupal now uses PHP's time zone database when rendering dates in local
+ time. Site-wide and user-configured time zone offsets have been converted
+ to time zone names, e.g. Africa/Abidjan.
+ * In some cases the upgrade and install scripts do not choose the preferred
+ site default time zone. The automatically-selected time zone can be
+ corrected at admin/config/regional/settings.
+ * If your site is being upgraded from Drupal 6 and you do not have the
+ contributed date or event modules installed, user time zone settings will
+ fallback to the system time zone and will have to be reconfigured by each user.
+ * User-configured time zones now serve as the default time zone for PHP
+ date/time functions.
+- Filter system:
+ * Revamped the filter API and text format storage.
+ * Added support for default text formats to be assigned on a per-role basis.
+ * Refactored the HTML corrector to take advantage of PHP 5 features.
+- User system:
+ * Added clean API functions for creating, loading, updating, and deleting
+ user roles and permissions.
+ * Refactored the "access rules" component of user module: The user module
+ now provides a simple interface for blocking single IP addresses. The
+ previous functionality in the user module for restricting certain e-mail
+ addresses and usernames is now available as a contributed module. Further,
+ IP address range blocking is no longer supported and should be implemented
+ at the operating system level.
+ * Removed per-user themes: Contributed modules with similar functionality
+ are available.
+- OpenID:
+ * Added support for Gmail and Google Apps for Domain identifiers. Users can
+ now login with their user@example.com identifier when example.com is powered
+ by Google.
+ * Made the OpenID module more pluggable.
+- Added code registry:
+ * Using the registry, modules declare their includable files via their .info file,
+ allowing Drupal to lazy-load classes and interfaces as needed.
+- Theme system:
+ * Removed the Bluemarine, Chameleon and Pushbutton themes. These themes live
+ on as contributed themes (http://drupal.org/project/bluemarine,
+ http://drupal.org/project/chameleon and http://drupal.org/project/pushbutton).
+ * Added Stark theme to make analyzing Drupal's default HTML and CSS easier.
+ * Added Seven as the default administration theme.
+ * Variable preprocessing of theme hooks prior to template rendering now goes
+ through two phases: a 'preprocess' phase and a new 'process' phase. See
+ http://api.drupal.org/api/function/theme/7 for details.
+ * Theme hooks implemented as functions (rather than as templates) can now
+ also have preprocess (and process) functions. See
+ http://api.drupal.org/api/function/theme/7 for details.
+ * Added Bartik as the default theme.
+- File handling:
+ * Files are now first class Drupal objects with file_load(), file_save(),
+ and file_validate() functions and corresponding hooks.
+ * The file_move(), file_copy() and file_delete() functions now operate on
+ file objects and invoke file hooks so that modules are notified and can
+ respond to changes.
+ * For the occasions when only basic file manipulation are needed--such as
+ uploading a site logo--that don't require the overhead of databases and
+ hooks, the current unmanaged copy, move and delete operations have been
+ preserved but renamed to file_unmanaged_*().
+ * Rewrote file handling to use PHP stream wrappers to enable support for
+ both public and private files and to support pluggable storage mechanisms
+ and access to remote resources (e.g. S3 storage or Flickr photos).
+ * The mime_extension_mapping variable has been removed. Modules that need to
+ alter the default MIME type extension mappings should implement
+ hook_file_mimetype_mapping_alter().
+ * Added the hook_file_url_alter() hook, which makes it possible to serve
+ files from a CDN.
+ * Added a field specifically for uploading files, previously provided by
+ the contributed module FileField.
+- Image handling:
+ * Improved image handling, including better support for add-on image
+ libraries.
+ * Added API and interface for creating advanced image thumbnails.
+ * Inclusion of additional effects such as rotate and desaturate.
+ * Added a field specifically for uploading images, previously provided by
+ the contributed module ImageField.
+- Added aliased multi-site support:
+ * Added support for mapping domain names to sites directories.
+- Added RDF support:
+ * Modules can declare RDF namespaces which are serialized in the <html> tag
+ for RDFa support.
+ * Modules can specify how their data structure maps to RDF.
+ * Added support for RDFa export of nodes, comments, terms, users, etc. and
+ their fields.
+- Search engine optimization and web linking:
+ * Added a rel="canonical" link on node and comment pages to prevent
+ duplicate content indexing by search engines.
+ * Added a default rel="shortlink" link on node and comment pages that
+ advertises a short link as an alternative URL to third-party services.
+ * Meta information is now alterable by all modules before rendering.
+- Field API:
+ * Custom data fields may be attached to nodes, users, comments and taxonomy
+ terms.
+ * Node bodies and teasers are now Field API fields instead of
+ being a hard-coded property of node objects.
+ * In addition, any other object type may register with Field API
+ and allow custom data fields to be attached to itself.
+ * Provides most of the features of the former Content Construction
+ Kit (CCK) module.
+ * Taxonomy terms are now Field API fields that can be added to any fieldable
+ object.
+- Installer:
+ * Refactored the installer into an API that allows Drupal to be installed
+ via a command line script.
+- Page organization
+ * Made the help text area a full featured region with blocks.
+ * Site mission is replaced with the highlighted content block region and
+ separate RSS feed description settings.
+ * The footer message setting was removed in favor of custom blocks.
+ * Made the main page content a block which can be moved and ordered
+ with other blocks in the same region.
+ * Blocks can now return structured arrays for later rendering just
+ like page callbacks.
+- Translation system
+ * The translation system now supports message context (msgctxt).
+ * Added support for translatable fields to Field API.
+- JavaScript changes
+ * Upgraded the core JavaScript library to jQuery version 1.4.4.
+ * Upgraded the jQuery Forms library to 2.52.
+ * Added jQuery UI 1.8.7, which allows improvements to Drupal's user
+ experience.
+- Better module version support
+ * Modules now can specify which version of another module they depend on.
+- Removed modules from core
+ * The following modules have been removed from core, because contributed
+ modules with similar functionality are available:
+ * Blog API module
+ * Ping module
+ * Throttle module
+- Improved node access control system.
+ * All modules may now influence the access to a node at runtime, not just
+ the module that defined a node.
+ * Users may now be allowed to bypass node access restrictions without giving
+ them complete access to the site.
+ * Access control affects both published and unpublished nodes.
+ * Numerous other improvements to the node access system.
+- Actions system
+ * Simplified definitions of actions and triggers.
+ * Removed dependency on the combination of hooks and operations. Triggers
+ now directly map to module hooks.
+- Task handling
+ * Added a queue API to process many or long-running tasks.
+ * Added queue API support to cron API.
+ * Added a locking framework to coordinate long-running operations across
+ requests.
+
+Drupal 6.23-dev, xxxx-xx-xx (development release)
+-----------------------
+
+Drupal 6.22, 2011-05-25
+-----------------------
+- Made Drupal 6 work better with IIS and Internet Explorer.
+- Fixed .po file imports to work better with custom textgroups.
+- Improved code documentation at various places.
+- Fixed a variety of other bugs.
+
+Drupal 6.21, 2011-05-25
+----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
+
+Drupal 6.20, 2010-12-15
+----------------------
+- Fixed a variety of small bugs, improved code documentation.
+
+Drupal 6.19, 2010-08-11
+----------------------
+- Fixed a variety of small bugs, improved code documentation.
+
+Drupal 6.18, 2010-08-11
+----------------------
+- Fixed security issues (OpenID authentication bypass, File download access
+ bypass, Comment unpublishing bypass, Actions cross site scripting),
+ see SA-CORE-2010-002.
+
+Drupal 6.17, 2010-06-02
+----------------------
+- Improved PostgreSQL compatibility
+- Better PHP 5.3 and PHP 4 compatibility
+- Better browser compatibility of CSS and JS aggregation
+- Improved logging for login failures
+- Fixed an incompatibility with some contributed modules and the locking system
+- Fixed a variety of other bugs.
+
+Drupal 6.16, 2010-03-03
+----------------------
+- Fixed security issues (Installation cross site scripting, Open redirection,
+ Locale module cross site scripting, Blocked user session regeneration),
+ see SA-CORE-2010-001.
+- Better support for updated jQuery versions.
+- Reduced resource usage of update.module.
+- Fixed several issues relating to support of install profiles and
+ distributions.
+- Added a locking framework to avoid data corruption on long operations.
+- Fixed a variety of other bugs.
+
+Drupal 6.15, 2009-12-16
+----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
+- Fixed a variety of other bugs.
+
+Drupal 6.14, 2009-09-16
+----------------------
+- Fixed security issues (OpenID association cross site request forgeries,
+ OpenID impersonation and File upload), see SA-CORE-2009-008.
+- Changed the system modules page to not run all cache rebuilds; use the
+ button on the performance settings page to achieve the same effect.
+- Added support for PHP 5.3.0 out of the box.
+- Fixed a variety of small bugs.
+
+Drupal 6.13, 2009-07-01
+----------------------
+- Fixed security issues (Cross site scripting, Input format access bypass and
+ Password leakage in URL), see SA-CORE-2009-007.
+- Fixed a variety of small bugs.
+
+Drupal 6.12, 2009-05-13
+----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
+- Fixed a variety of small bugs.
+
+Drupal 6.11, 2009-04-29
+----------------------
+- Fixed security issues (Cross site scripting and limited information
+ disclosure), see SA-CORE-2009-005
+- Fixed performance issues with the menu router cache, the update
+ status cache and improved cache invalidation
+- Fixed a variety of small bugs.
+
+Drupal 6.10, 2009-02-25
+----------------------
+- Fixed a security issue, (Local file inclusion on Windows),
+ see SA-CORE-2009-003
+- Fixed node_feed() so custom fields can show up in RSS feeds.
+- Improved PostgreSQL compatibility.
+- Fixed a variety of small bugs.
+
+Drupal 6.9, 2009-01-14
+----------------------
+- Fixed security issues, (Access Bypass, Validation Bypass and Hardening
+ against SQL injection), see SA-CORE-2009-001
+- Made HTTP request checking more robust and informative.
+- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and
+ basic shell scripts.
+- Removed t() calls from all schema documentation. Suggested best practice
+ changed for contributed modules, see http://drupal.org/node/322731.
+- Fixed a variety of small bugs.
+
+Drupal 6.8, 2008-12-11
+----------------------
+- Removed a previous change incompatible with PHP 5.1.x and lower.
+
+Drupal 6.7, 2008-12-10
+----------------------
+- Fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
+- Updated robots.txt and .htaccess to match current file use.
+- Fixed a variety of small bugs.
+
+Drupal 6.6, 2008-10-22
+----------------------
+- Fixed security issues, (File inclusion, Cross site scripting), see SA-2008-067
+- Fixed a variety of small bugs.
+
+Drupal 6.5, 2008-10-08
+----------------------
+- Fixed security issues, (File upload access bypass, Access rules bypass,
+ BlogAPI access bypass), see SA-2008-060.
+- Fixed a variety of small bugs.
+
+Drupal 6.4, 2008-08-13
+----------------------
+- Fixed a security issue (Cross site scripting, Arbitrary file uploads via
+ BlogAPI, Cross site request forgeries and Various Upload module
+ vulnerabilities), see SA-2008-047.
+- Improved error messages during installation.
+- Fixed a bug that prevented AHAH handlers to be attached to radios widgets.
+- Fixed a variety of small bugs.
+
+Drupal 6.3, 2008-07-09
+----------------------
+- Fixed security issues, (Cross site scripting, cross site request forgery,
+ session fixation and SQL injection), see SA-2008-044.
+- Slightly modified installation process to prevent file ownership issues on
+ shared hosts.
+- Improved PostgreSQL compatibility (rewritten queries; custom blocks).
+- Upgraded to jQuery 1.2.6.
+- Performance improvements to search, menu handling and form API caches.
+- Fixed Views compatibility issues (Views for Drupal 6 requires Drupal 6.3+).
+- Fixed a variety of small bugs.
+
+Drupal 6.2, 2008-04-09
+----------------------
+- Fixed a variety of small bugs.
+- Fixed a security issue (Access bypasses), see SA-2008-026.
+
+Drupal 6.1, 2008-02-27
+----------------------
+- Fixed a variety of small bugs.
+- Fixed a security issue (Cross site scripting), see SA-2008-018.
+
+Drupal 6.0, 2008-02-13
+----------------------
+- New, faster and better menu system.
+- New watchdog as a hook functionality.
+ * New hook_watchdog that can be implemented by any module to route log
+ messages to various destinations.
+ * Expands the severity levels from 3 (Error, Warning, Notice) to the 8
+ levels defined in RFC 3164.
+ * The watchdog module is now called dblog, and is optional, but enabled by
+ default in the default install profile.
+ * Extended the database log module so log messages can be filtered.
+ * Added syslog module: useful for monitoring large Drupal installations.
+- Added optional e-mail notifications when users are approved, blocked, or
+ deleted.
+- Drupal works with error reporting set to E_ALL.
+- Added scripts/drupal.sh to execute Drupal code from the command line. Useful
+ to use Drupal as a framework to build command-line tools.
+- Made signature support optional and made it possible to theme signatures.
+- Made it possible to filter the URL aliases on the URL alias administration
+ screen.
+- Language system improvements:
+ * Support for right to left languages.
+ * Language detection based on parts of the URL.
+ * Browser based language detection.
+ * Made it possible to specify a node's language.
+ * Support for translating posts on the site to different languages.
+ * Language dependent path aliases.
+ * Automatically import translations when adding a new language.
+ * JavaScript interface translation.
+ * Automatically import a module's translation upon enabling that module.
+- Moved "PHP input filter" to a standalone module so it can be deleted for
+ security reasons.
+- Usability:
+ * Improved handling of teasers in posts.
+ * Added sticky table headers.
+ * Check for clean URL support automatically with JavaScript.
+ * Removed default/settings.php. Instead the installer will create it from
+ default.settings.php.
+ * Made it possible to configure your own date formats.
+ * Remember anonymous comment posters.
+ * Only allow modules and themes to be enabled that have explicitly been
+ ported to the correct core API version.
+ * Can now specify the minimum PHP version required for a module within the
+ .info file.
+ * Drupal core no longer requires CREATE TEMPORARY TABLES or LOCK TABLES
+ database rights.
+ * Dynamically check password strength and confirmation.
+ * Refactored poll administration.
+ * Implemented drag-and-drop positioning for blocks, menu items, taxonomy
+ vocabularies and terms, forums, profile fields, and input format filters.
+- Theme system:
+ * Added .info files to themes and made it easier to specify regions and
+ features.
+ * Added theme registry: modules can directly provide .tpl.php files for
+ their themes without having to create theme_ functions.
+ * Used the Garland theme for the installation and maintenance pages.
+ * Added theme preprocess functions for themes that are templates.
+ * Added support for themeable functions in JavaScript.
+- Refactored update.php to a generic batch API to be able to run time-consuming
+ operations in multiple subsequent HTTP requests.
+- Installer:
+ * Themed the installer with the Garland theme.
+ * Added form to provide initial site information during installation.
+ * Added ability to provide extra installation steps programmatically.
+ * Made it possible to import interface translations at install time.
+- Added the HTML corrector filter:
+ * Fixes faulty and chopped off HTML in postings.
+ * Tags are now automatically closed at the end of the teaser.
+- Performance:
+ * Made it easier to conditionally load .include files and split up many core
+ modules.
+ * Added a JavaScript aggregator.
+ * Added block-level caching, improving performance for both authenticated
+ and anonymous users.
+ * Made Drupal work correctly when running behind a reverse proxy like
+ Squid or Pound.
+- File handling improvements:
+ * Entries in the files table are now keyed to a user instead of a node.
+ * Added reusable validation functions to check for uploaded file sizes,
+ extensions, and image resolution.
+ * Added ability to create and remove temporary files during a cron job.
+- Forum improvements:
+ * Any node type may now be posted in a forum.
+- Taxonomy improvements:
+ * Descriptions for terms are now shown on taxonomy/term pages as well
+ as RSS feeds.
+ * Added versioning support to categories by associating them with node
+ revisions.
+- Added support for OpenID.
+- Added support for triggering configurable actions.
+- Added the Update status module to automatically check for available updates
+ and warn sites if they are missing security updates or newer versions.
+ Sites deploying from CVS should use http://drupal.org/project/cvs_deploy.
+ Advanced settings provided by http://drupal.org/project/update_advanced.
+- Upgraded the core JavaScript library to jQuery version 1.2.3.
+- Added a new Schema API, which provides built-in support for core and
+ contributed modules to work with databases other than MySQL.
+- Removed drupal.module. The functionality lives on as the Site network
+ contributed module (http://drupal.org/project/site_network).
+- Removed old system updates. Updates from Drupal versions prior to 5.x will
+ require upgrading to 5.x before upgrading to 6.x.
+
+Drupal 5.23, 2010-08-11
+-----------------------
+- Fixed security issues (File download access bypass, Comment unpublishing
+ bypass), see SA-CORE-2010-002.
+
+Drupal 5.22, 2010-03-03
+-----------------------
+- Fixed security issues (Open redirection, Locale module cross site scripting,
+ Blocked user session regeneration), see SA-CORE-2010-001.
+
+Drupal 5.21, 2009-12-16
+-----------------------
+- Fixed a security issue (Cross site scripting), see SA-CORE-2009-009.
+- Fixed a variety of small bugs.
+
+Drupal 5.20, 2009-09-16
+-----------------------
+- Avoid security problems resulting from writing Drupal 6-style menu
+ declarations.
+- Fixed security issues (session fixation), see SA-CORE-2009-008.
+- Fixed a variety of small bugs.
+
+Drupal 5.19, 2009-07-01
+-----------------------
+- Fixed security issues (Cross site scripting and Password leakage in URL), see
+ SA-CORE-2009-007.
+- Fixed a variety of small bugs.
+
+Drupal 5.18, 2009-05-13
+-----------------------
+- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
+- Fixed a variety of small bugs.
+
+Drupal 5.17, 2009-04-29
+-----------------------
+- Fixed security issues (Cross site scripting and limited information
+ disclosure) see SA-CORE-2009-005.
+- Fixed a variety of small bugs.
+
+Drupal 5.16, 2009-02-25
+-----------------------
+- Fixed a security issue, (Local file inclusion on Windows), see SA-CORE-2009-004.
+- Fixed a variety of small bugs.
+
+Drupal 5.15, 2009-01-14
+-----------------------
+- Fixed security issues, (Hardening against SQL injection), see
+ SA-CORE-2009-001
+- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and basic shell
+ scripts.
+- Fixed a variety of small bugs.
+
+Drupal 5.14, 2008-12-11
+-----------------------
+- removed a previous change incompatible with PHP 5.1.x and lower.
+
+Drupal 5.13, 2008-12-10
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
+- updated robots.txt and .htaccess to match current file use.
+
+Drupal 5.12, 2008-10-22
+-----------------------
+- fixed security issues, (File inclusion), see SA-2008-067
+
+Drupal 5.11, 2008-10-08
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (File upload access bypass, Access rules bypass,
+ BlogAPI access bypass, Node validation bypass), see SA-2008-060
+
+Drupal 5.10, 2008-08-13
+-----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site scripting, Arbitrary file uploads via
+ BlogAPI and Cross site request forgery), see SA-2008-047
+
+Drupal 5.9, 2008-07-23
+----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Session fixation), see SA-2008-046
+
+Drupal 5.8, 2008-07-09
+----------------------
+- fixed a variety of small bugs.
+- fixed security issues, (Cross site scripting, cross site request forgery, and
+ session fixation), see SA-2008-044
+
+Drupal 5.7, 2008-01-28
+----------------------
+- fixed the input format configuration page.
+- fixed a variety of small bugs.
+
+Drupal 5.6, 2008-01-10
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (Cross site request forgery), see SA-2008-005
+- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
+- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
+
+Drupal 5.5, 2007-12-06
+----------------------
+- fixed missing missing brackets in a query in the user module.
+- fixed taxonomy feed bug introduced by SA-2007-031
+
+Drupal 5.4, 2007-12-05
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (SQL injection), see SA-2007-031
+
+Drupal 5.3, 2007-10-17
+----------------------
+- fixed a variety of small bugs.
+- fixed a security issue (HTTP response splitting), see SA-2007-024
+- fixed a security issue (Arbitrary code execution via installer), see SA-2007-025
+- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
+- fixed a security issue (User deletion cross site request forgery), see SA-2007-029
+- fixed a security issue (API handling of unpublished comment), see SA-2007-030
+
+Drupal 5.2, 2007-07-26
+----------------------
+- changed hook_link() $teaser argument to match documentation.
+- fixed a variety of small bugs.
+- fixed a security issue (cross-site request forgery), see SA-2007-017
+- fixed a security issue (cross-site scripting), see SA-2007-018
+
+Drupal 5.1, 2007-01-29
+----------------------
+- fixed security issue (code execution), see SA-2007-005
+- fixed a variety of small bugs.
+
+Drupal 5.0, 2007-01-15
+----------------------
+- Completely retooled the administration page
+ * /Admin now contains an administration page which may be themed
+ * Reorganised administration menu items by task and by module
+ * Added a status report page with detailed PHP/MySQL/Drupal information
+- Added web-based installer which can:
+ * Check installation and run-time requirements
+ * Automatically generate the database configuration file
+ * Install pre-made 'install profiles' or distributions
+ * Import the database structure with automatic table prefixing
+ * Be localized
+- Added new default Garland theme
+- Added color module to change some themes' color schemes
+- Included the jQuery JavaScript library 1.0.4 and converted all core JavaScript to use it
+- Introduced the ability to alter mail sent from system
+- Module system:
+ * Added .info files for module meta-data
+ * Added support for module dependencies
+ * Improved module installation screen
+ * Moved core modules to their own directories
+ * Added support for module uninstalling
+- Added support for different cache backends
+- Added support for a generic "sites/all" directory.
+- Usability:
+ * Added support for auto-complete forms (AJAX) to user profiles.
+ * Made it possible to instantly assign roles to newly created user accounts.
+ * Improved configurability of the contact forms.
+ * Reorganized the settings pages.
+ * Made it easy to investigate popular search terms.
+ * Added a 'select all' checkbox and a range select feature to administration tables.
+ * Simplified the 'break' tag to split teasers from body.
+ * Use proper capitalization for titles, menu items and operations.
+- Integrated urlfilter.module into filter.module
+- Block system:
+ * Extended the block visibility settings with a role specific setting.
+ * Made it possible to customize all block titles.
+- Poll module:
+ * Optionally allow people to inspect all votes.
+ * Optionally allow people to cancel their vote.
+- Distributed authentication:
+ * Added default server option.
+- Added default robots.txt to control crawlers.
+- Database API:
+ * Added db_table_exists().
+- Blogapi module:
+ * 'Blogapi new' and 'blogapi edit' nodeapi operations.
+- User module:
+ * Added hook_profile_alter().
+ * E-mail verification is made optional.
+ * Added mass editing and filtering on admin/user/user.
+- PHP Template engine:
+ * Add the ability to look for a series of suggested templates.
+ * Look for page templates based upon the path.
+ * Look for block templates based upon the region, module, and delta.
+- Content system:
+ * Made it easier for node access modules to work well with each other.
+ * Added configurable content types.
+ * Changed node rendering to work with structured arrays.
+- Performance:
+ * Improved session handling: reduces database overhead.
+ * Improved access checking: reduces database overhead.
+ * Made it possible to do memcached based session management.
+ * Omit sidebars when serving a '404 - Page not found': saves CPU cycles and bandwidth.
+ * Added an 'aggressive' caching policy.
+ * Added a CSS aggregator and compressor (up to 40% faster page loads).
+- Removed the archive module.
+- Upgrade system:
+ * Created space for update branches.
+- Forms API:
+ * Made it possible to programmatically submit forms.
+ * Improved api for multistep forms.
+- Theme system:
+ * Split up and removed drupal.css.
+ * Added nested lists generation.
+ * Added a self-clearing block class.
+
+Drupal 4.7.11, 2008-01-10
+-------------------------
+- fixed a security issue (Cross site request forgery), see SA-2008-005
+- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
+- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
+
+Drupal 4.7.10, 2007-12-06
+-------------------------
+- fixed taxonomy feed bug introduced by SA-2007-031
+
+Drupal 4.7.9, 2007-12-05
+------------------------
+- fixed a security issue (SQL injection), see SA-2007-031
+
+Drupal 4.7.8, 2007-10-17
+----------------------
+- fixed a security issue (HTTP response splitting), see SA-2007-024
+- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
+- fixed a security issue (API handling of unpublished comment), see SA-2007-030
+
+Drupal 4.7.7, 2007-07-26
+------------------------
+- fixed security issue (XSS), see SA-2007-018
+
+Drupal 4.7.6, 2007-01-29
+------------------------
+- fixed security issue (code execution), see SA-2007-005
+
+Drupal 4.7.5, 2007-01-05
+------------------------
+- Fixed security issue (XSS), see SA-2007-001
+- Fixed security issue (DoS), see SA-2007-002
+
+Drupal 4.7.4, 2006-10-18
+------------------------
+- Fixed security issue (XSS), see SA-2006-024
+- Fixed security issue (CSRF), see SA-2006-025
+- Fixed security issue (Form action attribute injection), see SA-2006-026
+
+Drupal 4.7.3, 2006-08-02
+------------------------
+- Fixed security issue (XSS), see SA-2006-011
+
+Drupal 4.7.2, 2006-06-01
+------------------------
+- Fixed critical upload issue, see SA-2006-007
+- Fixed taxonomy XSS issue, see SA-2006-008
+- Fixed a variety of small bugs.
+
+Drupal 4.7.1, 2006-05-24
+------------------------
+- Fixed critical SQL issue, see SA-2006-005
+- Fixed a serious upgrade related bug.
+- Fixed a variety of small bugs.
+
+Drupal 4.7.0, 2006-05-01
+------------------------
+- Added free tagging support.
+- Added a site-wide contact form.
+- Theme system:
+ * Added the PHPTemplate theme engine and removed the Xtemplate engine.
+ * Converted the bluemarine theme from XTemplate to PHPTemplate.
+ * Converted the pushbutton theme from XTemplate to PHPTemplate.
+- Usability:
+ * Reworked the 'request new password' functionality.
+ * Reworked the node and comment edit forms.
+ * Made it easy to add nodes to the navigation menu.
+ * Added site 'offline for maintenance' feature.
+ * Added support for auto-complete forms (AJAX).
+ * Added support for collapsible page sections (JS).
+ * Added support for resizable text fields (JS).
+ * Improved file upload functionality (AJAX).
+ * Reorganized some settings pages.
+ * Added friendly database error screens.
+ * Improved styling of update.php.
+- Refactored the forms API.
+ * Made it possible to alter, extend or theme forms.
+- Comment system:
+ * Added support for "mass comment operations" to ease repetitive tasks.
+ * Comment moderation has been removed.
+- Node system:
+ * Reworked the revision functionality.
+ * Removed the bookmarklet code. Third-party modules can now handle
+ This.
+- Upgrade system:
+ * Allows contributed modules to plug into the upgrade system.
+- Profiles:
+ * Added a block to display author information along with posts.
+ * Added support for private profile fields.
+- Statistics module:
+ * Added the ability to track page generation times.
+ * Made it possible to block certain IPs/hostnames.
+- Block system:
+ * Added support for theme-specific block regions.
+- Syndication:
+ * Made the aggregator module parse Atom feeds.
+ * Made the aggregator generate RSS feeds.
+ * Added RSS feed settings.
+- XML-RPC:
+ * Replaced the XML-RPC library by a better one.
+- Performance:
+ * Added 'loose caching' option for high-traffic sites.
+ * Improved performance of path aliasing.
+ * Added the ability to track page generation times.
+- Internationalization:
+ * Improved Unicode string handling API.
+ * Added support for PHP's multibyte string module.
+- Added support for PHP5's 'mysqli' extension.
+- Search module:
+ * Made indexer smarter and more robust.
+ * Added advanced search operators (e.g. phrase, node type, ...).
+ * Added customizable result ranking.
+- PostgreSQL support:
+ * Removed dependency on PL/pgSQL procedural language.
+- Menu system:
+ * Added support for external URLs.
+- Queue module:
+ * Removed from core.
+- HTTP handling:
+ * Added support for a tolerant Base URL.
+ * Output URIs relative to the root, without a base tag.
+
+Drupal 4.6.11, 2007-01-05
+-------------------------
+- Fixed security issue (XSS), see SA-2007-001
+- Fixed security issue (DoS), see SA-2007-002
+
+Drupal 4.6.10, 2006-10-18
+------------------------
+- Fixed security issue (XSS), see SA-2006-024
+- Fixed security issue (CSRF), see SA-2006-025
+- Fixed security issue (Form action attribute injection), see SA-2006-026
+
+Drupal 4.6.9, 2006-08-02
+------------------------
+- Fixed security issue (XSS), see SA-2006-011
+
+Drupal 4.6.8, 2006-06-01
+------------------------
+- Fixed critical upload issue, see SA-2006-007
+- Fixed taxonomy XSS issue, see SA-2006-008
+
+Drupal 4.6.7, 2006-05-24
+------------------------
+- Fixed critical SQL issue, see SA-2006-005
+
+Drupal 4.6.6, 2006-03-13
+------------------------
+- Fixed bugs, including 4 security vulnerabilities.
+
+Drupal 4.6.5, 2005-12-12
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.6.4, 2005-11-30
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.6.3, 2005-08-15
+------------------------
+- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
+
+Drupal 4.6.2, 2005-06-29
+------------------------
+- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
+
+Drupal 4.6.1, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.6.0, 2005-04-15
+------------------------
+- PHP5 compliance
+- Search:
+ * Added UTF-8 support to make it work with all languages.
+ * Improved search indexing algorithm.
+ * Improved search output.
+ * Impose a throttle on indexing of large sites.
+ * Added search block.
+- Syndication:
+ * Made the ping module ping pingomatic.com which, in turn, will ping all the major ping services.
+ * Made Drupal generate RSS 2.0 feeds.
+ * Made RSS feeds extensible.
+ * Added categories to RSS feeds.
+ * Added enclosures to RSS feeds.
+- Flood control mechanism:
+ * Added a mechanism to throttle certain operations.
+- Usability:
+ * Refactored the block configuration pages.
+ * Refactored the statistics pages.
+ * Refactored the watchdog pages.
+ * Refactored the throttle module configuration.
+ * Refactored the access rules page.
+ * Refactored the content administration page.
+ * Introduced forum configuration pages.
+ * Added a 'add child page' link to book pages.
+- Contact module:
+ * Added a simple contact module that allows users to contact each other using e-mail.
+- Multi-site configuration:
+ * Made it possible to run multiple sites from a single code base.
+- Added an image API: enables better image handling.
+- Block system:
+ * Extended the block visibility settings.
+- Theme system:
+ * Added new theme functions.
+- Database backend:
+ * The PEAR database backend is no longer supported.
+- Performance:
+ * Improved performance of the forum topics block.
+ * Improved performance of the tracker module.
+ * Improved performance of the node pages.
+- Documentation:
+ * Improved and extended PHPDoc/Doxygen comments.
+
+Drupal 4.5.8, 2006-03-13
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.5.7, 2005-12-12
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.5.6, 2005-11-30
+------------------------
+- Fixed bugs, including 3 security vulnerabilities.
+
+Drupal 4.5.5, 2005-08-15
+------------------------
+- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
+
+Drupal 4.5.4, 2005-06-29
+------------------------
+- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
+
+Drupal 4.5.3, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.5.2, 2005-01-15
+------------------------
+- Fixed bugs: a cross-site scripting (XSS) vulnerability has been fixed.
+
+Drupal 4.5.1, 2004-12-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.5.0, 2004-10-18
+------------------------
+- Navigation:
+ * Made it possible to add, delete, rename and move menu items.
+ * Introduced tabs and subtabs for local tasks.
+ * Reorganized the navigation menus.
+- User management:
+ * Added support for multiple roles per user.
+ * Made it possible to add custom profile fields.
+ * Made it possible to browse user profiles by field.
+- Node system:
+ * Added support for node-level permissions.
+- Comment module:
+ * Made it possible to leave contact information without having to register.
+- Upload module:
+ * Added support for uploading documents (includes images).
+- Forum module:
+ * Added support for sticky forum topics.
+ * Made it possible to track forum topics.
+- Syndication:
+ * Added support for RSS ping-notifications of http://technorati.com/.
+ * Refactored the categorization of syndicated news items.
+ * Added an URL alias for 'rss.xml'.
+ * Improved date parsing.
+- Database backend:
+ * Added support for multiple database connections.
+ * The PostgreSQL backend does no longer require PEAR.
+- Theme system:
+ * Changed all GIFs to PNGs.
+ * Reorganised the handling of themes, template engines, templates and styles.
+ * Unified and extended the available theme settings.
+ * Added theme screenshots.
+- Blocks:
+ * Added 'recent comments' block.
+ * Added 'categories' block.
+- Blogger API:
+ * Added support for auto-discovery of blogger API via RSD.
+- Performance:
+ * Added support for sending gzip compressed pages.
+ * Improved performance of the forum module.
+- Accessibility:
+ * Improved the accessibility of the archive module's calendar.
+ * Improved form handling and error reporting.
+ * Added HTTP redirects to prevent submitting twice when refreshing right after a form submission.
+- Refactored 403 (forbidden) handling and added support for custom 403 pages.
+- Documentation:
+ * Added PHPDoc/Doxygen comments.
+- Filter system:
+ * Added support for using multiple input formats on the site
+ * Expanded the embedded PHP-code feature so it can be used everywhere
+ * Added support for role-dependent filtering, through input formats
+- UI translation:
+ * Managing translations is now completely done through the administration interface
+ * Added support for importing/exporting gettext .po files
+
+Drupal 4.4.3, 2005-06-01
+------------------------
+- Fixed bugs, including a critical input validation bug.
+
+Drupal 4.4.2, 2004-07-04
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.4.1, 2004-05-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.4.0, 2004-04-01
+------------------------
+- Added support for the MetaWeblog API and MovableType extensions.
+- Added a file API: enables better document management.
+- Improved the watchdog and search module to log search keys.
+- News aggregator:
+ * Added support for conditional GET.
+ * Added OPML feed subscription list.
+ * Added support for <image>, <pubDate>, <dc:date>, <dcterms:created>, <dcterms:issued> and <dcterms:modified>.
+- Comment module:
+ * Made it possible to disable the "comment viewing controls".
+- Performance:
+ * Improved module loading when serving cached pages.
+ * Made it possible to automatically disable modules when under heavy load.
+ * Made it possible to automatically disable blocks when under heavy load.
+ * Improved performance and memory footprint of the locale module.
+- Theme system:
+ * Made all theme functions start with 'theme_'.
+ * Made all theme functions return their output.
+ * Migrated away from using the BaseTheme class.
+ * Added many new theme functions and refactored existing theme functions.
+ * Added avatar support to 'Xtemplate'.
+ * Replaced theme 'UnConeD' by 'Chameleon'.
+ * Replaced theme 'Marvin' by 'Pushbutton'.
+- Usability:
+ * Added breadcrumb navigation to all pages.
+ * Made it possible to add context-sensitive help to all pages.
+ * Replaced drop-down menus by radio buttons where appropriate.
+ * Removed the 'magic_quotes_gpc = 0' requirement.
+ * Added a 'book navigation' block.
+- Accessibility:
+ * Made themes degrade gracefully in absence of CSS.
+ * Grouped form elements using '<fieldset>' and '<legend>' tags.
+ * Added '<label>' tags to form elements.
+- Refactored 404 (file not found) handling and added support for custom 404 pages.
+- Improved the filter system to prevent conflicts between filters:
+ * Made it possible to change the order in which filters are applied.
+- Documentation:
+ * Added PHPDoc/Doxygen comments.
+
+Drupal 4.3.2, 2004-01-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.3.1, 2003-12-01
+------------------------
+- Fixed bugs: no critical bugs were identified.
+
+Drupal 4.3.0, 2003-11-01
+------------------------
+- Added support for configurable URLs.
+- Added support for sortable table columns.
+- Database backend:
+ * Added support for selective database table prefixing.
+- Performance:
+ * Optimized many SQL queries for speed by converting left joins to inner joins.
+- Comment module:
+ * Rewrote the comment housekeeping code to be much more efficient and scalable.
+ * Changed the comment module to use the standard pager.
+- User module:
+ * Added support for multiple sessions per user.
+ * Added support for anonymous user sessions.
+- Forum module:
+ * Improved the forum views and the themability thereof.
+- Book module:
+ * Improved integration of non-book nodes in the book outline.
+- Usability:
+ * Added support for "mass node operations" to ease repetitive tasks.
+ * Added support for breadcrumb navigation to several modules' user pages.
+ * Integrated the administration pages with the normal user pages.
+
+Drupal 4.2.0, 2003-08-01
+------------------------
+- Added support for clean URLs.
+- Added textarea hook and support for onload attributes: enables integration of WYSIWYG editors.
+- Rewrote the RSS/RDF parser:
+ * It will now use PHP's built-in XML parser to parse news feeds.
+- Rewrote the administration pages:
+ * Improved the navigational elements and added breadcrumb navigation.
+ * Improved the look and feel.
+ * Added context-sensitive help.
+- Database backend:
+ * Fixed numerous SQL queries to make Drupal ANSI compliant.
+ * Added MSSQL database scheme.
+- Search module:
+ * Changed the search module to use implicit AND'ing instead of implicit OR'ing.
+- Node system:
+ * Replaced the "post content" permission by more fine-grained permissions.
+ * Improved content submission:
+ + Improved teasers: teasers are now optional, teaser length can be configured, teaser and body are edited in a single textarea, users will no longer be bothered with teasers when the post is too short for one.
+ + Added the ability to preview both the short and the full version of your posts.
+ * Extended the node API which allows for better integration.
+ * Added default node settings to control the behavior for promotion, moderation and other options.
+- Themes:
+ * Replaced theme "Goofy" by "Xtemplate", a template driven theme.
+- Removed the 'register_globals = on' requirement.
+- Added better installation instructions.
+
+Drupal 4.1.0, 2003-02-01
+------------------------
+- Collaboratively revised and expanded the Drupal documentation.
+- Rewrote comment.module:
+ * Reintroduced comment rating/moderation.
+ * Added support for comment paging.
+ * Performance improvements: improved comment caching, faster SQL queries, etc.
+- Rewrote block.module:
+ * Performance improvements: blocks are no longer rendered when not displayed.
+- Rewrote forum.module:
+ * Added a lot of features one can find in stand-alone forum software including but not limited to support for topic paging, added support for icons, rewrote the statistics module, etc.
+- Rewrote statistics.module:
+ * Collects access counts for each node, referrer logs, number of users/guests.
+ * Export blocks displaying top viewed nodes over last 24 hour period, top viewed nodes over all time, last nodes viewed, how many users/guest online.
+- Added throttle.module:
+ * Auto-throttle congestion control mechanism: Drupal can adapt itself based on the server load.
+- Added profile.module:
+ * Enables to extend the user and registration page.
+- Added pager support to the main page.
+- Replaced weblogs.module by ping.module:
+ * Added support for normal and RSS notifications of http://blo.gs/.
+ * Added support for RSS ping-notifications of http://weblogs.com/.
+- Removed the rating module
+- Themes:
+ * Removed a significant portion of hard-coded mark-up.
+
+Drupal 4.0.0, 2002-06-15
+------------------------
+- Added tracker.module:
+ * Replaces the previous "your [site]" links (recent comments and nodes).
+- Added weblogs.module:
+ * This will ping weblogs.com when new content is promoted.
+- Added taxonomy module which replaces the meta module.
+ * Supports relations, hierarchies and synonyms.
+- Added a caching system:
+ * Speeds up pages for anonymous users and reduces system load.
+- Added support for external SMTP libraries.
+- Added an archive extension to the calendar.
+- Added support for the Blogger API.
+- Themes:
+ * Cleaned up the theme system.
+ * Moved themes that are not maintained to contributions CVS repository.
+- Database backend:
+ * Changed to PEAR database abstraction layer.
+ * Using ANSI SQL queries to be more portable.
+- Rewrote the user system:
+ * Added support for Drupal authentication through XML-RPC and through a Jabber server.
+ * Added support for modules to add more user data.
+ * Users may delete their own account.
+ * Added who's new and who's online blocks.
+- Changed block system:
+ * Various hard coded blocks are now dynamic.
+ * Blocks can now be enabled and/or be set by the user.
+ * Blocks can be set to only show up on some pages.
+ * Merged box module with block module.
+- Node system:
+ * Blogs can be updated.
+ * Teasers (abstracts) on all node types.
+ * Improved error checking.
+ * Content versioning support.
+ * Usability improvements.
+- Improved book module to support text, HTML and PHP pages.
+- Improved comment module to mark new comments.
+- Added a general outliner which will let any node type be linked to a book.
+- Added an update script that lets you upgrade from previous releases or on a day to day basis when using the development tree.
+- Search module:
+ * Improved the search system by making it context sensitive.
+ * Added indexing.
+- Various updates:
+ * Changed output to valid XHTML.
+ * Improved multiple sites using the same Drupal database support.
+ * Added support for session IDs in URLs instead of cookies.
+ * Made the type of content on the front page configurable.
+ * Made each cloud site have its own settings.
+ * Modules and themes can now be enabled/disabled using the administration pages.
+ * Added URL abstraction for links.
+ * Usability changes (renamed links, better UI, etc).
+- Collaboratively revised and expanded the Drupal documentation.
+
+Drupal 3.0.1, 2001-10-15
+------------------------
+- Various updates:
+ * Added missing translations
+ * Updated the themes: tidied up some HTML code and added new Drupal logos.
+
+Drupal 3.0.0, 2001-09-15
+------------------------
+- Major overhaul of the entire underlying design:
+ * Everything is based on nodes: nodes are a conceptual "black box" to couple and manage different types of content and that promotes reusing existing code, thus reducing the complexity and size of Drupal as well as improving long-term stability.
+- Rewrote submission/moderation queue and renamed it to queue.module.
+- Removed FAQ and documentation module and merged them into a "book module".
+- Removed ban module and integrated it into account.module as "access control":
+ * Access control is based on much more powerful regular expressions (regex) now rather than on MySQL pattern matching.
+- Rewrote watchdog and submission throttle:
+ * Improved watchdog messages and added watchdog filter.
+- Rewrote headline code and renamed it to import.module and export.module:
+ * Added various improvements, including a better parser, bundles and better control over individual feeds.
+- Rewrote section code and renamed it to meta.module:
+ * Supports unlimited amount of nested topics. Topics can be nested to create a multi-level hierarchy.
+- Rewrote configuration file resolving:
+ * Drupal tries to locate a configuration file that matches your domain name or uses conf.php if the former failed. Note also that the configuration files got renamed from .conf to .php for security's sake on mal-configured Drupal sites.
+- Added caching support which makes Drupal extremely scalable.
+- Added access.module:
+ * Allows you to set up 'roles' (groups) and to bind a set of permissions to each group.
+- Added blog.module.
+- Added poll.module.
+- Added system.module:
+ * Moved most of the configuration options from hostname.conf to the new administration section.
+ * Added support for custom "filters".
+- Added statistics.module
+- Added moderate.module:
+ * Allows to assign users editorial/moderator rights to certain nodes or topics.
+- Added page.module:
+ * Allows creation of static (and dynamic) pages through the administration interface.
+- Added help.module:
+ * Groups all available module documentation on a single page.
+- Added forum.module:
+ * Added an integrated forum.
+- Added cvs.module and cvs-to-sql.pl:
+ * Allows to display and mail CVS log messages as daily digests.
+- Added book.module:
+ * Allows collaborative handbook writing: primary used for Drupal documentation.
+- Removed cron.module and integrated it into conf.module.
+- Removed module.module as it was no longer needed.
+- Various updates:
+ * Added "auto-post new submissions" feature versus "moderate new submissions".
+ * Introduced links/Drupal tags: [[link]]
+ * Added preview functionality when submitting new content (such as a story) from the administration pages.
+ * Made the administration section only show those links a user has access to.
+ * Made all modules use specific form_* functions to guarantee a rock-solid forms and more consistent layout.
+ * Improved scheduler:
+ + Content can be scheduled to be 'posted', 'queued' and 'hidden'.
+ * Improved account module:
+ + Added "access control" to allow/deny certain usernames/e-mail addresses/hostnames.
+ * Improved locale module:
+ + Added new overview to easy the translation process.
+ * Improved comment module:
+ + Made it possible to permanently delete comments.
+ * Improved rating module
+ * Improved story module:
+ + Added preview functionality for administrators.
+ + Made it possible to permanently delete stories.
+ * Improved themes:
+ + W3C validation on a best effort basis.
+ + Removed $theme->control() from themes.
+ + Added theme "goofy".
+- Collaboratively revised and expanded the Drupal documentation.
+
+Drupal 2.0.0, 2001-03-15
+------------------------
+- Rewrote the comment/discussion code:
+ * Comment navigation should be less confusing now.
+ * Additional/alternative display and order methods have been added.
+ * Modules can be extended with a "comment system": modules can embed the existing comment system without having to write their own, duplicate comment system.
+- Added sections and section manager:
+ * Story sections can be maintained from the administration pages.
+ * Story sections make the open submission more adaptive in that you can set individual post, dump and expiration thresholds for each section according to the story type and urgency level: stories in certain sections do not "expire" and might stay interesting and active as time passes by, whereas news-related stories are only considered "hot" over a short period of time.
+- Multiple vhosts + multiple directories:
+ * You can set up multiple Drupal sites on top of the same physical source tree either by using vhosts or sub-directories.
+- Added "user ratings" similar to SlashCode's Karma or Scoop's Mojo:
+ * All rating logic is packed into a module to ease experimenting with different rating heuristics/algorithms.
+- Added "search infrastructure":
+ * Improved search page and integrated search functionality in the administration pages.
+- Added translation / localization / internationalization support:
+ * Because many people would love to see their website showing a lot less of English, and far more of their own language, Drupal provides a framework to set up a multi-lingual website or to overwrite the default English text in English.
+- Added fine-grained user permission (or group) system:
+ * Users can be granted access to specific administration sections. Example: a FAQ maintainer can be given access to maintain the FAQ and translators can be given access to the translation pages.
+- Added FAQ module
+- Changed the "open submission queue" into a (optional) module.
+- Various updates:
+ * Improved account module:
+ + User accounts can be deleted.
+ + Added fine-grained permission support.
+ * Improved block module
+ * Improved diary module:
+ + Diary entries can be deleted
+ * Improved headline module:
+ + Improved parser to support more "generic" RDF/RSS/XML backend.
+ * Improved module module
+ * Improved watchdog module
+ * Improved database abstraction layer
+ * Improved themes:
+ + W3C validation on a best effort basis
+ + Added theme "example" (alias "Stone Age")
+ * Added new scripts to directory "scripts"
+ * Added directory "misc"
+ * Added CREDITS file
+- Revised documentation
+
+Drupal 1.0.0, 2001-01-15
+------------------------
+- Initial release
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
new file mode 100644
index 0000000..e983474
--- /dev/null
+++ b/COPYRIGHT.txt
@@ -0,0 +1,25 @@
+
+All Drupal code is Copyright 2001 - 2010 by the original authors.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program as the file LICENSE.txt; if not, please see
+http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+
+Drupal is a registered trademark of Dries Buytaert.
+
+Drupal includes works under other copyright notices and distributed
+according to the terms of the GNU General Public License or a compatible
+license, including:
+
+ jQuery - Copyright (c) 2008 - 2009 John Resig
+
diff --git a/INSTALL.mysql.txt b/INSTALL.mysql.txt
new file mode 100644
index 0000000..a7c292e
--- /dev/null
+++ b/INSTALL.mysql.txt
@@ -0,0 +1,42 @@
+
+CREATE THE MySQL DATABASE
+--------------------------
+
+This step is only necessary if you don't already have a database set up (e.g.,
+by your host). In the following examples, 'username' is an example MySQL user
+which has the CREATE and GRANT privileges. Use the appropriate user name for
+your system.
+
+First, you must create a new database for your Drupal site (here, 'databasename'
+is the name of the new database):
+
+ mysqladmin -u username -p create databasename
+
+MySQL will prompt for the 'username' database password and then create the
+initial database files. Next you must log in and set the access database rights:
+
+ mysql -u username -p
+
+Again, you will be asked for the 'username' database password. At the MySQL
+prompt, enter following command:
+
+ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
+ ON databasename.*
+ TO 'username'@'localhost' IDENTIFIED BY 'password';
+
+where
+
+ 'databasename' is the name of your database
+ 'username@localhost' is the username of your MySQL account
+ 'password' is the password required for that username
+
+Note: Unless your database user has all of the privileges listed above, you will
+not be able to run Drupal.
+
+If successful, MySQL will reply with:
+
+ Query OK, 0 rows affected
+
+If the InnoDB storage engine is available, it will be used for all database
+tables. InnoDB provides features over MyISAM such as transaction support,
+row-level locks, and consistent non-locking reads.
diff --git a/INSTALL.pgsql.txt b/INSTALL.pgsql.txt
new file mode 100644
index 0000000..8fe8043
--- /dev/null
+++ b/INSTALL.pgsql.txt
@@ -0,0 +1,44 @@
+
+CREATE THE PostgreSQL DATABASE
+------------------------------
+
+Note that the database must be created with UTF-8 (Unicode) encoding.
+
+1. CREATE DATABASE USER
+
+ This step is only necessary if you don't already have a user set up (e.g., by
+ your host), or want to create a new user for use with Drupal only. The
+ following command creates a new user named 'username' and asks for a password
+ for that user:
+
+ createuser --pwprompt --encrypted --no-createrole --no-createdb username
+
+ If there are no errors, then the command was successful.
+
+2. CREATE DRUPAL DATABASE
+
+ This step is only necessary if you don't already have a database set up
+ (e.g., by your host) or want to create a new database for use with Drupal
+ only. The following command creates a new database named 'databasename',
+ which is owned by the previously created 'username':
+
+ createdb --encoding=UTF8 --owner=username databasename
+
+ If there are no errors, then the command was successful.
+
+3. CREATE SCHEMA OR SCHEMAS (Optional advanced step)
+
+ Drupal will run across different schemas within your database if you so wish.
+ By default, Drupal runs inside the 'public' schema but you can use $db_prefix
+ inside settings.php to define a schema for Drupal to run inside of, or
+ specify tables that are shared inside of a separate schema. Drupal will not
+ create schemas for you. In fact, the user that Drupal runs as should not be
+ allowed to do this. You'll need to execute the SQL below as a superuser,
+ replace 'username' with the username that Drupal uses to connect to
+ PostgreSQL, and replace 'schema_name' with a schema name you wish to use,
+ such as 'shared':
+
+ CREATE SCHEMA schema_name AUTHORIZATION username;
+
+ Do this for as many schemas as you need. See default.settings.php for
+ instructions on how to set which tables use which schemas.
diff --git a/INSTALL.sqlite.txt b/INSTALL.sqlite.txt
new file mode 100644
index 0000000..8e57d60
--- /dev/null
+++ b/INSTALL.sqlite.txt
@@ -0,0 +1,31 @@
+
+SQLITE REQUIREMENTS
+-------------------
+
+To use SQLite with your Drupal installation, the following requirements must be
+met: Server has PHP 5.2 or later with PDO, and the PDO SQLite driver must be
+enabled.
+
+SQLITE DATABASE CREATION
+------------------------
+
+The Drupal installer will create the SQLite database for you. The only
+requirement is that the installer must have write permissions to the directory
+where the database file resides. This directory (not just the database file) also
+has to remain writeable by the web server going forward for SQLite to continue to
+be able to operate.
+
+On the "Database configuration" form in the "Database file" field, you must
+supply the exact path to where you wish your database file to reside. It is
+strongly suggested that you choose a path that is outside of the webroot, yet
+ensure that the directory is writeable by the web server.
+
+If you must place your database file in your webroot, you could try using the
+following in your "Database file" field:
+
+ sites/default/files/.ht.sqlite
+
+Note: The .ht in the name will tell Apache to prevent the database from being
+downloaded. Please check that the file is, indeed, protected by your webserver.
+If not, please consult the documentation of your webserver on how to protect a
+file from downloading.
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644
index 0000000..7b526e3
--- /dev/null
+++ b/INSTALL.txt
@@ -0,0 +1,398 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * Requirements and notes
+ * Optional server requirements
+ * Installation
+ * Building and customizing your site
+ * Multisite configuration
+ * More information
+
+REQUIREMENTS AND NOTES
+----------------------
+
+Drupal requires:
+
+- A web server. Apache (version 2.0 or greater) is recommended.
+- PHP 5.2.4 (or greater) (http://www.php.net/).
+- One of the following databases:
+ - MySQL 5.0.15 (or greater) (http://www.mysql.com/).
+ - MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully
+ compatible drop-in replacement for MySQL.
+ - PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
+ - SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
+
+For more detailed information about Drupal requirements, including a list of
+PHP extensions and configurations that are required, see "System requirements"
+(http://drupal.org/requirements) in the Drupal.org online documentation.
+
+For detailed information on how to configure a test server environment using a
+variety of operating systems and web servers, see "Local server setup"
+(http://drupal.org/node/157602) in the Drupal.org online documentation.
+
+Note that all directories mentioned in this document are always relative to the
+directory of your Drupal installation, and commands are meant to be run from
+this directory (except for the initial commands that create that directory).
+
+OPTIONAL SERVER REQUIREMENTS
+----------------------------
+
+- If you want to use Drupal's "Clean URLs" feature on an Apache web server, you
+ will need the mod_rewrite module and the ability to use local .htaccess
+ files. For Clean URLs support on IIS, see "Clean URLs with IIS"
+ (http://drupal.org/node/3854) in the Drupal.org online documentation.
+
+- If you plan to use XML-based services such as RSS aggregation, you will need
+ PHP's XML extension. This extension is enabled by default on most PHP
+ installations.
+
+- To serve gzip compressed CSS and JS files on an Apache web server, you will
+ need the mod_headers module and the ability to use local .htaccess files.
+
+- Some Drupal functionality (e.g., checking whether Drupal and contributed
+ modules need updates, RSS aggregation, etc.) require that the web server be
+ able to go out to the web and download information. If you want to use this
+ functionality, you need to verify that your hosting provider or server
+ configuration allows the web server to initiate outbound connections. Most web
+ hosting setups allow this.
+
+INSTALLATION
+------------
+
+1. Download and extract Drupal.
+
+ You can obtain the latest Drupal release from http://drupal.org -- the files
+ are available in .tar.gz and .zip formats and can be extracted using most
+ compression tools.
+
+ To download and extract the files, on a typical Unix/Linux command line, use
+ the following commands (assuming you want version x.y of Drupal in .tar.gz
+ format):
+
+ wget http://drupal.org/files/projects/drupal-x.y.tar.gz
+ tar -zxvf drupal-x.y.tar.gz
+
+ This will create a new directory drupal-x.y/ containing all Drupal files and
+ directories. Then, to move the contents of that directory into a directory
+ within your web server's document root or your public HTML directory,
+ continue with this command:
+
+ mv drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
+
+2. Optionally, download a translation.
+
+ By default, Drupal is installed in English, and further languages may be
+ installed later. If you prefer to install Drupal in another language
+ initially:
+
+ - Download a translation file for the correct Drupal version and language
+ from the translation server: http://localize.drupal.org/translate/downloads
+
+ - Place the file into your installation profile's translations
+ directory. For instance, if you are using the Standard install profile,
+ move the .po file into the directory:
+
+ profiles/standard/translations/
+
+ For detailed instructions, visit http://drupal.org/localize
+
+3. Create the Drupal database.
+
+ Because Drupal stores all site information in a database, you must create
+ this database in order to install Drupal, and grant Drupal certain database
+ privileges (such as the ability to create tables). For details, consult
+ INSTALL.mysql.txt, INSTALL.pgsql.txt, or INSTALL.sqlite.txt. You may also
+ need to consult your web hosting provider for instructions specific to your
+ web host.
+
+ Take note of the username, password, database name, and hostname as you
+ create the database. You will enter this information during the install.
+
+4. Run the install script.
+
+ To run the install script, point your browser to the base URL of your
+ website (e.g., http://www.example.com).
+
+ You will be guided through several screens to set up the database, add the
+ site maintenance account (the first user, also known as user/1), and provide
+ basic web site settings.
+
+ During installation, several files and directories need to be created, which
+ the install script will try to do automatically. However, on some hosting
+ environments, manual steps are required, and the install script will tell
+ you that it cannot proceed until you fix certain issues. This is normal and
+ does not indicate a problem with your server.
+
+ The most common steps you may need to perform are:
+
+ a. Missing files directory.
+
+ The install script will attempt to create a file storage directory in
+ the default location at sites/default/files (the location of the files
+ directory may be changed after Drupal is installed).
+
+ If auto-creation fails, you can make it work by changing permissions on
+ the sites/default directory so that the web server can create the files
+ directory within it for you. (If you are creating a multisite
+ installation, substitute the correct sites directory for sites/default;
+ see the Multisite Configuration section of this file, below.)
+
+ For example, on a Unix/Linux command line, you can grant everyone
+ (including the web server) permission to write to the sites/default
+ directory with this command:
+
+ chmod a+w sites/default
+
+ Be sure to set the permissions back after the installation is finished!
+ Sample command:
+
+ chmod go-w sites/default
+
+ Alternatively, instead of allowing the web server to create the files
+ directory for you as described above, you can create it yourself. Sample
+ commands from a Unix/Linux command line:
+
+ mkdir sites/default/files
+ chmod a+w sites/default/files
+
+ b. Missing settings file.
+
+ Drupal will try to automatically create a settings.php configuration file,
+ which is normally in the directory sites/default (to avoid problems when
+ upgrading, Drupal is not packaged with this file). If auto-creation fails,
+ you will need to create this file yourself, using the file
+ sites/default/default.settings.php as a template.
+
+ For example, on a Unix/Linux command line, you can make a copy of the
+ default.settings.php file with the command:
+
+ cp sites/default/default.settings.php sites/default/settings.php
+
+ Next, grant write privileges to the file to everyone (including the web
+ server) with the command:
+
+ chmod a+w sites/default/settings.php
+
+ Be sure to set the permissions back after the installation is finished!
+ Sample command:
+
+ chmod go-w sites/default/settings.php
+
+ c. Write permissions after install.
+
+ The install script will attempt to write-protect the settings.php file and
+ the sites/default directory after saving your configuration. If this
+ fails, you will be notified, and you can do it manually. Sample commands
+ from a Unix/Linux command line:
+
+ chmod go-w sites/default/settings.php
+ chmod go-w sites/default
+
+5. Verify that the site is working.
+
+ When the install script finishes, you will be logged in with the site
+ maintenance account on a "Welcome" page. If the default Drupal theme is not
+ displaying properly and links on the page result in "Page Not Found" errors,
+ you may be experiencing problems with clean URLs. Visit
+ http://drupal.org/getting-started/clean-urls to troubleshoot.
+
+6. Change file system storage settings (optional).
+
+ The files directory created in step 4 is the default file system path used to
+ store all uploaded files, as well as some temporary files created by
+ Drupal. After installation, you can modify the file system path to store
+ uploaded files in a different location.
+
+ It is not necessary to modify this path, but you may wish to change it if:
+
+ - Your site runs multiple Drupal installations from a single codebase (modify
+ the file system path of each installation to a different directory so that
+ uploads do not overlap between installations).
+
+ - Your site runs on a number of web servers behind a load balancer or reverse
+ proxy (modify the file system path on each server to point to a shared file
+ repository).
+
+ - You want to restrict access to uploaded files.
+
+ To modify the file system path:
+
+ a. Ensure that the new location for the path exists and is writable by the
+ web server. For example, to create a new directory named uploads and grant
+ write permissions, use the following commands on a Unix/Linux command
+ line:
+
+ mkdir uploads
+ chmod a+w uploads
+
+ b. Navigate to Administration > Configuration > Media > File system, and
+ enter the desired path. Note that if you want to use private file storage,
+ you need to first enter the path for private files and save the
+ configuration, and then change the "Default download method" setting and
+ save again.
+
+ Changing the file system path after files have been uploaded may cause
+ unexpected problems on an existing site. If you modify the file system path
+ on an existing site, remember to copy all files from the original location
+ to the new location.
+
+7. Revoke documentation file permissions (optional).
+
+ Some administrators suggest making the documentation files, especially
+ CHANGELOG.txt, non-readable so that the exact version of Drupal you are
+ running is slightly more difficult to determine. If you wish to implement
+ this optional security measure, from a Unix/Linux command line you can use
+ the following command:
+
+ chmod a-r CHANGELOG.txt
+
+ Note that the example only affects CHANGELOG.txt. To completely hide all
+ documentation files from public view, repeat this command for each of the
+ Drupal documentation files in the installation directory, substituting the
+ name of each file for CHANGELOG.txt in the example.
+
+ For more information on setting file permissions, see "Modifying Linux,
+ Unix, and Mac file permissions" (http://drupal.org/node/202483) or
+ "Modifying Windows file permissions" (http://drupal.org/node/202491) in the
+ Drupal.org online documentation.
+
+8. Set up independent "cron" maintenance jobs.
+
+ Many Drupal modules have tasks that must be run periodically, including the
+ Search module (building and updating the index used for keyword searching),
+ the Aggregator module (retrieving feeds from other sites), and the System
+ module (performing routine maintenance and pruning of database tables). These
+ tasks are known as "cron maintenance tasks", named after the Unix/Linux
+ "cron" utility.
+
+ When you install Drupal, its built-in cron feature is enabled, which
+ automatically runs the cron tasks periodically, triggered by people visiting
+ pages of your site. You can configure the built-in cron feature by navigating
+ to Administration > Configuration > System > Cron.
+
+ It is also possible to run the cron tasks independent of site visits; this is
+ recommended for most sites. To do this, you will need to set up an automated
+ process to visit the page cron.php on your site, which executes the cron
+ tasks.
+
+ The URL of the cron.php page requires a "cron key" to protect against
+ unauthorized access. Your site's cron key is automatically generated during
+ installation and is specific to your site. The full URL of the page, with the
+ cron key, is available in the "Cron maintenance tasks" section of the Status
+ report page at Administration > Reports > Status report.
+
+ As an example for how to set up this automated process, you can use the
+ crontab utility on Unix/Linux systems. The following crontab line uses the
+ wget command to visit the cron.php page, and runs each hour, on the hour:
+
+ 0 * * * * wget -O - -q -t 1 http://example.com/cron.php?cron_key=YOURKEY
+
+ Replace the text "http://example.com/cron.php?cron_key=YOURKEY" in the
+ example with the full URL displayed under "Cron maintenance tasks" on the
+ "Status report" page.
+
+ More information about cron maintenance tasks is available at
+ http://drupal.org/cron, and sample cron shell scripts can be found in the
+ scripts/ directory. (Note that these scripts must be customized like the
+ above example, to add your site-specific cron key and domain name.)
+
+BUILDING AND CUSTOMIZING YOUR SITE
+----------------------------------
+
+A new installation of Drupal defaults to a very basic configuration. To extend
+your site, you use "modules" and "themes". A module is a plugin that adds
+functionality to Drupal, while a theme changes the look of your site. The core
+of Drupal provides several optional modules and themes, and you can download
+more at http://drupal.org/project/modules and http://drupal.org/project/themes
+
+Do not mix downloaded or custom modules and themes with Drupal's core modules
+and themes. Drupal's modules and themes are located in the top-level modules and
+themes directories, while the modules and themes you add to Drupal are normally
+placed in the sites/all/modules and sites/all/themes directories. If you run a
+multisite installation, you can also place modules and themes in the
+site-specific directories -- see the Multisite Configuration section, below.
+
+Never edit Drupal's core modules and themes; instead, use the hooks available in
+the Drupal API. To modify the behavior of Drupal, develop a module as described
+at http://drupal.org/developing/modules. To modify the look of Drupal, create a
+subtheme as described at http://drupal.org/node/225125, or a completely new
+theme as described at http://drupal.org/documentation/theme
+
+MULTISITE CONFIGURATION
+-----------------------
+
+A single Drupal installation can host several Drupal-powered sites, each with
+its own individual configuration.
+
+Additional site configurations are created in subdirectories within the 'sites'
+directory. Each subdirectory must have a 'settings.php' file, which specifies
+the configuration settings. The easiest way to create additional sites is to
+copy the 'default' directory and modify the 'settings.php' file as appropriate.
+The new directory name is constructed from the site's URL. The configuration for
+www.example.com could be in 'sites/example.com/settings.php' (note that 'www.'
+should be omitted if users can access your site at http://example.com/).
+
+Sites do not have to have a different domain. You can also use subdomains and
+subdirectories for Drupal sites. For example, example.com, sub.example.com, and
+sub.example.com/site3 can all be defined as independent Drupal sites. The setup
+for a configuration such as this would look like the following:
+
+ sites/default/settings.php
+ sites/example.com/settings.php
+ sites/sub.example.com/settings.php
+ sites/sub.example.com.site3/settings.php
+
+When searching for a site configuration (for example www.sub.example.com/site3),
+Drupal will search for configuration files in the following order, using the
+first configuration it finds:
+
+ sites/www.sub.example.com.site3/settings.php
+ sites/sub.example.com.site3/settings.php
+ sites/example.com.site3/settings.php
+ sites/www.sub.example.com/settings.php
+ sites/sub.example.com/settings.php
+ sites/example.com/settings.php
+ sites/default/settings.php
+
+If you are installing on a non-standard port, the port number is treated as the
+deepest subdomain. For example: http://www.example.com:8080/ could be loaded
+from sites/8080.www.example.com/. The port number will be removed according to
+the pattern above if no port-specific configuration is found, just like a real
+subdomain.
+
+Each site configuration can have its own site-specific modules and themes in
+addition to those installed in the standard 'modules' and 'themes' directories.
+To use site-specific modules or themes, simply create a 'modules' or 'themes'
+directory within the site configuration directory. For example, if
+sub.example.com has a custom theme and a custom module that should not be
+accessible to other sites, the setup would look like this:
+
+ sites/sub.example.com/
+ settings.php
+ themes/custom_theme
+ modules/custom_module
+
+NOTE: for more information about multiple virtual hosts or the configuration
+settings, consult http://drupal.org/getting-started/6/install/multi-site
+
+For more information on configuring Drupal's file system path in a multisite
+configuration, see step 6 above.
+
+MORE INFORMATION
+----------------
+
+- See the Drupal.org online documentation:
+ http://drupal.org/documentation
+
+- For a list of security announcements, see the "Security advisories" page at
+ http://drupal.org/security (available as an RSS feed). This page also
+ describes how to subscribe to these announcements via e-mail.
+
+- For information about the Drupal security process, or to find out how to
+ report a potential security issue to the Drupal security team, see the
+ "Security team" page at http://drupal.org/security-team
+
+- For information about the wide range of available support options, visit
+ http://drupal.org and click on Community and Support in the top or bottom
+ navigation.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt
new file mode 100644
index 0000000..30fa827
--- /dev/null
+++ b/MAINTAINERS.txt
@@ -0,0 +1,294 @@
+
+Drupal core is maintained by the community. To participate, go to
+
+ http://drupal.org/contribute
+
+The people listed here have agreed to do more quality assurance work for
+particular areas of Drupal. All of them are subject to change.
+
+
+Branch maintainers
+------------------
+
+Drupal 7
+- Dries Buytaert 'dries' <http://drupal.org/user/1>
+- Angela Byron 'webchick' <http://drupal.org/user/24967>
+
+
+Component maintainers
+---------------------
+
+Ajax system
+- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
+- Randy Fay 'rfay' <http://drupal.org/user/30906>
+- Earl Miles 'merlinofchaos' <http://drupal.org/user/26979>
+
+Base system
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
+
+Batch system
+- Yves Chedemois 'yched' <http://drupal.org/user/39567>
+
+Cache system
+- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
+
+Cron system
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+- Derek Wright 'dww' <http://drupal.org/user/46549>
+
+Database system
+- Larry Garfield 'Crell' <http://drupal.org/user/26398>
+
+ - MySQL driver
+ - Larry Garfield 'Crell' <http://drupal.org/user/26398>
+ - David Strauss 'David Strauss' <http://drupal.org/user/93254>
+
+ - PostgreSQL driver
+ - Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+ - Josh Waihi 'fiasco' <http://drupal.org/user/188162>
+
+ - Sqlite driver
+ - Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+ - Károly Négyesi 'chx' <http://drupal.org/user/9446>
+
+Database update system
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+
+Entity system
+- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
+- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
+
+File system
+- Andrew Morton 'drewish' <http://drupal.org/user/34869>
+- Aaron Winborn 'aaron' <http://drupal.org/user/33420>
+
+Form system
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
+- Wolfgang Ziegler 'fago' <http://drupal.org/user/16747>
+- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
+- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
+
+Image system
+- Andrew Morton 'drewish' <http://drupal.org/user/34869>
+- Nathan Haug 'quicksketch' <http://drupal.org/user/35821>
+
+Install system
+- David Rothstein 'David_Rothstein' <http://drupal.org/user/124982>
+
+JavaScript
+- ?
+
+Language system
+- Francesco Placella 'plach' <http://drupal.org/user/183211>
+- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
+
+Lock system
+- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+
+Mail system
+- ?
+
+Markup
+- Jacine Luisi 'Jacine' <http://drupal.org/user/88931>
+- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
+
+Menu system
+- Peter Wolanin 'pwolanin' <http://drupal.org/user/49851>
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+
+Path system
+- Dave Reid 'davereid' <http://drupal.org/user/53892>
+- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
+
+Render system
+- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
+- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
+- Franz Heinzmann 'Frando' <http://drupal.org/user/21850>
+
+Theme system
+- Earl Miles 'merlinofchaos' <http://drupal.org/user/26979>
+- Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
+- Joon Park 'dvessel' <http://drupal.org/user/56782>
+- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
+
+Token system
+- Dave Reid 'davereid' <http://drupal.org/user/53892>
+
+XML-RPC system
+- Frederic G. Marand 'fgm' <http://drupal.org/user/27985>
+
+
+Topic coordinators
+------------------
+
+Accessibility
+- Everett Zufelt 'Everett Zufelt' <http://drupal.org/user/406552>
+- Brandon Bowersox 'brandonojc' <http://drupal.org/user/186415>
+
+Documentation
+- Ariane Khachatourians 'arianek' <http://drupal.org/user/158886>
+- Jennifer Hodgdon 'jhodgdon' <http://drupal.org/user/155601>
+
+Security
+- Heine Deelstra 'Heine' <http://drupal.org/user/17943>
+
+Translations
+- Gerhard Killesreiter 'killes' <http://drupal.org/user/83>
+
+User experience and usability
+- Roy Scholten 'yoroy' <http://drupal.org/user/41502>
+- Bojhan Somers 'Bojhan' <http://drupal.org/user/87969>
+
+
+Module maintainers
+------------------
+
+Aggregator module
+- ?
+
+Block module
+- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
+
+Blog module
+- ?
+
+Book module
+- Peter Wolanin 'pwolanin' <http://drupal.org/user/49851>
+
+Color module
+- ?
+
+Comment module
+- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
+
+Contact module
+- Dave Reid 'davereid' <http://drupal.org/user/53892>
+
+Contextual module
+- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
+
+Dashboard module
+- ?
+
+Database logging module
+- Khalid Baheyeldin 'kbahey' <http://drupal.org/user/4063>
+
+Field module
+- Yves Chedemois 'yched' <http://drupal.org/user/39567>
+- Barry Jaspan 'bjaspan' <http://drupal.org/user/46413>
+
+Field UI module
+- Yves Chedemois 'yched' <http://drupal.org/user/39567>
+
+File module
+- Aaron Winborn 'aaron' <http://drupal.org/user/33420>
+
+Filter module
+- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
+
+Forum module
+- Lee Rowlands 'larowlan' <http://drupal.org/user/395439>
+
+Help module
+- ?
+
+Image module
+- Nathan Haug 'quicksketch' <http://drupal.org/user/35821>
+
+Locale module
+- Gábor Hojtsy 'Gábor Hojtsy' <http://drupal.org/user/4166>
+
+Menu module
+- ?
+
+Node module
+- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
+- David Strauss 'David Strauss' <http://drupal.org/user/93254>
+
+OpenID module
+- Vojtech Kusy 'wojtha' <http://drupal.org/user/56154>
+- Heine Deelstra 'Heine' <http://drupal.org/user/17943>
+- Christian Schmidt 'c960657' <http://drupal.org/user/216078>
+- Damien Tournoud 'DamZ' <http://drupal.org/user/22211>
+
+Overlay module
+- Katherine Senzee 'ksenzee' <http://drupal.org/user/139855>
+
+Path module
+- Dave Reid 'davereid' <http://drupal.org/user/53892>
+
+PHP module
+- ?
+
+Poll module
+- ?
+
+Profile module
+- ?
+
+RDF module
+- Stéphane Corlosquet 'scor' <http://drupal.org/user/52142>
+
+Search module
+- Doug Green 'douggreen' <http://drupal.org/user/29191>
+
+Shortcut module
+- David Rothstein 'David_Rothstein' <http://drupal.org/user/124982>
+- Kristof De Jaeger 'swentel' <http://drupal.org/user/107403>
+
+Simpletest module
+- Jimmy Berry 'boombatower' <http://drupal.org/user/214218>
+- Károly Négyesi 'chx' <http://drupal.org/user/9446>
+
+Statistics module
+- Dave Reid 'davereid' <http://drupal.org/user/53892>
+
+Syslog module
+- Khalid Baheyeldin 'kbahey' <http://drupal.org/user/4063>
+
+System module
+- ?
+
+Taxonomy module
+- Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
+- Benjamin Doherty 'bangpound' <http://drupal.org/user/100456>
+
+Toolbar module
+- ?
+
+Tracker module
+- David Strauss 'David Strauss' <http://drupal.org/user/93254>
+
+Translation module
+- Francesco Placella 'plach' <http://drupal.org/user/183211>
+
+Trigger module
+- ?
+
+Update module
+- Derek Wright 'dww' <http://drupal.org/user/46549>
+
+User module
+- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23>
+- David Strauss 'David Strauss' <http://drupal.org/user/93254>
+
+
+Theme maintainers
+-----------------
+
+Bartik theme
+- Jen Simmons 'jensimmons' <http://drupal.org/user/140882>
+- Jeff Burns 'Jeff Burnz' <http://drupal.org/user/61393>
+
+Garland theme
+- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
+
+Seven theme
+- Jeff Burns 'Jeff Burnz' <http://drupal.org/user/61393>
+
+Stark theme
+- John Albin Wilkins 'JohnAlbin' <http://drupal.org/user/32095>
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..921f99c
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,88 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * About Drupal
+ * Configuration and features
+ * Appearance
+ * Developing for Drupal
+
+ABOUT DRUPAL
+------------
+
+Drupal is an open source content management platform supporting a variety of
+websites ranging from personal weblogs to large community-driven websites. For
+more information, see the Drupal website at http://drupal.org/, and join the
+Drupal community at http://drupal.org/community.
+
+Legal information about Drupal:
+ * Know your rights when using Drupal:
+ See LICENSE.txt in the same directory as this document.
+ * Learn about the Drupal trademark and logo policy:
+ http://drupal.com/trademark
+
+CONFIGURATION AND FEATURES
+--------------------------
+
+Drupal core (what you get when you download and extract a drupal-x.y.tar.gz or
+drupal-x.y.zip file from http://drupal.org/project/drupal) has what you need to
+get started with your website. It includes several modules (extensions that add
+functionality) for common website features, such as managing content, user
+accounts, image uploading, and search. Core comes with many options that allow
+site-specific configuration. In addition to the core modules, there are
+thousands of contributed modules (for functionality not included with Drupal
+core) available for download.
+
+More about configuration:
+ * Install, upgrade, and maintain Drupal:
+ See INSTALL.txt and UPGRADE.txt in the same directory as this document.
+ * Learn about how to use Drupal to create your site:
+ http://drupal.org/documentation
+ * Download contributed modules to sites/all/modules to extend Drupal's
+ functionality:
+ http://drupal.org/project/modules
+ * See also: "Developing for Drupal" for writing your own modules, below.
+
+APPEARANCE
+----------
+
+In Drupal, the appearance of your site is set by the theme (themes are
+extensions that set fonts, colors, and layout). Drupal core comes with several
+themes. More themes are available for download, and you can also create your own
+custom theme.
+
+More about themes:
+ * Download contributed themes to sites/all/themes to modify Drupal's
+ appearance:
+ http://drupal.org/project/themes
+ * Develop your own theme:
+ http://drupal.org/documentation/theme
+
+DEVELOPING FOR DRUPAL
+---------------------
+
+Drupal contains an extensive API that allows you to add to and modify the
+functionality of your site. The API consists of "hooks", which allow modules to
+react to system events and customize Drupal's behavior, and functions that
+standardize common operations such as database queries and form generation. The
+flexible hook architecture means that you should never need to directly modify
+the files that come with Drupal core to achieve the functionality you want;
+instead, functionality modifications take the form of modules.
+
+When you need new functionality for your Drupal site, search for existing
+contributed modules. If you find a module that matches except for a bug or an
+additional needed feature, change the module and contribute your improvements
+back to the project in the form of a "patch". Create new custom modules only
+when nothing existing comes close to what you need.
+
+More about developing:
+ * Search for existing contributed modules:
+ http://drupal.org/project/modules
+ * Contribute a patch:
+ http://drupal.org/patch/submit
+ * Develop your own module:
+ http://drupal.org/developing/modules
+ * Follow best practices:
+ http://drupal.org/best-practices
+ * Refer to the API documentation:
+ http://api.drupal.org/api/drupal/7
diff --git a/UPGRADE.txt b/UPGRADE.txt
new file mode 100644
index 0000000..c6fee98
--- /dev/null
+++ b/UPGRADE.txt
@@ -0,0 +1,232 @@
+INTRODUCTION
+------------
+This document describes how to:
+
+ * Update your Drupal site from one minor 7.x version to another minor 7.x
+ version; for example, from 7.8 to 7.9, or from 7.6 to 7.10.
+
+ * Upgrade your Drupal site's major version from 6.x to 7.x.
+
+First steps and definitions:
+
+ * If you are upgrading to Drupal version x.y, then x is known as the major
+ version number, and y is known as the minor version number. The download
+ file will be named drupal-x.y.tar.gz (or drupal-x.y.zip).
+
+ * All directories mentioned in this document are relative to the directory of
+ your Drupal installation.
+
+ * Make a full backup of all files, directories, and your database(s) before
+ starting, and save it outside your Drupal installation directory.
+ Instructions may be found at http://drupal.org/upgrade/backing-up-the-db
+
+ * It is wise to try an update or upgrade on a test copy of your site before
+ applying it to your live site. Even minor updates can cause your site's
+ behavior to change.
+
+ * Each new release of Drupal has release notes, which explain the changes made
+ since the previous version and any special instructions needed to update or
+ upgrade to the new version. You can find a link to the release notes for the
+ version you are upgrading or updating to on the Drupal project page
+ (http://drupal.org/project/drupal).
+
+UPGRADE PROBLEMS
+----------------
+If you encounter errors during this process,
+
+ * Note any error messages you see.
+
+ * Restore your site to its previous state, using the file and database backups
+ you created before you started the upgrade process. Do not attempt to do
+ further upgrades on a site that had update problems.
+
+ * Consult one of the support options listed on http://drupal.org/support
+
+More in-depth information on upgrading can be found at http://drupal.org/upgrade
+
+MINOR VERSION UPDATES
+---------------------
+To update from one minor 7.x version of Drupal to any later 7.x version, after
+following the instructions in the INTRODUCTION section at the top of this file:
+
+1. Log in as a user with the permission "Administer software updates".
+
+2. Go to Administration > Configuration > Development > Maintenance mode.
+ Enable the "Put site into maintenance mode" checkbox and save the
+ configuration.
+
+3. Remove all old core files and directories, except for the 'sites' directory
+ and any custom files you added elsewhere.
+
+ If you made modifications to files like .htaccess or robots.txt, you will
+ need to re-apply them from your backup, after the new files are in place.
+
+ Sometimes an update includes changes to default.settings.php (this will be
+ noted in the release notes). If that's the case, follow these steps:
+
+ - Make a backup copy of your settings.php file, with a different file name.
+
+ - Make a copy of the new default.settings.php file, and name the copy
+ settings.php (overwriting your previous settings.php file).
+
+ - Copy the custom and site-specific entries from the backup you made into the
+ new settings.php file. You will definitely need the lines giving the
+ database information, and you will also want to copy in any other
+ customizations you have added.
+
+4. Download the latest Drupal 7.x release from http://drupal.org to a
+ directory outside of your web root. Extract the archive and copy the files
+ into your Drupal directory.
+
+ On a typical Unix/Linux command line, use the following commands to download
+ and extract:
+
+ wget http://drupal.org/files/projects/drupal-x.y.tar.gz
+ tar -zxvf drupal-x.y.tar.gz
+
+ This creates a new directory drupal-x.y/ containing all Drupal files and
+ directories. Copy the files into your Drupal installation directory:
+
+ cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
+
+ If you do not have command line access to your server, download the archive
+ from http://drupal.org using your web browser, extract it, and then use an
+ FTP client to upload the files to your web root.
+
+5. Re-apply any modifications to files such as .htaccess or robots.txt.
+
+6. Run update.php by visiting http://www.example.com/update.php (replace
+ www.example.com with your domain name). This will update the core database
+ tables.
+
+ If you are unable to access update.php do the following:
+
+ - Open settings.php with a text editor.
+
+ - Find the line that says:
+ $update_free_access = FALSE;
+
+ - Change it into:
+ $update_free_access = TRUE;
+
+ - Once the upgrade is done, $update_free_access must be reverted to FALSE.
+
+7. Go to Administration > Reports > Status report. Verify that everything is
+ working as expected.
+
+8. Ensure that $update_free_access is FALSE in settings.php.
+
+9. Go to Administration > Configuration > Development > Maintenance mode.
+ Disable the "Put site into maintenance mode" checkbox and save the
+ configuration.
+
+MAJOR VERSION UPGRADE
+---------------------
+To upgrade from a previous major version of Drupal to Drupal 7.x, after
+following the instructions in the INTRODUCTION section at the top of this file:
+
+1. Check on the Drupal 7 status of your contributed and custom modules and
+ themes. See http://drupal.org/node/948216 for information on upgrading
+ contributed modules and themes. See http://drupal.org/node/895314 for a list
+ of modules that have been moved into core for Drupal 7, and instructions on
+ how to update them. See http://drupal.org/update/modules for information on
+ how to update your custom modules, and http://drupal.org/update/theme for
+ custom themes.
+
+ You may decide at this point that you cannot upgrade your site, because
+ needed modules or themes are not ready for Drupal 7.
+
+2. Update to the latest available version of Drupal 6.x (if your current version
+ is Drupal 5.x, you have to upgrade to 6.x first). If you need to update,
+ download Drupal 6.x and follow the instructions in its UPGRADE.txt. This
+ document only applies for upgrades from 6.x to 7.x.
+
+3. Log in as user ID 1 (the site maintenance user).
+
+4. Go to Administer > Site configuration > Site maintenance. Select
+ "Off-line" and save the configuration.
+
+5. Go to Administer > Site building > Themes. Enable "Garland" and select it as
+ the default theme.
+
+6. Go to Administer > Site building > Modules. Disable all modules that are not
+ listed under "Core - required" or "Core - optional". It is possible that some
+ modules cannot be disabled, because others depend on them. Repeat this step
+ until all non-core modules are disabled.
+
+ If you know that you will not re-enable some modules for Drupal 7.x and you
+ no longer need their data, then you can uninstall them under the Uninstall
+ tab after disabling them.
+
+7. On the command line or in your FTP client, remove the file
+
+ sites/default/default.settings.php
+
+8. Remove all old core files and directories, except for the 'sites' directory
+ and any custom files you added elsewhere.
+
+ If you made modifications to files like .htaccess or robots.txt, you will
+ need to re-apply them from your backup, after the new files are in place.
+
+9. If you uninstalled any modules, remove them from the sites/all/modules and
+ other sites/*/modules directories. Leave other modules in place, even though
+ they are incompatible with Drupal 7.x.
+
+10. Download the latest Drupal 7.x release from http://drupal.org to a
+ directory outside of your web root. Extract the archive and copy the files
+ into your Drupal directory.
+
+ On a typical Unix/Linux command line, use the following commands to download
+ and extract:
+
+ wget http://drupal.org/files/projects/drupal-x.y.tar.gz
+ tar -zxvf drupal-x.y.tar.gz
+
+ This creates a new directory drupal-x.y/ containing all Drupal files and
+ directories. Copy the files into your Drupal installation directory:
+
+ cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
+
+ If you do not have command line access to your server, download the archive
+ from http://drupal.org using your web browser, extract it, and then use an
+ FTP client to upload the files to your web root.
+
+11. Re-apply any modifications to files such as .htaccess or robots.txt.
+
+12. Make your settings.php file writeable, so that the update process can
+ convert it to the format of Drupal 7.x. settings.php is usually located in
+
+ sites/default/settings.php
+
+13. Run update.php by visiting http://www.example.com/update.php (replace
+ www.example.com with your domain name). This will update the core database
+ tables.
+
+ If you are unable to access update.php do the following:
+
+ - Open settings.php with a text editor.
+
+ - Find the line that says:
+ $update_free_access = FALSE;
+
+ - Change it into:
+ $update_free_access = TRUE;
+
+ - Once the upgrade is done, $update_free_access must be reverted to FALSE.
+
+14. Backup your database after the core upgrade has run.
+
+15. Replace and update your non-core modules and themes, following the
+ procedures at http://drupal.org/node/948216
+
+16. Go to Administration > Reports > Status report. Verify that everything is
+ working as expected.
+
+17. Ensure that $update_free_access is FALSE in settings.php.
+
+18. Go to Administration > Configuration > Development > Maintenance mode.
+ Disable the "Put site into maintenance mode" checkbox and save the
+ configuration.
+
+To get started with Drupal 7 administration, visit
+http://drupal.org/getting-started/7/admin
diff --git a/authorize.php b/authorize.php
new file mode 100644
index 0000000..3c0bd7b
--- /dev/null
+++ b/authorize.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * @file
+ * Administrative script for running authorized file operations.
+ *
+ * Using this script, the site owner (the user actually owning the files on
+ * the webserver) can authorize certain file-related operations to proceed
+ * with elevated privileges, for example to deploy and upgrade modules or
+ * themes. Users should not visit this page directly, but instead use an
+ * administrative user interface which knows how to redirect the user to this
+ * script as part of a multistep process. This script actually performs the
+ * selected operations without loading all of Drupal, to be able to more
+ * gracefully recover from errors. Access to the script is controlled by a
+ * global killswitch in settings.php ('allow_authorize_operations') and via
+ * the 'administer software updates' permission.
+ *
+ * There are helper functions for setting up an operation to run via this
+ * system in modules/system/system.module. For more information, see:
+ * @link authorize Authorized operation helper functions @endlink
+ */
+
+/**
+ * Root directory of Drupal installation.
+ */
+define('DRUPAL_ROOT', getcwd());
+
+/**
+ * Global flag to identify update.php and authorize.php runs, and so
+ * avoid various unwanted operations, such as hook_init() and
+ * hook_exit() invokes, css/js preprocessing and translation, and
+ * solve some theming issues. This flag is checked on several places
+ * in Drupal code (not just authorize.php).
+ */
+define('MAINTENANCE_MODE', 'update');
+
+/**
+ * Renders a 403 access denied page for authorize.php.
+ */
+function authorize_access_denied_page() {
+ drupal_add_http_header('Status', '403 Forbidden');
+ watchdog('access denied', 'authorize.php', NULL, WATCHDOG_WARNING);
+ drupal_set_title('Access denied');
+ return t('You are not allowed to access this page.');
+}
+
+/**
+ * Determines if the current user is allowed to run authorize.php.
+ *
+ * The killswitch in settings.php overrides all else, otherwise, the user must
+ * have access to the 'administer software updates' permission.
+ *
+ * @return
+ * TRUE if the current user can run authorize.php, otherwise FALSE.
+ */
+function authorize_access_allowed() {
+ return variable_get('allow_authorize_operations', TRUE) && user_access('administer software updates');
+}
+
+// *** Real work of the script begins here. ***
+
+require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
+require_once DRUPAL_ROOT . '/includes/session.inc';
+require_once DRUPAL_ROOT . '/includes/common.inc';
+require_once DRUPAL_ROOT . '/includes/file.inc';
+require_once DRUPAL_ROOT . '/includes/module.inc';
+require_once DRUPAL_ROOT . '/includes/ajax.inc';
+
+// We prepare only a minimal bootstrap. This includes the database and
+// variables, however, so we have access to the class autoloader registry.
+drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
+
+// This must go after drupal_bootstrap(), which unsets globals!
+global $conf;
+
+// We have to enable the user and system modules, even to check access and
+// display errors via the maintenance theme.
+$module_list['system']['filename'] = 'modules/system/system.module';
+$module_list['user']['filename'] = 'modules/user/user.module';
+module_list(TRUE, FALSE, FALSE, $module_list);
+drupal_load('module', 'system');
+drupal_load('module', 'user');
+
+// We also want to have the language system available, but we do *NOT* want to
+// actually call drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE), since that would
+// also force us through the DRUPAL_BOOTSTRAP_PAGE_HEADER phase, which loads
+// all the modules, and that's exactly what we're trying to avoid.
+drupal_language_initialize();
+
+// Initialize the maintenance theme for this administrative script.
+drupal_maintenance_theme();
+
+$output = '';
+$show_messages = TRUE;
+
+if (authorize_access_allowed()) {
+ // Load both the Form API and Batch API.
+ require_once DRUPAL_ROOT . '/includes/form.inc';
+ require_once DRUPAL_ROOT . '/includes/batch.inc';
+ // Load the code that drives the authorize process.
+ require_once DRUPAL_ROOT . '/includes/authorize.inc';
+
+ // For the sake of Batch API and a few other low-level functions, we need to
+ // initialize the URL path into $_GET['q']. However, we do not want to raise
+ // our bootstrap level, nor do we want to call drupal_initialize_path(),
+ // since that is assuming that modules are loaded and invoking hooks.
+ // However, all we really care is if we're in the middle of a batch, in which
+ // case $_GET['q'] will already be set, we just initialize it to an empty
+ // string if it's not already defined.
+ if (!isset($_GET['q'])) {
+ $_GET['q'] = '';
+ }
+
+ if (isset($_SESSION['authorize_operation']['page_title'])) {
+ drupal_set_title($_SESSION['authorize_operation']['page_title']);
+ }
+ else {
+ drupal_set_title(t('Authorize file system changes'));
+ }
+
+ // See if we've run the operation and need to display a report.
+ if (isset($_SESSION['authorize_results']) && $results = $_SESSION['authorize_results']) {
+
+ // Clear the session out.
+ unset($_SESSION['authorize_results']);
+ unset($_SESSION['authorize_operation']);
+ unset($_SESSION['authorize_filetransfer_info']);
+
+ if (!empty($results['page_title'])) {
+ drupal_set_title($results['page_title']);
+ }
+ if (!empty($results['page_message'])) {
+ drupal_set_message($results['page_message']['message'], $results['page_message']['type']);
+ }
+
+ $output = theme('authorize_report', array('messages' => $results['messages']));
+
+ $links = array();
+ if (is_array($results['tasks'])) {
+ $links += $results['tasks'];
+ }
+ else {
+ $links = array_merge($links, array(
+ l(t('Administration pages'), 'admin'),
+ l(t('Front page'), '<front>'),
+ ));
+ }
+
+ $output .= theme('item_list', array('items' => $links, 'title' => t('Next steps')));
+ }
+ // If a batch is running, let it run.
+ elseif (isset($_GET['batch'])) {
+ $output = _batch_page();
+ }
+ else {
+ if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_info'])) {
+ $output = t('It appears you have reached this page in error.');
+ }
+ elseif (!$batch = batch_get()) {
+ // We have a batch to process, show the filetransfer form.
+ $elements = drupal_get_form('authorize_filetransfer_form');
+ $output = drupal_render($elements);
+ }
+ }
+ // We defer the display of messages until all operations are done.
+ $show_messages = !(($batch = batch_get()) && isset($batch['running']));
+}
+else {
+ $output = authorize_access_denied_page();
+}
+
+if (!empty($output)) {
+ print theme('update_page', array('content' => $output, 'show_messages' => $show_messages));
+}
diff --git a/cron.php b/cron.php
new file mode 100644
index 0000000..c6ce531
--- /dev/null
+++ b/cron.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
+ */
+
+/**
+ * Root directory of Drupal installation.
+ */
+define('DRUPAL_ROOT', getcwd());
+
+include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
+drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+
+if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
+ watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
+ drupal_access_denied();
+}
+elseif (variable_get('maintenance_mode', 0)) {
+ watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
+ drupal_access_denied();
+}
+else {
+ drupal_cron_run();
+}
diff --git a/includes/actions.inc b/includes/actions.inc
new file mode 100644
index 0000000..760de83
--- /dev/null
+++ b/includes/actions.inc
@@ -0,0 +1,383 @@
+<?php
+
+/**
+ * @file
+ * This is the actions engine for executing stored actions.
+ */
+
+/**
+ * @defgroup actions Actions
+ * @{
+ * Functions that perform an action on a certain system object.
+ *
+ * Action functions are declared by modules by implementing hook_action_info().
+ * Modules can cause action functions to run by calling actions_do(), and
+ * trigger.module provides a user interface that lets administrators define
+ * events that cause action functions to run.
+ *
+ * Each action function takes two to four arguments:
+ * - $entity: The object that the action acts on, such as a node, comment, or
+ * user.
+ * - $context: Array of additional information about what triggered the action.
+ * - $a1, $a2: Optional additional information, which can be passed into
+ * actions_do() and will be passed along to the action function.
+ *
+ * @} End of "defgroup actions".
+ */
+
+/**
+ * Performs a given list of actions by executing their callback functions.
+ *
+ * Given the IDs of actions to perform, this function finds out what the
+ * callback functions for the actions are by querying the database. Then
+ * it calls each callback using the function call $function($object, $context,
+ * $a1, $a2), passing the input arguments of this function (see below) to the
+ * action function.
+ *
+ * @param $action_ids
+ * The IDs of the actions to perform. Can be a single action ID or an array
+ * of IDs. IDs of configurable actions must be given as numeric action IDs;
+ * IDs of non-configurable actions may be given as action function names.
+ * @param $object
+ * The object that the action will act on: a node, user, or comment object.
+ * @param $context
+ * Associative array containing extra information about what triggered
+ * the action call, with $context['hook'] giving the name of the hook
+ * that resulted in this call to actions_do().
+ * @param $a1
+ * Passed along to the callback.
+ * @param $a2
+ * Passed along to the callback.
+ * @return
+ * An associative array containing the results of the functions that
+ * perform the actions, keyed on action ID.
+ *
+ * @ingroup actions
+ */
+function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a2 = NULL) {
+ // $stack tracks the number of recursive calls.
+ static $stack;
+ $stack++;
+ if ($stack > variable_get('actions_max_stack', 35)) {
+ watchdog('actions', 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.', array(), WATCHDOG_ERROR);
+ return;
+ }
+ $actions = array();
+ $available_actions = actions_list();
+ $actions_result = array();
+ if (is_array($action_ids)) {
+ $conditions = array();
+ foreach ($action_ids as $action_id) {
+ if (is_numeric($action_id)) {
+ $conditions[] = $action_id;
+ }
+ elseif (isset($available_actions[$action_id])) {
+ $actions[$action_id] = $available_actions[$action_id];
+ }
+ }
+
+ // When we have action instances we must go to the database to retrieve
+ // instance data.
+ if (!empty($conditions)) {
+ $query = db_select('actions');
+ $query->addField('actions', 'aid');
+ $query->addField('actions', 'type');
+ $query->addField('actions', 'callback');
+ $query->addField('actions', 'parameters');
+ $query->condition('aid', $conditions, 'IN');
+ $result = $query->execute();
+ foreach ($result as $action) {
+ $actions[$action->aid] = $action->parameters ? unserialize($action->parameters) : array();
+ $actions[$action->aid]['callback'] = $action->callback;
+ $actions[$action->aid]['type'] = $action->type;
+ }
+ }
+
+ // Fire actions, in no particular order.
+ foreach ($actions as $action_id => $params) {
+ // Configurable actions need parameters.
+ if (is_numeric($action_id)) {
+ $function = $params['callback'];
+ if (function_exists($function)) {
+ $context = array_merge($context, $params);
+ $actions_result[$action_id] = $function($object, $context, $a1, $a2);
+ }
+ else {
+ $actions_result[$action_id] = FALSE;
+ }
+ }
+ // Singleton action; $action_id is the function name.
+ else {
+ $actions_result[$action_id] = $action_id($object, $context, $a1, $a2);
+ }
+ }
+ }
+ // Optimized execution of a single action.
+ else {
+ // If it's a configurable action, retrieve stored parameters.
+ if (is_numeric($action_ids)) {
+ $action = db_query("SELECT callback, parameters FROM {actions} WHERE aid = :aid", array(':aid' => $action_ids))->fetchObject();
+ $function = $action->callback;
+ if (function_exists($function)) {
+ $context = array_merge($context, unserialize($action->parameters));
+ $actions_result[$action_ids] = $function($object, $context, $a1, $a2);
+ }
+ else {
+ $actions_result[$action_ids] = FALSE;
+ }
+ }
+ // Singleton action; $action_ids is the function name.
+ else {
+ if (function_exists($action_ids)) {
+ $actions_result[$action_ids] = $action_ids($object, $context, $a1, $a2);
+ }
+ else {
+ // Set to avoid undefined index error messages later.
+ $actions_result[$action_ids] = FALSE;
+ }
+ }
+ }
+ $stack--;
+ return $actions_result;
+}
+
+/**
+ * Discovers all available actions by invoking hook_action_info().
+ *
+ * This function contrasts with actions_get_all_actions(); see the
+ * documentation of actions_get_all_actions() for an explanation.
+ *
+ * @param $reset
+ * Reset the action info static cache.
+ * @return
+ * An associative array keyed on action function name, with the same format
+ * as the return value of hook_action_info(), containing all
+ * modules' hook_action_info() return values as modified by any
+ * hook_action_info_alter() implementations.
+ *
+ * @see hook_action_info()
+ */
+function actions_list($reset = FALSE) {
+ $actions = &drupal_static(__FUNCTION__);
+ if (!isset($actions) || $reset) {
+ $actions = module_invoke_all('action_info');
+ drupal_alter('action_info', $actions);
+ }
+
+ // See module_implements() for an explanation of this cast.
+ return (array) $actions;
+}
+
+/**
+ * Retrieves all action instances from the database.
+ *
+ * This function differs from the actions_list() function, which gathers
+ * actions by invoking hook_action_info(). The actions returned by this
+ * function and the actions returned by actions_list() are partially
+ * synchronized. Non-configurable actions from hook_action_info()
+ * implementations are put into the database when actions_synchronize() is
+ * called, which happens when admin/config/system/actions is visited. Configurable
+ * actions are not added to the database until they are configured in the
+ * user interface, in which case a database row is created for each
+ * configuration of each action.
+ *
+ * @return
+ * Associative array keyed by numeric action ID. Each value is an associative
+ * array with keys 'callback', 'label', 'type' and 'configurable'.
+ */
+function actions_get_all_actions() {
+ $actions = db_query("SELECT aid, type, callback, parameters, label FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
+ foreach ($actions as &$action) {
+ $action['configurable'] = (bool) $action['parameters'];
+ unset($action['parameters']);
+ unset($action['aid']);
+ }
+ return $actions;
+}
+
+/**
+ * Creates an associative array keyed by hashes of function names or IDs.
+ *
+ * Hashes are used to prevent actual function names from going out into HTML
+ * forms and coming back.
+ *
+ * @param $actions
+ * An associative array with function names or action IDs as keys
+ * and associative arrays with keys 'label', 'type', etc. as values.
+ * This is usually the output of actions_list() or actions_get_all_actions().
+ * @return
+ * An associative array whose keys are hashes of the input array keys, and
+ * whose corresponding values are associative arrays with components
+ * 'callback', 'label', 'type', and 'configurable' from the input array.
+ */
+function actions_actions_map($actions) {
+ $actions_map = array();
+ foreach ($actions as $callback => $array) {
+ $key = drupal_hash_base64($callback);
+ $actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback;
+ $actions_map[$key]['label'] = $array['label'];
+ $actions_map[$key]['type'] = $array['type'];
+ $actions_map[$key]['configurable'] = $array['configurable'];
+ }
+ return $actions_map;
+}
+
+/**
+ * Given a hash of an action array key, returns the key (function or ID).
+ *
+ * Faster than actions_actions_map() when you only need the function name or ID.
+ *
+ * @param $hash
+ * Hash of a function name or action ID array key. The array key
+ * is a key into the return value of actions_list() (array key is the action
+ * function name) or actions_get_all_actions() (array key is the action ID).
+ * @return
+ * The corresponding array key, or FALSE if no match is found.
+ */
+function actions_function_lookup($hash) {
+ // Check for a function name match.
+ $actions_list = actions_list();
+ foreach ($actions_list as $function => $array) {
+ if (drupal_hash_base64($function) == $hash) {
+ return $function;
+ }
+ }
+ $aid = FALSE;
+ // Must be a configurable action; check database.
+ $result = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($result as $row) {
+ if (drupal_hash_base64($row['aid']) == $hash) {
+ $aid = $row['aid'];
+ break;
+ }
+ }
+ return $aid;
+}
+
+/**
+ * Synchronizes actions that are provided by modules in hook_action_info().
+ *
+ * Actions provided by modules in hook_action_info() implementations are
+ * synchronized with actions that are stored in the actions database table.
+ * This is necessary so that actions that do not require configuration can
+ * receive action IDs.
+ *
+ * @param $delete_orphans
+ * If TRUE, any actions that exist in the database but are no longer
+ * found in the code (for example, because the module that provides them has
+ * been disabled) will be deleted.
+ */
+function actions_synchronize($delete_orphans = FALSE) {
+ $actions_in_code = actions_list(TRUE);
+ $actions_in_db = db_query("SELECT aid, callback, label FROM {actions} WHERE parameters = ''")->fetchAllAssoc('callback', PDO::FETCH_ASSOC);
+
+ // Go through all the actions provided by modules.
+ foreach ($actions_in_code as $callback => $array) {
+ // Ignore configurable actions since their instances get put in when the
+ // user adds the action.
+ if (!$array['configurable']) {
+ // If we already have an action ID for this action, no need to assign aid.
+ if (isset($actions_in_db[$callback])) {
+ unset($actions_in_db[$callback]);
+ }
+ else {
+ // This is a new singleton that we don't have an aid for; assign one.
+ db_insert('actions')
+ ->fields(array(
+ 'aid' => $callback,
+ 'type' => $array['type'],
+ 'callback' => $callback,
+ 'parameters' => '',
+ 'label' => $array['label'],
+ ))
+ ->execute();
+ watchdog('actions', "Action '%action' added.", array('%action' => $array['label']));
+ }
+ }
+ }
+
+ // Any actions that we have left in $actions_in_db are orphaned.
+ if ($actions_in_db) {
+ $orphaned = array_keys($actions_in_db);
+
+ if ($delete_orphans) {
+ $actions = db_query('SELECT aid, label FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll();
+ foreach ($actions as $action) {
+ actions_delete($action->aid);
+ watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => $action->label));
+ }
+ }
+ else {
+ $link = l(t('Remove orphaned actions'), 'admin/config/system/actions/orphan');
+ $count = count($actions_in_db);
+ $orphans = implode(', ', $orphaned);
+ watchdog('actions', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_INFO);
+ }
+ }
+}
+
+/**
+ * Saves an action and its user-supplied parameter values to the database.
+ *
+ * @param $function
+ * The name of the function to be called when this action is performed.
+ * @param $type
+ * The type of action, to describe grouping and/or context, e.g., 'node',
+ * 'user', 'comment', or 'system'.
+ * @param $params
+ * An associative array with parameter names as keys and parameter values as
+ * values.
+ * @param $label
+ * A user-supplied label of this particular action, e.g., 'Send e-mail
+ * to Jim'.
+ * @param $aid
+ * The ID of this action. If omitted, a new action is created.
+ * @return
+ * The ID of the action.
+ */
+function actions_save($function, $type, $params, $label, $aid = NULL) {
+ // aid is the callback for singleton actions so we need to keep a separate
+ // table for numeric aids.
+ if (!$aid) {
+ $aid = db_next_id();
+ }
+
+ db_merge('actions')
+ ->key(array('aid' => $aid))
+ ->fields(array(
+ 'callback' => $function,
+ 'type' => $type,
+ 'parameters' => serialize($params),
+ 'label' => $label,
+ ))
+ ->execute();
+
+ watchdog('actions', 'Action %action saved.', array('%action' => $label));
+ return $aid;
+}
+
+/**
+ * Retrieves a single action from the database.
+ *
+ * @param $aid
+ * The ID of the action to retrieve.
+ * @return
+ * The appropriate action row from the database as an object.
+ */
+function actions_load($aid) {
+ return db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetchObject();
+}
+
+/**
+ * Deletes a single action from the database.
+ *
+ * @param $aid
+ * The ID of the action to delete.
+ */
+function actions_delete($aid) {
+ db_delete('actions')
+ ->condition('aid', $aid)
+ ->execute();
+ module_invoke_all('actions_delete', $aid);
+}
+
diff --git a/includes/ajax.inc b/includes/ajax.inc
new file mode 100644
index 0000000..d70808e
--- /dev/null
+++ b/includes/ajax.inc
@@ -0,0 +1,1213 @@
+<?php
+
+/**
+ * @file
+ * Functions for use with Drupal's Ajax framework.
+ */
+
+/**
+ * @defgroup ajax Ajax framework
+ * @{
+ * Functions for Drupal's Ajax framework.
+ *
+ * Drupal's Ajax framework is used to dynamically update parts of a page's HTML
+ * based on data from the server. Upon a specified event, such as a button
+ * click, a callback function is triggered which performs server-side logic and
+ * may return updated markup, which is then replaced on-the-fly with no page
+ * refresh necessary.
+ *
+ * This framework creates a PHP macro language that allows the server to
+ * instruct JavaScript to perform actions on the client browser. When using
+ * forms, it can be used with the #ajax property.
+ * The #ajax property can be used to bind events to the Ajax framework. By
+ * default, #ajax uses 'system/ajax' as its path for submission and thus calls
+ * ajax_form_callback() and a defined #ajax['callback'] function.
+ * However, you may optionally specify a different path to request or a
+ * different callback function to invoke, which can return updated HTML or can
+ * also return a richer set of @link ajax_commands Ajax framework commands @endlink.
+ *
+ * Standard form handling is as follows:
+ * - A form element has a #ajax property that includes #ajax['callback'] and
+ * omits #ajax['path']. See below about using #ajax['path'] to implement
+ * advanced use-cases that require something other than standard form
+ * handling.
+ * - On the specified element, Ajax processing is triggered by a change to
+ * that element.
+ * - The browser submits an HTTP POST request to the 'system/ajax' Drupal
+ * path.
+ * - The menu page callback for 'system/ajax', ajax_form_callback(), calls
+ * drupal_process_form() to process the form submission and rebuild the
+ * form if necessary. The form is processed in much the same way as if it
+ * were submitted without Ajax, with the same #process functions and
+ * validation and submission handlers called in either case, making it easy
+ * to create Ajax-enabled forms that degrade gracefully when JavaScript is
+ * disabled.
+ * - After form processing is complete, ajax_form_callback() calls the
+ * function named by #ajax['callback'], which returns the form element that
+ * has been updated and needs to be returned to the browser, or
+ * alternatively, an array of custom Ajax commands.
+ * - The page delivery callback for 'system/ajax', ajax_deliver(), renders the
+ * element returned by #ajax['callback'], and returns the JSON string
+ * created by ajax_render() to the browser.
+ * - The browser unserializes the returned JSON string into an array of
+ * command objects and executes each command, resulting in the old page
+ * content within and including the HTML element specified by
+ * #ajax['wrapper'] being replaced by the new content returned by
+ * #ajax['callback'], using a JavaScript animation effect specified by
+ * #ajax['effect'].
+ *
+ * A simple example of basic Ajax use from the
+ * @link http://drupal.org/project/examples Examples module @endlink follows:
+ * @code
+ * function main_page() {
+ * return drupal_get_form('ajax_example_simplest');
+ * }
+ *
+ * function ajax_example_simplest($form, &$form_state) {
+ * $form = array();
+ * $form['changethis'] = array(
+ * '#type' => 'select',
+ * '#options' => array(
+ * 'one' => 'one',
+ * 'two' => 'two',
+ * 'three' => 'three',
+ * ),
+ * '#ajax' => array(
+ * 'callback' => 'ajax_example_simplest_callback',
+ * 'wrapper' => 'replace_textfield_div',
+ * ),
+ * );
+
+ * // This entire form element will be replaced with an updated value.
+ * $form['replace_textfield'] = array(
+ * '#type' => 'textfield',
+ * '#title' => t("The default value will be changed"),
+ * '#description' => t("Say something about why you chose") . "'" .
+ * (!empty($form_state['values']['changethis'])
+ * ? $form_state['values']['changethis'] : t("Not changed yet")) . "'",
+ * '#prefix' => '<div id="replace_textfield_div">',
+ * '#suffix' => '</div>',
+ * );
+ * return $form;
+ * }
+ *
+ * function ajax_example_simplest_callback($form, $form_state) {
+ * // The form has already been submitted and updated. We can return the replaced
+ * // item as it is.
+ * return $form['replace_textfield'];
+ * }
+ * @endcode
+ *
+ * In the above example, the 'changethis' element is Ajax-enabled. The default
+ * #ajax['event'] is 'change', so when the 'changethis' element changes,
+ * an Ajax call is made. The form is submitted and reprocessed, and then the
+ * callback is called. In this case, the form has been automatically
+ * built changing $form['replace_textfield']['#description'], so the callback
+ * just returns that part of the form.
+ *
+ * To implement Ajax handling in a form, add '#ajax' to the form
+ * definition of a field. That field will trigger an Ajax event when it is
+ * clicked (or changed, depending on the kind of field). #ajax supports
+ * the following parameters (either 'path' or 'callback' is required at least):
+ * - #ajax['callback']: The callback to invoke to handle the server side of the
+ * Ajax event, which will receive a $form and $form_state as arguments, and
+ * returns a renderable array (most often a form or form fragment), an HTML
+ * string, or an array of Ajax commands. If returning a renderable array or
+ * a string, the value will replace the original element named in
+ * #ajax['wrapper'], and
+ * theme_status_messages()
+ * will be prepended to that
+ * element. (If the status messages are not wanted, return an array
+ * of Ajax commands instead.)
+ * #ajax['wrapper']. If an array of Ajax commands is returned, it will be
+ * executed by the calling code.
+ * - #ajax['path']: The menu path to use for the request. This is often omitted
+ * and the default is used. This path should map
+ * to a menu page callback that returns data using ajax_render(). Defaults to
+ * 'system/ajax', which invokes ajax_form_callback(), eventually calling
+ * the function named in #ajax['callback']. If you use a custom
+ * path, you must set up the menu entry and handle the entire callback in your
+ * own code.
+ * - #ajax['wrapper']: The CSS ID of the area to be replaced by the content
+ * returned by the #ajax['callback'] function. The content returned from
+ * the callback will replace the entire element named by #ajax['wrapper'].
+ * The wrapper is usually created using #prefix and #suffix properties in the
+ * form. Note that this is the wrapper ID, not a CSS selector. So to replace
+ * the element referred to by the CSS selector #some-selector on the page,
+ * use #ajax['wrapper'] = 'some-selector', not '#some-selector'.
+ * - #ajax['effect']: The jQuery effect to use when placing the new HTML.
+ * Defaults to no effect. Valid options are 'none', 'slide', or 'fade'.
+ * - #ajax['speed']: The effect speed to use. Defaults to 'slow'. May be
+ * 'slow', 'fast' or a number in milliseconds which represents the length
+ * of time the effect should run.
+ * - #ajax['event']: The JavaScript event to respond to. This is normally
+ * selected automatically for the type of form widget being used, and
+ * is only needed if you need to override the default behavior.
+ * - #ajax['prevent']: A JavaScript event to prevent when 'event' is triggered.
+ * Defaults to 'click' for #ajax on #type 'submit', 'button', and
+ * 'image_button'. Multiple events may be specified separated by spaces.
+ * For example, when binding #ajax behaviors to form buttons, pressing the
+ * ENTER key within a textfield triggers the 'click' event of the form's first
+ * submit button. Triggering Ajax in this situation leads to problems, like
+ * breaking autocomplete textfields. Because of that, Ajax behaviors are bound
+ * to the 'mousedown' event on form buttons by default. However, binding to
+ * 'mousedown' rather than 'click' means that it is possible to trigger a
+ * click by pressing the mouse, holding the mouse button down until the Ajax
+ * request is complete and the button is re-enabled, and then releasing the
+ * mouse button. For this case, 'prevent' can be set to 'click', so an
+ * additional event handler is bound to prevent such a click from triggering a
+ * non-Ajax form submission. This also prevents a textfield's ENTER press
+ * triggering a button's non-Ajax form submission behavior.
+ * - #ajax['method']: The jQuery method to use to place the new HTML.
+ * Defaults to 'replaceWith'. May be: 'replaceWith', 'append', 'prepend',
+ * 'before', 'after', or 'html'. See the
+ * @link http://api.jquery.com/category/manipulation/ jQuery manipulators documentation @endlink
+ * for more information on these methods.
+ * - #ajax['progress']: Choose either a throbber or progress bar that is
+ * displayed while awaiting a response from the callback, and add an optional
+ * message. Possible keys: 'type', 'message', 'url', 'interval'.
+ * More information is available in the
+ * @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7 Form API Reference @endlink
+ *
+ * In addition to using Form API for doing in-form modification, Ajax may be
+ * enabled by adding classes to buttons and links. By adding the 'use-ajax'
+ * class to a link, the link will be loaded via an Ajax call. When using this
+ * method, the href of the link can contain '/nojs/' as part of the path. When
+ * the Ajax framework makes the request, it will convert this to '/ajax/'.
+ * The server is then able to easily tell if this request was made through an
+ * actual Ajax request or in a degraded state, and respond appropriately.
+ *
+ * Similarly, submit buttons can be given the class 'use-ajax-submit'. The
+ * form will then be submitted via Ajax to the path specified in the #action.
+ * Like the ajax-submit class above, this path will have '/nojs/' replaced with
+ * '/ajax/' so that the submit handler can tell if the form was submitted
+ * in a degraded state or not.
+ *
+ * When responding to Ajax requests, the server should do what it needs to do
+ * for that request, then create a commands array. This commands array will
+ * be converted to a JSON object and returned to the client, which will then
+ * iterate over the array and process it like a macro language.
+ *
+ * Each command item is an associative array which will be converted to a command
+ * object on the JavaScript side. $command_item['command'] is the type of
+ * command, e.g. 'alert' or 'replace', and will correspond to a method in the
+ * Drupal.ajax[command] space. The command array may contain any other data
+ * that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
+ *
+ * Commands are usually created with a couple of helper functions, so they
+ * look like this:
+ * @code
+ * $commands = array();
+ * // Replace the content of '#object-1' on the page with 'some html here'.
+ * $commands[] = ajax_command_replace('#object-1', 'some html here');
+ * // Add a visual "changed" marker to the '#object-1' element.
+ * $commands[] = ajax_command_changed('#object-1');
+ * // Menu 'page callback' and #ajax['callback'] functions are supposed to
+ * // return render arrays. If returning an Ajax commands array, it must be
+ * // encapsulated in a render array structure.
+ * return array('#type' => 'ajax', '#commands' => $commands);
+ * @endcode
+ *
+ * When returning an Ajax command array, it is often useful to have
+ * status messages rendered along with other tasks in the command array.
+ * In that case the the Ajax commands array may be constructed like this:
+ * @code
+ * $commands = array();
+ * $commands[] = ajax_command_replace(NULL, $output);
+ * $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
+ * return array('#type' => 'ajax', '#commands' => $commands);
+ * @endcode
+ *
+ * See @link ajax_commands Ajax framework commands @endlink
+ */
+
+/**
+ * Render a commands array into JSON.
+ *
+ * @param $commands
+ * A list of macro commands generated by the use of ajax_command_*()
+ * functions.
+ */
+function ajax_render($commands = array()) {
+ // Ajax responses aren't rendered with html.tpl.php, so we have to call
+ // drupal_get_css() and drupal_get_js() here, in order to have new files added
+ // during this request to be loaded by the page. We only want to send back
+ // files that the page hasn't already loaded, so we implement simple diffing
+ // logic using array_diff_key().
+ foreach (array('css', 'js') as $type) {
+ // It is highly suspicious if $_POST['ajax_page_state'][$type] is empty,
+ // since the base page ought to have at least one JS file and one CSS file
+ // loaded. It probably indicates an error, and rather than making the page
+ // reload all of the files, instead we return no new files.
+ if (empty($_POST['ajax_page_state'][$type])) {
+ $items[$type] = array();
+ }
+ else {
+ $function = 'drupal_add_' . $type;
+ $items[$type] = $function();
+ drupal_alter($type, $items[$type]);
+ // @todo Inline CSS and JS items are indexed numerically. These can't be
+ // reliably diffed with array_diff_key(), since the number can change
+ // due to factors unrelated to the inline content, so for now, we strip
+ // the inline items from Ajax responses, and can add support for them
+ // when drupal_add_css() and drupal_add_js() are changed to using md5()
+ // or some other hash of the inline content.
+ foreach ($items[$type] as $key => $item) {
+ if (is_numeric($key)) {
+ unset($items[$type][$key]);
+ }
+ }
+ // Ensure that the page doesn't reload what it already has.
+ $items[$type] = array_diff_key($items[$type], $_POST['ajax_page_state'][$type]);
+ }
+ }
+
+ // Settings are handled separately, later in this function, so that changes to
+ // the ajaxPageState setting that occur during drupal_get_css() and
+ // drupal_get_js() get included, and because the jQuery.extend() code produced
+ // by drupal_get_js() for adding settings isn't appropriate during an Ajax
+ // response, because it does not pass TRUE for the "deep" parameter, and
+ // therefore, can clobber existing settings on the page.
+ if (isset($items['js']['settings'])) {
+ unset($items['js']['settings']);
+ }
+
+ // Render the HTML to load these files, and add Ajax commands to insert this
+ // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
+ // data from being altered again, as we already altered it above.
+ $styles = drupal_get_css($items['css'], TRUE);
+ $scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
+ $scripts_header = drupal_get_js('header', $items['js'], TRUE);
+
+ $extra_commands = array();
+ if (!empty($styles)) {
+ $extra_commands[] = ajax_command_prepend('head', $styles);
+ }
+ if (!empty($scripts_header)) {
+ $extra_commands[] = ajax_command_prepend('head', $scripts_header);
+ }
+ if (!empty($scripts_footer)) {
+ $extra_commands[] = ajax_command_append('body', $scripts_footer);
+ }
+ if (!empty($extra_commands)) {
+ $commands = array_merge($extra_commands, $commands);
+ }
+
+ $scripts = drupal_add_js();
+ if (!empty($scripts['settings'])) {
+ $settings = $scripts['settings'];
+ // Automatically extract any settings added via drupal_add_js() and make
+ // them the first command.
+ array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
+ }
+
+ // Allow modules to alter any Ajax response.
+ drupal_alter('ajax_render', $commands);
+
+ return drupal_json_encode($commands);
+}
+
+/**
+ * Get a form submitted via #ajax during an Ajax callback.
+ *
+ * This will load a form from the form cache used during Ajax operations. It
+ * pulls the form info from $_POST.
+ *
+ * @return
+ * An array containing the $form and $form_state. Use the list() function
+ * to break these apart:
+ * @code
+ * list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
+ * @endcode
+ */
+function ajax_get_form() {
+ $form_state = form_state_defaults();
+
+ $form_build_id = $_POST['form_build_id'];
+
+ // Get the form from the cache.
+ $form = form_get_cache($form_build_id, $form_state);
+ if (!$form) {
+ // If $form cannot be loaded from the cache, the form_build_id in $_POST
+ // must be invalid, which means that someone performed a POST request onto
+ // system/ajax without actually viewing the concerned form in the browser.
+ // This is likely a hacking attempt as it never happens under normal
+ // circumstances, so we just do nothing.
+ watchdog('ajax', 'Invalid form POST data.', array(), WATCHDOG_WARNING);
+ drupal_exit();
+ }
+
+ // Since some of the submit handlers are run, redirects need to be disabled.
+ $form_state['no_redirect'] = TRUE;
+
+ // When a form is rebuilt after Ajax processing, its #build_id and #action
+ // should not change.
+ // @see drupal_rebuild_form()
+ $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
+ $form_state['rebuild_info']['copy']['#action'] = TRUE;
+
+ // The form needs to be processed; prepare for that by setting a few internal
+ // variables.
+ $form_state['input'] = $_POST;
+ $form_id = $form['#form_id'];
+
+ return array($form, $form_state, $form_id, $form_build_id);
+}
+
+/**
+ * Menu callback; handles Ajax requests for the #ajax Form API property.
+ *
+ * This rebuilds the form from cache and invokes the defined #ajax['callback']
+ * to return an Ajax command structure for JavaScript. In case no 'callback' has
+ * been defined, nothing will happen.
+ *
+ * The Form API #ajax property can be set both for buttons and other input
+ * elements.
+ *
+ * This function is also the canonical example of how to implement
+ * #ajax['path']. If processing is required that cannot be accomplished with
+ * a callback, re-implement this function and set #ajax['path'] to the
+ * enhanced function.
+ */
+function ajax_form_callback() {
+ list($form, $form_state) = ajax_get_form();
+ drupal_process_form($form['#form_id'], $form, $form_state);
+
+ // We need to return the part of the form (or some other content) that needs
+ // to be re-rendered so the browser can update the page with changed content.
+ // Since this is the generic menu callback used by many Ajax elements, it is
+ // up to the #ajax['callback'] function of the element (may or may not be a
+ // button) that triggered the Ajax request to determine what needs to be
+ // rendered.
+ if (!empty($form_state['triggering_element'])) {
+ $callback = $form_state['triggering_element']['#ajax']['callback'];
+ }
+ if (!empty($callback) && function_exists($callback)) {
+ return $callback($form, $form_state);
+ }
+}
+
+/**
+ * Theme callback for Ajax requests.
+ *
+ * Many different pages can invoke an Ajax request to system/ajax or another
+ * generic Ajax path. It is almost always desired for an Ajax response to be
+ * rendered using the same theme as the base page, because most themes are built
+ * with the assumption that they control the entire page, so if the CSS for two
+ * themes are both loaded for a given page, they may conflict with each other.
+ * For example, Bartik is Drupal's default theme, and Seven is Drupal's default
+ * administration theme. Depending on whether the "Use the administration theme
+ * when editing or creating content" checkbox is checked, the node edit form may
+ * be displayed in either theme, but the Ajax response to the Field module's
+ * "Add another item" button should be rendered using the same theme as the rest
+ * of the page. Therefore, system_menu() sets the 'theme callback' for
+ * 'system/ajax' to this function, and it is recommended that modules
+ * implementing other generic Ajax paths do the same.
+ */
+function ajax_base_page_theme() {
+ if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
+ $theme = $_POST['ajax_page_state']['theme'];
+ $token = $_POST['ajax_page_state']['theme_token'];
+
+ // Prevent a request forgery from giving a person access to a theme they
+ // shouldn't be otherwise allowed to see. However, since everyone is allowed
+ // to see the default theme, token validation isn't required for that, and
+ // bypassing it allows most use-cases to work even when accessed from the
+ // page cache.
+ if ($theme === variable_get('theme_default', 'bartik') || drupal_valid_token($token, $theme)) {
+ return $theme;
+ }
+ }
+}
+
+/**
+ * Package and send the result of a page callback to the browser as an Ajax response.
+ *
+ * This function is the equivalent of drupal_deliver_html_page(), but for Ajax
+ * requests. Like that function, it:
+ * - Adds needed HTTP headers.
+ * - Prints rendered output.
+ * - Performs end-of-request tasks.
+ *
+ * @param $page_callback_result
+ * The result of a page callback. Can be one of:
+ * - NULL: to indicate no content.
+ * - An integer menu status constant: to indicate an error condition.
+ * - A string of HTML content.
+ * - A renderable array of content.
+ *
+ * @see drupal_deliver_html_page()
+ */
+function ajax_deliver($page_callback_result) {
+ // Browsers do not allow JavaScript to read the contents of a user's local
+ // files. To work around that, the jQuery Form plugin submits forms containing
+ // a file input element to an IFRAME, instead of using XHR. Browsers do not
+ // normally expect JSON strings as content within an IFRAME, so the response
+ // must be customized accordingly.
+ // @see http://malsup.com/jquery/form/#file-upload
+ // @see Drupal.ajax.prototype.beforeSend()
+ $iframe_upload = !empty($_POST['ajax_iframe_upload']);
+
+ // Emit a Content-Type HTTP header if none has been added by the page callback
+ // or by a wrapping delivery callback.
+ if (is_null(drupal_get_http_header('Content-Type'))) {
+ if (!$iframe_upload) {
+ // Standard JSON can be returned to a browser's XHR object, and to
+ // non-browser user agents.
+ // @see http://www.ietf.org/rfc/rfc4627.txt?number=4627
+ drupal_add_http_header('Content-Type', 'application/json; charset=utf-8');
+ }
+ else {
+ // Browser IFRAMEs expect HTML. With most other content types, Internet
+ // Explorer presents the user with a download prompt.
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
+ }
+ }
+
+ // Print the response.
+ $commands = ajax_prepare_response($page_callback_result);
+ $json = ajax_render($commands);
+ if (!$iframe_upload) {
+ // Standard JSON can be returned to a browser's XHR object, and to
+ // non-browser user agents.
+ print $json;
+ }
+ else {
+ // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
+ // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
+ // links. This corrupts the JSON response. Protect the integrity of the
+ // JSON data by making it the value of a textarea.
+ // @see http://malsup.com/jquery/form/#file-upload
+ // @see http://drupal.org/node/1009382
+ print '<textarea>' . $json . '</textarea>';
+ }
+
+ // Perform end-of-request tasks.
+ ajax_footer();
+}
+
+/**
+ * Converts the return value of a page callback into an Ajax commands array.
+ *
+ * @param $page_callback_result
+ * The result of a page callback. Can be one of:
+ * - NULL: to indicate no content.
+ * - An integer menu status constant: to indicate an error condition.
+ * - A string of HTML content.
+ * - A renderable array of content.
+ *
+ * @return
+ * An Ajax commands array that can be passed to ajax_render().
+ */
+function ajax_prepare_response($page_callback_result) {
+ $commands = array();
+ if (!isset($page_callback_result)) {
+ // Simply delivering an empty commands array is sufficient. This results
+ // in the Ajax request being completed, but nothing being done to the page.
+ }
+ elseif (is_int($page_callback_result)) {
+ switch ($page_callback_result) {
+ case MENU_NOT_FOUND:
+ $commands[] = ajax_command_alert(t('The requested page could not be found.'));
+ break;
+
+ case MENU_ACCESS_DENIED:
+ $commands[] = ajax_command_alert(t('You are not authorized to access this page.'));
+ break;
+
+ case MENU_SITE_OFFLINE:
+ $commands[] = ajax_command_alert(filter_xss_admin(variable_get('maintenance_mode_message',
+ t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))));
+ break;
+ }
+ }
+ elseif (is_array($page_callback_result) && isset($page_callback_result['#type']) && ($page_callback_result['#type'] == 'ajax')) {
+ // Complex Ajax callbacks can return a result that contains an error message
+ // or a specific set of commands to send to the browser.
+ $page_callback_result += element_info('ajax');
+ $error = $page_callback_result['#error'];
+ if (isset($error) && $error !== FALSE) {
+ if ((empty($error) || $error === TRUE)) {
+ $error = t('An error occurred while handling the request: The server received invalid input.');
+ }
+ $commands[] = ajax_command_alert($error);
+ }
+ else {
+ $commands = $page_callback_result['#commands'];
+ }
+ }
+ else {
+ // Like normal page callbacks, simple Ajax callbacks can return HTML
+ // content, as a string or render array. This HTML is inserted in some
+ // relationship to #ajax['wrapper'], as determined by which jQuery DOM
+ // manipulation method is used. The method used is specified by
+ // #ajax['method']. The default method is 'replaceWith', which completely
+ // replaces the old wrapper element and its content with the new HTML.
+ $html = is_string($page_callback_result) ? $page_callback_result : drupal_render($page_callback_result);
+ $commands[] = ajax_command_insert(NULL, $html);
+ // Add the status messages inside the new content's wrapper element, so that
+ // on subsequent Ajax requests, it is treated as old content.
+ $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
+ }
+
+ return $commands;
+}
+
+/**
+ * Perform end-of-Ajax-request tasks.
+ *
+ * This function is the equivalent of drupal_page_footer(), but for Ajax
+ * requests.
+ *
+ * @see drupal_page_footer()
+ */
+function ajax_footer() {
+ // Even for Ajax requests, invoke hook_exit() implementations. There may be
+ // modules that need very fast Ajax responses, and therefore, run Ajax
+ // requests with an early bootstrap.
+ if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')) {
+ module_invoke_all('exit');
+ }
+
+ // Commit the user session. See above comment about the possibility of this
+ // function running without session.inc loaded.
+ if (function_exists('drupal_session_commit')) {
+ drupal_session_commit();
+ }
+}
+
+/**
+ * Form element process callback to handle #ajax.
+ *
+ * @param $element
+ * An associative array containing the properties of the element.
+ *
+ * @return
+ * The processed element.
+ *
+ * @see ajax_pre_render_element()
+ */
+function ajax_process_form($element, &$form_state) {
+ $element = ajax_pre_render_element($element);
+ if (!empty($element['#ajax_processed'])) {
+ $form_state['cache'] = TRUE;
+ }
+ return $element;
+}
+
+/**
+ * Add Ajax information about an element to the page to communicate with JavaScript.
+ *
+ * If #ajax['path'] is set on an element, this additional JavaScript is added
+ * to the page header to attach the Ajax behaviors. See ajax.js for more
+ * information.
+ *
+ * @param $element
+ * An associative array containing the properties of the element.
+ * Properties used:
+ * - #ajax['event']
+ * - #ajax['prevent']
+ * - #ajax['path']
+ * - #ajax['options']
+ * - #ajax['wrapper']
+ * - #ajax['parameters']
+ * - #ajax['effect']
+ *
+ * @return
+ * The processed element with the necessary JavaScript attached to it.
+ */
+function ajax_pre_render_element($element) {
+ // Skip already processed elements.
+ if (isset($element['#ajax_processed'])) {
+ return $element;
+ }
+ // Initialize #ajax_processed, so we do not process this element again.
+ $element['#ajax_processed'] = FALSE;
+
+ // Nothing to do if there is neither a callback nor a path.
+ if (!(isset($element['#ajax']['callback']) || isset($element['#ajax']['path']))) {
+ return $element;
+ }
+
+ // Add a reasonable default event handler if none was specified.
+ if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {
+ switch ($element['#type']) {
+ case 'submit':
+ case 'button':
+ case 'image_button':
+ // Pressing the ENTER key within a textfield triggers the click event of
+ // the form's first submit button. Triggering Ajax in this situation
+ // leads to problems, like breaking autocomplete textfields, so we bind
+ // to mousedown instead of click.
+ // @see http://drupal.org/node/216059
+ $element['#ajax']['event'] = 'mousedown';
+ // Retain keyboard accessibility by setting 'keypress'. This causes
+ // ajax.js to trigger 'event' when SPACE or ENTER are pressed while the
+ // button has focus.
+ $element['#ajax']['keypress'] = TRUE;
+ // Binding to mousedown rather than click means that it is possible to
+ // trigger a click by pressing the mouse, holding the mouse button down
+ // until the Ajax request is complete and the button is re-enabled, and
+ // then releasing the mouse button. Set 'prevent' so that ajax.js binds
+ // an additional handler to prevent such a click from triggering a
+ // non-Ajax form submission. This also prevents a textfield's ENTER
+ // press triggering this button's non-Ajax form submission behavior.
+ if (!isset($element['#ajax']['prevent'])) {
+ $element['#ajax']['prevent'] = 'click';
+ }
+ break;
+
+ case 'password':
+ case 'textfield':
+ case 'textarea':
+ $element['#ajax']['event'] = 'blur';
+ break;
+
+ case 'radio':
+ case 'checkbox':
+ case 'select':
+ $element['#ajax']['event'] = 'change';
+ break;
+
+ case 'link':
+ $element['#ajax']['event'] = 'click';
+ break;
+
+ default:
+ return $element;
+ }
+ }
+
+ // Attach JavaScript settings to the element.
+ if (isset($element['#ajax']['event'])) {
+ $element['#attached']['library'][] = array('system', 'jquery.form');
+ $element['#attached']['library'][] = array('system', 'drupal.ajax');
+
+ $settings = $element['#ajax'];
+
+ // Assign default settings.
+ $settings += array(
+ 'path' => 'system/ajax',
+ 'options' => array(),
+ );
+
+ // @todo Legacy support. Remove in Drupal 8.
+ if (isset($settings['method']) && $settings['method'] == 'replace') {
+ $settings['method'] = 'replaceWith';
+ }
+
+ // Change path to URL.
+ $settings['url'] = url($settings['path'], $settings['options']);
+ unset($settings['path'], $settings['options']);
+
+ // Add special data to $settings['submit'] so that when this element
+ // triggers an Ajax submission, Drupal's form processing can determine which
+ // element triggered it.
+ // @see _form_element_triggered_scripted_submission()
+ if (isset($settings['trigger_as'])) {
+ // An element can add a 'trigger_as' key within #ajax to make the element
+ // submit as though another one (for example, a non-button can use this
+ // to submit the form as though a button were clicked). When using this,
+ // the 'name' key is always required to identify the element to trigger
+ // as. The 'value' key is optional, and only needed when multiple elements
+ // share the same name, which is commonly the case for buttons.
+ $settings['submit']['_triggering_element_name'] = $settings['trigger_as']['name'];
+ if (isset($settings['trigger_as']['value'])) {
+ $settings['submit']['_triggering_element_value'] = $settings['trigger_as']['value'];
+ }
+ unset($settings['trigger_as']);
+ }
+ elseif (isset($element['#name'])) {
+ // Most of the time, elements can submit as themselves, in which case the
+ // 'trigger_as' key isn't needed, and the element's name is used.
+ $settings['submit']['_triggering_element_name'] = $element['#name'];
+ // If the element is a (non-image) button, its name may not identify it
+ // uniquely, in which case a match on value is also needed.
+ // @see _form_button_was_clicked()
+ if (isset($element['#button_type']) && empty($element['#has_garbage_value'])) {
+ $settings['submit']['_triggering_element_value'] = $element['#value'];
+ }
+ }
+
+ // Convert a simple #ajax['progress'] string into an array.
+ if (isset($settings['progress']) && is_string($settings['progress'])) {
+ $settings['progress'] = array('type' => $settings['progress']);
+ }
+ // Change progress path to a full URL.
+ if (isset($settings['progress']['path'])) {
+ $settings['progress']['url'] = url($settings['progress']['path']);
+ unset($settings['progress']['path']);
+ }
+
+ $element['#attached']['js'][] = array(
+ 'type' => 'setting',
+ 'data' => array('ajax' => array($element['#id'] => $settings)),
+ );
+
+ // Indicate that Ajax processing was successful.
+ $element['#ajax_processed'] = TRUE;
+ }
+ return $element;
+}
+
+/**
+ * @} End of "defgroup ajax".
+ */
+
+/**
+ * @defgroup ajax_commands Ajax framework commands
+ * @{
+ * Functions to create various Ajax commands.
+ *
+ * These functions can be used to create arrays for use with the
+ * ajax_render() function.
+ */
+
+/**
+ * Creates a Drupal Ajax 'alert' command.
+ *
+ * The 'alert' command instructs the client to display a JavaScript alert
+ * dialog box.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.alert()
+ * defined in misc/ajax.js.
+ *
+ * @param $text
+ * The message string to display to the user.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_alert($text) {
+ return array(
+ 'command' => 'alert',
+ 'text' => $text,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert' command using the method in #ajax['method'].
+ *
+ * This command instructs the client to insert the given HTML using whichever
+ * jQuery DOM manipulation method has been specified in the #ajax['method']
+ * variable of the element that triggered the request.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_insert($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => NULL,
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/replaceWith' command.
+ *
+ * The 'insert/replaceWith' command instructs the client to use jQuery's
+ * replaceWith() method to replace each element matched matched by the given
+ * selector with the given HTML.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery replaceWith() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * See @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
+ */
+function ajax_command_replace($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'replaceWith',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/html' command.
+ *
+ * The 'insert/html' command instructs the client to use jQuery's html()
+ * method to set the HTML content of each element matched by the given
+ * selector while leaving the outer tags intact.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery html() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Attributes/html#val
+ */
+function ajax_command_html($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'html',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/prepend' command.
+ *
+ * The 'insert/prepend' command instructs the client to use jQuery's prepend()
+ * method to prepend the given HTML content to the inside each element matched
+ * by the given selector.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery prepend() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Manipulation/prepend#content
+ */
+function ajax_command_prepend($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'prepend',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/append' command.
+ *
+ * The 'insert/append' command instructs the client to use jQuery's append()
+ * method to append the given HTML content to the inside of each element matched
+ * by the given selector.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery append() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Manipulation/append#content
+ */
+function ajax_command_append($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'append',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/after' command.
+ *
+ * The 'insert/after' command instructs the client to use jQuery's after()
+ * method to insert the given HTML content after each element matched by
+ * the given selector.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery after() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Manipulation/after#content
+ */
+function ajax_command_after($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'after',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'insert/before' command.
+ *
+ * The 'insert/before' command instructs the client to use jQuery's before()
+ * method to insert the given HTML content before each of elements matched by
+ * the given selector.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.insert()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $html
+ * The data to use with the jQuery before() method.
+ * @param $settings
+ * An optional array of settings that will be used for this command only.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Manipulation/before#content
+ */
+function ajax_command_before($selector, $html, $settings = NULL) {
+ return array(
+ 'command' => 'insert',
+ 'method' => 'before',
+ 'selector' => $selector,
+ 'data' => $html,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'remove' command.
+ *
+ * The 'remove' command instructs the client to use jQuery's remove() method
+ * to remove each of elements matched by the given selector, and everything
+ * within them.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.remove()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Manipulation/remove#expr
+ */
+function ajax_command_remove($selector) {
+ return array(
+ 'command' => 'remove',
+ 'selector' => $selector,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'changed' command.
+ *
+ * This command instructs the client to mark each of the elements matched by the
+ * given selector as 'ajax-changed'.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.changed()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $asterisk
+ * An optional CSS selector which must be inside $selector. If specified,
+ * an asterisk will be appended to the HTML inside the $asterisk selector.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_changed($selector, $asterisk = '') {
+ return array(
+ 'command' => 'changed',
+ 'selector' => $selector,
+ 'asterisk' => $asterisk,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'css' command.
+ *
+ * The 'css' command will instruct the client to use the jQuery css() method
+ * to apply the CSS arguments to elements matched by the given selector.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.css()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $argument
+ * An array of key/value pairs to set in the CSS for the selector.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/CSS/css#properties
+ */
+function ajax_command_css($selector, $argument) {
+ return array(
+ 'command' => 'css',
+ 'selector' => $selector,
+ 'argument' => $argument,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'settings' command.
+ *
+ * The 'settings' command instructs the client either to use the given array as
+ * the settings for ajax-loaded content or to extend Drupal.settings with the
+ * given array, depending on the value of the $merge parameter.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.settings()
+ * defined in misc/ajax.js.
+ *
+ * @param $argument
+ * An array of key/value pairs to add to the settings. This will be utilized
+ * for all commands after this if they do not include their own settings
+ * array.
+ * @param $merge
+ * Whether or not the passed settings in $argument should be merged into the
+ * global Drupal.settings on the page. By default (FALSE), the settings that
+ * are passed to Drupal.attachBehaviors will not include the global
+ * Drupal.settings.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_settings($argument, $merge = FALSE) {
+ return array(
+ 'command' => 'settings',
+ 'settings' => $argument,
+ 'merge' => $merge,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'data' command.
+ *
+ * The 'data' command instructs the client to attach the name=value pair of
+ * data to the selector via jQuery's data cache.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.data()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $name
+ * The name or key (in the key value pair) of the data attached to this
+ * selector.
+ * @param $value
+ * The value of the data. Not just limited to strings can be any format.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see http://docs.jquery.com/Core/data#namevalue
+ */
+function ajax_command_data($selector, $name, $value) {
+ return array(
+ 'command' => 'data',
+ 'selector' => $selector,
+ 'name' => $name,
+ 'value' => $value,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'invoke' command.
+ *
+ * The 'invoke' command will instruct the client to invoke the given jQuery
+ * method with the supplied arguments on the elements matched by the given
+ * selector. Intended for simple jQuery commands, such as attr(), addClass(),
+ * removeClass(), toggleClass(), etc.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.invoke()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string. If the command is a response to a request from
+ * an #ajax form element then this value can be NULL.
+ * @param $method
+ * The jQuery method to invoke.
+ * @param $arguments
+ * (optional) A list of arguments to the jQuery $method, if any.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_invoke($selector, $method, array $arguments = array()) {
+ return array(
+ 'command' => 'invoke',
+ 'selector' => $selector,
+ 'method' => $method,
+ 'arguments' => $arguments,
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'restripe' command.
+ *
+ * The 'restripe' command instructs the client to restripe a table. This is
+ * usually used after a table has been modified by a replace or append command.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.restripe()
+ * defined in misc/ajax.js.
+ *
+ * @param $selector
+ * A jQuery selector string.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_restripe($selector) {
+ return array(
+ 'command' => 'restripe',
+ 'selector' => $selector,
+ );
+}
+
diff --git a/includes/archiver.inc b/includes/archiver.inc
new file mode 100644
index 0000000..fec053b
--- /dev/null
+++ b/includes/archiver.inc
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Shared classes and interfaces for the archiver system.
+ */
+
+/**
+ * Common interface for all Archiver classes.
+ */
+interface ArchiverInterface {
+
+ /**
+ * Constructor for a new archiver instance.
+ *
+ * @param $file_path
+ * The full system path of the archive to manipulate. Only local files
+ * are supported. If the file does not yet exist, it will be created if
+ * appropriate.
+ */
+ public function __construct($file_path);
+
+ /**
+ * Add the specified file or directory to the archive.
+ *
+ * @param $file_path
+ * The full system path of the file or directory to add. Only local files
+ * and directories are supported.
+ * @return ArchiverInterface
+ * The called object.
+ */
+ public function add($file_path);
+
+ /**
+ * Remove the specified file from the archive.
+ *
+ * @param $path
+ * The file name relative to the root of the archive to remove.
+ * @return ArchiverInterface
+ * The called object.
+ */
+ public function remove($path);
+
+ /**
+ * Extract multiple files in the archive to the specified path.
+ *
+ * @param $path
+ * A full system path of the directory to which to extract files.
+ * @param $files
+ * Optionally specify a list of files to be extracted. Files are
+ * relative to the root of the archive. If not specified, all files
+ * in the archive will be extracted
+ * @return ArchiverInterface
+ * The called object.
+ */
+ public function extract($path, Array $files = array());
+
+ /**
+ * List all files in the archive.
+ *
+ * @return
+ * An array of file names relative to the root of the archive.
+ */
+ public function listContents();
+}
+
diff --git a/includes/authorize.inc b/includes/authorize.inc
new file mode 100644
index 0000000..8528604
--- /dev/null
+++ b/includes/authorize.inc
@@ -0,0 +1,326 @@
+<?php
+
+/**
+ * @file
+ * Helper functions and form handlers used for the authorize.php script.
+ */
+
+/**
+ * Build the form for choosing a FileTransfer type and supplying credentials.
+ */
+function authorize_filetransfer_form($form, &$form_state) {
+ global $base_url, $is_https;
+ $form = array();
+
+ // If possible, we want to post this form securely via https.
+ $form['#https'] = TRUE;
+
+ // CSS we depend on lives in modules/system/maintenance.css, which is loaded
+ // via the default maintenance theme.
+ $form['#attached']['js'][] = $base_url . '/misc/authorize.js';
+
+ // Get all the available ways to transfer files.
+ if (empty($_SESSION['authorize_filetransfer_info'])) {
+ drupal_set_message(t('Unable to continue, no available methods of file transfer'), 'error');
+ return array();
+ }
+ $available_backends = $_SESSION['authorize_filetransfer_info'];
+
+ if (!$is_https) {
+ $form['information']['https_warning'] = array(
+ '#prefix' => '<div class="messages error">',
+ '#markup' => t('WARNING: You are not using an encrypted connection, so your password will be sent in plain text. <a href="@https-link">Learn more</a>.', array('@https-link' => 'http://drupal.org/https-information')),
+ '#suffix' => '</div>',
+ );
+ }
+
+ // Decide on a default backend.
+ if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default'])) {
+ $authorize_filetransfer_default = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
+ }
+ elseif ($authorize_filetransfer_default = variable_get('authorize_filetransfer_default', NULL));
+ else {
+ $authorize_filetransfer_default = key($available_backends);
+ }
+
+ $form['information']['main_header'] = array(
+ '#prefix' => '<h3>',
+ '#markup' => t('To continue, provide your server connection details'),
+ '#suffix' => '</h3>',
+ );
+
+ $form['connection_settings']['#tree'] = TRUE;
+ $form['connection_settings']['authorize_filetransfer_default'] = array(
+ '#type' => 'select',
+ '#title' => t('Connection method'),
+ '#default_value' => $authorize_filetransfer_default,
+ '#weight' => -10,
+ );
+
+ /*
+ * Here we create two submit buttons. For a JS enabled client, they will
+ * only ever see submit_process. However, if a client doesn't have JS
+ * enabled, they will see submit_connection on the first form (when picking
+ * what filetransfer type to use, and submit_process on the second one (which
+ * leads to the actual operation).
+ */
+ $form['submit_connection'] = array(
+ '#prefix' => "<br style='clear:both'/>",
+ '#name' => 'enter_connection_settings',
+ '#type' => 'submit',
+ '#value' => t('Enter connection settings'),
+ '#weight' => 100,
+ );
+
+ $form['submit_process'] = array(
+ '#name' => 'process_updates',
+ '#type' => 'submit',
+ '#value' => t('Continue'),
+ '#weight' => 100,
+ '#attributes' => array('style' => 'display:none'),
+ );
+
+ // Build a container for each connection type.
+ foreach ($available_backends as $name => $backend) {
+ $form['connection_settings']['authorize_filetransfer_default']['#options'][$name] = $backend['title'];
+ $form['connection_settings'][$name] = array(
+ '#type' => 'container',
+ '#attributes' => array('class' => array("filetransfer-$name", 'filetransfer')),
+ );
+ // We can't use #prefix on the container itself since then the header won't
+ // be hidden and shown when the containers are being manipulated via JS.
+ $form['connection_settings'][$name]['header'] = array(
+ '#markup' => '<h4>' . t('@backend connection settings', array('@backend' => $backend['title'])) . '</h4>',
+ );
+
+ $form['connection_settings'][$name] += _authorize_filetransfer_connection_settings($name);
+
+ // Start non-JS code.
+ if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default']) && $form_state['values']['connection_settings']['authorize_filetransfer_default'] == $name) {
+
+ // If the user switches from JS to non-JS, Drupal (and Batch API) will
+ // barf. This is a known bug: http://drupal.org/node/229825.
+ setcookie('has_js', '', time() - 3600, '/');
+ unset($_COOKIE['has_js']);
+
+ // Change the submit button to the submit_process one.
+ $form['submit_process']['#attributes'] = array();
+ unset($form['submit_connection']);
+
+ // Activate the proper filetransfer settings form.
+ $form['connection_settings'][$name]['#attributes']['style'] = 'display:block';
+ // Disable the select box.
+ $form['connection_settings']['authorize_filetransfer_default']['#disabled'] = TRUE;
+
+ // Create a button for changing the type of connection.
+ $form['connection_settings']['change_connection_type'] = array(
+ '#name' => 'change_connection_type',
+ '#type' => 'submit',
+ '#value' => t('Change connection type'),
+ '#weight' => -5,
+ '#attributes' => array('class' => array('filetransfer-change-connection-type')),
+ );
+ }
+ // End non-JS code.
+ }
+ return $form;
+}
+
+/**
+ * Generate the Form API array for the settings for a given connection backend.
+ *
+ * @param $backend
+ * The name of the backend (e.g. 'ftp', 'ssh', etc).
+ * @return
+ * Form API array of connection settings for the given backend.
+ *
+ * @see hook_filetransfer_backends()
+ */
+function _authorize_filetransfer_connection_settings($backend) {
+ $defaults = variable_get('authorize_filetransfer_connection_settings_' . $backend, array());
+ $form = array();
+
+ // Create an instance of the file transfer class to get its settings form.
+ $filetransfer = authorize_get_filetransfer($backend);
+ if ($filetransfer) {
+ $form = $filetransfer->getSettingsForm();
+ }
+ // Fill in the defaults based on the saved settings, if any.
+ _authorize_filetransfer_connection_settings_set_defaults($form, NULL, $defaults);
+ return $form;
+}
+
+/**
+ * Recursively fill in the default settings on a file transfer connection form.
+ *
+ * The default settings for the file transfer connection forms are saved in
+ * the database. The settings are stored as a nested array in the case of a
+ * settings form that has fieldsets or otherwise uses a nested structure.
+ * Therefore, to properly add defaults, we need to walk through all the
+ * children form elements and process those defaults recursively.
+ *
+ * @param $element
+ * Reference to the Form API form element we're operating on.
+ * @param $key
+ * The key for our current form element, if any.
+ * @param array $defaults
+ * The default settings for the file transfer backend we're operating on.
+ * @return
+ * Nothing, this function just sets $element['#default_value'] if needed.
+ */
+function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) {
+ // If we're operating on a form element which isn't a fieldset, and we have
+ // a default setting saved, stash it in #default_value.
+ if (!empty($key) && isset($defaults[$key]) && isset($element['#type']) && $element['#type'] != 'fieldset') {
+ $element['#default_value'] = $defaults[$key];
+ }
+ // Now, we walk through all the child elements, and recursively invoke
+ // ourself on each one. Since the $defaults settings array can be nested
+ // (because of #tree, any values inside fieldsets will be nested), if
+ // there's a subarray of settings for the form key we're currently
+ // processing, pass in that subarray to the recursive call. Otherwise, just
+ // pass on the whole $defaults array.
+ foreach (element_children($element) as $child_key) {
+ _authorize_filetransfer_connection_settings_set_defaults($element[$child_key], $child_key, ((isset($defaults[$key]) && is_array($defaults[$key])) ? $defaults[$key] : $defaults));
+ }
+}
+
+/**
+ * Validate callback for the filetransfer authorization form.
+ *
+ * @see authorize_filetransfer_form()
+ */
+function authorize_filetransfer_form_validate($form, &$form_state) {
+ // Only validate the form if we have collected all of the user input and are
+ // ready to proceed with updating or installing.
+ if ($form_state['triggering_element']['#name'] != 'process_updates') {
+ return;
+ }
+
+ if (isset($form_state['values']['connection_settings'])) {
+ $backend = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
+ $filetransfer = authorize_get_filetransfer($backend, $form_state['values']['connection_settings'][$backend]);
+ try {
+ if (!$filetransfer) {
+ throw new Exception(t('Error, this type of connection protocol (%backend) does not exist.', array('%backend' => $backend)));
+ }
+ $filetransfer->connect();
+ }
+ catch (Exception $e) {
+ // The format of this error message is similar to that used on the
+ // database connection form in the installer.
+ form_set_error('connection_settings', t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array(
+ '!message' => '<p class="error">' . $e->getMessage() . '</p>',
+ '@handbook_url' => 'http://drupal.org/documentation/install/modules-themes',
+ )));
+ }
+ }
+}
+
+/**
+ * Submit callback when a file transfer is being authorized.
+ *
+ * @see authorize_filetransfer_form()
+ */
+function authorize_filetransfer_form_submit($form, &$form_state) {
+ global $base_url;
+ switch ($form_state['triggering_element']['#name']) {
+ case 'process_updates':
+
+ // Save the connection settings to the DB.
+ $filetransfer_backend = $form_state['values']['connection_settings']['authorize_filetransfer_default'];
+
+ // If the database is available then try to save our settings. We have
+ // to make sure it is available since this code could potentially (will
+ // likely) be called during the installation process, before the
+ // database is set up.
+ try {
+ $connection_settings = array();
+ foreach ($form_state['values']['connection_settings'][$filetransfer_backend] as $key => $value) {
+ // We do *not* want to store passwords in the database, unless the
+ // backend explicitly says so via the magic #filetransfer_save form
+ // property. Otherwise, we store everything that's not explicitly
+ // marked with #filetransfer_save set to FALSE.
+ if (!isset($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save'])) {
+ if ($form['connection_settings'][$filetransfer_backend][$key]['#type'] != 'password') {
+ $connection_settings[$key] = $value;
+ }
+ }
+ // The attribute is defined, so only save if set to TRUE.
+ elseif ($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save']) {
+ $connection_settings[$key] = $value;
+ }
+ }
+ // Set this one as the default authorize method.
+ variable_set('authorize_filetransfer_default', $filetransfer_backend);
+ // Save the connection settings minus the password.
+ variable_set('authorize_filetransfer_connection_settings_' . $filetransfer_backend, $connection_settings);
+
+ $filetransfer = authorize_get_filetransfer($filetransfer_backend, $form_state['values']['connection_settings'][$filetransfer_backend]);
+
+ // Now run the operation.
+ authorize_run_operation($filetransfer);
+ }
+ catch (Exception $e) {
+ // If there is no database available, we don't care and just skip
+ // this part entirely.
+ }
+
+ break;
+
+ case 'enter_connection_settings':
+ $form_state['rebuild'] = TRUE;
+ break;
+
+ case 'change_connection_type':
+ $form_state['rebuild'] = TRUE;
+ unset($form_state['values']['connection_settings']['authorize_filetransfer_default']);
+ break;
+ }
+}
+
+/**
+ * Run the operation specified in $_SESSION['authorize_operation']
+ *
+ * @param $filetransfer
+ * The FileTransfer object to use for running the operation.
+ */
+function authorize_run_operation($filetransfer) {
+ $operation = $_SESSION['authorize_operation'];
+ unset($_SESSION['authorize_operation']);
+
+ if (!empty($operation['page_title'])) {
+ drupal_set_title($operation['page_title']);
+ }
+
+ require_once DRUPAL_ROOT . '/' . $operation['file'];
+ call_user_func_array($operation['callback'], array_merge(array($filetransfer), $operation['arguments']));
+}
+
+/**
+ * Get a FileTransfer class for a specific transfer method and settings.
+ *
+ * @param $backend
+ * The FileTransfer backend to get the class for.
+ * @param $settings
+ * Array of settings for the FileTransfer.
+ * @return
+ * An instantiated FileTransfer object for the requested method and settings,
+ * or FALSE if there was an error finding or instantiating it.
+ */
+function authorize_get_filetransfer($backend, $settings = array()) {
+ $filetransfer = FALSE;
+ if (!empty($_SESSION['authorize_filetransfer_info'][$backend])) {
+ $backend_info = $_SESSION['authorize_filetransfer_info'][$backend];
+ if (!empty($backend_info['file'])) {
+ $file = $backend_info['file path'] . '/' . $backend_info['file'];
+ require_once $file;
+ }
+ if (class_exists($backend_info['class'])) {
+ // PHP 5.2 doesn't support $class::factory() syntax, so we have to
+ // use call_user_func_array() until we can require PHP 5.3.
+ $filetransfer = call_user_func_array(array($backend_info['class'], 'factory'), array(DRUPAL_ROOT, $settings));
+ }
+ }
+ return $filetransfer;
+}
diff --git a/includes/batch.inc b/includes/batch.inc
new file mode 100644
index 0000000..727c625
--- /dev/null
+++ b/includes/batch.inc
@@ -0,0 +1,534 @@
+<?php
+
+
+/**
+ * @file
+ * Batch processing API for processes to run in multiple HTTP requests.
+ *
+ * Note that batches are usually invoked by form submissions, which is
+ * why the core interaction functions of the batch processing API live in
+ * form.inc.
+ *
+ * @see form.inc
+ * @see batch_set()
+ * @see batch_process()
+ * @see batch_get()
+ */
+
+/**
+ * Loads a batch from the database.
+ *
+ * @param $id
+ * The ID of the batch to load. When a progressive batch is being processed,
+ * the relevant ID is found in $_REQUEST['id'].
+ * @return
+ * An array representing the batch, or FALSE if no batch was found.
+ */
+function batch_load($id) {
+ $batch = db_query("SELECT batch FROM {batch} WHERE bid = :bid AND token = :token", array(
+ ':bid' => $id,
+ ':token' => drupal_get_token($id),
+ ))->fetchField();
+ if ($batch) {
+ return unserialize($batch);
+ }
+ return FALSE;
+}
+
+/**
+ * State-based dispatcher for the batch processing page.
+ *
+ * @see _batch_shutdown()
+ */
+function _batch_page() {
+ $batch = &batch_get();
+
+ if (!isset($_REQUEST['id'])) {
+ return FALSE;
+ }
+
+ // Retrieve the current state of the batch.
+ if (!$batch) {
+ $batch = batch_load($_REQUEST['id']);
+ if (!$batch) {
+ drupal_set_message(t('No active batch.'), 'error');
+ drupal_goto();
+ }
+ }
+
+ // Register database update for the end of processing.
+ drupal_register_shutdown_function('_batch_shutdown');
+
+ // Add batch-specific CSS.
+ foreach ($batch['sets'] as $batch_set) {
+ if (isset($batch_set['css'])) {
+ foreach ($batch_set['css'] as $css) {
+ drupal_add_css($css);
+ }
+ }
+ }
+
+ $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
+ $output = NULL;
+ switch ($op) {
+ case 'start':
+ $output = _batch_start();
+ break;
+
+ case 'do':
+ // JavaScript-based progress page callback.
+ _batch_do();
+ break;
+
+ case 'do_nojs':
+ // Non-JavaScript-based progress page.
+ $output = _batch_progress_page_nojs();
+ break;
+
+ case 'finished':
+ $output = _batch_finished();
+ break;
+ }
+
+ return $output;
+}
+
+/**
+ * Initialize the batch processing.
+ *
+ * JavaScript-enabled clients are identified by the 'has_js' cookie set in
+ * drupal.js. If no JavaScript-enabled page has been visited during the current
+ * user's browser session, the non-JavaScript version is returned.
+ */
+function _batch_start() {
+ if (isset($_COOKIE['has_js']) && $_COOKIE['has_js']) {
+ return _batch_progress_page_js();
+ }
+ else {
+ return _batch_progress_page_nojs();
+ }
+}
+
+/**
+ * Output a batch processing page with JavaScript support.
+ *
+ * This initializes the batch and error messages. Note that in JavaScript-based
+ * processing, the batch processing page is displayed only once and updated via
+ * AHAH requests, so only the first batch set gets to define the page title.
+ * Titles specified by subsequent batch sets are not displayed.
+ *
+ * @see batch_set()
+ * @see _batch_do()
+ */
+function _batch_progress_page_js() {
+ $batch = batch_get();
+
+ $current_set = _batch_current_set();
+ drupal_set_title($current_set['title'], PASS_THROUGH);
+
+ // Merge required query parameters for batch processing into those provided by
+ // batch_set() or hook_batch_alter().
+ $batch['url_options']['query']['id'] = $batch['id'];
+
+ $js_setting = array(
+ 'batch' => array(
+ 'errorMessage' => $current_set['error_message'] . '<br />' . $batch['error_message'],
+ 'initMessage' => $current_set['init_message'],
+ 'uri' => url($batch['url'], $batch['url_options']),
+ ),
+ );
+ drupal_add_js($js_setting, 'setting');
+ drupal_add_library('system', 'drupal.batch');
+
+ return '<div id="progress"></div>';
+}
+
+/**
+ * Do one execution pass in JavaScript-mode and return progress to the browser.
+ *
+ * @see _batch_progress_page_js()
+ * @see _batch_process()
+ */
+function _batch_do() {
+ // HTTP POST required.
+ if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+ drupal_set_message(t('HTTP POST is required.'), 'error');
+ drupal_set_title(t('Error'));
+ return '';
+ }
+
+ // Perform actual processing.
+ list($percentage, $message) = _batch_process();
+
+ drupal_json_output(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message));
+}
+
+/**
+ * Output a batch processing page without JavaScript support.
+ *
+ * @see _batch_process()
+ */
+function _batch_progress_page_nojs() {
+ $batch = &batch_get();
+
+ $current_set = _batch_current_set();
+ drupal_set_title($current_set['title'], PASS_THROUGH);
+
+ $new_op = 'do_nojs';
+
+ if (!isset($batch['running'])) {
+ // This is the first page so we return some output immediately.
+ $percentage = 0;
+ $message = $current_set['init_message'];
+ $batch['running'] = TRUE;
+ }
+ else {
+ // This is one of the later requests; do some processing first.
+
+ // Error handling: if PHP dies due to a fatal error (e.g. a nonexistent
+ // function), it will output whatever is in the output buffer, followed by
+ // the error message.
+ ob_start();
+ $fallback = $current_set['error_message'] . '<br />' . $batch['error_message'];
+ $fallback = theme('maintenance_page', array('content' => $fallback, 'show_messages' => FALSE));
+
+ // We strip the end of the page using a marker in the template, so any
+ // additional HTML output by PHP shows up inside the page rather than below
+ // it. While this causes invalid HTML, the same would be true if we didn't,
+ // as content is not allowed to appear after </html> anyway.
+ list($fallback) = explode('<!--partial-->', $fallback);
+ print $fallback;
+
+ // Perform actual processing.
+ list($percentage, $message) = _batch_process($batch);
+ if ($percentage == 100) {
+ $new_op = 'finished';
+ }
+
+ // PHP did not die; remove the fallback output.
+ ob_end_clean();
+ }
+
+ // Merge required query parameters for batch processing into those provided by
+ // batch_set() or hook_batch_alter().
+ $batch['url_options']['query']['id'] = $batch['id'];
+ $batch['url_options']['query']['op'] = $new_op;
+
+ $url = url($batch['url'], $batch['url_options']);
+ $element = array(
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'http-equiv' => 'Refresh',
+ 'content' => '0; URL=' . $url,
+ ),
+ );
+ drupal_add_html_head($element, 'batch_progress_meta_refresh');
+
+ return theme('progress_bar', array('percent' => $percentage, 'message' => $message));
+}
+
+/**
+ * Process sets in a batch.
+ *
+ * If the batch was marked for progressive execution (default), this executes as
+ * many operations in batch sets until an execution time of 1 second has been
+ * exceeded. It will continue with the next operation of the same batch set in
+ * the next request.
+ *
+ * @return
+ * An array containing a completion value (in percent) and a status message.
+ */
+function _batch_process() {
+ $batch = &batch_get();
+ $current_set = &_batch_current_set();
+ // Indicate that this batch set needs to be initialized.
+ $set_changed = TRUE;
+
+ // If this batch was marked for progressive execution (e.g. forms submitted by
+ // drupal_form_submit()), initialize a timer to determine whether we need to
+ // proceed with the same batch phase when a processing time of 1 second has
+ // been exceeded.
+ if ($batch['progressive']) {
+ timer_start('batch_processing');
+ }
+
+ if (empty($current_set['start'])) {
+ $current_set['start'] = microtime(TRUE);
+ }
+
+ $queue = _batch_queue($current_set);
+
+ while (!$current_set['success']) {
+ // If this is the first time we iterate this batch set in the current
+ // request, we check if it requires an additional file for functions
+ // definitions.
+ if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) {
+ include_once DRUPAL_ROOT . '/' . $current_set['file'];
+ }
+
+ $task_message = '';
+ // Assume a single pass operation and set the completion level to 1 by
+ // default.
+ $finished = 1;
+
+ if ($item = $queue->claimItem()) {
+ list($function, $args) = $item->data;
+
+ // Build the 'context' array and execute the function call.
+ $batch_context = array(
+ 'sandbox' => &$current_set['sandbox'],
+ 'results' => &$current_set['results'],
+ 'finished' => &$finished,
+ 'message' => &$task_message,
+ );
+ call_user_func_array($function, array_merge($args, array(&$batch_context)));
+
+ if ($finished >= 1) {
+ // Make sure this step is not counted twice when computing $current.
+ $finished = 0;
+ // Remove the processed operation and clear the sandbox.
+ $queue->deleteItem($item);
+ $current_set['count']--;
+ $current_set['sandbox'] = array();
+ }
+ }
+
+ // When all operations in the current batch set are completed, browse
+ // through the remaining sets, marking them 'successfully processed'
+ // along the way, until we find a set that contains operations.
+ // _batch_next_set() executes form submit handlers stored in 'control'
+ // sets (see form_execute_handlers()), which can in turn add new sets to
+ // the batch.
+ $set_changed = FALSE;
+ $old_set = $current_set;
+ while (empty($current_set['count']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
+ $current_set = &_batch_current_set();
+ $current_set['start'] = microtime(TRUE);
+ $set_changed = TRUE;
+ }
+
+ // At this point, either $current_set contains operations that need to be
+ // processed or all sets have been completed.
+ $queue = _batch_queue($current_set);
+
+ // If we are in progressive mode, break processing after 1 second.
+ if ($batch['progressive'] && timer_read('batch_processing') > 1000) {
+ // Record elapsed wall clock time.
+ $current_set['elapsed'] = round((microtime(TRUE) - $current_set['start']) * 1000, 2);
+ break;
+ }
+ }
+
+ if ($batch['progressive']) {
+ // Gather progress information.
+
+ // Reporting 100% progress will cause the whole batch to be considered
+ // processed. If processing was paused right after moving to a new set,
+ // we have to use the info from the new (unprocessed) set.
+ if ($set_changed && isset($current_set['queue'])) {
+ // Processing will continue with a fresh batch set.
+ $remaining = $current_set['count'];
+ $total = $current_set['total'];
+ $progress_message = $current_set['init_message'];
+ $task_message = '';
+ }
+ else {
+ // Processing will continue with the current batch set.
+ $remaining = $old_set['count'];
+ $total = $old_set['total'];
+ $progress_message = $old_set['progress_message'];
+ }
+
+ // Total progress is the number of operations that have fully run plus the
+ // completion level of the current operation.
+ $current = $total - $remaining + $finished;
+ $percentage = _batch_api_percentage($total, $current);
+ $elapsed = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0;
+ $values = array(
+ '@remaining' => $remaining,
+ '@total' => $total,
+ '@current' => floor($current),
+ '@percentage' => $percentage,
+ '@elapsed' => format_interval($elapsed / 1000),
+ // If possible, estimate remaining processing time.
+ '@estimate' => ($current > 0) ? format_interval(($elapsed * ($total - $current) / $current) / 1000) : '-',
+ );
+ $message = strtr($progress_message, $values);
+ if (!empty($message)) {
+ $message .= '<br />';
+ }
+ if (!empty($task_message)) {
+ $message .= $task_message;
+ }
+
+ return array($percentage, $message);
+ }
+ else {
+ // If we are not in progressive mode, the entire batch has been processed.
+ return _batch_finished();
+ }
+}
+
+/**
+ * Helper function for _batch_process(): returns the formatted percentage.
+ *
+ * @param $total
+ * The total number of operations.
+ * @param $current
+ * The number of the current operation. This may be a floating point number
+ * rather than an integer in the case of a multi-step operation that is not
+ * yet complete; in that case, the fractional part of $current represents the
+ * fraction of the operation that has been completed.
+ * @return
+ * The properly formatted percentage, as a string. We output percentages
+ * using the correct number of decimal places so that we never print "100%"
+ * until we are finished, but we also never print more decimal places than
+ * are meaningful.
+ */
+function _batch_api_percentage($total, $current) {
+ if (!$total || $total == $current) {
+ // If $total doesn't evaluate as true or is equal to the current set, then
+ // we're finished, and we can return "100".
+ $percentage = "100";
+ }
+ else {
+ // We add a new digit at 200, 2000, etc. (since, for example, 199/200
+ // would round up to 100% if we didn't).
+ $decimal_places = max(0, floor(log10($total / 2.0)) - 1);
+ do {
+ // Calculate the percentage to the specified number of decimal places.
+ $percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places));
+ // When $current is an integer, the above calculation will always be
+ // correct. However, if $current is a floating point number (in the case
+ // of a multi-step batch operation that is not yet complete), $percentage
+ // may be erroneously rounded up to 100%. To prevent that, we add one
+ // more decimal place and try again.
+ $decimal_places++;
+ } while ($percentage == '100');
+ }
+ return $percentage;
+}
+
+/**
+ * Return the batch set being currently processed.
+ */
+function &_batch_current_set() {
+ $batch = &batch_get();
+ return $batch['sets'][$batch['current_set']];
+}
+
+/**
+ * Retrieve the next set in a batch.
+ *
+ * If there is a subsequent set in this batch, assign it as the new set to
+ * process and execute its form submit handler (if defined), which may add
+ * further sets to this batch.
+ *
+ * @return
+ * TRUE if a subsequent set was found in the batch.
+ */
+function _batch_next_set() {
+ $batch = &batch_get();
+ if (isset($batch['sets'][$batch['current_set'] + 1])) {
+ $batch['current_set']++;
+ $current_set = &_batch_current_set();
+ if (isset($current_set['form_submit']) && ($function = $current_set['form_submit']) && function_exists($function)) {
+ // We use our stored copies of $form and $form_state to account for
+ // possible alterations by previous form submit handlers.
+ $function($batch['form_state']['complete form'], $batch['form_state']);
+ }
+ return TRUE;
+ }
+}
+
+/**
+ * End the batch processing.
+ *
+ * Call the 'finished' callback of each batch set to allow custom handling of
+ * the results and resolve page redirection.
+ */
+function _batch_finished() {
+ $batch = &batch_get();
+
+ // Execute the 'finished' callbacks for each batch set, if defined.
+ foreach ($batch['sets'] as $batch_set) {
+ if (isset($batch_set['finished'])) {
+ // Check if the set requires an additional file for function definitions.
+ if (isset($batch_set['file']) && is_file($batch_set['file'])) {
+ include_once DRUPAL_ROOT . '/' . $batch_set['file'];
+ }
+ if (function_exists($batch_set['finished'])) {
+ $queue = _batch_queue($batch_set);
+ $operations = $queue->getAllItems();
+ $batch_set['finished']($batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
+ }
+ }
+ }
+
+ // Clean up the batch table and unset the static $batch variable.
+ if ($batch['progressive']) {
+ db_delete('batch')
+ ->condition('bid', $batch['id'])
+ ->execute();
+ foreach ($batch['sets'] as $batch_set) {
+ if ($queue = _batch_queue($batch_set)) {
+ $queue->deleteQueue();
+ }
+ }
+ }
+ $_batch = $batch;
+ $batch = NULL;
+
+ // Clean-up the session. Not needed for CLI updates.
+ if (isset($_SESSION)) {
+ unset($_SESSION['batches'][$batch['id']]);
+ if (empty($_SESSION['batches'])) {
+ unset($_SESSION['batches']);
+ }
+ }
+
+ // Redirect if needed.
+ if ($_batch['progressive']) {
+ // Revert the 'destination' that was saved in batch_process().
+ if (isset($_batch['destination'])) {
+ $_GET['destination'] = $_batch['destination'];
+ }
+
+ // Determine the target path to redirect to.
+ if (!isset($_batch['form_state']['redirect'])) {
+ if (isset($_batch['redirect'])) {
+ $_batch['form_state']['redirect'] = $_batch['redirect'];
+ }
+ else {
+ $_batch['form_state']['redirect'] = $_batch['source_url'];
+ }
+ }
+
+ // Use drupal_redirect_form() to handle the redirection logic.
+ drupal_redirect_form($_batch['form_state']);
+
+ // If no redirection happened, redirect to the originating page. In case the
+ // form needs to be rebuilt, save the final $form_state for
+ // drupal_build_form().
+ if (!empty($_batch['form_state']['rebuild'])) {
+ $_SESSION['batch_form_state'] = $_batch['form_state'];
+ }
+ $function = $_batch['redirect_callback'];
+ if (function_exists($function)) {
+ $function($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id'])));
+ }
+ }
+}
+
+/**
+ * Shutdown function; store the current batch data for the next request.
+ */
+function _batch_shutdown() {
+ if ($batch = batch_get()) {
+ db_update('batch')
+ ->fields(array('batch' => serialize($batch)))
+ ->condition('bid', $batch['id'])
+ ->execute();
+ }
+}
+
diff --git a/includes/batch.queue.inc b/includes/batch.queue.inc
new file mode 100644
index 0000000..8464836
--- /dev/null
+++ b/includes/batch.queue.inc
@@ -0,0 +1,71 @@
+<?php
+
+
+/**
+ * @file
+ * Queue handlers used by the Batch API.
+ *
+ * Those implementations:
+ * - ensure FIFO ordering,
+ * - let an item be repeatedly claimed until it is actually deleted (no notion
+ * of lease time or 'expire' date), to allow multipass operations.
+ */
+
+/**
+ * Batch queue implementation.
+ *
+ * Stale items from failed batches are cleaned from the {queue} table on cron
+ * using the 'created' date.
+ */
+class BatchQueue extends SystemQueue {
+
+ public function claimItem($lease_time = 0) {
+ $item = db_query_range('SELECT data, item_id FROM {queue} q WHERE name = :name ORDER BY item_id ASC', 0, 1, array(':name' => $this->name))->fetchObject();
+ if ($item) {
+ $item->data = unserialize($item->data);
+ return $item;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Retrieve all remaining items in the queue.
+ *
+ * This is specific to Batch API and is not part of the DrupalQueueInterface,
+ */
+ public function getAllItems() {
+ $result = array();
+ $items = db_query('SELECT data FROM {queue} q WHERE name = :name ORDER BY item_id ASC', array(':name' => $this->name))->fetchAll();
+ foreach ($items as $item) {
+ $result[] = unserialize($item->data);
+ }
+ return $result;
+ }
+}
+
+/**
+ * Batch queue implementation used for non-progressive batches.
+ */
+class BatchMemoryQueue extends MemoryQueue {
+
+ public function claimItem($lease_time = 0) {
+ if (!empty($this->queue)) {
+ reset($this->queue);
+ return current($this->queue);
+ }
+ return FALSE;
+ }
+
+ /**
+ * Retrieve all remaining items in the queue.
+ *
+ * This is specific to Batch API and is not part of the DrupalQueueInterface,
+ */
+ public function getAllItems() {
+ $result = array();
+ foreach ($this->queue as $item) {
+ $result[] = $item->data;
+ }
+ return $result;
+ }
+}
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
new file mode 100644
index 0000000..147aacc
--- /dev/null
+++ b/includes/bootstrap.inc
@@ -0,0 +1,3276 @@
+<?php
+
+/**
+ * @file
+ * Functions that need to be loaded on every Drupal request.
+ */
+
+/**
+ * The current system version.
+ */
+define('VERSION', '7.9');
+
+/**
+ * Core API compatibility.
+ */
+define('DRUPAL_CORE_COMPATIBILITY', '7.x');
+
+/**
+ * Minimum supported version of PHP.
+ */
+define('DRUPAL_MINIMUM_PHP', '5.2.4');
+
+/**
+ * Minimum recommended value of PHP memory_limit.
+ */
+define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M');
+
+/**
+ * Indicates that the item should never be removed unless explicitly selected.
+ *
+ * The item may be removed using cache_clear_all() with a cache ID.
+ */
+define('CACHE_PERMANENT', 0);
+
+/**
+ * Indicates that the item should be removed at the next general cache wipe.
+ */
+define('CACHE_TEMPORARY', -1);
+
+/**
+ * @defgroup logging_severity_levels Logging severity levels
+ * @{
+ * Logging severity levels as defined in RFC 3164.
+ *
+ * The WATCHDOG_* constant definitions correspond to the logging severity levels
+ * defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
+ * for use in the syslog() function, but their values on Windows builds do not
+ * correspond to RFC 3164. The associated PHP bug report was closed with the
+ * comment, "And it's also not a bug, as Windows just have less log levels,"
+ * and "So the behavior you're seeing is perfectly normal."
+ *
+ * @see http://www.faqs.org/rfcs/rfc3164.html
+ * @see http://bugs.php.net/bug.php?id=18090
+ * @see http://php.net/manual/function.syslog.php
+ * @see http://php.net/manual/network.constants.php
+ * @see watchdog()
+ * @see watchdog_severity_levels()
+ */
+
+/**
+ * Log message severity -- Emergency: system is unusable.
+ */
+define('WATCHDOG_EMERGENCY', 0);
+
+/**
+ * Log message severity -- Alert: action must be taken immediately.
+ */
+define('WATCHDOG_ALERT', 1);
+
+/**
+ * Log message severity -- Critical: critical conditions.
+ */
+define('WATCHDOG_CRITICAL', 2);
+
+/**
+ * Log message severity -- Error: error conditions.
+ */
+define('WATCHDOG_ERROR', 3);
+
+/**
+ * Log message severity -- Warning: warning conditions.
+ */
+define('WATCHDOG_WARNING', 4);
+
+/**
+ * Log message severity -- Notice: normal but significant condition.
+ */
+define('WATCHDOG_NOTICE', 5);
+
+/**
+ * Log message severity -- Informational: informational messages.
+ */
+define('WATCHDOG_INFO', 6);
+
+/**
+ * Log message severity -- Debug: debug-level messages.
+ */
+define('WATCHDOG_DEBUG', 7);
+
+/**
+ * @} End of "defgroup logging_severity_levels".
+ */
+
+/**
+ * First bootstrap phase: initialize configuration.
+ */
+define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
+
+/**
+ * Second bootstrap phase: try to serve a cached page.
+ */
+define('DRUPAL_BOOTSTRAP_PAGE_CACHE', 1);
+
+/**
+ * Third bootstrap phase: initialize database layer.
+ */
+define('DRUPAL_BOOTSTRAP_DATABASE', 2);
+
+/**
+ * Fourth bootstrap phase: initialize the variable system.
+ */
+define('DRUPAL_BOOTSTRAP_VARIABLES', 3);
+
+/**
+ * Fifth bootstrap phase: initialize session handling.
+ */
+define('DRUPAL_BOOTSTRAP_SESSION', 4);
+
+/**
+ * Sixth bootstrap phase: set up the page header.
+ */
+define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 5);
+
+/**
+ * Seventh bootstrap phase: find out language of the page.
+ */
+define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
+
+/**
+ * Final bootstrap phase: Drupal is fully loaded; validate and fix
+ * input data.
+ */
+define('DRUPAL_BOOTSTRAP_FULL', 7);
+
+/**
+ * Role ID for anonymous users; should match what's in the "role" table.
+ */
+define('DRUPAL_ANONYMOUS_RID', 1);
+
+/**
+ * Role ID for authenticated users; should match what's in the "role" table.
+ */
+define('DRUPAL_AUTHENTICATED_RID', 2);
+
+/**
+ * The number of bytes in a kilobyte. For more information, visit
+ * http://en.wikipedia.org/wiki/Kilobyte.
+ */
+define('DRUPAL_KILOBYTE', 1024);
+
+/**
+ * The language code used when no language is explicitly assigned.
+ *
+ * Defined by ISO639-2 for "Undetermined".
+ */
+define('LANGUAGE_NONE', 'und');
+
+/**
+ * The type of language used to define the content language.
+ */
+define('LANGUAGE_TYPE_CONTENT', 'language_content');
+
+/**
+ * The type of language used to select the user interface.
+ */
+define('LANGUAGE_TYPE_INTERFACE', 'language');
+
+/**
+ * The type of language used for URLs.
+ */
+define('LANGUAGE_TYPE_URL', 'language_url');
+
+/**
+ * Language written left to right. Possible value of $language->direction.
+ */
+define('LANGUAGE_LTR', 0);
+
+/**
+ * Language written right to left. Possible value of $language->direction.
+ */
+define('LANGUAGE_RTL', 1);
+
+/**
+ * For convenience, define a short form of the request time global.
+ *
+ * REQUEST_TIME is a float with microseconds since PHP 5.4.0, but float
+ * timestamps confuses most of the PHP functions (including date_create()).
+ */
+define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
+
+/**
+ * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
+ */
+define('CHECK_PLAIN', 0);
+
+/**
+ * Flag for drupal_set_title(); text has already been sanitized.
+ */
+define('PASS_THROUGH', -1);
+
+/**
+ * Signals that the registry lookup cache should be reset.
+ */
+define('REGISTRY_RESET_LOOKUP_CACHE', 1);
+
+/**
+ * Signals that the registry lookup cache should be written to storage.
+ */
+define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
+
+/**
+ * Regular expression to match PHP function names.
+ *
+ * @see http://php.net/manual/en/language.functions.php
+ */
+define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
+
+/**
+ * Provides a caching wrapper to be used in place of large array structures.
+ *
+ * This class should be extended by systems that need to cache large amounts
+ * of data and have it represented as an array to calling functions. These
+ * arrays can become very large, so ArrayAccess is used to allow different
+ * strategies to be used for caching internally (lazy loading, building caches
+ * over time etc.). This can dramatically reduce the amount of data that needs
+ * to be loaded from cache backends on each request, and memory usage from
+ * static caches of that same data.
+ *
+ * Note that array_* functions do not work with ArrayAccess. Systems using
+ * DrupalCacheArray should use this only internally. If providing API functions
+ * that return the full array, this can be cached separately or returned
+ * directly. However since DrupalCacheArray holds partial content by design, it
+ * should be a normal PHP array or otherwise contain the full structure.
+ *
+ * Note also that due to limitations in PHP prior to 5.3.4, it is impossible to
+ * write directly to the contents of nested arrays contained in this object.
+ * Only writes to the top-level array elements are possible. So if you
+ * previously had set $object['foo'] = array(1, 2, 'bar' => 'baz'), but later
+ * want to change the value of 'bar' from 'baz' to 'foobar', you cannot do so
+ * a targeted write like $object['foo']['bar'] = 'foobar'. Instead, you must
+ * overwrite the entire top-level 'foo' array with the entire set of new
+ * values: $object['foo'] = array(1, 2, 'bar' => 'foobar'). Due to this same
+ * limitation, attempts to create references to any contained data, nested or
+ * otherwise, will fail silently. So $var = &$object['foo'] will not throw an
+ * error, and $var will be populated with the contents of $object['foo'], but
+ * that data will be passed by value, not reference. For more information on
+ * the PHP limitation, see the note in the official PHP documentation at·
+ * http://php.net/manual/en/arrayaccess.offsetget.php on
+ * ArrayAccess::offsetGet().
+ *
+ * By default, the class accounts for caches where calling functions might
+ * request keys in the array that won't exist even after a cache rebuild. This
+ * prevents situations where a cache rebuild would be triggered over and over
+ * due to a 'missing' item. These cases are stored internally as a value of
+ * NULL. This means that the offsetGet() and offsetExists() methods
+ * must be overridden if caching an array where the top level values can
+ * legitimately be NULL, and where $object->offsetExists() needs to correctly
+ * return (equivalent to array_key_exists() vs. isset()). This should not
+ * be necessary in the majority of cases.
+ *
+ * Classes extending this class must override at least the
+ * resolveCacheMiss() method to have a working implementation.
+ *
+ * offsetSet() is not overridden by this class by default. In practice this
+ * means that assigning an offset via arrayAccess will only apply while the
+ * object is in scope and will not be written back to the persistent cache.
+ * This follows a similar pattern to static vs. persistent caching in
+ * procedural code. Extending classes may wish to alter this behaviour, for
+ * example by overriding offsetSet() and adding an automatic call to persist().
+ *
+ * @see SchemaCache
+ */
+abstract class DrupalCacheArray implements ArrayAccess {
+
+ /**
+ * A cid to pass to cache_set() and cache_get().
+ */
+ private $cid;
+
+ /**
+ * A bin to pass to cache_set() and cache_get().
+ */
+ private $bin;
+
+ /**
+ * An array of keys to add to the cache at the end of the request.
+ */
+ protected $keysToPersist = array();
+
+ /**
+ * Storage for the data itself.
+ */
+ protected $storage = array();
+
+ /**
+ * Constructor.
+ *
+ * @param $cid
+ * The cid for the array being cached.
+ * @param $bin
+ * The bin to cache the array.
+ */
+ public function __construct($cid, $bin) {
+ $this->cid = $cid;
+ $this->bin = $bin;
+
+ if ($cached = cache_get($this->cid, $this->bin)) {
+ $this->storage = $cached->data;
+ }
+ }
+
+ public function offsetExists($offset) {
+ return $this->offsetGet($offset) !== NULL;
+ }
+
+ public function offsetGet($offset) {
+ if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) {
+ return $this->storage[$offset];
+ }
+ else {
+ return $this->resolveCacheMiss($offset);
+ }
+ }
+
+ public function offsetSet($offset, $value) {
+ $this->storage[$offset] = $value;
+ }
+
+ public function offsetUnset($offset) {
+ unset($this->storage[$offset]);
+ }
+
+ /**
+ * Flags an offset value to be written to the persistent cache.
+ *
+ * If a value is assigned to a cache object with offsetSet(), by default it
+ * will not be written to the persistent cache unless it is flagged with this
+ * method. This allows items to be cached for the duration of a request,
+ * without necessarily writing back to the persistent cache at the end.
+ *
+ * @param $offset
+ * The array offset that was request.
+ * @param $persist
+ * Optional boolean to specify whether the offset should be persisted or
+ * not, defaults to TRUE. When called with $persist = FALSE the offset will
+ * be unflagged so that it will not written at the end of the request.
+ */
+ protected function persist($offset, $persist = TRUE) {
+ $this->keysToPersist[$offset] = $persist;
+ }
+
+ /**
+ * Resolves a cache miss.
+ *
+ * When an offset is not found in the object, this is treated as a cache
+ * miss. This method allows classes implementing the interface to look up
+ * the actual value and allow it to be cached.
+ *
+ * @param $offset
+ * The offset that was requested.
+ *
+ * @return
+ * The value of the offset, or NULL if no value was found.
+ */
+ abstract protected function resolveCacheMiss($offset);
+
+ /**
+ * Immediately write a value to the persistent cache.
+ *
+ * @param $cid
+ * The cache ID.
+ * @param $bin
+ * The cache bin.
+ * @param $data
+ * The data to write to the persistent cache.
+ * @param $lock
+ * Whether to acquire a lock before writing to cache.
+ */
+ protected function set($cid, $data, $bin, $lock = TRUE) {
+ // Lock cache writes to help avoid stampedes.
+ // To implement locking for cache misses, override __construct().
+ $lock_name = $cid . ':' . $bin;
+ if (!$lock || lock_acquire($lock_name)) {
+ if ($cached = cache_get($cid, $bin)) {
+ $data = $cached->data + $data;
+ }
+ cache_set($cid, $data, $bin);
+ if ($lock) {
+ lock_release($lock_name);
+ }
+ }
+ }
+
+ public function __destruct() {
+ $data = array();
+ foreach ($this->keysToPersist as $offset => $persist) {
+ if ($persist) {
+ $data[$offset] = $this->storage[$offset];
+ }
+ }
+ if (!empty($data)) {
+ $this->set($this->cid, $data, $this->bin);
+ }
+ }
+}
+
+/**
+ * Start the timer with the specified name. If you start and stop the same
+ * timer multiple times, the measured intervals will be accumulated.
+ *
+ * @param $name
+ * The name of the timer.
+ */
+function timer_start($name) {
+ global $timers;
+
+ $timers[$name]['start'] = microtime(TRUE);
+ $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
+}
+
+/**
+ * Read the current timer value without stopping the timer.
+ *
+ * @param $name
+ * The name of the timer.
+ *
+ * @return
+ * The current timer value in ms.
+ */
+function timer_read($name) {
+ global $timers;
+
+ if (isset($timers[$name]['start'])) {
+ $stop = microtime(TRUE);
+ $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
+
+ if (isset($timers[$name]['time'])) {
+ $diff += $timers[$name]['time'];
+ }
+ return $diff;
+ }
+ return $timers[$name]['time'];
+}
+
+/**
+ * Stop the timer with the specified name.
+ *
+ * @param $name
+ * The name of the timer.
+ *
+ * @return
+ * A timer array. The array contains the number of times the timer has been
+ * started and stopped (count) and the accumulated timer value in ms (time).
+ */
+function timer_stop($name) {
+ global $timers;
+
+ if (isset($timers[$name]['start'])) {
+ $stop = microtime(TRUE);
+ $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
+ if (isset($timers[$name]['time'])) {
+ $timers[$name]['time'] += $diff;
+ }
+ else {
+ $timers[$name]['time'] = $diff;
+ }
+ unset($timers[$name]['start']);
+ }
+
+ return $timers[$name];
+}
+
+/**
+ * Finds the appropriate configuration directory.
+ *
+ * Finds a matching configuration directory by stripping the website's
+ * hostname from left to right and pathname from right to left. The first
+ * configuration file found will be used and the remaining ones will be ignored.
+ * If no configuration file is found, return a default value '$confdir/default'.
+ *
+ * With a site located at http://www.example.com:8080/mysite/test/, the file,
+ * settings.php, is searched for in the following directories:
+ *
+ * - $confdir/8080.www.example.com.mysite.test
+ * - $confdir/www.example.com.mysite.test
+ * - $confdir/example.com.mysite.test
+ * - $confdir/com.mysite.test
+ *
+ * - $confdir/8080.www.example.com.mysite
+ * - $confdir/www.example.com.mysite
+ * - $confdir/example.com.mysite
+ * - $confdir/com.mysite
+ *
+ * - $confdir/8080.www.example.com
+ * - $confdir/www.example.com
+ * - $confdir/example.com
+ * - $confdir/com
+ *
+ * - $confdir/default
+ *
+ * If a file named sites.php is present in the $confdir, it will be loaded
+ * prior to scanning for directories. It should define an associative array
+ * named $sites, which maps domains to directories. It should be in the form
+ * of:
+ * @code
+ * $sites = array(
+ * 'The url to alias' => 'A directory within the sites directory'
+ * );
+ * @endcode
+ * For example:
+ * @code
+ * $sites = array(
+ * 'devexample.com' => 'example.com',
+ * 'localhost.example' => 'example.com',
+ * );
+ * @endcode
+ * The above array will cause Drupal to look for a directory named
+ * "example.com" in the sites directory whenever a request comes from
+ * "example.com", "devexample.com", or "localhost/example". That is useful
+ * on development servers, where the domain name may not be the same as the
+ * domain of the live server. Since Drupal stores file paths into the database
+ * (files, system table, etc.) this will ensure the paths are correct while
+ * accessed on development servers.
+ *
+ * @param bool $require_settings
+ * Only configuration directories with an existing settings.php file
+ * will be recognized. Defaults to TRUE. During initial installation,
+ * this is set to FALSE so that Drupal can detect a matching directory,
+ * then create a new settings.php file in it.
+ * @param bool $reset
+ * Force a full search for matching directories even if one had been
+ * found previously. Defaults to FALSE.
+ *
+ * @return
+ * The path of the matching directory.
+ */
+function conf_path($require_settings = TRUE, $reset = FALSE) {
+ $conf = &drupal_static(__FUNCTION__, '');
+
+ if ($conf && !$reset) {
+ return $conf;
+ }
+
+ $confdir = 'sites';
+
+ $sites = array();
+ if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/sites.php')) {
+ // This will overwrite $sites with the desired mappings.
+ include(DRUPAL_ROOT . '/' . $confdir . '/sites.php');
+ }
+
+ $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
+ $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
+ for ($i = count($uri) - 1; $i > 0; $i--) {
+ for ($j = count($server); $j > 0; $j--) {
+ $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
+ if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $sites[$dir])) {
+ $dir = $sites[$dir];
+ }
+ if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir))) {
+ $conf = "$confdir/$dir";
+ return $conf;
+ }
+ }
+ }
+ $conf = "$confdir/default";
+ return $conf;
+}
+
+/**
+ * Set appropriate server variables needed for command line scripts to work.
+ *
+ * This function can be called by command line scripts before bootstrapping
+ * Drupal, to ensure that the page loads with the desired server parameters.
+ * This is because many parts of Drupal assume that they are running in a web
+ * browser and therefore use information from the global PHP $_SERVER variable
+ * that does not get set when Drupal is run from the command line.
+ *
+ * In many cases, the default way in which this function populates the $_SERVER
+ * variable is sufficient, and it can therefore be called without passing in
+ * any input. However, command line scripts running on a multisite installation
+ * (or on any installation that has settings.php stored somewhere other than
+ * the sites/default folder) need to pass in the URL of the site to allow
+ * Drupal to detect the correct location of the settings.php file. Passing in
+ * the 'url' parameter is also required for functions like request_uri() to
+ * return the expected values.
+ *
+ * Most other parameters do not need to be passed in, but may be necessary in
+ * some cases; for example, if Drupal's ip_address() function needs to return
+ * anything but the standard localhost value ('127.0.0.1'), the command line
+ * script should pass in the desired value via the 'REMOTE_ADDR' key.
+ *
+ * @param $variables
+ * (optional) An associative array of variables within $_SERVER that should
+ * be replaced. If the special element 'url' is provided in this array, it
+ * will be used to populate some of the server defaults; it should be set to
+ * the URL of the current page request, excluding any $_GET request but
+ * including the script name (e.g., http://www.example.com/mysite/index.php).
+ *
+ * @see conf_path()
+ * @see request_uri()
+ * @see ip_address()
+ */
+function drupal_override_server_variables($variables = array()) {
+ // Allow the provided URL to override any existing values in $_SERVER.
+ if (isset($variables['url'])) {
+ $url = parse_url($variables['url']);
+ if (isset($url['host'])) {
+ $_SERVER['HTTP_HOST'] = $url['host'];
+ }
+ if (isset($url['path'])) {
+ $_SERVER['SCRIPT_NAME'] = $url['path'];
+ }
+ unset($variables['url']);
+ }
+ // Define default values for $_SERVER keys. These will be used if $_SERVER
+ // does not already define them and no other values are passed in to this
+ // function.
+ $defaults = array(
+ 'HTTP_HOST' => 'localhost',
+ 'SCRIPT_NAME' => NULL,
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'SERVER_NAME' => NULL,
+ 'SERVER_SOFTWARE' => NULL,
+ 'HTTP_USER_AGENT' => NULL,
+ );
+ // Replace elements of the $_SERVER array, as appropriate.
+ $_SERVER = $variables + $_SERVER + $defaults;
+}
+
+/**
+ * Initialize PHP environment.
+ */
+function drupal_environment_initialize() {
+ if (!isset($_SERVER['HTTP_REFERER'])) {
+ $_SERVER['HTTP_REFERER'] = '';
+ }
+ if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
+ $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
+ }
+
+ if (isset($_SERVER['HTTP_HOST'])) {
+ // As HTTP_HOST is user input, ensure it only contains characters allowed
+ // in hostnames. See RFC 952 (and RFC 2181).
+ // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
+ $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
+ if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
+ // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
+ header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
+ exit;
+ }
+ }
+ else {
+ // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
+ // defined for E_ALL compliance.
+ $_SERVER['HTTP_HOST'] = '';
+ }
+
+ // When clean URLs are enabled, emulate ?q=foo/bar using REQUEST_URI. It is
+ // not possible to append the query string using mod_rewrite without the B
+ // flag (this was added in Apache 2.2.8), because mod_rewrite unescapes the
+ // path before passing it on to PHP. This is a problem when the path contains
+ // e.g. "&" or "%" that have special meanings in URLs and must be encoded.
+ $_GET['q'] = request_path();
+
+ // Enforce E_ALL, but allow users to set levels not part of E_ALL.
+ error_reporting(E_ALL | error_reporting());
+
+ // Override PHP settings required for Drupal to work properly.
+ // sites/default/default.settings.php contains more runtime settings.
+ // The .htaccess file contains settings that cannot be changed at runtime.
+
+ // Don't escape quotes when reading files from the database, disk, etc.
+ ini_set('magic_quotes_runtime', '0');
+ // Use session cookies, not transparent sessions that puts the session id in
+ // the query string.
+ ini_set('session.use_cookies', '1');
+ ini_set('session.use_only_cookies', '1');
+ ini_set('session.use_trans_sid', '0');
+ // Don't send HTTP headers using PHP's session handler.
+ ini_set('session.cache_limiter', 'none');
+ // Use httponly session cookies.
+ ini_set('session.cookie_httponly', '1');
+
+ // Set sane locale settings, to ensure consistent string, dates, times and
+ // numbers handling.
+ setlocale(LC_ALL, 'C');
+}
+
+/**
+ * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
+ *
+ * @return
+ * TRUE if only containing valid characters, or FALSE otherwise.
+ */
+function drupal_valid_http_host($host) {
+ return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
+}
+
+/**
+ * Loads the configuration and sets the base URL, cookie domain, and
+ * session name correctly.
+ */
+function drupal_settings_initialize() {
+ global $base_url, $base_path, $base_root;
+
+ // Export the following settings.php variables to the global namespace
+ global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
+ $conf = array();
+
+ if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
+ include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
+ }
+ $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
+
+ if (isset($base_url)) {
+ // Parse fixed base URL from settings.php.
+ $parts = parse_url($base_url);
+ $http_protocol = $parts['scheme'];
+ if (!isset($parts['path'])) {
+ $parts['path'] = '';
+ }
+ $base_path = $parts['path'] . '/';
+ // Build $base_root (everything until first slash after "scheme://").
+ $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
+ }
+ else {
+ // Create base URL
+ $http_protocol = $is_https ? 'https' : 'http';
+ $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
+
+ $base_url = $base_root;
+
+ // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
+ // be modified by a visitor.
+ if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) {
+ $base_path = $dir;
+ $base_url .= $base_path;
+ $base_path .= '/';
+ }
+ else {
+ $base_path = '/';
+ }
+ }
+ $base_secure_url = str_replace('http://', 'https://', $base_url);
+ $base_insecure_url = str_replace('https://', 'http://', $base_url);
+
+ if ($cookie_domain) {
+ // If the user specifies the cookie domain, also use it for session name.
+ $session_name = $cookie_domain;
+ }
+ else {
+ // Otherwise use $base_url as session name, without the protocol
+ // to use the same session identifiers across http and https.
+ list( , $session_name) = explode('://', $base_url, 2);
+ // HTTP_HOST can be modified by a visitor, but we already sanitized it
+ // in drupal_settings_initialize().
+ if (!empty($_SERVER['HTTP_HOST'])) {
+ $cookie_domain = $_SERVER['HTTP_HOST'];
+ // Strip leading periods, www., and port numbers from cookie domain.
+ $cookie_domain = ltrim($cookie_domain, '.');
+ if (strpos($cookie_domain, 'www.') === 0) {
+ $cookie_domain = substr($cookie_domain, 4);
+ }
+ $cookie_domain = explode(':', $cookie_domain);
+ $cookie_domain = '.' . $cookie_domain[0];
+ }
+ }
+ // Per RFC 2109, cookie domains must contain at least one dot other than the
+ // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
+ if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
+ ini_set('session.cookie_domain', $cookie_domain);
+ }
+ // To prevent session cookies from being hijacked, a user can configure the
+ // SSL version of their website to only transfer session cookies via SSL by
+ // using PHP's session.cookie_secure setting. The browser will then use two
+ // separate session cookies for the HTTPS and HTTP versions of the site. So we
+ // must use different session identifiers for HTTPS and HTTP to prevent a
+ // cookie collision.
+ if ($is_https) {
+ ini_set('session.cookie_secure', TRUE);
+ }
+ $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS';
+ session_name($prefix . substr(hash('sha256', $session_name), 0, 32));
+}
+
+/**
+ * Returns and optionally sets the filename for a system item (module,
+ * theme, etc.). The filename, whether provided, cached, or retrieved
+ * from the database, is only returned if the file exists.
+ *
+ * This function plays a key role in allowing Drupal's resources (modules
+ * and themes) to be located in different places depending on a site's
+ * configuration. For example, a module 'foo' may legally be be located
+ * in any of these three places:
+ *
+ * modules/foo/foo.module
+ * sites/all/modules/foo/foo.module
+ * sites/example.com/modules/foo/foo.module
+ *
+ * Calling drupal_get_filename('module', 'foo') will give you one of
+ * the above, depending on where the module is located.
+ *
+ * @param $type
+ * The type of the item (i.e. theme, theme_engine, module, profile).
+ * @param $name
+ * The name of the item for which the filename is requested.
+ * @param $filename
+ * The filename of the item if it is to be set explicitly rather
+ * than by consulting the database.
+ *
+ * @return
+ * The filename of the requested item.
+ */
+function drupal_get_filename($type, $name, $filename = NULL) {
+ // The location of files will not change during the request, so do not use
+ // drupal_static().
+ static $files = array(), $dirs = array();
+
+ if (!isset($files[$type])) {
+ $files[$type] = array();
+ }
+
+ if (!empty($filename) && file_exists($filename)) {
+ $files[$type][$name] = $filename;
+ }
+ elseif (isset($files[$type][$name])) {
+ // nothing
+ }
+ // Verify that we have an active database connection, before querying
+ // the database. This is required because this function is called both
+ // before we have a database connection (i.e. during installation) and
+ // when a database connection fails.
+ else {
+ try {
+ if (function_exists('db_query')) {
+ $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
+ if (file_exists(DRUPAL_ROOT . '/' . $file)) {
+ $files[$type][$name] = $file;
+ }
+ }
+ }
+ catch (Exception $e) {
+ // The database table may not exist because Drupal is not yet installed,
+ // or the database might be down. We have a fallback for this case so we
+ // hide the error completely.
+ }
+ // Fallback to searching the filesystem if the database could not find the
+ // file or the file returned by the database is not found.
+ if (!isset($files[$type][$name])) {
+ // We have a consistent directory naming: modules, themes...
+ $dir = $type . 's';
+ if ($type == 'theme_engine') {
+ $dir = 'themes/engines';
+ $extension = 'engine';
+ }
+ elseif ($type == 'theme') {
+ $extension = 'info';
+ }
+ else {
+ $extension = $type;
+ }
+
+ if (!isset($dirs[$dir][$extension])) {
+ $dirs[$dir][$extension] = TRUE;
+ if (!function_exists('drupal_system_listing')) {
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ }
+ // Scan the appropriate directories for all files with the requested
+ // extension, not just the file we are currently looking for. This
+ // prevents unnecessary scans from being repeated when this function is
+ // called more than once in the same page request.
+ $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
+ foreach ($matches as $matched_name => $file) {
+ $files[$type][$matched_name] = $file->uri;
+ }
+ }
+ }
+ }
+
+ if (isset($files[$type][$name])) {
+ return $files[$type][$name];
+ }
+}
+
+/**
+ * Load the persistent variable table.
+ *
+ * The variable table is composed of values that have been saved in the table
+ * with variable_set() as well as those explicitly specified in the configuration
+ * file.
+ */
+function variable_initialize($conf = array()) {
+ // NOTE: caching the variables improves performance by 20% when serving
+ // cached pages.
+ if ($cached = cache_get('variables', 'cache_bootstrap')) {
+ $variables = $cached->data;
+ }
+ else {
+ // Cache miss. Avoid a stampede.
+ $name = 'variable_init';
+ if (!lock_acquire($name, 1)) {
+ // Another request is building the variable cache.
+ // Wait, then re-run this function.
+ lock_wait($name);
+ return variable_initialize($conf);
+ }
+ else {
+ // Proceed with variable rebuild.
+ $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed());
+ cache_set('variables', $variables, 'cache_bootstrap');
+ lock_release($name);
+ }
+ }
+
+ foreach ($conf as $name => $value) {
+ $variables[$name] = $value;
+ }
+
+ return $variables;
+}
+
+/**
+ * Returns a persistent variable.
+ *
+ * Case-sensitivity of the variable_* functions depends on the database
+ * collation used. To avoid problems, always use lower case for persistent
+ * variable names.
+ *
+ * @param $name
+ * The name of the variable to return.
+ * @param $default
+ * The default value to use if this variable has never been set.
+ *
+ * @return
+ * The value of the variable.
+ *
+ * @see variable_del()
+ * @see variable_set()
+ */
+function variable_get($name, $default = NULL) {
+ global $conf;
+
+ return isset($conf[$name]) ? $conf[$name] : $default;
+}
+
+/**
+ * Sets a persistent variable.
+ *
+ * Case-sensitivity of the variable_* functions depends on the database
+ * collation used. To avoid problems, always use lower case for persistent
+ * variable names.
+ *
+ * @param $name
+ * The name of the variable to set.
+ * @param $value
+ * The value to set. This can be any PHP data type; these functions take care
+ * of serialization as necessary.
+ *
+ * @see variable_del()
+ * @see variable_get()
+ */
+function variable_set($name, $value) {
+ global $conf;
+
+ db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute();
+
+ cache_clear_all('variables', 'cache_bootstrap');
+
+ $conf[$name] = $value;
+}
+
+/**
+ * Unsets a persistent variable.
+ *
+ * Case-sensitivity of the variable_* functions depends on the database
+ * collation used. To avoid problems, always use lower case for persistent
+ * variable names.
+ *
+ * @param $name
+ * The name of the variable to undefine.
+ *
+ * @see variable_get()
+ * @see variable_set()
+ */
+function variable_del($name) {
+ global $conf;
+
+ db_delete('variable')
+ ->condition('name', $name)
+ ->execute();
+ cache_clear_all('variables', 'cache_bootstrap');
+
+ unset($conf[$name]);
+}
+
+/**
+ * Retrieve the current page from the cache.
+ *
+ * Note: we do not serve cached pages to authenticated users, or to anonymous
+ * users when $_SESSION is non-empty. $_SESSION may contain status messages
+ * from a form submission, the contents of a shopping cart, or other user-
+ * specific content that should not be cached and displayed to other users.
+ *
+ * @param $check_only
+ * (optional) Set to TRUE to only return whether a previous call found a
+ * cache entry.
+ *
+ * @return
+ * The cache object, if the page was found in the cache, NULL otherwise.
+ */
+function drupal_page_get_cache($check_only = FALSE) {
+ global $base_root;
+ static $cache_hit = FALSE;
+
+ if ($check_only) {
+ return $cache_hit;
+ }
+
+ if (drupal_page_is_cacheable()) {
+ $cache = cache_get($base_root . request_uri(), 'cache_page');
+ if ($cache !== FALSE) {
+ $cache_hit = TRUE;
+ }
+ return $cache;
+ }
+}
+
+/**
+ * Determine the cacheability of the current page.
+ *
+ * @param $allow_caching
+ * Set to FALSE if you want to prevent this page to get cached.
+ *
+ * @return
+ * TRUE if the current page can be cached, FALSE otherwise.
+ */
+function drupal_page_is_cacheable($allow_caching = NULL) {
+ $allow_caching_static = &drupal_static(__FUNCTION__, TRUE);
+ if (isset($allow_caching)) {
+ $allow_caching_static = $allow_caching;
+ }
+
+ return $allow_caching_static && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
+ && !drupal_is_cli();
+}
+
+/**
+ * Invoke a bootstrap hook in all bootstrap modules that implement it.
+ *
+ * @param $hook
+ * The name of the bootstrap hook to invoke.
+ *
+ * @see bootstrap_hooks()
+ */
+function bootstrap_invoke_all($hook) {
+ // Bootstrap modules should have been loaded when this function is called, so
+ // we don't need to tell module_list() to reset its internal list (and we
+ // therefore leave the first parameter at its default value of FALSE). We
+ // still pass in TRUE for the second parameter, though; in case this is the
+ // first time during the bootstrap that module_list() is called, we want to
+ // make sure that its internal cache is primed with the bootstrap modules
+ // only.
+ foreach (module_list(FALSE, TRUE) as $module) {
+ drupal_load('module', $module);
+ module_invoke($module, $hook);
+ }
+}
+
+/**
+ * Includes a file with the provided type and name. This prevents
+ * including a theme, engine, module, etc., more than once.
+ *
+ * @param $type
+ * The type of item to load (i.e. theme, theme_engine, module).
+ * @param $name
+ * The name of the item to load.
+ *
+ * @return
+ * TRUE if the item is loaded or has already been loaded.
+ */
+function drupal_load($type, $name) {
+ // Once a file is included this can't be reversed during a request so do not
+ // use drupal_static() here.
+ static $files = array();
+
+ if (isset($files[$type][$name])) {
+ return TRUE;
+ }
+
+ $filename = drupal_get_filename($type, $name);
+
+ if ($filename) {
+ include_once DRUPAL_ROOT . '/' . $filename;
+ $files[$type][$name] = TRUE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Set an HTTP response header for the current page.
+ *
+ * Note: When sending a Content-Type header, always include a 'charset' type,
+ * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
+ *
+ * @param $name
+ * The HTTP header name, or the special 'Status' header name.
+ * @param $value
+ * The HTTP header value; if equal to FALSE, the specified header is unset.
+ * If $name is 'Status', this is expected to be a status code followed by a
+ * reason phrase, e.g. "404 Not Found".
+ * @param $append
+ * Whether to append the value to an existing header or to replace it.
+ */
+function drupal_add_http_header($name, $value, $append = FALSE) {
+ // The headers as name/value pairs.
+ $headers = &drupal_static('drupal_http_headers', array());
+
+ $name_lower = strtolower($name);
+ _drupal_set_preferred_header_name($name);
+
+ if ($value === FALSE) {
+ $headers[$name_lower] = FALSE;
+ }
+ elseif (isset($headers[$name_lower]) && $append) {
+ // Multiple headers with identical names may be combined using comma (RFC
+ // 2616, section 4.2).
+ $headers[$name_lower] .= ',' . $value;
+ }
+ else {
+ $headers[$name_lower] = $value;
+ }
+ drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
+}
+
+/**
+ * Get the HTTP response headers for the current page.
+ *
+ * @param $name
+ * An HTTP header name. If omitted, all headers are returned as name/value
+ * pairs. If an array value is FALSE, the header has been unset.
+ * @return
+ * A string containing the header value, or FALSE if the header has been set,
+ * or NULL if the header has not been set.
+ */
+function drupal_get_http_header($name = NULL) {
+ $headers = &drupal_static('drupal_http_headers', array());
+ if (isset($name)) {
+ $name = strtolower($name);
+ return isset($headers[$name]) ? $headers[$name] : NULL;
+ }
+ else {
+ return $headers;
+ }
+}
+
+/**
+ * Header names are case-insensitive, but for maximum compatibility they should
+ * follow "common form" (see RFC 2617, section 4.2).
+ */
+function _drupal_set_preferred_header_name($name = NULL) {
+ static $header_names = array();
+
+ if (!isset($name)) {
+ return $header_names;
+ }
+ $header_names[strtolower($name)] = $name;
+}
+
+/**
+ * Send the HTTP response headers previously set using drupal_add_http_header().
+ * Add default headers, unless they have been replaced or unset using
+ * drupal_add_http_header().
+ *
+ * @param $default_headers
+ * An array of headers as name/value pairs.
+ * @param $single
+ * If TRUE and headers have already be sent, send only the specified header.
+ */
+function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
+ $headers_sent = &drupal_static(__FUNCTION__, FALSE);
+ $headers = drupal_get_http_header();
+ if ($only_default && $headers_sent) {
+ $headers = array();
+ }
+ $headers_sent = TRUE;
+
+ $header_names = _drupal_set_preferred_header_name();
+ foreach ($default_headers as $name => $value) {
+ $name_lower = strtolower($name);
+ if (!isset($headers[$name_lower])) {
+ $headers[$name_lower] = $value;
+ $header_names[$name_lower] = $name;
+ }
+ }
+ foreach ($headers as $name_lower => $value) {
+ if ($name_lower == 'status') {
+ header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
+ }
+ // Skip headers that have been unset.
+ elseif ($value) {
+ header($header_names[$name_lower] . ': ' . $value);
+ }
+ }
+}
+
+/**
+ * Set HTTP headers in preparation for a page response.
+ *
+ * Authenticated users are always given a 'no-cache' header, and will fetch a
+ * fresh page on every request. This prevents authenticated users from seeing
+ * locally cached pages.
+ *
+ * Also give each page a unique ETag. This will force clients to include both
+ * an If-Modified-Since header and an If-None-Match header when doing
+ * conditional requests for the page (required by RFC 2616, section 13.3.4),
+ * making the validation more robust. This is a workaround for a bug in Mozilla
+ * Firefox that is triggered when Drupal's caching is enabled and the user
+ * accesses Drupal via an HTTP proxy (see
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
+ * user requests a page, and then logs out and requests the same page again,
+ * Firefox may send a conditional request based on the page that was cached
+ * locally when the user was logged in. If this page did not have an ETag
+ * header, the request only contains an If-Modified-Since header. The date will
+ * be recent, because with authenticated users the Last-Modified header always
+ * refers to the time of the request. If the user accesses Drupal via a proxy
+ * server, and the proxy already has a cached copy of the anonymous page with an
+ * older Last-Modified date, the proxy may respond with 304 Not Modified, making
+ * the client think that the anonymous and authenticated pageviews are
+ * identical.
+ *
+ * @see drupal_page_set_cache()
+ */
+function drupal_page_header() {
+ $headers_sent = &drupal_static(__FUNCTION__, FALSE);
+ if ($headers_sent) {
+ return TRUE;
+ }
+ $headers_sent = TRUE;
+
+ $default_headers = array(
+ 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
+ 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
+ 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
+ 'ETag' => '"' . REQUEST_TIME . '"',
+ );
+ drupal_send_headers($default_headers);
+}
+
+/**
+ * Set HTTP headers in preparation for a cached page response.
+ *
+ * The headers allow as much as possible in proxies and browsers without any
+ * particular knowledge about the pages. Modules can override these headers
+ * using drupal_add_http_header().
+ *
+ * If the request is conditional (using If-Modified-Since and If-None-Match),
+ * and the conditions match those currently in the cache, a 304 Not Modified
+ * response is sent.
+ */
+function drupal_serve_page_from_cache(stdClass $cache) {
+ // Negotiate whether to use compression.
+ $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+ $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
+
+ // Get headers set in hook_boot(). Keys are lower-case.
+ $hook_boot_headers = drupal_get_http_header();
+
+ // Headers generated in this function, that may be replaced or unset using
+ // drupal_add_http_headers(). Keys are mixed-case.
+ $default_headers = array();
+
+ foreach ($cache->data['headers'] as $name => $value) {
+ // In the case of a 304 response, certain headers must be sent, and the
+ // remaining may not (see RFC 2616, section 10.3.5). Do not override
+ // headers set in hook_boot().
+ $name_lower = strtolower($name);
+ if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) {
+ drupal_add_http_header($name, $value);
+ unset($cache->data['headers'][$name]);
+ }
+ }
+
+ // If the client sent a session cookie, a cached copy will only be served
+ // to that one particular client due to Vary: Cookie. Thus, do not set
+ // max-age > 0, allowing the page to be cached by external proxies, when a
+ // session cookie is present unless the Vary header has been replaced or
+ // unset in hook_boot().
+ $max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? variable_get('page_cache_maximum_age', 0) : 0;
+ $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
+
+ // Entity tag should change if the output changes.
+ $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
+ header('Etag: ' . $etag);
+
+ // See if the client has provided the required HTTP headers.
+ $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
+ $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
+
+ if ($if_modified_since && $if_none_match
+ && $if_none_match == $etag // etag must match
+ && $if_modified_since == $cache->created) { // if-modified-since must match
+ header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
+ drupal_send_headers($default_headers);
+ return;
+ }
+
+ // Send the remaining headers.
+ foreach ($cache->data['headers'] as $name => $value) {
+ drupal_add_http_header($name, $value);
+ }
+
+ $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
+
+ // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
+ // by sending an Expires date in the past. HTTP/1.1 clients ignores the
+ // Expires header if a Cache-Control: max-age= directive is specified (see RFC
+ // 2616, section 14.9.3).
+ $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT';
+
+ drupal_send_headers($default_headers);
+
+ // Allow HTTP proxies to cache pages for anonymous users without a session
+ // cookie. The Vary header is used to indicates the set of request-header
+ // fields that fully determines whether a cache is permitted to use the
+ // response to reply to a subsequent request for a given URL without
+ // revalidation. If a Vary header has been set in hook_boot(), it is assumed
+ // that the module knows how to cache the page.
+ if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) {
+ header('Vary: Cookie');
+ }
+
+ if ($page_compression) {
+ header('Vary: Accept-Encoding', FALSE);
+ // If page_compression is enabled, the cache contains gzipped data.
+ if ($return_compressed) {
+ // $cache->data['body'] is already gzip'ed, so make sure
+ // zlib.output_compression does not compress it once more.
+ ini_set('zlib.output_compression', '0');
+ header('Content-Encoding: gzip');
+ }
+ else {
+ // The client does not support compression, so unzip the data in the
+ // cache. Strip the gzip header and run uncompress.
+ $cache->data['body'] = gzinflate(substr(substr($cache->data['body'], 10), 0, -8));
+ }
+ }
+
+ // Print the page.
+ print $cache->data['body'];
+}
+
+/**
+ * Define the critical hooks that force modules to always be loaded.
+ */
+function bootstrap_hooks() {
+ return array('boot', 'exit', 'watchdog', 'language_init');
+}
+
+/**
+ * Unserializes and appends elements from a serialized string.
+ *
+ * @param $obj
+ * The object to which the elements are appended.
+ * @param $field
+ * The attribute of $obj whose value should be unserialized.
+ */
+function drupal_unpack($obj, $field = 'data') {
+ if ($obj->$field && $data = unserialize($obj->$field)) {
+ foreach ($data as $key => $value) {
+ if (!empty($key) && !isset($obj->$key)) {
+ $obj->$key = $value;
+ }
+ }
+ }
+ return $obj;
+}
+
+/**
+ * Translates a string to the current language or to a given language.
+ *
+ * The t() function serves two purposes. First, at run-time it translates
+ * user-visible text into the appropriate language. Second, various mechanisms
+ * that figure out what text needs to be translated work off t() -- the text
+ * inside t() calls is added to the database of strings to be translated.
+ * These strings are expected to be in English, so the first argument should
+ * always be in English. To enable a fully-translatable site, it is important
+ * that all human-readable text that will be displayed on the site or sent to
+ * a user is passed through the t() function, or a related function. See the
+ * @link http://drupal.org/node/322729 Localization API @endlink pages for
+ * more information, including recommendations on how to break up or not
+ * break up strings for translation.
+ *
+ * You should never use t() to translate variables, such as calling
+ * @code t($text); @endcode, unless the text that the variable holds has been
+ * passed through t() elsewhere (e.g., $text is one of several translated
+ * literal strings in an array). It is especially important never to call
+ * @code t($user_text); @endcode, where $user_text is some text that a user
+ * entered - doing that can lead to cross-site scripting and other security
+ * problems. However, you can use variable substitution in your string, to put
+ * variable text such as user names or link URLs into translated text. Variable
+ * substitution looks like this:
+ * @code
+ * $text = t("@name's blog", array('@name' => format_username($account)));
+ * @endcode
+ * Basically, you can put variables like @name into your string, and t() will
+ * substitute their sanitized values at translation time (see $args below or
+ * the Localization API pages referenced above for details). Translators can
+ * then rearrange the string as necessary for the language (e.g., in Spanish,
+ * it might be "blog de @name").
+ *
+ * During the Drupal installation phase, some resources used by t() wil not be
+ * available to code that needs localization. See st() and get_t() for
+ * alternatives.
+ *
+ * @param $string
+ * A string containing the English string to translate.
+ * @param $args
+ * An associative array of replacements to make after translation.
+ * See format_string().
+ * @param $options
+ * An associative array of additional options, with the following elements:
+ * - 'langcode' (defaults to the current language): The language code to
+ * translate to a language other than what is used to display the page.
+ * - 'context' (defaults to the empty context): The context the source string
+ * belongs to.
+ *
+ * @return
+ * The translated string.
+ *
+ * @see st()
+ * @see get_t()
+ * @ingroup sanitization
+ */
+function t($string, array $args = array(), array $options = array()) {
+ global $language;
+ static $custom_strings;
+
+ // Merge in default.
+ if (empty($options['langcode'])) {
+ $options['langcode'] = isset($language->language) ? $language->language : 'en';
+ }
+ if (empty($options['context'])) {
+ $options['context'] = '';
+ }
+
+ // First, check for an array of customized strings. If present, use the array
+ // *instead of* database lookups. This is a high performance way to provide a
+ // handful of string replacements. See settings.php for examples.
+ // Cache the $custom_strings variable to improve performance.
+ if (!isset($custom_strings[$options['langcode']])) {
+ $custom_strings[$options['langcode']] = variable_get('locale_custom_strings_' . $options['langcode'], array());
+ }
+ // Custom strings work for English too, even if locale module is disabled.
+ if (isset($custom_strings[$options['langcode']][$options['context']][$string])) {
+ $string = $custom_strings[$options['langcode']][$options['context']][$string];
+ }
+ // Translate with locale module if enabled.
+ elseif ($options['langcode'] != 'en' && function_exists('locale')) {
+ $string = locale($string, $options['context'], $options['langcode']);
+ }
+ if (empty($args)) {
+ return $string;
+ }
+ else {
+ return format_string($string, $args);
+ }
+}
+
+/**
+ * Replace placeholders with sanitized values in a string.
+ *
+ * @param $string
+ * A string containing placeholders.
+ * @param $args
+ * An associative array of replacements to make. Occurrences in $string of
+ * any key in $args are replaced with the corresponding value, after
+ * sanitization. The sanitization function depends on the first character of
+ * the key:
+ * - !variable: Inserted as is. Use this for text that has already been
+ * sanitized.
+ * - @variable: Escaped to HTML using check_plain(). Use this for anything
+ * displayed on a page on the site.
+ * - %variable: Escaped as a placeholder for user-submitted content using
+ * drupal_placeholder(), which shows up as <em>emphasized</em> text.
+ *
+ * @see t()
+ * @ingroup sanitization
+ */
+function format_string($string, array $args = array()) {
+ // Transform arguments before inserting them.
+ foreach ($args as $key => $value) {
+ switch ($key[0]) {
+ case '@':
+ // Escaped only.
+ $args[$key] = check_plain($value);
+ break;
+
+ case '%':
+ default:
+ // Escaped and placeholder.
+ $args[$key] = drupal_placeholder($value);
+ break;
+
+ case '!':
+ // Pass-through.
+ }
+ }
+ return strtr($string, $args);
+}
+
+/**
+ * Encode special characters in a plain-text string for display as HTML.
+ *
+ * Also validates strings as UTF-8 to prevent cross site scripting attacks on
+ * Internet Explorer 6.
+ *
+ * @param $text
+ * The text to be checked or processed.
+ *
+ * @return
+ * An HTML safe version of $text, or an empty string if $text is not
+ * valid UTF-8.
+ *
+ * @see drupal_validate_utf8()
+ * @ingroup sanitization
+ */
+function check_plain($text) {
+ return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
+}
+
+/**
+ * Checks whether a string is valid UTF-8.
+ *
+ * All functions designed to filter input should use drupal_validate_utf8
+ * to ensure they operate on valid UTF-8 strings to prevent bypass of the
+ * filter.
+ *
+ * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
+ * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
+ * bytes. When these subsequent bytes are HTML control characters such as
+ * quotes or angle brackets, parts of the text that were deemed safe by filters
+ * end up in locations that are potentially unsafe; An onerror attribute that
+ * is outside of a tag, and thus deemed safe by a filter, can be interpreted
+ * by the browser as if it were inside the tag.
+ *
+ * The function does not return FALSE for strings containing character codes
+ * above U+10FFFF, even though these are prohibited by RFC 3629.
+ *
+ * @param $text
+ * The text to check.
+ * @return
+ * TRUE if the text is valid UTF-8, FALSE if not.
+ */
+function drupal_validate_utf8($text) {
+ if (strlen($text) == 0) {
+ return TRUE;
+ }
+ // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
+ // containing invalid UTF-8 byte sequences. It does not reject character
+ // codes above U+10FFFF (represented by 4 or more octets), though.
+ return (preg_match('/^./us', $text) == 1);
+}
+
+/**
+ * Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable.
+ *
+ * Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an
+ * equivalent using other environment variables.
+ */
+function request_uri() {
+ if (isset($_SERVER['REQUEST_URI'])) {
+ $uri = $_SERVER['REQUEST_URI'];
+ }
+ else {
+ if (isset($_SERVER['argv'])) {
+ $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
+ }
+ elseif (isset($_SERVER['QUERY_STRING'])) {
+ $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
+ }
+ else {
+ $uri = $_SERVER['SCRIPT_NAME'];
+ }
+ }
+ // Prevent multiple slashes to avoid cross site requests via the Form API.
+ $uri = '/' . ltrim($uri, '/');
+
+ return $uri;
+}
+
+/**
+ * Log an exception.
+ *
+ * This is a wrapper function for watchdog() which automatically decodes an
+ * exception.
+ *
+ * @param $type
+ * The category to which this message belongs.
+ * @param $exception
+ * The exception that is going to be logged.
+ * @param $message
+ * The message to store in the log. If empty, a text that contains all useful
+ * information about the passed-in exception is used.
+ * @param $variables
+ * Array of variables to replace in the message on display. Defaults to the
+ * return value of drupal_decode_exception().
+ * @param $severity
+ * The severity of the message, as per RFC 3164.
+ * @param $link
+ * A link to associate with the message.
+ *
+ * @see watchdog()
+ * @see drupal_decode_exception()
+ */
+function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
+
+ // Use a default value if $message is not set.
+ if (empty($message)) {
+ // The exception message is run through check_plain() by _drupal_decode_exception().
+ $message = '%type: !message in %function (line %line of %file).';
+ }
+ // $variables must be an array so that we can add the exception information.
+ if (!is_array($variables)) {
+ $variables = array();
+ }
+
+ require_once DRUPAL_ROOT . '/includes/errors.inc';
+ $variables += _drupal_decode_exception($exception);
+ watchdog($type, $message, $variables, $severity, $link);
+}
+
+/**
+ * Log a system message.
+ *
+ * @param $type
+ * The category to which this message belongs. Can be any string, but the
+ * general practice is to use the name of the module calling watchdog().
+ * @param $message
+ * The message to store in the log. Keep $message translatable
+ * by not concatenating dynamic values into it! Variables in the
+ * message should be added by using placeholder strings alongside
+ * the variables argument to declare the value of the placeholders.
+ * See t() for documentation on how $message and $variables interact.
+ * @param $variables
+ * Array of variables to replace in the message on display or
+ * NULL if message is already translated or not possible to
+ * translate.
+ * @param $severity
+ * The severity of the message, as per RFC 3164. Possible values are
+ * WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
+ * @param $link
+ * A link to associate with the message.
+ *
+ * @see watchdog_severity_levels()
+ * @see hook_watchdog()
+ */
+function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
+ global $user, $base_root;
+
+ static $in_error_state = FALSE;
+
+ // It is possible that the error handling will itself trigger an error. In that case, we could
+ // end up in an infinite loop. To avoid that, we implement a simple static semaphore.
+ if (!$in_error_state && function_exists('module_implements')) {
+ $in_error_state = TRUE;
+
+ // Prepare the fields to be logged
+ $log_entry = array(
+ 'type' => $type,
+ 'message' => $message,
+ 'variables' => $variables,
+ 'severity' => $severity,
+ 'link' => $link,
+ 'user' => $user,
+ 'request_uri' => $base_root . request_uri(),
+ 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
+ 'ip' => ip_address(),
+ 'timestamp' => REQUEST_TIME,
+ );
+
+ // Call the logging hooks to log/process the message
+ foreach (module_implements('watchdog') as $module) {
+ module_invoke($module, 'watchdog', $log_entry);
+ }
+
+ // It is critical that the semaphore is only cleared here, in the parent
+ // watchdog() call (not outside the loop), to prevent recursive execution.
+ $in_error_state = FALSE;
+ }
+}
+
+/**
+ * Set a message which reflects the status of the performed operation.
+ *
+ * If the function is called with no arguments, this function returns all set
+ * messages without clearing them.
+ *
+ * @param $message
+ * The message to be displayed to the user. For consistency with other
+ * messages, it should begin with a capital letter and end with a period.
+ * @param $type
+ * The type of the message. One of the following values are possible:
+ * - 'status'
+ * - 'warning'
+ * - 'error'
+ * @param $repeat
+ * If this is FALSE and the message is already set, then the message won't
+ * be repeated.
+ */
+function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
+ if ($message) {
+ if (!isset($_SESSION['messages'][$type])) {
+ $_SESSION['messages'][$type] = array();
+ }
+
+ if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
+ $_SESSION['messages'][$type][] = $message;
+ }
+
+ // Mark this page as being uncacheable.
+ drupal_page_is_cacheable(FALSE);
+ }
+
+ // Messages not set when DB connection fails.
+ return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
+}
+
+/**
+ * Return all messages that have been set.
+ *
+ * @param $type
+ * (optional) Only return messages of this type.
+ * @param $clear_queue
+ * (optional) Set to FALSE if you do not want to clear the messages queue
+ * @return
+ * An associative array, the key is the message type, the value an array
+ * of messages. If the $type parameter is passed, you get only that type,
+ * or an empty array if there are no such messages. If $type is not passed,
+ * all message types are returned, or an empty array if none exist.
+ */
+function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
+ if ($messages = drupal_set_message()) {
+ if ($type) {
+ if ($clear_queue) {
+ unset($_SESSION['messages'][$type]);
+ }
+ if (isset($messages[$type])) {
+ return array($type => $messages[$type]);
+ }
+ }
+ else {
+ if ($clear_queue) {
+ unset($_SESSION['messages']);
+ }
+ return $messages;
+ }
+ }
+ return array();
+}
+
+/**
+ * Get the title of the current page, for display on the page and in the title bar.
+ *
+ * @return
+ * The current page's title.
+ */
+function drupal_get_title() {
+ $title = drupal_set_title();
+
+ // During a bootstrap, menu.inc is not included and thus we cannot provide a title.
+ if (!isset($title) && function_exists('menu_get_active_title')) {
+ $title = check_plain(menu_get_active_title());
+ }
+
+ return $title;
+}
+
+/**
+ * Set the title of the current page, for display on the page and in the title bar.
+ *
+ * @param $title
+ * Optional string value to assign to the page title; or if set to NULL
+ * (default), leaves the current title unchanged.
+ * @param $output
+ * Optional flag - normally should be left as CHECK_PLAIN. Only set to
+ * PASS_THROUGH if you have already removed any possibly dangerous code
+ * from $title using a function like check_plain() or filter_xss(). With this
+ * flag the string will be passed through unchanged.
+ *
+ * @return
+ * The updated title of the current page.
+ */
+function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
+ $stored_title = &drupal_static(__FUNCTION__);
+
+ if (isset($title)) {
+ $stored_title = ($output == PASS_THROUGH) ? $title : check_plain($title);
+ }
+
+ return $stored_title;
+}
+
+/**
+ * Check to see if an IP address has been blocked.
+ *
+ * Blocked IP addresses are stored in the database by default. However for
+ * performance reasons we allow an override in settings.php. This allows us
+ * to avoid querying the database at this critical stage of the bootstrap if
+ * an administrative interface for IP address blocking is not required.
+ *
+ * @param $ip
+ * IP address to check.
+ * @return bool
+ * TRUE if access is denied, FALSE if access is allowed.
+ */
+function drupal_is_denied($ip) {
+ // Because this function is called on every page request, we first check
+ // for an array of IP addresses in settings.php before querying the
+ // database.
+ $blocked_ips = variable_get('blocked_ips');
+ $denied = FALSE;
+ if (isset($blocked_ips) && is_array($blocked_ips)) {
+ $denied = in_array($ip, $blocked_ips);
+ }
+ // Only check if database.inc is loaded already. If
+ // $conf['page_cache_without_database'] = TRUE; is set in settings.php,
+ // then the database won't be loaded here so the IPs in the database
+ // won't be denied. However the user asked explicitly not to use the
+ // database and also in this case it's quite likely that the user relies
+ // on higher performance solutions like a firewall.
+ elseif (class_exists('Database', FALSE)) {
+ $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
+ }
+ return $denied;
+}
+
+/**
+ * Handle denied users.
+ *
+ * @param $ip
+ * IP address to check. Prints a message and exits if access is denied.
+ */
+function drupal_block_denied($ip) {
+ // Deny access to blocked IP addresses - t() is not yet available.
+ if (drupal_is_denied($ip)) {
+ header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
+ print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
+ exit();
+ }
+}
+
+/**
+ * Returns a string of highly randomized bytes (over the full 8-bit range).
+ *
+ * This function is better than simply calling mt_rand() or any other built-in
+ * PHP function because it can return a long string of bytes (compared to < 4
+ * bytes normally from mt_rand()) and uses the best available pseudo-random source.
+ *
+ * @param $count
+ * The number of characters (bytes) to return in the string.
+ */
+function drupal_random_bytes($count) {
+ // $random_state does not use drupal_static as it stores random bytes.
+ static $random_state, $bytes;
+ // Initialize on the first call. The contents of $_SERVER includes a mix of
+ // user-specific and system information that varies a little with each page.
+ if (!isset($random_state)) {
+ $random_state = print_r($_SERVER, TRUE);
+ if (function_exists('getmypid')) {
+ // Further initialize with the somewhat random PHP process ID.
+ $random_state .= getmypid();
+ }
+ $bytes = '';
+ }
+ if (strlen($bytes) < $count) {
+ // /dev/urandom is available on many *nix systems and is considered the
+ // best commonly available pseudo-random source.
+ if ($fh = @fopen('/dev/urandom', 'rb')) {
+ // PHP only performs buffered reads, so in reality it will always read
+ // at least 4096 bytes. Thus, it costs nothing extra to read and store
+ // that much so as to speed any additional invocations.
+ $bytes .= fread($fh, max(4096, $count));
+ fclose($fh);
+ }
+ // If /dev/urandom is not available or returns no bytes, this loop will
+ // generate a good set of pseudo-random bytes on any system.
+ // Note that it may be important that our $random_state is passed
+ // through hash() prior to being rolled into $output, that the two hash()
+ // invocations are different, and that the extra input into the first one -
+ // the microtime() - is prepended rather than appended. This is to avoid
+ // directly leaking $random_state via the $output stream, which could
+ // allow for trivial prediction of further "random" numbers.
+ while (strlen($bytes) < $count) {
+ $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
+ $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+ }
+ }
+ $output = substr($bytes, 0, $count);
+ $bytes = substr($bytes, $count);
+ return $output;
+}
+
+/**
+ * Calculate a base-64 encoded, URL-safe sha-256 hmac.
+ *
+ * @param $data
+ * String to be validated with the hmac.
+ * @param $key
+ * A secret string key.
+ *
+ * @return
+ * A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
+ * any = padding characters removed.
+ */
+function drupal_hmac_base64($data, $key) {
+ $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
+ // Modify the hmac so it's safe to use in URLs.
+ return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
+}
+
+/**
+ * Calculate a base-64 encoded, URL-safe sha-256 hash.
+ *
+ * @param $data
+ * String to be hashed.
+ *
+ * @return
+ * A base-64 encoded sha-256 hash, with + replaced with -, / with _ and
+ * any = padding characters removed.
+ */
+function drupal_hash_base64($data) {
+ $hash = base64_encode(hash('sha256', $data, TRUE));
+ // Modify the hash so it's safe to use in URLs.
+ return strtr($hash, array('+' => '-', '/' => '_', '=' => ''));
+}
+
+/**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is similar to PHP's array_merge_recursive() function, but it
+ * handles non-array values differently. When merging values that are not both
+ * arrays, the latter value replaces the former rather than merging with it.
+ *
+ * Example:
+ * @code
+ * $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b')));
+ * $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd')));
+ *
+ * // This results in array('fragment' => array('x', 'y'), 'attributes' => array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b', 'c', 'd'))).
+ * $incorrect = array_merge_recursive($link_options_1, $link_options_2);
+ *
+ * // This results in array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))).
+ * $correct = drupal_array_merge_deep($link_options_1, $link_options_2);
+ * @endcode
+ *
+ * @param ...
+ * Arrays to merge.
+ *
+ * @return
+ * The merged array.
+ *
+ * @see drupal_array_merge_deep_array()
+ */
+function drupal_array_merge_deep() {
+ return drupal_array_merge_deep_array(func_get_args());
+}
+
+/**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is equivalent to drupal_array_merge_deep(), except the
+ * input arrays are passed as a single array parameter rather than a variable
+ * parameter list.
+ *
+ * The following are equivalent:
+ * - drupal_array_merge_deep($a, $b);
+ * - drupal_array_merge_deep_array(array($a, $b));
+ *
+ * The following are also equivalent:
+ * - call_user_func_array('drupal_array_merge_deep', $arrays_to_merge);
+ * - drupal_array_merge_deep_array($arrays_to_merge);
+ *
+ * @see drupal_array_merge_deep()
+ */
+function drupal_array_merge_deep_array($arrays) {
+ $result = array();
+
+ foreach ($arrays as $array) {
+ foreach ($array as $key => $value) {
+ // Renumber integer keys as array_merge_recursive() does. Note that PHP
+ // automatically converts array keys that are integer strings (e.g., '1')
+ // to integers.
+ if (is_integer($key)) {
+ $result[] = $value;
+ }
+ // Recurse when both values are arrays.
+ elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
+ $result[$key] = drupal_array_merge_deep_array(array($result[$key], $value));
+ }
+ // Otherwise, use the latter value, overriding any previous value.
+ else {
+ $result[$key] = $value;
+ }
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Generates a default anonymous $user object.
+ *
+ * @return Object - the user object.
+ */
+function drupal_anonymous_user() {
+ $user = new stdClass();
+ $user->uid = 0;
+ $user->hostname = ip_address();
+ $user->roles = array();
+ $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
+ $user->cache = 0;
+ return $user;
+}
+
+/**
+ * A string describing a phase of Drupal to load. Each phase adds to the
+ * previous one, so invoking a later phase automatically runs the earlier
+ * phases too. The most important usage is that if you want to access the
+ * Drupal database from a script without loading anything else, you can
+ * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
+ *
+ * @param $phase
+ * A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
+ * @param $new_phase
+ * A boolean, set to FALSE if calling drupal_bootstrap from inside a
+ * function called from drupal_bootstrap (recursion).
+ * @return
+ * The most recently completed phase.
+ *
+ */
+function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
+ // Not drupal_static(), because does not depend on any run-time information.
+ static $phases = array(
+ DRUPAL_BOOTSTRAP_CONFIGURATION,
+ DRUPAL_BOOTSTRAP_PAGE_CACHE,
+ DRUPAL_BOOTSTRAP_DATABASE,
+ DRUPAL_BOOTSTRAP_VARIABLES,
+ DRUPAL_BOOTSTRAP_SESSION,
+ DRUPAL_BOOTSTRAP_PAGE_HEADER,
+ DRUPAL_BOOTSTRAP_LANGUAGE,
+ DRUPAL_BOOTSTRAP_FULL,
+ );
+ // Not drupal_static(), because the only legitimate API to control this is to
+ // call drupal_bootstrap() with a new phase parameter.
+ static $final_phase;
+ // Not drupal_static(), because it's impossible to roll back to an earlier
+ // bootstrap state.
+ static $stored_phase = -1;
+
+ // When not recursing, store the phase name so it's not forgotten while
+ // recursing.
+ if ($new_phase) {
+ $final_phase = $phase;
+ }
+ if (isset($phase)) {
+ // Call a phase if it has not been called before and is below the requested
+ // phase.
+ while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) {
+ $current_phase = array_shift($phases);
+
+ // This function is re-entrant. Only update the completed phase when the
+ // current call actually resulted in a progress in the bootstrap process.
+ if ($current_phase > $stored_phase) {
+ $stored_phase = $current_phase;
+ }
+
+ switch ($current_phase) {
+ case DRUPAL_BOOTSTRAP_CONFIGURATION:
+ _drupal_bootstrap_configuration();
+ break;
+
+ case DRUPAL_BOOTSTRAP_PAGE_CACHE:
+ _drupal_bootstrap_page_cache();
+ break;
+
+ case DRUPAL_BOOTSTRAP_DATABASE:
+ _drupal_bootstrap_database();
+ break;
+
+ case DRUPAL_BOOTSTRAP_VARIABLES:
+ _drupal_bootstrap_variables();
+ break;
+
+ case DRUPAL_BOOTSTRAP_SESSION:
+ require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc');
+ drupal_session_initialize();
+ break;
+
+ case DRUPAL_BOOTSTRAP_PAGE_HEADER:
+ _drupal_bootstrap_page_header();
+ break;
+
+ case DRUPAL_BOOTSTRAP_LANGUAGE:
+ drupal_language_initialize();
+ break;
+
+ case DRUPAL_BOOTSTRAP_FULL:
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ _drupal_bootstrap_full();
+ break;
+ }
+ }
+ }
+ return $stored_phase;
+}
+
+/**
+ * Return the time zone of the current user.
+ */
+function drupal_get_user_timezone() {
+ global $user;
+ if (variable_get('configurable_timezones', 1) && $user->uid && $user->timezone) {
+ return $user->timezone;
+ }
+ else {
+ // Ignore PHP strict notice if time zone has not yet been set in the php.ini
+ // configuration.
+ return variable_get('date_default_timezone', @date_default_timezone_get());
+ }
+}
+
+/**
+ * Custom PHP error handler.
+ *
+ * @param $error_level
+ * The level of the error raised.
+ * @param $message
+ * The error message.
+ * @param $filename
+ * The filename that the error was raised in.
+ * @param $line
+ * The line number the error was raised at.
+ * @param $context
+ * An array that points to the active symbol table at the point the error occurred.
+ */
+function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
+ require_once DRUPAL_ROOT . '/includes/errors.inc';
+ _drupal_error_handler_real($error_level, $message, $filename, $line, $context);
+}
+
+/**
+ * Custom PHP exception handler.
+ *
+ * Uncaught exceptions are those not enclosed in a try/catch block. They are
+ * always fatal: the execution of the script will stop as soon as the exception
+ * handler exits.
+ *
+ * @param $exception
+ * The exception object that was thrown.
+ */
+function _drupal_exception_handler($exception) {
+ require_once DRUPAL_ROOT . '/includes/errors.inc';
+
+ try {
+ // Log the message to the watchdog and return an error page to the user.
+ _drupal_log_error(_drupal_decode_exception($exception), TRUE);
+ }
+ catch (Exception $exception2) {
+ // Another uncaught exception was thrown while handling the first one.
+ // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
+ if (error_displayable()) {
+ print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
+ print '<h2>Original</h2><p>' . _drupal_render_exception_safe($exception) . '</p>';
+ print '<h2>Additional</h2><p>' . _drupal_render_exception_safe($exception2) . '</p><hr />';
+ }
+ }
+}
+
+/**
+ * Bootstrap configuration: Setup script environment and load settings.php.
+ */
+function _drupal_bootstrap_configuration() {
+ // Set the Drupal custom error handler.
+ set_error_handler('_drupal_error_handler');
+ set_exception_handler('_drupal_exception_handler');
+
+ drupal_environment_initialize();
+ // Start a page timer:
+ timer_start('page');
+ // Initialize the configuration, including variables from settings.php.
+ drupal_settings_initialize();
+}
+
+/**
+ * Bootstrap page cache: Try to serve a page from cache.
+ */
+function _drupal_bootstrap_page_cache() {
+ global $user;
+
+ // Allow specifying special cache handlers in settings.php, like
+ // using memcached or files for storing cache information.
+ require_once DRUPAL_ROOT . '/includes/cache.inc';
+ foreach (variable_get('cache_backends', array()) as $include) {
+ require_once DRUPAL_ROOT . '/' . $include;
+ }
+ // Check for a cache mode force from settings.php.
+ if (variable_get('page_cache_without_database')) {
+ $cache_enabled = TRUE;
+ }
+ else {
+ drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE);
+ $cache_enabled = variable_get('cache');
+ }
+ drupal_block_denied(ip_address());
+ // If there is no session cookie and cache is enabled (or forced), try
+ // to serve a cached page.
+ if (!isset($_COOKIE[session_name()]) && $cache_enabled) {
+ // Make sure there is a user object because its timestamp will be
+ // checked, hook_boot might check for anonymous user etc.
+ $user = drupal_anonymous_user();
+ // Get the page from the cache.
+ $cache = drupal_page_get_cache();
+ // If there is a cached page, display it.
+ if (is_object($cache)) {
+ header('X-Drupal-Cache: HIT');
+ // Restore the metadata cached with the page.
+ $_GET['q'] = $cache->data['path'];
+ drupal_set_title($cache->data['title'], PASS_THROUGH);
+ date_default_timezone_set(drupal_get_user_timezone());
+ // If the skipping of the bootstrap hooks is not enforced, call
+ // hook_boot.
+ if (variable_get('page_cache_invoke_hooks', TRUE)) {
+ bootstrap_invoke_all('boot');
+ }
+ drupal_serve_page_from_cache($cache);
+ // If the skipping of the bootstrap hooks is not enforced, call
+ // hook_exit.
+ if (variable_get('page_cache_invoke_hooks', TRUE)) {
+ bootstrap_invoke_all('exit');
+ }
+ // We are done.
+ exit;
+ }
+ else {
+ header('X-Drupal-Cache: MISS');
+ }
+ }
+}
+
+/**
+ * Bootstrap database: Initialize database system and register autoload functions.
+ */
+function _drupal_bootstrap_database() {
+ // Redirect the user to the installation script if Drupal has not been
+ // installed yet (i.e., if no $databases array has been defined in the
+ // settings.php file) and we are not already installing.
+ if (empty($GLOBALS['databases']) && !drupal_installation_attempted()) {
+ include_once DRUPAL_ROOT . '/includes/install.inc';
+ install_goto('install.php');
+ }
+
+ // The user agent header is used to pass a database prefix in the request when
+ // running tests. However, for security reasons, it is imperative that we
+ // validate we ourselves made the request.
+ if ($test_prefix = drupal_valid_test_ua()) {
+ // Set the test run id for use in other parts of Drupal.
+ $test_info = &$GLOBALS['drupal_test_info'];
+ $test_info['test_run_id'] = $test_prefix;
+ $test_info['in_child_site'] = TRUE;
+
+ foreach ($GLOBALS['databases']['default'] as &$value) {
+ // Extract the current default database prefix.
+ if (!isset($value['prefix'])) {
+ $current_prefix = '';
+ }
+ elseif (is_array($value['prefix'])) {
+ $current_prefix = $value['prefix']['default'];
+ }
+ else {
+ $current_prefix = $value['prefix'];
+ }
+
+ // Remove the current database prefix and replace it by our own.
+ $value['prefix'] = array(
+ 'default' => $current_prefix . $test_prefix,
+ );
+ }
+ }
+
+ // Initialize the database system. Note that the connection
+ // won't be initialized until it is actually requested.
+ require_once DRUPAL_ROOT . '/includes/database/database.inc';
+
+ // Register autoload functions so that we can access classes and interfaces.
+ // The database autoload routine comes first so that we can load the database
+ // system without hitting the database. That is especially important during
+ // the install or upgrade process.
+ spl_autoload_register('drupal_autoload_class');
+ spl_autoload_register('drupal_autoload_interface');
+}
+
+/**
+ * Bootstrap variables: Load system variables and all enabled bootstrap modules.
+ */
+function _drupal_bootstrap_variables() {
+ global $conf;
+
+ // Initialize the lock system.
+ require_once DRUPAL_ROOT . '/' . variable_get('lock_inc', 'includes/lock.inc');
+ lock_initialize();
+
+ // Load variables from the database, but do not overwrite variables set in settings.php.
+ $conf = variable_initialize(isset($conf) ? $conf : array());
+ // Load bootstrap modules.
+ require_once DRUPAL_ROOT . '/includes/module.inc';
+ module_load_all(TRUE);
+}
+
+/**
+ * Bootstrap page header: Invoke hook_boot(), initialize locking system, and send default HTTP headers.
+ */
+function _drupal_bootstrap_page_header() {
+ bootstrap_invoke_all('boot');
+
+ if (!drupal_is_cli()) {
+ ob_start();
+ drupal_page_header();
+ }
+}
+
+/**
+ * Returns the current bootstrap phase for this Drupal process.
+ *
+ * The current phase is the one most recently completed by drupal_bootstrap().
+ *
+ * @see drupal_bootstrap()
+ */
+function drupal_get_bootstrap_phase() {
+ return drupal_bootstrap();
+}
+
+/**
+ * Checks the current User-Agent string to see if this is an internal request
+ * from SimpleTest. If so, returns the test prefix for this test.
+ *
+ * @return
+ * Either the simpletest prefix (the string "simpletest" followed by any
+ * number of digits) or FALSE if the user agent does not contain a valid
+ * HMAC and timestamp.
+ */
+function drupal_valid_test_ua() {
+ global $drupal_hash_salt;
+ // No reason to reset this.
+ static $test_prefix;
+
+ if (isset($test_prefix)) {
+ return $test_prefix;
+ }
+
+ if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) {
+ list(, $prefix, $time, $salt, $hmac) = $matches;
+ $check_string = $prefix . ';' . $time . ';' . $salt;
+ // We use the salt from settings.php to make the HMAC key, since
+ // the database is not yet initialized and we can't access any Drupal variables.
+ // The file properties add more entropy not easily accessible to others.
+ $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $time_diff = REQUEST_TIME - $time;
+ // Since we are making a local request a 5 second time window is allowed,
+ // and the HMAC must match.
+ if ($time_diff >= 0 && $time_diff <= 5 && $hmac == drupal_hmac_base64($check_string, $key)) {
+ $test_prefix = $prefix;
+ return $test_prefix;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Generate a user agent string with a HMAC and timestamp for simpletest.
+ */
+function drupal_generate_test_ua($prefix) {
+ global $drupal_hash_salt;
+ static $key;
+
+ if (!isset($key)) {
+ // We use the salt from settings.php to make the HMAC key, since
+ // the database is not yet initialized and we can't access any Drupal variables.
+ // The file properties add more entropy not easily accessible to others.
+ $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ }
+ // Generate a moderately secure HMAC based on the database credentials.
+ $salt = uniqid('', TRUE);
+ $check_string = $prefix . ';' . time() . ';' . $salt;
+ return $check_string . ';' . drupal_hmac_base64($check_string, $key);
+}
+
+/**
+ * Enables use of the theme system without requiring database access.
+ *
+ * Loads and initializes the theme system for site installs, updates and when
+ * the site is in maintenance mode. This also applies when the database fails.
+ *
+ * @see _drupal_maintenance_theme()
+ */
+function drupal_maintenance_theme() {
+ require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc';
+ _drupal_maintenance_theme();
+}
+
+/**
+ * Returns a simple 404 Not Found page.
+ *
+ * If fast 404 pages are enabled, and this is a matching page then print a
+ * simple 404 page and exit.
+ *
+ * This function is called from drupal_deliver_html_page() at the time when a
+ * a normal 404 page is generated, but it can also optionally be called directly
+ * from settings.php to prevent a Drupal bootstrap on these pages. See
+ * documentation in settings.php for the benefits and drawbacks of using this.
+ *
+ * Paths to dynamically-generated content, such as image styles, should also be
+ * accounted for in this function.
+ */
+function drupal_fast_404() {
+ $exclude_paths = variable_get('404_fast_paths_exclude', FALSE);
+ if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) {
+ $fast_paths = variable_get('404_fast_paths', FALSE);
+ if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
+ drupal_add_http_header('Status', '404 Not Found');
+ $fast_404_html = variable_get('404_fast_html', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>');
+ // Replace @path in the variable with the page path.
+ print strtr($fast_404_html, array('@path' => check_plain(request_uri())));
+ exit;
+ }
+ }
+}
+
+/**
+ * Return TRUE if a Drupal installation is currently being attempted.
+ */
+function drupal_installation_attempted() {
+ return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
+}
+
+/**
+ * Returns the name of the proper localization function.
+ *
+ * get_t() exists to support localization for code that might run during
+ * the installation phase, when some elements of the system might not have
+ * loaded.
+ *
+ * This would include implementations of hook_install(), which could run
+ * during the Drupal installation phase, and might also be run during
+ * non-installation time, such as while installing the module from the the
+ * module administration page.
+ *
+ * Example usage:
+ * @code
+ * $t = get_t();
+ * $translated = $t('translate this');
+ * @endcode
+ *
+ * Use t() if your code will never run during the Drupal installation phase.
+ * Use st() if your code will only run during installation and never any other
+ * time. Use get_t() if your code could run in either circumstance.
+ *
+ * @see t()
+ * @see st()
+ * @ingroup sanitization
+ */
+function get_t() {
+ static $t;
+ // This is not converted to drupal_static because there is no point in
+ // resetting this as it can not change in the course of a request.
+ if (!isset($t)) {
+ $t = drupal_installation_attempted() ? 'st' : 't';
+ }
+ return $t;
+}
+
+/**
+ * Initialize all the defined language types.
+ */
+function drupal_language_initialize() {
+ global $language;
+ $types = language_types();
+
+ // Ensure the language is correctly returned, even without multilanguage
+ // support. Also make sure we have a $language fallback, in case a language
+ // negotiation callback needs to do a full bootstrap.
+ // Useful for eg. XML/HTML 'lang' attributes.
+ $default = language_default();
+ foreach ($types as $type) {
+ $GLOBALS[$type] = $default;
+ }
+ if (drupal_multilingual()) {
+ include_once DRUPAL_ROOT . '/includes/language.inc';
+ foreach ($types as $type) {
+ $GLOBALS[$type] = language_initialize($type);
+ }
+ // Allow modules to react on language system initialization in multilingual
+ // environments.
+ bootstrap_invoke_all('language_init');
+ }
+
+ // Send appropriate HTTP-Header for browsers and search engines.
+ header('Content-Language: ' . $language->language);
+}
+
+/**
+ * The built-in language types.
+ *
+ * @return
+ * An array of key-values pairs where the key is the language type and the
+ * value is its configurability.
+ */
+function drupal_language_types() {
+ return array(
+ LANGUAGE_TYPE_INTERFACE => TRUE,
+ LANGUAGE_TYPE_CONTENT => FALSE,
+ LANGUAGE_TYPE_URL => FALSE,
+ );
+}
+
+/**
+ * Return true if there is more than one language enabled.
+ */
+function drupal_multilingual() {
+ // The "language_count" variable stores the number of enabled languages to
+ // avoid unnecessarily querying the database when building the list of
+ // enabled languages on monolingual sites.
+ return variable_get('language_count', 1) > 1;
+}
+
+/**
+ * Return an array of the available language types.
+ */
+function language_types() {
+ return array_keys(variable_get('language_types', drupal_language_types()));
+}
+
+/**
+ * Returns a list of installed languages, indexed by the specified key.
+ *
+ * @param $field
+ * (optional) The field to index the list with.
+ *
+ * @return
+ * An associative array, keyed on the values of $field.
+ * - If $field is 'weight' or 'enabled', the array is nested, with the outer
+ * array's values each being associative arrays with language codes as
+ * keys and language objects as values.
+ * - For all other values of $field, the array is only one level deep, and
+ * the array's values are language objects.
+ */
+function language_list($field = 'language') {
+ $languages = &drupal_static(__FUNCTION__);
+ // Init language list
+ if (!isset($languages)) {
+ if (drupal_multilingual() || module_exists('locale')) {
+ $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
+ // Users cannot uninstall the native English language. However, we allow
+ // it to be hidden from the installed languages. Therefore, at least one
+ // other language must be enabled then.
+ if (!$languages['language']['en']->enabled && !variable_get('language_native_enabled', TRUE)) {
+ unset($languages['language']['en']);
+ }
+ }
+ else {
+ // No locale module, so use the default language only.
+ $default = language_default();
+ $languages['language'][$default->language] = $default;
+ }
+ }
+
+ // Return the array indexed by the right field
+ if (!isset($languages[$field])) {
+ $languages[$field] = array();
+ foreach ($languages['language'] as $lang) {
+ // Some values should be collected into an array
+ if (in_array($field, array('enabled', 'weight'))) {
+ $languages[$field][$lang->$field][$lang->language] = $lang;
+ }
+ else {
+ $languages[$field][$lang->$field] = $lang;
+ }
+ }
+ }
+ return $languages[$field];
+}
+
+/**
+ * Default language used on the site
+ *
+ * @param $property
+ * Optional property of the language object to return
+ */
+function language_default($property = NULL) {
+ $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
+ return $property ? $language->$property : $language;
+}
+
+/**
+ * Returns the requested URL path of the page being viewed.
+ *
+ * Examples:
+ * - http://example.com/node/306 returns "node/306".
+ * - http://example.com/drupalfolder/node/306 returns "node/306" while
+ * base_path() returns "/drupalfolder/".
+ * - http://example.com/path/alias (which is a path alias for node/306) returns
+ * "path/alias" as opposed to the internal path.
+ * - http://example.com/index.php returns an empty string (meaning: front page).
+ * - http://example.com/index.php?page=1 returns an empty string.
+ *
+ * @return
+ * The requested Drupal URL path.
+ *
+ * @see current_path()
+ */
+function request_path() {
+ static $path;
+
+ if (isset($path)) {
+ return $path;
+ }
+
+ if (isset($_GET['q'])) {
+ // This is a request with a ?q=foo/bar query string. $_GET['q'] is
+ // overwritten in drupal_path_initialize(), but request_path() is called
+ // very early in the bootstrap process, so the original value is saved in
+ // $path and returned in later calls.
+ $path = $_GET['q'];
+ }
+ elseif (isset($_SERVER['REQUEST_URI'])) {
+ // This request is either a clean URL, or 'index.php', or nonsense.
+ // Extract the path from REQUEST_URI.
+ $request_path = strtok($_SERVER['REQUEST_URI'], '?');
+ $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
+ // Unescape and strip $base_path prefix, leaving q without a leading slash.
+ $path = substr(urldecode($request_path), $base_path_len + 1);
+ // If the path equals the script filename, either because 'index.php' was
+ // explicitly provided in the URL, or because the server added it to
+ // $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some
+ // versions of Microsoft IIS do this), the front page should be served.
+ if ($path == basename($_SERVER['PHP_SELF'])) {
+ $path = '';
+ }
+ }
+ else {
+ // This is the front page.
+ $path = '';
+ }
+
+ // Under certain conditions Apache's RewriteRule directive prepends the value
+ // assigned to $_GET['q'] with a slash. Moreover we can always have a trailing
+ // slash in place, hence we need to normalize $_GET['q'].
+ $path = trim($path, '/');
+
+ return $path;
+}
+
+/**
+ * Return a component of the current Drupal path.
+ *
+ * When viewing a page at the path "admin/structure/types", for example, arg(0)
+ * returns "admin", arg(1) returns "structure", and arg(2) returns "types".
+ *
+ * Avoid use of this function where possible, as resulting code is hard to read.
+ * In menu callback functions, attempt to use named arguments. See the explanation
+ * in menu.inc for how to construct callbacks that take arguments. When attempting
+ * to use this function to load an element from the current path, e.g. loading the
+ * node on a node page, please use menu_get_object() instead.
+ *
+ * @param $index
+ * The index of the component, where each component is separated by a '/'
+ * (forward-slash), and where the first component has an index of 0 (zero).
+ * @param $path
+ * A path to break into components. Defaults to the path of the current page.
+ *
+ * @return
+ * The component specified by $index, or NULL if the specified component was
+ * not found. If called without arguments, it returns an array containing all
+ * the components of the current path.
+ */
+function arg($index = NULL, $path = NULL) {
+ // Even though $arguments doesn't need to be resettable for any functional
+ // reasons (the result of explode() does not depend on any run-time
+ // information), it should be resettable anyway in case a module needs to
+ // free up the memory used by it.
+ // Use the advanced drupal_static() pattern, since this is called very often.
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast)) {
+ $drupal_static_fast['arguments'] = &drupal_static(__FUNCTION__);
+ }
+ $arguments = &$drupal_static_fast['arguments'];
+
+ if (!isset($path)) {
+ $path = $_GET['q'];
+ }
+ if (!isset($arguments[$path])) {
+ $arguments[$path] = explode('/', $path);
+ }
+ if (!isset($index)) {
+ return $arguments[$path];
+ }
+ if (isset($arguments[$path][$index])) {
+ return $arguments[$path][$index];
+ }
+}
+
+/**
+ * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
+ * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
+ * the proxy server, and not the client's. The actual header name can be
+ * configured by the reverse_proxy_header variable.
+ *
+ * @return
+ * IP address of client machine, adjusted for reverse proxy and/or cluster
+ * environments.
+ */
+function ip_address() {
+ $ip_address = &drupal_static(__FUNCTION__);
+
+ if (!isset($ip_address)) {
+ $ip_address = $_SERVER['REMOTE_ADDR'];
+
+ if (variable_get('reverse_proxy', 0)) {
+ $reverse_proxy_header = variable_get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR');
+ if (!empty($_SERVER[$reverse_proxy_header])) {
+ // If an array of known reverse proxy IPs is provided, then trust
+ // the XFF header if request really comes from one of them.
+ $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
+
+ // Turn XFF header into an array.
+ $forwarded = explode(',', $_SERVER[$reverse_proxy_header]);
+
+ // Trim the forwarded IPs; they may have been delimited by commas and spaces.
+ $forwarded = array_map('trim', $forwarded);
+
+ // Tack direct client IP onto end of forwarded array.
+ $forwarded[] = $ip_address;
+
+ // Eliminate all trusted IPs.
+ $untrusted = array_diff($forwarded, $reverse_proxy_addresses);
+
+ // The right-most IP is the most specific we can trust.
+ $ip_address = array_pop($untrusted);
+ }
+ }
+ }
+
+ return $ip_address;
+}
+
+/**
+ * @ingroup schemaapi
+ * @{
+ */
+
+/**
+ * Get the schema definition of a table, or the whole database schema.
+ *
+ * The returned schema will include any modifications made by any
+ * module that implements hook_schema_alter().
+ *
+ * @param $table
+ * The name of the table. If not given, the schema of all tables is returned.
+ * @param $rebuild
+ * If true, the schema will be rebuilt instead of retrieved from the cache.
+ */
+function drupal_get_schema($table = NULL, $rebuild = FALSE) {
+ static $schema;
+
+ if ($rebuild || !isset($table)) {
+ $schema = drupal_get_complete_schema($rebuild);
+ }
+ elseif (!isset($schema)) {
+ $schema = new SchemaCache();
+ }
+
+ if (!isset($table)) {
+ return $schema;
+ }
+ if (isset($schema[$table])) {
+ return $schema[$table];
+ }
+ else {
+ return FALSE;
+ }
+}
+
+/**
+ * Extends DrupalCacheArray to allow for dynamic building of the schema cache.
+ */
+class SchemaCache extends DrupalCacheArray {
+
+ public function __construct() {
+ // Cache by request method.
+ parent::__construct('schema:runtime:' . ($_SERVER['REQUEST_METHOD'] == 'GET'), 'cache');
+ }
+
+ protected function resolveCacheMiss($offset) {
+ $complete_schema = drupal_get_complete_schema();
+ $value = isset($complete_schema[$offset]) ? $complete_schema[$offset] : NULL;
+ $this->storage[$offset] = $value;
+ $this->persist($offset);
+ return $value;
+ }
+}
+
+/**
+ * Get the whole database schema.
+ *
+ * The returned schema will include any modifications made by any
+ * module that implements hook_schema_alter().
+ *
+ * @param $rebuild
+ * If true, the schema will be rebuilt instead of retrieved from the cache.
+ */
+function drupal_get_complete_schema($rebuild = FALSE) {
+ static $schema = array();
+
+ if (empty($schema) || $rebuild) {
+ // Try to load the schema from cache.
+ if (!$rebuild && $cached = cache_get('schema')) {
+ $schema = $cached->data;
+ }
+ // Otherwise, rebuild the schema cache.
+ else {
+ $schema = array();
+ // Load the .install files to get hook_schema.
+ // On some databases this function may be called before bootstrap has
+ // been completed, so we force the functions we need to load just in case.
+ if (function_exists('module_load_all_includes')) {
+ // This function can be called very early in the bootstrap process, so
+ // we force the module_list() cache to be refreshed to ensure that it
+ // contains the complete list of modules before we go on to call
+ // module_load_all_includes().
+ module_list(TRUE);
+ module_load_all_includes('install');
+ }
+
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ // Invoke hook_schema for all modules.
+ foreach (module_implements('schema') as $module) {
+ // Cast the result of hook_schema() to an array, as a NULL return value
+ // would cause array_merge() to set the $schema variable to NULL as well.
+ // That would break modules which use $schema further down the line.
+ $current = (array) module_invoke($module, 'schema');
+ // Set 'module' and 'name' keys for each table, and remove descriptions,
+ // as they needlessly slow down cache_get() for every single request.
+ _drupal_schema_initialize($current, $module);
+ $schema = array_merge($schema, $current);
+ }
+
+ drupal_alter('schema', $schema);
+ // If the schema is empty, avoid saving it: some database engines require
+ // the schema to perform queries, and this could lead to infinite loops.
+ if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
+ cache_set('schema', $schema);
+ }
+ if ($rebuild) {
+ cache_clear_all('schema:', 'cache', TRUE);
+ }
+ }
+ }
+
+ return $schema;
+}
+
+/**
+ * @} End of "ingroup schemaapi".
+ */
+
+
+/**
+ * @ingroup registry
+ * @{
+ */
+
+/**
+ * Confirm that an interface is available.
+ *
+ * This function is rarely called directly. Instead, it is registered as an
+ * spl_autoload() handler, and PHP calls it for us when necessary.
+ *
+ * @param $interface
+ * The name of the interface to check or load.
+ * @return
+ * TRUE if the interface is currently available, FALSE otherwise.
+ */
+function drupal_autoload_interface($interface) {
+ return _registry_check_code('interface', $interface);
+}
+
+/**
+ * Confirm that a class is available.
+ *
+ * This function is rarely called directly. Instead, it is registered as an
+ * spl_autoload() handler, and PHP calls it for us when necessary.
+ *
+ * @param $class
+ * The name of the class to check or load.
+ * @return
+ * TRUE if the class is currently available, FALSE otherwise.
+ */
+function drupal_autoload_class($class) {
+ return _registry_check_code('class', $class);
+}
+
+/**
+ * Helper to check for a resource in the registry.
+ *
+ * @param $type
+ * The type of resource we are looking up, or one of the constants
+ * REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE, which
+ * signal that we should reset or write the cache, respectively.
+ * @param $name
+ * The name of the resource, or NULL if either of the REGISTRY_* constants
+ * is passed in.
+ * @return
+ * TRUE if the resource was found, FALSE if not.
+ * NULL if either of the REGISTRY_* constants is passed in as $type.
+ */
+function _registry_check_code($type, $name = NULL) {
+ static $lookup_cache, $cache_update_needed;
+
+ if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) {
+ return TRUE;
+ }
+
+ if (!isset($lookup_cache)) {
+ $lookup_cache = array();
+ if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
+ $lookup_cache = $cache->data;
+ }
+ }
+
+ // When we rebuild the registry, we need to reset this cache so
+ // we don't keep lookups for resources that changed during the rebuild.
+ if ($type == REGISTRY_RESET_LOOKUP_CACHE) {
+ $cache_update_needed = TRUE;
+ $lookup_cache = NULL;
+ return;
+ }
+
+ // Called from drupal_page_footer, we write to permanent storage if there
+ // changes to the lookup cache for this request.
+ if ($type == REGISTRY_WRITE_LOOKUP_CACHE) {
+ if ($cache_update_needed) {
+ cache_set('lookup_cache', $lookup_cache, 'cache_bootstrap');
+ }
+ return;
+ }
+
+ // $type is either 'interface' or 'class', so we only need the first letter to
+ // keep the cache key unique.
+ $cache_key = $type[0] . $name;
+ if (isset($lookup_cache[$cache_key])) {
+ if ($lookup_cache[$cache_key]) {
+ require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
+ }
+ return (bool) $lookup_cache[$cache_key];
+ }
+
+ // This function may get called when the default database is not active, but
+ // there is no reason we'd ever want to not use the default database for
+ // this query.
+ $file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
+ ':name' => $name,
+ ':type' => $type,
+ ))
+ ->fetchField();
+
+ // Flag that we've run a lookup query and need to update the cache.
+ $cache_update_needed = TRUE;
+
+ // Misses are valuable information worth caching, so cache even if
+ // $file is FALSE.
+ $lookup_cache[$cache_key] = $file;
+
+ if ($file) {
+ require_once DRUPAL_ROOT . '/' . $file;
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+/**
+ * Rescan all enabled modules and rebuild the registry.
+ *
+ * Rescans all code in modules or includes directories, storing the location of
+ * each interface or class in the database.
+ */
+function registry_rebuild() {
+ system_rebuild_module_data();
+ registry_update();
+}
+
+/**
+ * Update the registry based on the latest files listed in the database.
+ *
+ * This function should be used when system_rebuild_module_data() does not need
+ * to be called, because it is already known that the list of files in the
+ * {system} table matches those in the file system.
+ *
+ * @see registry_rebuild()
+ */
+function registry_update() {
+ require_once DRUPAL_ROOT . '/includes/registry.inc';
+ _registry_update();
+}
+
+/**
+ * @} End of "ingroup registry".
+ */
+
+/**
+ * Central static variable storage.
+ *
+ * All functions requiring a static variable to persist or cache data within
+ * a single page request are encouraged to use this function unless it is
+ * absolutely certain that the static variable will not need to be reset during
+ * the page request. By centralizing static variable storage through this
+ * function, other functions can rely on a consistent API for resetting any
+ * other function's static variables.
+ *
+ * Example:
+ * @code
+ * function language_list($field = 'language') {
+ * $languages = &drupal_static(__FUNCTION__);
+ * if (!isset($languages)) {
+ * // If this function is being called for the first time after a reset,
+ * // query the database and execute any other code needed to retrieve
+ * // information about the supported languages.
+ * ...
+ * }
+ * if (!isset($languages[$field])) {
+ * // If this function is being called for the first time for a particular
+ * // index field, then execute code needed to index the information already
+ * // available in $languages by the desired field.
+ * ...
+ * }
+ * // Subsequent invocations of this function for a particular index field
+ * // skip the above two code blocks and quickly return the already indexed
+ * // information.
+ * return $languages[$field];
+ * }
+ * function locale_translate_overview_screen() {
+ * // When building the content for the translations overview page, make
+ * // sure to get completely fresh information about the supported languages.
+ * drupal_static_reset('language_list');
+ * ...
+ * }
+ * @endcode
+ *
+ * In a few cases, a function can have certainty that there is no legitimate
+ * use-case for resetting that function's static variable. This is rare,
+ * because when writing a function, it's hard to forecast all the situations in
+ * which it will be used. A guideline is that if a function's static variable
+ * does not depend on any information outside of the function that might change
+ * during a single page request, then it's ok to use the "static" keyword
+ * instead of the drupal_static() function.
+ *
+ * Example:
+ * @code
+ * function actions_do(...) {
+ * // $stack tracks the number of recursive calls.
+ * static $stack;
+ * $stack++;
+ * if ($stack > variable_get('actions_max_stack', 35)) {
+ * ...
+ * return;
+ * }
+ * ...
+ * $stack--;
+ * }
+ * @endcode
+ *
+ * In a few cases, a function needs a resettable static variable, but the
+ * function is called many times (100+) during a single page request, so
+ * every microsecond of execution time that can be removed from the function
+ * counts. These functions can use a more cumbersome, but faster variant of
+ * calling drupal_static(). It works by storing the reference returned by
+ * drupal_static() in the calling function's own static variable, thereby
+ * removing the need to call drupal_static() for each iteration of the function.
+ * Conceptually, it replaces:
+ * @code
+ * $foo = &drupal_static(__FUNCTION__);
+ * @endcode
+ * with:
+ * @code
+ * // Unfortunately, this does not work.
+ * static $foo = &drupal_static(__FUNCTION__);
+ * @endcode
+ * However, the above line of code does not work, because PHP only allows static
+ * variables to be initializied by literal values, and does not allow static
+ * variables to be assigned to references.
+ * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
+ * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
+ * The example below shows the syntax needed to work around both limitations.
+ * For benchmarks and more information, see http://drupal.org/node/619666.
+ *
+ * Example:
+ * @code
+ * function user_access($string, $account = NULL) {
+ * // Use the advanced drupal_static() pattern, since this is called very often.
+ * static $drupal_static_fast;
+ * if (!isset($drupal_static_fast)) {
+ * $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
+ * }
+ * $perm = &$drupal_static_fast['perm'];
+ * ...
+ * }
+ * @endcode
+ *
+ * @param $name
+ * Globally unique name for the variable. For a function with only one static,
+ * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant)
+ * is recommended. For a function with multiple static variables add a
+ * distinguishing suffix to the function name for each one.
+ * @param $default_value
+ * Optional default value.
+ * @param $reset
+ * TRUE to reset a specific named variable, or all variables if $name is NULL.
+ * Resetting every variable should only be used, for example, for running
+ * unit tests with a clean environment. Should be used only though via
+ * function drupal_static_reset() and the return value should not be used in
+ * this case.
+ *
+ * @return
+ * Returns a variable by reference.
+ *
+ * @see drupal_static_reset()
+ */
+function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
+ static $data = array(), $default = array();
+ // First check if dealing with a previously defined static variable.
+ if (isset($data[$name]) || array_key_exists($name, $data)) {
+ // Non-NULL $name and both $data[$name] and $default[$name] statics exist.
+ if ($reset) {
+ // Reset pre-existing static variable to its default value.
+ $data[$name] = $default[$name];
+ }
+ return $data[$name];
+ }
+ // Neither $data[$name] nor $default[$name] static variables exist.
+ if (isset($name)) {
+ if ($reset) {
+ // Reset was called before a default is set and yet a variable must be
+ // returned.
+ return $data;
+ }
+ // First call with new non-NULL $name. Initialize a new static variable.
+ $default[$name] = $data[$name] = $default_value;
+ return $data[$name];
+ }
+ // Reset all: ($name == NULL). This needs to be done one at a time so that
+ // references returned by earlier invocations of drupal_static() also get
+ // reset.
+ foreach ($default as $name => $value) {
+ $data[$name] = $value;
+ }
+ // As the function returns a reference, the return should always be a
+ // variable.
+ return $data;
+}
+
+/**
+ * Reset one or all centrally stored static variable(s).
+ *
+ * @param $name
+ * Name of the static variable to reset. Omit to reset all variables.
+ */
+function drupal_static_reset($name = NULL) {
+ drupal_static($name, NULL, TRUE);
+}
+
+/**
+ * Detect whether the current script is running in a command-line environment.
+ */
+function drupal_is_cli() {
+ return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
+}
+
+/**
+ * Formats text for emphasized display in a placeholder inside a sentence.
+ * Used automatically by t().
+ *
+ * @param $text
+ * The text to format (plain-text).
+ *
+ * @return
+ * The formatted text (html).
+ */
+function drupal_placeholder($text) {
+ return '<em class="placeholder">' . check_plain($text) . '</em>';
+}
+
+/**
+ * Register a function for execution on shutdown.
+ *
+ * Wrapper for register_shutdown_function() that catches thrown exceptions to
+ * avoid "Exception thrown without a stack frame in Unknown".
+ *
+ * @param $callback
+ * The shutdown function to register.
+ * @param ...
+ * Additional arguments to pass to the shutdown function.
+ *
+ * @return
+ * Array of shutdown functions to be executed.
+ *
+ * @see register_shutdown_function()
+ * @ingroup php_wrappers
+ */
+function &drupal_register_shutdown_function($callback = NULL) {
+ // We cannot use drupal_static() here because the static cache is reset during
+ // batch processing, which breaks batch handling.
+ static $callbacks = array();
+
+ if (isset($callback)) {
+ // Only register the internal shutdown function once.
+ if (empty($callbacks)) {
+ register_shutdown_function('_drupal_shutdown_function');
+ }
+ $args = func_get_args();
+ array_shift($args);
+ // Save callback and arguments
+ $callbacks[] = array('callback' => $callback, 'arguments' => $args);
+ }
+ return $callbacks;
+}
+
+/**
+ * Internal function used to execute registered shutdown functions.
+ */
+function _drupal_shutdown_function() {
+ $callbacks = &drupal_register_shutdown_function();
+
+ // Set the CWD to DRUPAL_ROOT as it is not guaranteed to be the same as it
+ // was in the normal context of execution.
+ chdir(DRUPAL_ROOT);
+
+ try {
+ while (list($key, $callback) = each($callbacks)) {
+ call_user_func_array($callback['callback'], $callback['arguments']);
+ }
+ }
+ catch (Exception $exception) {
+ // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
+ require_once DRUPAL_ROOT . '/includes/errors.inc';
+ if (error_displayable()) {
+ print '<h1>Uncaught exception thrown in shutdown function.</h1>';
+ print '<p>' . _drupal_render_exception_safe($exception) . '</p><hr />';
+ }
+ }
+}
diff --git a/includes/cache-install.inc b/includes/cache-install.inc
new file mode 100644
index 0000000..d9bb0f9
--- /dev/null
+++ b/includes/cache-install.inc
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Provides a stub cache implementation to be used during installation.
+ */
+
+/**
+ * A stub cache implementation to be used during the installation process.
+ *
+ * The stub implementation is needed when database access is not yet available.
+ * Because Drupal's caching system never requires that cached data be present,
+ * these stub functions can short-circuit the process and sidestep the need for
+ * any persistent storage. Obviously, using this cache implementation during
+ * normal operations would have a negative impact on performance.
+ */
+class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterface {
+ function get($cid) {
+ return FALSE;
+ }
+
+ function getMultiple(&$cids) {
+ return array();
+ }
+
+ function set($cid, $data, $expire = CACHE_PERMANENT) {
+ }
+
+ function clear($cid = NULL, $wildcard = FALSE) {
+ // If there is a database cache, attempt to clear it whenever possible. The
+ // reason for doing this is that the database cache can accumulate data
+ // during installation due to any full bootstraps that may occur at the
+ // same time (for example, Ajax requests triggered by the installer). If we
+ // didn't try to clear it whenever this function is called, the data in the
+ // cache would become stale; for example, the installer sometimes calls
+ // variable_set(), which updates the {variable} table and then clears the
+ // cache to make sure that the next page request picks up the new value.
+ // Not actually clearing the cache here therefore leads old variables to be
+ // loaded on the first page requests after installation, which can cause
+ // subtle bugs, some of which would not be fixed unless the site
+ // administrator cleared the cache manually.
+ try {
+ if (class_exists('Database')) {
+ parent::clear($cid, $wildcard);
+ }
+ }
+ // If the attempt at clearing the cache causes an error, that means that
+ // either the database connection is not set up yet or the relevant cache
+ // table in the database has not yet been created, so we can safely do
+ // nothing here.
+ catch (Exception $e) {
+ }
+ }
+
+ function isEmpty() {
+ return TRUE;
+ }
+}
diff --git a/includes/cache.inc b/includes/cache.inc
new file mode 100644
index 0000000..8666874
--- /dev/null
+++ b/includes/cache.inc
@@ -0,0 +1,508 @@
+<?php
+
+/**
+ * Get the cache object for a cache bin.
+ *
+ * By default, this returns an instance of the DrupalDatabaseCache class.
+ * Classes implementing DrupalCacheInterface can register themselves both as a
+ * default implementation and for specific bins.
+ *
+ * @see DrupalCacheInterface
+ *
+ * @param $bin
+ * The cache bin for which the cache object should be returned.
+ * @return DrupalCacheInterface
+ * The cache object associated with the specified bin.
+ */
+function _cache_get_object($bin) {
+ // We do not use drupal_static() here because we do not want to change the
+ // storage of a cache bin mid-request.
+ static $cache_objects;
+ if (!isset($cache_objects[$bin])) {
+ $class = variable_get('cache_class_' . $bin);
+ if (!isset($class)) {
+ $class = variable_get('cache_default_class', 'DrupalDatabaseCache');
+ }
+ $cache_objects[$bin] = new $class($bin);
+ }
+ return $cache_objects[$bin];
+}
+
+/**
+ * Return data from the persistent cache
+ *
+ * Data may be stored as either plain text or as serialized data. cache_get
+ * will automatically return unserialized objects and arrays.
+ *
+ * @param $cid
+ * The cache ID of the data to retrieve.
+ * @param $bin
+ * The cache bin to store the data in. Valid core values are 'cache_block',
+ * 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
+ * 'cache_menu', 'cache_page', 'cache_path', 'cache_update' or 'cache' for
+ * the default cache.
+ *
+ * @return
+ * The cache or FALSE on failure.
+ */
+function cache_get($cid, $bin = 'cache') {
+ return _cache_get_object($bin)->get($cid);
+}
+
+/**
+ * Return data from the persistent cache when given an array of cache IDs.
+ *
+ * @param $cids
+ * An array of cache IDs for the data to retrieve. This is passed by
+ * reference, and will have the IDs successfully returned from cache removed.
+ * @param $bin
+ * The cache bin where the data is stored.
+ * @return
+ * An array of the items successfully returned from cache indexed by cid.
+ */
+function cache_get_multiple(array &$cids, $bin = 'cache') {
+ return _cache_get_object($bin)->getMultiple($cids);
+}
+
+/**
+ * Store data in the persistent cache.
+ *
+ * The persistent cache is split up into several cache bins. In the default
+ * cache implementation, each cache bin corresponds to a database table by the
+ * same name. Other implementations might want to store several bins in data
+ * structures that get flushed together. While it is not a problem for most
+ * cache bins if the entries in them are flushed before their expire time, some
+ * might break functionality or are extremely expensive to recalculate. These
+ * will be marked with a (*). The other bins expired automatically by core.
+ * Contributed modules can add additional bins and get them expired
+ * automatically by implementing hook_flush_caches().
+ *
+ * - cache: Generic cache storage bin (used for variables, theme registry,
+ * locale date, list of simpletest tests etc).
+ *
+ * - cache_block: Stores the content of various blocks.
+ *
+ * - cache field: Stores the field data belonging to a given object.
+ *
+ * - cache_filter: Stores filtered pieces of content.
+ *
+ * - cache_form(*): Stores multistep forms. Flushing this bin means that some
+ * forms displayed to users lose their state and the data already submitted
+ * to them.
+ *
+ * - cache_menu: Stores the structure of visible navigation menus per page.
+ *
+ * - cache_page: Stores generated pages for anonymous users. It is flushed
+ * very often, whenever a page changes, at least for every ode and comment
+ * submission. This is the only bin affected by the page cache setting on
+ * the administrator panel.
+ *
+ * - cache path: Stores the system paths that have an alias.
+ *
+ * - cache update(*): Stores available releases. The update server (for
+ * example, drupal.org) needs to produce the relevant XML for every project
+ * installed on the current site. As this is different for (almost) every
+ * site, it's very expensive to recalculate for the update server.
+ *
+ * The reasons for having several bins are as follows:
+ *
+ * - smaller bins mean smaller database tables and allow for faster selects and
+ * inserts
+ * - we try to put fast changing cache items and rather static ones into
+ * different bins. The effect is that only the fast changing bins will need a
+ * lot of writes to disk. The more static bins will also be better cacheable
+ * with MySQL's query cache.
+ *
+ * @param $cid
+ * The cache ID of the data to store.
+ * @param $data
+ * The data to store in the cache. Complex data types will be automatically
+ * serialized before insertion.
+ * Strings will be stored as plain text and not serialized.
+ * @param $bin
+ * The cache bin to store the data in. Valid core values are 'cache_block',
+ * 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
+ * 'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
+ * cache.
+ * @param $expire
+ * One of the following values:
+ * - CACHE_PERMANENT: Indicates that the item should never be removed unless
+ * explicitly told to using cache_clear_all() with a cache ID.
+ * - CACHE_TEMPORARY: Indicates that the item should be removed at the next
+ * general cache wipe.
+ * - A Unix timestamp: Indicates that the item should be kept at least until
+ * the given time, after which it behaves like CACHE_TEMPORARY.
+ */
+function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
+ return _cache_get_object($bin)->set($cid, $data, $expire);
+}
+
+/**
+ * Expire data from the cache.
+ *
+ * If called without arguments, expirable entries will be cleared from the
+ * cache_page and cache_block bins.
+ *
+ * @param $cid
+ * If set, the cache ID to delete. Otherwise, all cache entries that can
+ * expire are deleted.
+ *
+ * @param $bin
+ * If set, the bin $bin to delete from. Mandatory
+ * argument if $cid is set.
+ *
+ * @param $wildcard
+ * If $wildcard is TRUE, cache IDs starting with $cid are deleted in
+ * addition to the exact cache ID specified by $cid. If $wildcard is
+ * TRUE and $cid is '*' then the entire bin $bin is emptied.
+ */
+function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
+ if (!isset($cid) && !isset($bin)) {
+ // Clear the block cache first, so stale data will
+ // not end up in the page cache.
+ if (module_exists('block')) {
+ cache_clear_all(NULL, 'cache_block');
+ }
+ cache_clear_all(NULL, 'cache_page');
+ return;
+ }
+ return _cache_get_object($bin)->clear($cid, $wildcard);
+}
+
+/**
+ * Check if a cache bin is empty.
+ *
+ * A cache bin is considered empty if it does not contain any valid data for any
+ * cache ID.
+ *
+ * @param $bin
+ * The cache bin to check.
+ * @return
+ * TRUE if the cache bin specified is empty.
+ */
+function cache_is_empty($bin) {
+ return _cache_get_object($bin)->isEmpty();
+}
+
+/**
+ * Interface for cache implementations.
+ *
+ * All cache implementations have to implement this interface.
+ * DrupalDatabaseCache provides the default implementation, which can be
+ * consulted as an example.
+ *
+ * To make Drupal use your implementation for a certain cache bin, you have to
+ * set a variable with the name of the cache bin as its key and the name of
+ * your class as its value. For example, if your implementation of
+ * DrupalCacheInterface was called MyCustomCache, the following line would make
+ * Drupal use it for the 'cache_page' bin:
+ * @code
+ * variable_set('cache_class_cache_page', 'MyCustomCache');
+ * @endcode
+ *
+ * Additionally, you can register your cache implementation to be used by
+ * default for all cache bins by setting the variable 'cache_default_class' to
+ * the name of your implementation of the DrupalCacheInterface, e.g.
+ * @code
+ * variable_set('cache_default_class', 'MyCustomCache');
+ * @endcode
+ *
+ * To implement a completely custom cache bin, use the same variable format:
+ * @code
+ * variable_set('cache_class_custom_bin', 'MyCustomCache');
+ * @endcode
+ * To access your custom cache bin, specify the name of the bin when storing
+ * or retrieving cached data:
+ * @code
+ * cache_set($cid, $data, 'custom_bin', $expire);
+ * cache_get($cid, 'custom_bin');
+ * @endcode
+ *
+ * @see _cache_get_object()
+ * @see DrupalDatabaseCache
+ */
+interface DrupalCacheInterface {
+ /**
+ * Constructor.
+ *
+ * @param $bin
+ * The cache bin for which the object is created.
+ */
+ function __construct($bin);
+
+ /**
+ * Return data from the persistent cache. Data may be stored as either plain
+ * text or as serialized data. cache_get will automatically return
+ * unserialized objects and arrays.
+ *
+ * @param $cid
+ * The cache ID of the data to retrieve.
+ * @return
+ * The cache or FALSE on failure.
+ */
+ function get($cid);
+
+ /**
+ * Return data from the persistent cache when given an array of cache IDs.
+ *
+ * @param $cids
+ * An array of cache IDs for the data to retrieve. This is passed by
+ * reference, and will have the IDs successfully returned from cache
+ * removed.
+ * @return
+ * An array of the items successfully returned from cache indexed by cid.
+ */
+ function getMultiple(&$cids);
+
+ /**
+ * Store data in the persistent cache.
+ *
+ * @param $cid
+ * The cache ID of the data to store.
+ * @param $data
+ * The data to store in the cache. Complex data types will be automatically
+ * serialized before insertion.
+ * Strings will be stored as plain text and not serialized.
+ * @param $expire
+ * One of the following values:
+ * - CACHE_PERMANENT: Indicates that the item should never be removed unless
+ * explicitly told to using cache_clear_all() with a cache ID.
+ * - CACHE_TEMPORARY: Indicates that the item should be removed at the next
+ * general cache wipe.
+ * - A Unix timestamp: Indicates that the item should be kept at least until
+ * the given time, after which it behaves like CACHE_TEMPORARY.
+ */
+ function set($cid, $data, $expire = CACHE_PERMANENT);
+
+
+ /**
+ * Expire data from the cache. If called without arguments, expirable
+ * entries will be cleared from the cache_page and cache_block bins.
+ *
+ * @param $cid
+ * If set, the cache ID to delete. Otherwise, all cache entries that can
+ * expire are deleted.
+ * @param $wildcard
+ * If set to TRUE, the $cid is treated as a substring
+ * to match rather than a complete ID. The match is a right hand
+ * match. If '*' is given as $cid, the bin $bin will be emptied.
+ */
+ function clear($cid = NULL, $wildcard = FALSE);
+
+ /**
+ * Check if a cache bin is empty.
+ *
+ * A cache bin is considered empty if it does not contain any valid data for
+ * any cache ID.
+ *
+ * @return
+ * TRUE if the cache bin specified is empty.
+ */
+ function isEmpty();
+}
+
+/**
+ * Default cache implementation.
+ *
+ * This is Drupal's default cache implementation. It uses the database to store
+ * cached data. Each cache bin corresponds to a database table by the same name.
+ */
+class DrupalDatabaseCache implements DrupalCacheInterface {
+ protected $bin;
+
+ function __construct($bin) {
+ $this->bin = $bin;
+ }
+
+ function get($cid) {
+ $cids = array($cid);
+ $cache = $this->getMultiple($cids);
+ return reset($cache);
+ }
+
+ function getMultiple(&$cids) {
+ try {
+ // Garbage collection necessary when enforcing a minimum cache lifetime.
+ $this->garbageCollection($this->bin);
+
+ // When serving cached pages, the overhead of using db_select() was found
+ // to add around 30% overhead to the request. Since $this->bin is a
+ // variable, this means the call to db_query() here uses a concatenated
+ // string. This is highly discouraged under any other circumstances, and
+ // is used here only due to the performance overhead we would incur
+ // otherwise. When serving an uncached page, the overhead of using
+ // db_select() is a much smaller proportion of the request.
+ $result = db_query('SELECT cid, data, created, expire, serialized FROM {' . db_escape_table($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
+ $cache = array();
+ foreach ($result as $item) {
+ $item = $this->prepareItem($item);
+ if ($item) {
+ $cache[$item->cid] = $item;
+ }
+ }
+ $cids = array_diff($cids, array_keys($cache));
+ return $cache;
+ }
+ catch (Exception $e) {
+ // If the database is never going to be available, cache requests should
+ // return FALSE in order to allow exception handling to occur.
+ return array();
+ }
+ }
+
+ /**
+ * Garbage collection for get() and getMultiple().
+ *
+ * @param $bin
+ * The bin being requested.
+ */
+ protected function garbageCollection() {
+ global $user;
+
+ // Garbage collection necessary when enforcing a minimum cache lifetime.
+ $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
+ if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
+ // Reset the variable immediately to prevent a meltdown in heavy load situations.
+ variable_set('cache_flush_' . $this->bin, 0);
+ // Time to flush old cache data
+ db_delete($this->bin)
+ ->condition('expire', CACHE_PERMANENT, '<>')
+ ->condition('expire', $cache_flush, '<=')
+ ->execute();
+ }
+ }
+
+ /**
+ * Prepare a cached item.
+ *
+ * Checks that items are either permanent or did not expire, and unserializes
+ * data as appropriate.
+ *
+ * @param $cache
+ * An item loaded from cache_get() or cache_get_multiple().
+ * @return
+ * The item with data unserialized as appropriate or FALSE if there is no
+ * valid item to load.
+ */
+ protected function prepareItem($cache) {
+ global $user;
+
+ if (!isset($cache->data)) {
+ return FALSE;
+ }
+ // If enforcing a minimum cache lifetime, validate that the data is
+ // currently valid for this user before we return it by making sure the cache
+ // entry was created before the timestamp in the current session's cache
+ // timer. The cache variable is loaded into the $user object by _drupal_session_read()
+ // in session.inc. If the data is permanent or we're not enforcing a minimum
+ // cache lifetime always return the cached data.
+ if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
+ // This cache data is too old and thus not valid for us, ignore it.
+ return FALSE;
+ }
+
+ if ($cache->serialized) {
+ $cache->data = unserialize($cache->data);
+ }
+
+ return $cache;
+ }
+
+ function set($cid, $data, $expire = CACHE_PERMANENT) {
+ $fields = array(
+ 'serialized' => 0,
+ 'created' => REQUEST_TIME,
+ 'expire' => $expire,
+ );
+ if (!is_string($data)) {
+ $fields['data'] = serialize($data);
+ $fields['serialized'] = 1;
+ }
+ else {
+ $fields['data'] = $data;
+ $fields['serialized'] = 0;
+ }
+
+ try {
+ db_merge($this->bin)
+ ->key(array('cid' => $cid))
+ ->fields($fields)
+ ->execute();
+ }
+ catch (Exception $e) {
+ // The database may not be available, so we'll ignore cache_set requests.
+ }
+ }
+
+ function clear($cid = NULL, $wildcard = FALSE) {
+ global $user;
+
+ if (empty($cid)) {
+ if (variable_get('cache_lifetime', 0)) {
+ // We store the time in the current user's $user->cache variable which
+ // will be saved into the sessions bin by _drupal_session_write(). We then
+ // simulate that the cache was flushed for this user by not returning
+ // cached data that was cached before the timestamp.
+ $user->cache = REQUEST_TIME;
+
+ $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
+ if ($cache_flush == 0) {
+ // This is the first request to clear the cache, start a timer.
+ variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
+ }
+ elseif (REQUEST_TIME > ($cache_flush + variable_get('cache_lifetime', 0))) {
+ // Clear the cache for everyone, cache_lifetime seconds have
+ // passed since the first request to clear the cache.
+ db_delete($this->bin)
+ ->condition('expire', CACHE_PERMANENT, '<>')
+ ->condition('expire', REQUEST_TIME, '<')
+ ->execute();
+ variable_set('cache_flush_' . $this->bin, 0);
+ }
+ }
+ else {
+ // No minimum cache lifetime, flush all temporary cache entries now.
+ db_delete($this->bin)
+ ->condition('expire', CACHE_PERMANENT, '<>')
+ ->condition('expire', REQUEST_TIME, '<')
+ ->execute();
+ }
+ }
+ else {
+ if ($wildcard) {
+ if ($cid == '*') {
+ db_truncate($this->bin)->execute();
+ }
+ else {
+ db_delete($this->bin)
+ ->condition('cid', db_like($cid) . '%', 'LIKE')
+ ->execute();
+ }
+ }
+ elseif (is_array($cid)) {
+ // Delete in chunks when a large array is passed.
+ do {
+ db_delete($this->bin)
+ ->condition('cid', array_splice($cid, 0, 1000), 'IN')
+ ->execute();
+ }
+ while (count($cid));
+ }
+ else {
+ db_delete($this->bin)
+ ->condition('cid', $cid)
+ ->execute();
+ }
+ }
+ }
+
+ function isEmpty() {
+ $this->garbageCollection();
+ $query = db_select($this->bin);
+ $query->addExpression('1');
+ $result = $query->range(0, 1)
+ ->execute()
+ ->fetchField();
+ return empty($result);
+ }
+}
diff --git a/includes/common.inc b/includes/common.inc
new file mode 100644
index 0000000..3a3d274
--- /dev/null
+++ b/includes/common.inc
@@ -0,0 +1,7869 @@
+<?php
+
+/**
+ * @file
+ * Common functions that many Drupal modules will need to reference.
+ *
+ * The functions that are critical and need to be available even when serving
+ * a cached page are instead located in bootstrap.inc.
+ */
+
+/**
+ * @defgroup php_wrappers PHP wrapper functions
+ * @{
+ * Functions that are wrappers or custom implementations of PHP functions.
+ *
+ * Certain PHP functions should not be used in Drupal. Instead, Drupal's
+ * replacement functions should be used.
+ *
+ * For example, for improved or more secure UTF8-handling, or RFC-compliant
+ * handling of URLs in Drupal.
+ *
+ * For ease of use and memorizing, all these wrapper functions use the same name
+ * as the original PHP function, but prefixed with "drupal_". Beware, however,
+ * that not all wrapper functions support the same arguments as the original
+ * functions.
+ *
+ * You should always use these wrapper functions in your code.
+ *
+ * Wrong:
+ * @code
+ * $my_substring = substr($original_string, 0, 5);
+ * @endcode
+ *
+ * Correct:
+ * @code
+ * $my_substring = drupal_substr($original_string, 0, 5);
+ * @endcode
+ *
+ * @}
+ */
+
+/**
+ * Return status for saving which involved creating a new item.
+ */
+define('SAVED_NEW', 1);
+
+/**
+ * Return status for saving which involved an update to an existing item.
+ */
+define('SAVED_UPDATED', 2);
+
+/**
+ * Return status for saving which deleted an existing item.
+ */
+define('SAVED_DELETED', 3);
+
+/**
+ * The default group for system CSS files added to the page.
+ */
+define('CSS_SYSTEM', -100);
+
+/**
+ * The default group for module CSS files added to the page.
+ */
+define('CSS_DEFAULT', 0);
+
+/**
+ * The default group for theme CSS files added to the page.
+ */
+define('CSS_THEME', 100);
+
+/**
+ * The default group for JavaScript libraries, settings or jQuery plugins added
+ * to the page.
+ */
+define('JS_LIBRARY', -100);
+
+/**
+ * The default group for module JavaScript code added to the page.
+ */
+define('JS_DEFAULT', 0);
+
+/**
+ * The default group for theme JavaScript code added to the page.
+ */
+define('JS_THEME', 100);
+
+/**
+ * Error code indicating that the request made by drupal_http_request() exceeded
+ * the specified timeout.
+ */
+define('HTTP_REQUEST_TIMEOUT', -1);
+
+/**
+ * Constants defining cache granularity for blocks and renderable arrays.
+ *
+ * Modules specify the caching patterns for their blocks using binary
+ * combinations of these constants in their hook_block_info():
+ * $block[delta]['cache'] = DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE;
+ * DRUPAL_CACHE_PER_ROLE is used as a default when no caching pattern is
+ * specified. Use DRUPAL_CACHE_CUSTOM to disable standard block cache and
+ * implement
+ *
+ * The block cache is cleared in cache_clear_all(), and uses the same clearing
+ * policy than page cache (node, comment, user, taxonomy added or updated...).
+ * Blocks requiring more fine-grained clearing might consider disabling the
+ * built-in block cache (DRUPAL_NO_CACHE) and roll their own.
+ *
+ * Note that user 1 is excluded from block caching.
+ */
+
+/**
+ * The block should not get cached. This setting should be used:
+ * - for simple blocks (notably those that do not perform any db query),
+ * where querying the db cache would be more expensive than directly generating
+ * the content.
+ * - for blocks that change too frequently.
+ */
+define('DRUPAL_NO_CACHE', -1);
+
+/**
+ * The block is handling its own caching in its hook_block_view(). From the
+ * perspective of the block cache system, this is equivalent to DRUPAL_NO_CACHE.
+ * Useful when time based expiration is needed or a site uses a node access
+ * which invalidates standard block cache.
+ */
+define('DRUPAL_CACHE_CUSTOM', -2);
+
+/**
+ * The block or element can change depending on the roles the user viewing the
+ * page belongs to. This is the default setting for blocks, used when the block
+ * does not specify anything.
+ */
+define('DRUPAL_CACHE_PER_ROLE', 0x0001);
+
+/**
+ * The block or element can change depending on the user viewing the page.
+ * This setting can be resource-consuming for sites with large number of users,
+ * and thus should only be used when DRUPAL_CACHE_PER_ROLE is not sufficient.
+ */
+define('DRUPAL_CACHE_PER_USER', 0x0002);
+
+/**
+ * The block or element can change depending on the page being viewed.
+ */
+define('DRUPAL_CACHE_PER_PAGE', 0x0004);
+
+/**
+ * The block or element is the same for every user on every page where it is visible.
+ */
+define('DRUPAL_CACHE_GLOBAL', 0x0008);
+
+/**
+ * Add content to a specified region.
+ *
+ * @param $region
+ * Page region the content is added to.
+ * @param $data
+ * Content to be added.
+ */
+function drupal_add_region_content($region = NULL, $data = NULL) {
+ static $content = array();
+
+ if (isset($region) && isset($data)) {
+ $content[$region][] = $data;
+ }
+ return $content;
+}
+
+/**
+ * Get assigned content for a given region.
+ *
+ * @param $region
+ * A specified region to fetch content for. If NULL, all regions will be
+ * returned.
+ * @param $delimiter
+ * Content to be inserted between imploded array elements.
+ */
+function drupal_get_region_content($region = NULL, $delimiter = ' ') {
+ $content = drupal_add_region_content();
+ if (isset($region)) {
+ if (isset($content[$region]) && is_array($content[$region])) {
+ return implode($delimiter, $content[$region]);
+ }
+ }
+ else {
+ foreach (array_keys($content) as $region) {
+ if (is_array($content[$region])) {
+ $content[$region] = implode($delimiter, $content[$region]);
+ }
+ }
+ return $content;
+ }
+}
+
+/**
+ * Get the name of the currently active install profile.
+ *
+ * When this function is called during Drupal's initial installation process,
+ * the name of the profile that's about to be installed is stored in the global
+ * installation state. At all other times, the standard Drupal systems variable
+ * table contains the name of the current profile, and we can call variable_get()
+ * to determine what one is active.
+ *
+ * @return $profile
+ * The name of the install profile.
+ */
+function drupal_get_profile() {
+ global $install_state;
+
+ if (isset($install_state['parameters']['profile'])) {
+ $profile = $install_state['parameters']['profile'];
+ }
+ else {
+ $profile = variable_get('install_profile', 'standard');
+ }
+
+ return $profile;
+}
+
+
+/**
+ * Set the breadcrumb trail for the current page.
+ *
+ * @param $breadcrumb
+ * Array of links, starting with "home" and proceeding up to but not including
+ * the current page.
+ */
+function drupal_set_breadcrumb($breadcrumb = NULL) {
+ $stored_breadcrumb = &drupal_static(__FUNCTION__);
+
+ if (isset($breadcrumb)) {
+ $stored_breadcrumb = $breadcrumb;
+ }
+ return $stored_breadcrumb;
+}
+
+/**
+ * Get the breadcrumb trail for the current page.
+ */
+function drupal_get_breadcrumb() {
+ $breadcrumb = drupal_set_breadcrumb();
+
+ if (!isset($breadcrumb)) {
+ $breadcrumb = menu_get_active_breadcrumb();
+ }
+
+ return $breadcrumb;
+}
+
+/**
+ * Returns a string containing RDF namespace declarations for use in XML and
+ * XHTML output.
+ */
+function drupal_get_rdf_namespaces() {
+ $xml_rdf_namespaces = array();
+
+ // Serializes the RDF namespaces in XML namespace syntax.
+ if (function_exists('rdf_get_namespaces')) {
+ foreach (rdf_get_namespaces() as $prefix => $uri) {
+ $xml_rdf_namespaces[] = 'xmlns:' . $prefix . '="' . $uri . '"';
+ }
+ }
+ return count($xml_rdf_namespaces) ? "\n " . implode("\n ", $xml_rdf_namespaces) : '';
+}
+
+/**
+ * Add output to the head tag of the HTML page.
+ *
+ * This function can be called as long the headers aren't sent. Pass no
+ * arguments (or NULL for both) to retrieve the currently stored elements.
+ *
+ * @param $data
+ * A renderable array. If the '#type' key is not set then 'html_tag' will be
+ * added as the default '#type'.
+ * @param $key
+ * A unique string key to allow implementations of hook_html_head_alter() to
+ * identify the element in $data. Required if $data is not NULL.
+ *
+ * @return
+ * An array of all stored HEAD elements.
+ *
+ * @see theme_html_tag()
+ */
+function drupal_add_html_head($data = NULL, $key = NULL) {
+ $stored_head = &drupal_static(__FUNCTION__);
+
+ if (!isset($stored_head)) {
+ // Make sure the defaults, including Content-Type, come first.
+ $stored_head = _drupal_default_html_head();
+ }
+
+ if (isset($data) && isset($key)) {
+ if (!isset($data['#type'])) {
+ $data['#type'] = 'html_tag';
+ }
+ $stored_head[$key] = $data;
+ }
+ return $stored_head;
+}
+
+/**
+ * Returns elements that are always displayed in the HEAD tag of the HTML page.
+ */
+function _drupal_default_html_head() {
+ // Add default elements. Make sure the Content-Type comes first because the
+ // IE browser may be vulnerable to XSS via encoding attacks from any content
+ // that comes before this META tag, such as a TITLE tag.
+ $elements['system_meta_content_type'] = array(
+ '#type' => 'html_tag',
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'http-equiv' => 'Content-Type',
+ 'content' => 'text/html; charset=utf-8',
+ ),
+ // Security: This always has to be output first.
+ '#weight' => -1000,
+ );
+ // Show Drupal and the major version number in the META GENERATOR tag.
+ // Get the major version.
+ list($version, ) = explode('.', VERSION);
+ $elements['system_meta_generator'] = array(
+ '#type' => 'html_tag',
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'name' => 'Generator',
+ 'content' => 'Drupal ' . $version . ' (http://drupal.org)',
+ ),
+ );
+ // Also send the generator in the HTTP header.
+ $elements['system_meta_generator']['#attached']['drupal_add_http_header'][] = array('X-Generator', $elements['system_meta_generator']['#attributes']['content']);
+ return $elements;
+}
+
+/**
+ * Retrieve output to be displayed in the HEAD tag of the HTML page.
+ */
+function drupal_get_html_head() {
+ $elements = drupal_add_html_head();
+ drupal_alter('html_head', $elements);
+ return drupal_render($elements);
+}
+
+/**
+ * Add a feed URL for the current page.
+ *
+ * This function can be called as long the HTML header hasn't been sent.
+ *
+ * @param $url
+ * An internal system path or a fully qualified external URL of the feed.
+ * @param $title
+ * The title of the feed.
+ */
+function drupal_add_feed($url = NULL, $title = '') {
+ $stored_feed_links = &drupal_static(__FUNCTION__, array());
+
+ if (isset($url)) {
+ $stored_feed_links[$url] = theme('feed_icon', array('url' => $url, 'title' => $title));
+
+ drupal_add_html_head_link(array(
+ 'rel' => 'alternate',
+ 'type' => 'application/rss+xml',
+ 'title' => $title,
+ // Force the URL to be absolute, for consistency with other <link> tags
+ // output by Drupal.
+ 'href' => url($url, array('absolute' => TRUE)),
+ ));
+ }
+ return $stored_feed_links;
+}
+
+/**
+ * Get the feed URLs for the current page.
+ *
+ * @param $delimiter
+ * A delimiter to split feeds by.
+ */
+function drupal_get_feeds($delimiter = "\n") {
+ $feeds = drupal_add_feed();
+ return implode($feeds, $delimiter);
+}
+
+/**
+ * @defgroup http_handling HTTP handling
+ * @{
+ * Functions to properly handle HTTP responses.
+ */
+
+/**
+ * Process a URL query parameter array to remove unwanted elements.
+ *
+ * @param $query
+ * (optional) An array to be processed. Defaults to $_GET.
+ * @param $exclude
+ * (optional) A list of $query array keys to remove. Use "parent[child]" to
+ * exclude nested items. Defaults to array('q').
+ * @param $parent
+ * Internal use only. Used to build the $query array key for nested items.
+ *
+ * @return
+ * An array containing query parameters, which can be used for url().
+ */
+function drupal_get_query_parameters(array $query = NULL, array $exclude = array('q'), $parent = '') {
+ // Set defaults, if none given.
+ if (!isset($query)) {
+ $query = $_GET;
+ }
+ // If $exclude is empty, there is nothing to filter.
+ if (empty($exclude)) {
+ return $query;
+ }
+ elseif (!$parent) {
+ $exclude = array_flip($exclude);
+ }
+
+ $params = array();
+ foreach ($query as $key => $value) {
+ $string_key = ($parent ? $parent . '[' . $key . ']' : $key);
+ if (isset($exclude[$string_key])) {
+ continue;
+ }
+
+ if (is_array($value)) {
+ $params[$key] = drupal_get_query_parameters($value, $exclude, $string_key);
+ }
+ else {
+ $params[$key] = $value;
+ }
+ }
+
+ return $params;
+}
+
+/**
+ * Split an URL-encoded query string into an array.
+ *
+ * @param $query
+ * The query string to split.
+ *
+ * @return
+ * An array of url decoded couples $param_name => $value.
+ */
+function drupal_get_query_array($query) {
+ $result = array();
+ if (!empty($query)) {
+ foreach (explode('&', $query) as $param) {
+ $param = explode('=', $param);
+ $result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
+ }
+ }
+ return $result;
+}
+
+/**
+ * Parse an array into a valid, rawurlencoded query string.
+ *
+ * This differs from http_build_query() as we need to rawurlencode() (instead of
+ * urlencode()) all query parameters.
+ *
+ * @param $query
+ * The query parameter array to be processed, e.g. $_GET.
+ * @param $parent
+ * Internal use only. Used to build the $query array key for nested items.
+ *
+ * @return
+ * A rawurlencoded string which can be used as or appended to the URL query
+ * string.
+ *
+ * @see drupal_get_query_parameters()
+ * @ingroup php_wrappers
+ */
+function drupal_http_build_query(array $query, $parent = '') {
+ $params = array();
+
+ foreach ($query as $key => $value) {
+ $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
+
+ // Recurse into children.
+ if (is_array($value)) {
+ $params[] = drupal_http_build_query($value, $key);
+ }
+ // If a query parameter value is NULL, only append its key.
+ elseif (!isset($value)) {
+ $params[] = $key;
+ }
+ else {
+ // For better readability of paths in query strings, we decode slashes.
+ $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value));
+ }
+ }
+
+ return implode('&', $params);
+}
+
+/**
+ * Prepare a 'destination' URL query parameter for use in combination with drupal_goto().
+ *
+ * Used to direct the user back to the referring page after completing a form.
+ * By default the current URL is returned. If a destination exists in the
+ * previous request, that destination is returned. As such, a destination can
+ * persist across multiple pages.
+ *
+ * @see drupal_goto()
+ */
+function drupal_get_destination() {
+ $destination = &drupal_static(__FUNCTION__);
+
+ if (isset($destination)) {
+ return $destination;
+ }
+
+ if (isset($_GET['destination'])) {
+ $destination = array('destination' => $_GET['destination']);
+ }
+ else {
+ $path = $_GET['q'];
+ $query = drupal_http_build_query(drupal_get_query_parameters());
+ if ($query != '') {
+ $path .= '?' . $query;
+ }
+ $destination = array('destination' => $path);
+ }
+ return $destination;
+}
+
+/**
+ * Wrapper around parse_url() to parse a system URL string into an associative array, suitable for url().
+ *
+ * This function should only be used for URLs that have been generated by the
+ * system, resp. url(). It should not be used for URLs that come from external
+ * sources, or URLs that link to external resources.
+ *
+ * The returned array contains a 'path' that may be passed separately to url().
+ * For example:
+ * @code
+ * $options = drupal_parse_url($_GET['destination']);
+ * $my_url = url($options['path'], $options);
+ * $my_link = l('Example link', $options['path'], $options);
+ * @endcode
+ *
+ * This is required, because url() does not support relative URLs containing a
+ * query string or fragment in its $path argument. Instead, any query string
+ * needs to be parsed into an associative query parameter array in
+ * $options['query'] and the fragment into $options['fragment'].
+ *
+ * @param $url
+ * The URL string to parse, f.e. $_GET['destination'].
+ *
+ * @return
+ * An associative array containing the keys:
+ * - 'path': The path of the URL. If the given $url is external, this includes
+ * the scheme and host.
+ * - 'query': An array of query parameters of $url, if existent.
+ * - 'fragment': The fragment of $url, if existent.
+ *
+ * @see url()
+ * @see drupal_goto()
+ * @ingroup php_wrappers
+ */
+function drupal_parse_url($url) {
+ $options = array(
+ 'path' => NULL,
+ 'query' => array(),
+ 'fragment' => '',
+ );
+
+ // External URLs: not using parse_url() here, so we do not have to rebuild
+ // the scheme, host, and path without having any use for it.
+ if (strpos($url, '://') !== FALSE) {
+ // Split off everything before the query string into 'path'.
+ $parts = explode('?', $url);
+ $options['path'] = $parts[0];
+ // If there is a query string, transform it into keyed query parameters.
+ if (isset($parts[1])) {
+ $query_parts = explode('#', $parts[1]);
+ parse_str($query_parts[0], $options['query']);
+ // Take over the fragment, if there is any.
+ if (isset($query_parts[1])) {
+ $options['fragment'] = $query_parts[1];
+ }
+ }
+ }
+ // Internal URLs.
+ else {
+ // parse_url() does not support relative URLs, so make it absolute. E.g. the
+ // relative URL "foo/bar:1" isn't properly parsed.
+ $parts = parse_url('http://example.com/' . $url);
+ // Strip the leading slash that was just added.
+ $options['path'] = substr($parts['path'], 1);
+ if (isset($parts['query'])) {
+ parse_str($parts['query'], $options['query']);
+ }
+ if (isset($parts['fragment'])) {
+ $options['fragment'] = $parts['fragment'];
+ }
+ }
+ // The 'q' parameter contains the path of the current page if clean URLs are
+ // disabled. It overrides the 'path' of the URL when present, even if clean
+ // URLs are enabled, due to how Apache rewriting rules work.
+ if (isset($options['query']['q'])) {
+ $options['path'] = $options['query']['q'];
+ unset($options['query']['q']);
+ }
+
+ return $options;
+}
+
+/**
+ * Encodes a Drupal path for use in a URL.
+ *
+ * For aesthetic reasons slashes are not escaped.
+ *
+ * Note that url() takes care of calling this function, so a path passed to that
+ * function should not be encoded in advance.
+ *
+ * @param $path
+ * The Drupal path to encode.
+ */
+function drupal_encode_path($path) {
+ return str_replace('%2F', '/', rawurlencode($path));
+}
+
+/**
+ * Send the user to a different Drupal page.
+ *
+ * This issues an on-site HTTP redirect. The function makes sure the redirected
+ * URL is formatted correctly.
+ *
+ * Usually the redirected URL is constructed from this function's input
+ * parameters. However you may override that behavior by setting a
+ * destination in either the $_REQUEST-array (i.e. by using
+ * the query string of an URI) This is used to direct the user back to
+ * the proper page after completing a form. For example, after editing
+ * a post on the 'admin/content'-page or after having logged on using the
+ * 'user login'-block in a sidebar. The function drupal_get_destination()
+ * can be used to help set the destination URL.
+ *
+ * Drupal will ensure that messages set by drupal_set_message() and other
+ * session data are written to the database before the user is redirected.
+ *
+ * This function ends the request; use it instead of a return in your menu
+ * callback.
+ *
+ * @param $path
+ * A Drupal path or a full URL.
+ * @param $options
+ * An associative array of additional URL options to pass to url().
+ * @param $http_response_code
+ * Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
+ * - 301 Moved Permanently (the recommended value for most redirects)
+ * - 302 Found (default in Drupal and PHP, sometimes used for spamming search
+ * engines)
+ * - 303 See Other
+ * - 304 Not Modified
+ * - 305 Use Proxy
+ * - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
+ * Note: Other values are defined by RFC 2616, but are rarely used and poorly
+ * supported.
+ *
+ * @see drupal_get_destination()
+ * @see url()
+ */
+function drupal_goto($path = '', array $options = array(), $http_response_code = 302) {
+ // A destination in $_GET always overrides the function arguments.
+ // We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector.
+ if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
+ $destination = drupal_parse_url($_GET['destination']);
+ $path = $destination['path'];
+ $options['query'] = $destination['query'];
+ $options['fragment'] = $destination['fragment'];
+ }
+
+ drupal_alter('drupal_goto', $path, $options, $http_response_code);
+
+ // The 'Location' HTTP header must be absolute.
+ $options['absolute'] = TRUE;
+
+ $url = url($pat