Skip to content
Commits on Source (9709)
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# Ignore configuration files that may contain sensitive information.
sites/*/settings*.php
# Ignore paths that contain user-generated content.
sites/*/files
sites/*/private
......@@ -3,7 +3,7 @@
#
# Protect files and directories from prying eyes.
<FilesMatch "(\.(engine|inc|info|install|module|profile|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)|code-style\.pl|Entries.*|Repository|Root|Tag|Template)$">
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock))$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
Order allow,deny
</FilesMatch>
......@@ -13,71 +13,137 @@ Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Customized error messages.
# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php
# Set the default handler.
DirectoryIndex index.php
DirectoryIndex index.php index.html index.htm
# Override PHP settings. More in sites/default/settings.php
# but the following cannot be changed at runtime.
# PHP 4, Apache 1.
<IfModule mod_php4.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
</IfModule>
# PHP 4, Apache 2.
<IfModule sapi_apache2.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
</IfModule>
# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_environment_initialize() in
# includes/bootstrap.inc for settings that can be changed at runtime.
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
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
# Do not cache dynamically generated pages.
ExpiresByType text/html A1
<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
# If your site can be accessed both with and without the prefix www. you
# can use one of the following settings to force user to use only one option:
# Set "protossl" to "s" if we were accessed via https://. This is used later
# if you enable "www." stripping or enforcement, in order to ensure that
# you don't bounce between http and https.
RewriteRule ^ - [E=protossl]
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=protossl:s]
# Make sure Authorization HTTP header is available to PHP
# even when running as CGI or FastCGI.
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# 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:
#
# If you want the site to be accessed WITH the www. only, adapt and
# 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} ^example\.com$ [NC]
# RewriteRule .* http://www.example.com/ [L,R=301]
# RewriteCond %{HTTP_HOST} .
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#
# If you want the site to be accessed only WITHOUT the www. prefix, adapt
# and uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
# RewriteRule .* http://example.com/ [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%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory and
# the rewrite rules are not working properly.
#RewriteBase /drupal
# 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 /
# Rewrite URLs of the form 'index.php?q=x'.
# 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
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
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>
# $Id$
# Add headers to all responses.
<IfModule mod_headers.c>
# Disable content sniffing, since it's an attack vector.
Header always set X-Content-Type-Options nosniff
</IfModule>
This diff is collapsed.
All Drupal code is Copyright 2001 - 2013 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:
Javascript
Farbtastic - Copyright (c) 2010 Matt Farina
jQuery - Copyright (c) 2010 John Resig
jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman
jQuery Cookie - Copyright (c) 2006 Klaus Hartl
jQuery Form - Copyright (c) 2010 Mike Alsup
jQuery Once - Copyright (c) 2009 Konstantin K�fer
jQuery UI - Copyright (c) 2010 by the original authors
(http://jqueryui.com/about)
Sizzle.js - Copyright (c) 2010 The Dojo Foundation (http://sizzlejs.com/)
PHP
ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet
// $Id$
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.
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):
......@@ -14,31 +13,33 @@ 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 login and set the access database rights:
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:
prompt, enter the following command:
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE
TEMPORARY TABLES, LOCK TABLES
ON databasename.*
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
CREATE TEMPORARY TABLES ON databasename.*
TO 'username'@'localhost' IDENTIFIED BY 'password';
where
where:
'databasename' is the name of your database
'username@localhost' is the username of your MySQL account
'username' is the username of your MySQL account
'localhost' is the web server host where Drupal is installed
'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.
Note: Unless the database user/host combination for your Drupal installation
has all of the privileges listed above (except possibly CREATE TEMPORARY TABLES,
which is currently only used by Drupal core automated tests and some
contributed modules), you will not be able to install or run Drupal.
If successful, MySQL will reply with:
Query OK, 0 rows affected
To activate the new permissions, enter the following command:
FLUSH PRIVILEGES;
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.
// $Id$
CREATE THE PostgreSQL DATABASE
------------------------------
......@@ -7,22 +6,39 @@ 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 setup (e.g.
by your host) or you want to create new user for use with Drupal only. The
following command creates a new user named "username" and asks for a
password for that 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-adduser --no-createdb username
createuser --pwprompt --encrypted --no-createrole --no-createdb username
If everything works correctly, you'll see a "CREATE USER" notice.
If there are no errors, then the command was successful.
2. CREATE THE DRUPAL DATABASE
2. CREATE DRUPAL DATABASE
This step is only necessary if you don't already have a database setup (e.g.
by your host) or you want to create new database for use with Drupal only.
The following command creates a new database named "databasename", which is
owned by previously created "username":
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=UNICODE --owner=username databasename
createdb --encoding=UTF8 --owner=username databasename
If everything works correctly, you'll see a "CREATE DATABASE" notice.
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.
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.
This diff is collapsed.
// $Id$
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
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.
......@@ -305,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
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
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.
......
// $Id$
List of maintainers
--------------------------------------------------------------------------------
Drupal core is built and maintained by the Drupal project community. Everyone is
encouraged to submit issues and changes (patches) to improve Drupal, and to
contribute in other ways -- see https://www.drupal.org/contribute to find out
how.
LEGEND
======
Branch maintainers
------------------
- M: the maintainer
- S: status:
"supported" : someone is actually paid to look after this.
"maintained" : someone actually looks after it.
"fixes/patches" : it has a maintainer but they don't have time to
do much other than throw the odd patch in.
"orphan" : no current maintainer, but maybe you could take
the role as you write new code?
- W: website with status or information
The Drupal Core branch maintainers oversee the development of Drupal as a whole.
The branch maintainers for Drupal 7 are:
--------------------------------------------------------------------------------
- Dries Buytaert 'dries' https://www.drupal.org/u/dries
- Angela Byron 'webchick' https://www.drupal.org/u/webchick
- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
BLOG API
M: James Walker <walkah@walkah.net>
S: maintained
DISTRIBUTED AUTHENTICATION MODULES
M: Moshe Weitzman <weitzman@tejasa.com>
S: maintained
Component maintainers
---------------------
DOCUMENTATION COORDINATOR
M: Steven Peck <speck@blkmtn.org>
S: maintained
The Drupal Core component maintainers oversee the development of Drupal
subsystems. See https://www.drupal.org/contribute/core-maintainers for more
information on their responsibilities, and to find out how to become a component
maintainer. Current component maintainers for Drupal 7:
FILTER SYSTEM
M: Steven Wittens <unconed@drupal.org>
S: maintained
Ajax system
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
FORM SYSTEM
M: Károly Négyesi <chx@mail.tvnet.hu>
S: maintained
Base system
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
LOCALE MODULE
M: Gabor Hojtsy <goba@php.net>
S: maintained
Batch system
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
MENU SYSTEM
M: Richard Archer <drupal@juggernaut.com.au>
S: maintained
Cache system
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
PATH MODULE
M: Matt Westgate <drupal@asitis.org>
S: maintained
Cron system
- Derek Wright 'dww' https://www.drupal.org/u/dww
POSTGRESQL
M: Sammy Spets <sammys-drupal@synerger.com>
S: maintained
Database system
- Larry Garfield 'Crell' https://www.drupal.org/u/crell
SECURITY COORDINATOR
M: Heine Deelstra <hdeelstra@gmail.com>
S: maintained
- MySQL driver
- Larry Garfield 'Crell' https://www.drupal.org/u/crell
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
STATISTICS MODULE
M: Jeremy Andrews <jeremy@kerneltrap.com>
S: maintained
- PostgreSQL driver
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
- Josh Waihi 'fiasco' https://www.drupal.org/u/josh-waihi
XML-RPC SERVER/CLIENT
M: Károly Négyesi <chx@mail.tvnet.hu>
S: maintained
- Sqlite driver
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
DEBIAN PACKAGE
M: Hilko Bengen <bengen@debian.org>
S: maintained
Database update system
- Ashok Modi 'BTMash' https://www.drupal.org/u/btmash
TRANSLATIONS COORDINATOR
M: Gerhard Killesreiter <gerhard@killesreiter.de>
S: maintained
Entity system
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
THE REST:
M: Dries <dries@drupal.org>
File system
- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Form system
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Image system
- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Install system
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
JavaScript
- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
- Steve De Jonghe 'seutje' https://www.drupal.org/u/seutje
Language system
- Francesco Placella 'plach' https://www.drupal.org/u/plach
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Lock system
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Mail system
- ?
Markup
- Jacine Luisi 'Jacine' https://www.drupal.org/u/jacine
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Menu system
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Path system
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Render system
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Theme system
- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Joon Park 'dvessel' https://www.drupal.org/u/dvessel
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Token system
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
XML-RPC system
- Frederic G. Marand 'fgm' https://www.drupal.org/u/fgm
Topic coordinators
------------------
Accessibility
- Everett Zufelt 'Everett Zufelt' https://www.drupal.org/u/everett-zufelt
- Brandon Bowersox-Johnson 'bowersox' https://www.drupal.org/u/bowersox
Documentation
- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
Translations
- Gerhard Killesreiter 'killes' https://www.drupal.org/u/gerhard-killesreiter
User experience and usability
- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
- Bojhan Somers 'Bojhan' https://www.drupal.org/u/bojhan
Node Access
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
Security team
-----------------
To report a security issue, see: https://www.drupal.org/security-team/report-issue
The Drupal security team provides Security Advisories for vulnerabilities,
assists developers in resolving security issues, and provides security
documentation. See https://www.drupal.org/security-team for more information.
The security team lead is:
- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess
Module maintainers
------------------
Aggregator module
- ?
Block module
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Blog module
- ?
Book module
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Color module
- ?
Comment module
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Contact module
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
Contextual module
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Dashboard module
- ?
Database logging module
- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
Field module
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
- Barry Jaspan 'bjaspan' https://www.drupal.org/u/bjaspan
Field UI module
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
File module
- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Filter module
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Forum module
- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
Help module
- ?
Image module
- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Locale module
- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
Menu module
- ?
Node module
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
OpenID module
- Vojtech Kusy 'wojtha' https://www.drupal.org/u/wojtha
- Christian Schmidt 'c960657' https://www.drupal.org/u/c960657
- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Overlay module
- Katherine Senzee 'ksenzee' https://www.drupal.org/u/ksenzee
Path module
- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
PHP module
- ?
Poll module
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
Profile module
- ?
RDF module
- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor
Search module
- Doug Green 'douggreen' https://www.drupal.org/u/douggreen
Shortcut module
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
Simpletest module
- Jimmy Berry 'boombatower' https://www.drupal.org/u/boombatower
Statistics module
- Tim Millwood 'timmillwood' https://www.drupal.org/u/timmillwood
Syslog module
- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
System module
- ?
Taxonomy module
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
Toolbar module
- ?
Tracker module
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Translation module
- Francesco Placella 'plach' https://www.drupal.org/u/plach
Trigger module
- ?
Update module
- Derek Wright 'dww' https://www.drupal.org/u/dww
User module
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Theme maintainers
-----------------
Bartik theme
- Jen Simmons 'jensimmons' https://www.drupal.org/u/jensimmons
- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Garland theme
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Seven theme
- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Stark theme
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
CONTENTS OF THIS FILE
---------------------
* About Drupal
* Configuration and features
* Installation profiles
* 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.
INSTALLATION PROFILES
---------------------
Installation profiles define additional steps (such as enabling modules,
defining content types, etc.) that run after the base installation provided
by core when Drupal is first installed. There are two basic installation
profiles provided with Drupal core.
Installation profiles from the Drupal community modify the installation process
to provide a website for a specific use case, such as a CMS for media
publishers, a web-based project tracking tool, or a full-fledged CRM for
non-profit organizations raising money and accepting donations. They can be
distributed as bare installation profiles or as "distributions". Distributions
include Drupal core, the installation profile, and all other required
extensions, such as contributed and custom modules, themes, and third-party
libraries. Bare installation profiles require you to download Drupal Core and
the required extensions separately; place the downloaded profile in the
/profiles directory before you start the installation process. Note that the
contents of this directory may be overwritten during updates of Drupal core;
it is advised to keep code backups or use a version control system.
Additionally, modules and themes may be placed inside subdirectories in a
specific installation profile such as profiles/your_site_profile/modules and
profiles/your_site_profile/themes respectively to restrict their usage to only
sites that were installed with that specific profile.
More about installation profiles and distributions:
* Read about the difference between installation profiles and distributions:
http://drupal.org/node/1089736
* Download contributed installation profiles and distributions:
http://drupal.org/project/distributions
* Develop your own installation profile or distribution:
http://drupal.org/developing/distributions
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
// $Id$
INTRODUCTION
------------
This document describes how to:
UPGRADING
---------
* 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.
1. Backup your database and Drupal directory - especially your
"sites" directory which contains your configuration file and
added modules and themes, any contributed modules in your
"modules" directory, and your "files" directory which contains
uploaded files.
* Upgrade your Drupal site's major version from 6.x to 7.x.
Note: for a single site setup the configuration file is the
"settings.php" file located at sites/default/settings.php.
For multisite configuration the configuration file is located
in a structure like the following:
First steps and definitions:
sites/default/settings.php
sites/example.com/settings.php
sites/sub.example.com/settings.php
sites/sub.example.com.path/settings.php
* 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).
More information on multisite configuration is located in
the INSTALL.txt file.
* All directories mentioned in this document are relative to the directory of
your Drupal installation.
2. Log on as the user with user ID 1. User ID 1 is the first
account created and the main administrator account. User
ID 1 needs to be logged in so that you can access update.php
(step 9) which can only be run by user ID 1. Do not close
your browser until step 10 is complete.
* 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
3. Place the site in "Off-line" mode, to mask any errors from
site visitors.
* 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.
4. Disable contributed modules and switch to a core theme
(Bluemarine or Garland).
* 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).
5. Remove all of the old files and directories from the Drupal
installation directory.
UPGRADE PROBLEMS
----------------
If you encounter errors during this process,
6. Unpack the new Drupal files and directories into the Drupal
installation directory.
* Note any error messages you see.
7. Copy the backed up "files" and "sites" directories to the
Drupal installation directory. If the original .htaccess or
robots.txt files have been modified, copy the backed up
versions of these files to the installation directory as
well.
* 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.
8. Verify the new configuration file to make sure it has the
latest and correct information.
* Consult one of the support options listed on http://drupal.org/support
9. Re-install contributed modules.
More in-depth information on upgrading can be found at http://drupal.org/upgrade
Note: make sure the version of a module matches your
version of Drupal. Modules from previous versions may
not be compatible with the current version. Check
http://drupal.org/project/Modules for the version of a
module to match your version of Drupal.
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:
10. Run update.php by visiting http://www.example.com/update.php
(replace www.example.com with your drupal installation's
domain name and path). This step will update the database to
the new Drupal installation.
1. Log in as a user with the permission "Administer software updates".
Note: if you are unable to access update.php do the following:
2. Go to Administration > Configuration > Development > Maintenance mode.
Enable the "Put site into maintenance mode" checkbox and save the
configuration.
- Open update.php with a text editor.
3. Remove all old core files and directories, except for the 'sites' directory
and any custom files you added elsewhere.
- There is a line near top of update.php that says
$access_check = TRUE;. Change it to $access_check = FALSE;.
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.
- As soon as the script is done, you must change the update.php
script back to its original form to $access_check = TRUE;.
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:
11. Finally, return site to "Online" mode so your visitors may resume
browsing.
- Locate your settings.php file in the /sites/* directory. (Typically
sites/default.)
For more information on upgrading visit the Drupal handbook at
http://drupal.org/upgrade
- 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.
You can find the release notes for your version at
https://www.drupal.org/project/drupal. At bottom of the project page under
"Downloads" use the link for your version of Drupal to view the release
notes. If your version is not listed, use the 'View all releases' link. From
this page you can scroll down or use the filter to find your version and its
release notes.
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. In addition to updating to the latest available version of Drupal 6.x core,
you must also upgrade all of your contributed modules for Drupal to their
latest Drupal 6.x versions.
4. Log in as user ID 1 (the site maintenance user).
5. Go to Administer > Site configuration > Site maintenance. Select
"Off-line" and save the configuration.
6. Go to Administer > Site building > Themes. Enable "Garland" and select it as
the default theme.
7. 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.
8. On the command line or in your FTP client, remove the file
sites/default/default.settings.php
9. 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.
10. 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.
11. 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.
12. Re-apply any modifications to files such as .htaccess or robots.txt.
13. 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
14. 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.
15. Backup your database after the core upgrade has run.
16. Replace and update your non-core modules and themes, following the
procedures at http://drupal.org/node/948216
17. Go to Administration > Reports > Status report. Verify that everything is
working as expected.
18. Ensure that $update_free_access is FALSE in settings.php.
19. 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
<?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
*/
/**
* Defines the root directory of the Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
/**
* Global flag to identify update.php and authorize.php runs.
*
* Identifies update.php and authorize.php runs, avoiding unwanted operations
* such as hook_init() and hook_exit() invokes, css/js preprocessing and
* translation, and solves some theming issues. The flag is checked in other
* 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, and FALSE if not.
*/
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/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));
}
<?php
// $Id$
/**
* @file
* Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
*/
include_once './includes/bootstrap.inc';
/**
* Root directory of Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
drupal_cron_run();
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();
}
<?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.
*
* @}
*/
/**
* 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;
}
/**
* Returns an action array key (function or ID), given its hash.
*
* 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);
}
This diff is collapsed.
<?php
/**
* @file
* Shared classes and interfaces for the archiver system.
*/
/**
* Defines the common interface for all Archiver classes.
*/
interface ArchiverInterface {
/**
* Constructs 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);
/**
* Adds 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);
/**
* Removes 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);
/**
* Extracts 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());
/**
* Lists all files in the archive.
*
* @return
* An array of file names relative to the root of the archive.
*/
public function listContents();
}
<?php
/**
* @file
* Helper functions and form handlers used for the authorize.php script.
*/
/**
* Form constructor for the file transfer authorization form.
*
* Allows the user to choose a FileTransfer type and supply credentials.
*
* @see authorize_filetransfer_form_validate()
* @see authorize_filetransfer_form_submit()
* @ingroup forms
*/
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;
}
/**
* Generates the Form API array for a given connection backend's settings.
*
* @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;
}
/**
* Sets the default settings on a file transfer connection form recursively.
*
* 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.
*/
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));
}
}
/**
* Form validation handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
* @see authorize_filetransfer_submit()
*/
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',
)));
}
}
}
/**
* Form submission handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
* @see authorize_filetransfer_validate()
*/
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;
}
}
/**
* Runs 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']));
}
/**
* Gets 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;
}
<?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;
}
/**
* Renders the batch processing page based on the current state of the batch.
*
* @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;
}
/**
* Initializes 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();
}
}
/**
* Outputs 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>';
}
/**
* Does one execution pass with JavaScript and returns 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));
}
/**
* Outputs 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));
}
/**
* Processes 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();
}
}
/**
* Formats the percent completion for a batch set.
*
* @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.
*
* @see _batch_process()
*/
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;
}
/**
* Returns the batch set being currently processed.
*/
function &_batch_current_set() {
$batch = &batch_get();
return $batch['sets'][$batch['current_set']];
}
/**
* Retrieves 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;
}
}
/**
* Ends 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 (is_callable($batch_set['finished'])) {
$queue = _batch_queue($batch_set);
$operations = $queue->getAllItems();
call_user_func($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: Stores the current batch data for the next request.
*
* @see _batch_page()
* @see drupal_register_shutdown_function()
*/
function _batch_shutdown() {
if ($batch = batch_get()) {
db_update('batch')
->fields(array('batch' => serialize($batch)))
->condition('bid', $batch['id'])
->execute();
}
}