Initial code using Drupal 6.38
This commit is contained in:
commit
4824608a33
467 changed files with 90887 additions and 0 deletions
116
.htaccess
Normal file
116
.htaccess
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#
|
||||
# Apache/PHP/Drupal settings:
|
||||
#
|
||||
|
||||
# Protect files and directories from prying eyes.
|
||||
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl|svn-base)$|^(code-style\.pl|Entries.*|Repository|Root|Tag|Template|all-wcprops|entries|format)$">
|
||||
Order allow,deny
|
||||
</FilesMatch>
|
||||
|
||||
# Don't show directory listings for URLs which map to a directory.
|
||||
Options -Indexes
|
||||
|
||||
# Follow symbolic links in this directory.
|
||||
Options +FollowSymLinks
|
||||
|
||||
# Make Drupal handle any 404 errors.
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
# Force simple error message for requests for non-existent favicon.ico.
|
||||
<Files favicon.ico>
|
||||
# There is no end quote below, for compatibility with Apache 1.3.
|
||||
ErrorDocument 404 "The requested file favicon.ico was not found.
|
||||
</Files>
|
||||
|
||||
# Set the default handler.
|
||||
DirectoryIndex index.php
|
||||
|
||||
# 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
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 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
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 0
|
||||
</IfModule>
|
||||
|
||||
# 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_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 0
|
||||
</IfModule>
|
||||
|
||||
# Requires mod_expires to be enabled.
|
||||
<IfModule mod_expires.c>
|
||||
# Enable expirations.
|
||||
ExpiresActive On
|
||||
|
||||
# Cache all files for 2 weeks after access (A).
|
||||
ExpiresDefault A1209600
|
||||
|
||||
<FilesMatch \.php$>
|
||||
# Do not allow PHP scripts to be cached unless they explicitly send cache
|
||||
# headers themselves. Otherwise all scripts would have to overwrite the
|
||||
# headers set by mod_expires if they want another caching behavior. This may
|
||||
# fail if an error occurs early in the bootstrap process, and it may cause
|
||||
# problems if a non-Drupal PHP file is installed in a subdirectory.
|
||||
ExpiresActive Off
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
# Various rewrite rules.
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
# If your site can be accessed both with and without the 'www.' prefix, you
|
||||
# can use one of the following settings to redirect users to your preferred
|
||||
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
|
||||
#
|
||||
# To redirect all users to access the site WITH the 'www.' prefix,
|
||||
# (http://example.com/... will be redirected to http://www.example.com/...)
|
||||
# adapt and uncomment the following:
|
||||
# RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
|
||||
# RewriteRule ^(.*)$ http://www.example.com/$1 [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 and adapt the following:
|
||||
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
|
||||
# RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
|
||||
|
||||
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
|
||||
# VirtualDocumentRoot and the rewrite rules are not working properly.
|
||||
# For example if your site is at http://example.com/drupal uncomment and
|
||||
# modify the following line:
|
||||
# RewriteBase /drupal
|
||||
#
|
||||
# If your site is running in a VirtualDocumentRoot at http://example.com/,
|
||||
# uncomment the following line:
|
||||
# RewriteBase /
|
||||
|
||||
# Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
|
||||
</IfModule>
|
||||
|
||||
# $Id$
|
||||
1138
CHANGELOG.txt
Normal file
1138
CHANGELOG.txt
Normal file
File diff suppressed because it is too large
Load diff
29
COPYRIGHT.txt
Normal file
29
COPYRIGHT.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
All Drupal code is Copyright 2001 - 2012 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.
|
||||
|
||||
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) 2007 Matt Farina
|
||||
|
||||
jQuery - Copyright (c) 2008 John Resig
|
||||
|
||||
jQuery Form - Copyright (c) 2007 Mike Alsup
|
||||
|
||||
39
INSTALL.mysql.txt
Normal file
39
INSTALL.mysql.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
CREATE THE MySQL DATABASE
|
||||
--------------------------
|
||||
|
||||
This step is only necessary if you don't already have a database set-up (e.g. by
|
||||
your host). In the following examples, 'username' is an example MySQL user which
|
||||
has the CREATE and GRANT privileges. Use the appropriate user name for your
|
||||
system.
|
||||
|
||||
First, you must create a new database for your Drupal site (here, 'databasename'
|
||||
is the name of the new database):
|
||||
|
||||
mysqladmin -u username -p create databasename
|
||||
|
||||
MySQL will prompt for the 'username' database password and then create the
|
||||
initial database files. Next you must login and set the access database rights:
|
||||
|
||||
mysql -u username -p
|
||||
|
||||
Again, you will be asked for the 'username' database password. At the MySQL
|
||||
prompt, enter following command:
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
|
||||
CREATE TEMPORARY TABLES ON databasename.*
|
||||
TO 'username'@'localhost' IDENTIFIED BY 'password';
|
||||
|
||||
where
|
||||
|
||||
'databasename' is the name of your database
|
||||
'username@localhost' is the username of your MySQL account
|
||||
'password' is the password required for that username
|
||||
|
||||
Note: Unless your database user has all of the privileges listed above, you will
|
||||
not be able to run Drupal.
|
||||
|
||||
If successful, MySQL will reply with:
|
||||
|
||||
Query OK, 0 rows affected
|
||||
|
||||
27
INSTALL.pgsql.txt
Normal file
27
INSTALL.pgsql.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
CREATE THE PostgreSQL DATABASE
|
||||
------------------------------
|
||||
|
||||
Note that the database must be created with UTF-8 (Unicode) encoding.
|
||||
|
||||
1. CREATE DATABASE USER
|
||||
|
||||
This step is only necessary if you don't already have a user set up (e.g.
|
||||
by your host) or 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:
|
||||
|
||||
createuser --pwprompt --encrypted --no-adduser --no-createdb username
|
||||
|
||||
If everything works correctly, you'll see a "CREATE USER" notice.
|
||||
|
||||
2. CREATE THE DRUPAL DATABASE
|
||||
|
||||
This step is only necessary if you don't already have a database set up (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":
|
||||
|
||||
createdb --encoding=UNICODE --owner=username databasename
|
||||
|
||||
If everything works correctly, you'll see a "CREATE DATABASE" notice.
|
||||
349
INSTALL.txt
Normal file
349
INSTALL.txt
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Requirements
|
||||
* Optional requirements
|
||||
* Installation
|
||||
* Drupal administration
|
||||
* Customizing your theme(s)
|
||||
* Multisite Configuration
|
||||
* More Information
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
Drupal requires a web server, PHP 4 (4.3.5 or greater) or PHP 5
|
||||
(http://www.php.net/) and either MySQL (http://www.mysql.com/) or PostgreSQL
|
||||
(http://www.postgresql.org/). The Apache web server and MySQL database are
|
||||
recommended; other web server and database combinations such as IIS and
|
||||
PostgreSQL have been tested to a lesser extent. When using MySQL, version 4.1.1
|
||||
or greater is recommended to assure you can safely transfer the database.
|
||||
|
||||
For more detailed information about Drupal requirements, see "Requirements"
|
||||
(http://drupal.org/requirements) in the Drupal handbook.
|
||||
|
||||
For detailed information on how to configure a test server environment using
|
||||
a variety of operating systems and web servers, see "Local server setup"
|
||||
(http://drupal.org/node/157602) in the Drupal handbook.
|
||||
|
||||
OPTIONAL TASKS
|
||||
--------------
|
||||
|
||||
- To use XML-based services such as the Blogger API and RSS syndication,
|
||||
you will need PHP's XML extension. This extension is enabled by default.
|
||||
|
||||
- To use Drupal's "Clean URLs" feature on an Apache web server, you will need
|
||||
the mod_rewrite module and the ability to use local .htaccess files. For
|
||||
Clean URLs support on IIS, see "Using Clean URLs with IIS"
|
||||
(http://drupal.org/node/3854) in the Drupal handbook.
|
||||
|
||||
- Various Drupal features require that the web server process (for
|
||||
example, httpd) be able to initiate outbound connections. This is usually
|
||||
possible, but some hosting providers or server configurations forbid such
|
||||
connections. The features that depend on this functionality include the
|
||||
integrated "Update status" module (which downloads information about
|
||||
available updates of Drupal core and any installed contributed modules and
|
||||
themes), the ability to log in via OpenID, fetching aggregator feeds, or
|
||||
other network-dependent services.
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
1. DOWNLOAD DRUPAL AND OPTIONALLY A TRANSLATION
|
||||
|
||||
You can obtain the latest Drupal release from http://drupal.org/. The files
|
||||
are in .tar.gz format and can be extracted using most compression tools. On a
|
||||
typical Unix command line, use:
|
||||
|
||||
wget http://drupal.org/files/projects/drupal-x.x.tar.gz
|
||||
tar -zxvf drupal-x.x.tar.gz
|
||||
|
||||
This will create a new directory drupal-x.x/ containing all Drupal files
|
||||
and directories. Move the contents of that directory into a directory within
|
||||
your web server's document root or your public HTML directory:
|
||||
|
||||
mv drupal-x.x/* drupal-x.x/.htaccess /var/www/html
|
||||
|
||||
If you would like to have the default English interface translated to a
|
||||
different language, we have good news. You can install and use Drupal in
|
||||
other languages from the start. Check whether a released package of the
|
||||
language desired is available for this Drupal version at
|
||||
http://localize.drupal.org and download the package. Extract
|
||||
the contents to the same directory where you extracted Drupal into.
|
||||
|
||||
2. CREATE THE CONFIGURATION FILE AND GRANT WRITE PERMISSIONS
|
||||
|
||||
Drupal comes with a default.settings.php file in the sites/default
|
||||
directory. The installer uses this file as a template to create your
|
||||
settings file using the details you provide through the install process.
|
||||
To avoid problems when upgrading, Drupal is not packaged with an actual
|
||||
settings file. You must create a file named settings.php. You may do so
|
||||
by making a copy of default.settings.php (or create an empty file with
|
||||
this name in the same directory). For example, (from the installation
|
||||
directory) make a copy of the default.settings.php file with the command:
|
||||
|
||||
cp sites/default/default.settings.php sites/default/settings.php
|
||||
|
||||
Next, give the web server write privileges to the sites/default/settings.php
|
||||
file with the command (from the installation directory):
|
||||
|
||||
chmod o+w sites/default/settings.php
|
||||
|
||||
So that the files directory can be created automatically, give the web server
|
||||
write privileges to the sites/default directory with the command (from the
|
||||
installation directory):
|
||||
|
||||
chmod o+w sites/default
|
||||
|
||||
3. CREATE THE DRUPAL DATABASE
|
||||
|
||||
Drupal requires access to a database in order to be installed. Your database
|
||||
user will need sufficient privileges to run Drupal. Additional information
|
||||
about privileges, and instructions to create a database using the command
|
||||
line are available in INSTALL.mysql.txt (for MySQL) or INSTALL.pgsql.txt
|
||||
(for PostgreSQL).
|
||||
|
||||
To create a database using PHPMyAdmin or a web-based control panel consult
|
||||
the documentation or ask your webhost service provider.
|
||||
|
||||
Take note of the username, password, database name and hostname as you
|
||||
create the database. You will enter these items in the install script.
|
||||
|
||||
4. RUN THE INSTALL SCRIPT
|
||||
|
||||
To run the install script point your browser to the base URL of your website
|
||||
(e.g., http://www.example.com).
|
||||
|
||||
You will be guided through several screens to set up the database,
|
||||
create tables, add the first user account and provide basic web
|
||||
site settings.
|
||||
|
||||
The install script will attempt to create a files storage directory
|
||||
in the default location at sites/default/files (the location of the
|
||||
files directory may be changed after Drupal is installed). In some
|
||||
cases, you may need to create the directory and modify its permissions
|
||||
manually. Use the following commands (from the installation directory)
|
||||
to create the files directory and grant the web server write privileges to it:
|
||||
|
||||
mkdir sites/default/files
|
||||
chmod o+w sites/default/files
|
||||
|
||||
The install script will attempt to write-protect the settings.php file and
|
||||
the sites/default directory after saving your configuration. However, you
|
||||
may need to manually write-protect them using the commands (from the
|
||||
installation directory):
|
||||
|
||||
chmod a-w sites/default/settings.php
|
||||
chmod a-w sites/default
|
||||
|
||||
If you make manual changes to the file later, be sure to protect it again
|
||||
after making your modifications. Failure to remove write permissions to that
|
||||
file is a security risk. Although the default location for the settings.php
|
||||
file is at sites/default/settings.php, it may be in another location
|
||||
if you use the multi-site setup, as explained below.
|
||||
|
||||
5. CONFIGURE DRUPAL
|
||||
|
||||
When the install script succeeds, you will be directed to the "Welcome"
|
||||
page, and you will be logged in as the administrator already. Proceed with
|
||||
the initial configuration steps suggested on the "Welcome" page.
|
||||
|
||||
If the default Drupal theme is not displaying properly and links on the page
|
||||
result in "Page Not Found" errors, try manually setting the $base_url variable
|
||||
in the settings.php file if not already set. It's currently known that servers
|
||||
running FastCGI can run into problems if the $base_url variable is left
|
||||
commented out (see http://bugs.php.net/bug.php?id=19656).
|
||||
|
||||
6. REVIEW FILE SYSTEM STORAGE SETTINGS AND FILE PERMISSIONS
|
||||
|
||||
The files directory created in step 4 is the default file system path used
|
||||
to store all uploaded files, as well as some temporary files created by Drupal.
|
||||
After installation, the settings for the file system path may be modified
|
||||
to store uploaded files in a different location.
|
||||
|
||||
It is not necessary to modify this path, but you may wish to change it if:
|
||||
|
||||
* your site runs multiple Drupal installations from a single codebase
|
||||
(modify the file system path of each installation to a different
|
||||
directory so that uploads do not overlap between installations); or,
|
||||
|
||||
* your site runs a number of web server front-ends behind a load
|
||||
balancer or reverse proxy (modify the file system path on each
|
||||
server to point to a shared file repository).
|
||||
|
||||
To modify the file system path:
|
||||
|
||||
* Ensure that the new location for the path exists or create it if
|
||||
necessary. To create a new directory named uploads, for example,
|
||||
use the following command from a shell or system prompt (while in
|
||||
the installation directory):
|
||||
|
||||
mkdir uploads
|
||||
|
||||
* Ensure that the new location for the path is writable by the web
|
||||
server process. To grant write permissions for a directory named
|
||||
uploads, you may need to use the following command from a shell
|
||||
or system prompt (while in the installation directory):
|
||||
|
||||
chmod o+w uploads
|
||||
|
||||
* Access the file system path settings in Drupal by selecting these
|
||||
menu items from the Navigation menu:
|
||||
|
||||
Administer > Site configuration > File system
|
||||
|
||||
Enter the path to the new location (e.g.: uploads) at the File
|
||||
System Path prompt.
|
||||
|
||||
Changing the file system path after files have been uploaded may cause
|
||||
unexpected problems on an existing site. If you modify the file system path
|
||||
on an existing site, remember to copy all files from the original location
|
||||
to the new location.
|
||||
|
||||
Some administrators suggest making the documentation files, especially
|
||||
CHANGELOG.txt, non-readable so that the exact version of Drupal you are
|
||||
running is slightly more difficult to determine. If you wish to implement
|
||||
this optional security measure, use the following command from a shell or
|
||||
system prompt (while in the installation directory):
|
||||
|
||||
chmod a-r CHANGELOG.txt
|
||||
|
||||
Note that the example only affects CHANGELOG.txt. To completely hide
|
||||
all documentation files from public view, repeat this command for each of
|
||||
the Drupal documentation files in the installation directory, substituting the
|
||||
name of each file for CHANGELOG.txt in the example.
|
||||
|
||||
For more information on setting file permissions, see "Modifying Linux, Unix,
|
||||
and Mac file permissions" (http://drupal.org/node/202483) or "Modifying
|
||||
Windows file permissions" (http://drupal.org/node/202491) in the online
|
||||
handbook.
|
||||
|
||||
7. CRON MAINTENANCE TASKS
|
||||
|
||||
Many Drupal modules have periodic tasks that must be triggered by a cron
|
||||
maintenance task, including search module (to build and update the index
|
||||
used for keyword searching), aggregator module (to retrieve feeds from other
|
||||
sites), ping module (to notify other sites about new or updated content), and
|
||||
system module (to perform routine maintenance and pruning on system tables).
|
||||
To activate these tasks, call the cron page by visiting
|
||||
http://www.example.com/cron.php, which, in turn, executes tasks on behalf
|
||||
of installed modules.
|
||||
|
||||
Most systems support the crontab utility for scheduling tasks like this. The
|
||||
following example crontab line will activate the cron tasks automatically on
|
||||
the hour:
|
||||
|
||||
0 * * * * wget -O - -q -t 1 http://www.example.com/cron.php
|
||||
|
||||
More information about cron maintenance tasks are available in the help pages
|
||||
and in Drupal's online handbook at http://drupal.org/cron. Example scripts can
|
||||
be found in the scripts/ directory.
|
||||
|
||||
DRUPAL ADMINISTRATION
|
||||
---------------------
|
||||
|
||||
A new installation of Drupal defaults to a very basic configuration with only a
|
||||
few active modules and minimal user access rights.
|
||||
|
||||
Use your administration panel to enable and configure services. For example:
|
||||
|
||||
General Settings Administer > Site configuration > Site information
|
||||
Enable Modules Administer > Site building > Modules
|
||||
Configure Themes Administer > Site building > Themes
|
||||
Set User Permissions Administer > User management > Permissions
|
||||
|
||||
For more information on configuration options, read the instructions which
|
||||
accompany the different configuration settings and consult the various help
|
||||
pages available in the administration panel.
|
||||
|
||||
Community-contributed modules and themes are available at http://drupal.org/.
|
||||
|
||||
CUSTOMIZING YOUR THEME(S)
|
||||
-------------------------
|
||||
|
||||
Now that your installation is running, you will want to customize the look of
|
||||
your site. Several sample themes are included and more can be downloaded from
|
||||
drupal.org.
|
||||
|
||||
Simple customization of your theme can be done using only CSS. Further changes
|
||||
require understanding the phptemplate engine that is part of Drupal. See
|
||||
http://drupal.org/handbook/customization to find out more.
|
||||
|
||||
MULTISITE CONFIGURATION
|
||||
-----------------------
|
||||
|
||||
A single Drupal installation can host several Drupal-powered sites, each with
|
||||
its own individual configuration.
|
||||
|
||||
Additional site configurations are created in subdirectories within the 'sites'
|
||||
directory. Each subdirectory must have a 'settings.php' file which specifies the
|
||||
configuration settings. The easiest way to create additional sites is to copy
|
||||
the 'default' directory and modify the 'settings.php' file as appropriate. The
|
||||
new directory name is constructed from the site's URL. The configuration for
|
||||
www.example.com could be in 'sites/example.com/settings.php' (note that 'www.'
|
||||
should be omitted if users can access your site at http://example.com/).
|
||||
|
||||
Sites do not have to have a different domain. You can also use subdomains and
|
||||
subdirectories for Drupal sites. For example, example.com, sub.example.com,
|
||||
and sub.example.com/site3 can all be defined as independent Drupal sites. The
|
||||
setup for a configuration such as this would look like the following:
|
||||
|
||||
sites/default/settings.php
|
||||
sites/example.com/settings.php
|
||||
sites/sub.example.com/settings.php
|
||||
sites/sub.example.com.site3/settings.php
|
||||
|
||||
When searching for a site configuration (for example www.sub.example.com/site3),
|
||||
Drupal will search for configuration files in the following order, using the
|
||||
first configuration it finds:
|
||||
|
||||
sites/www.sub.example.com.site3/settings.php
|
||||
sites/sub.example.com.site3/settings.php
|
||||
sites/example.com.site3/settings.php
|
||||
sites/www.sub.example.com/settings.php
|
||||
sites/sub.example.com/settings.php
|
||||
sites/example.com/settings.php
|
||||
sites/default/settings.php
|
||||
|
||||
If you are installing on a non-standard port, the port number is treated as the
|
||||
deepest subdomain. For example: http://www.example.com:8080/ could be loaded
|
||||
from sites/8080.www.example.com/. The port number will be removed according to
|
||||
the pattern above if no port-specific configuration is found, just like a real
|
||||
subdomain.
|
||||
|
||||
Each site configuration can have its own site-specific modules and themes in
|
||||
addition to those installed in the standard 'modules' and 'themes' directories.
|
||||
To use site-specific modules or themes, simply create a 'modules' or 'themes'
|
||||
directory within the site configuration directory. For example, if
|
||||
sub.example.com has a custom theme and a custom module that should not be
|
||||
accessible to other sites, the setup would look like this:
|
||||
|
||||
sites/sub.example.com/:
|
||||
settings.php
|
||||
themes/custom_theme
|
||||
modules/custom_module
|
||||
|
||||
NOTE: for more information about multiple virtual hosts or the configuration
|
||||
settings, consult the Drupal handbook at drupal.org.
|
||||
|
||||
For more information on configuring Drupal's file system path in a multi-site
|
||||
configuration, see step 6 above.
|
||||
|
||||
MORE INFORMATION
|
||||
----------------
|
||||
|
||||
- For additional documentation, see the online Drupal handbook at
|
||||
http://drupal.org/handbook.
|
||||
|
||||
- For a list of security announcements, see the "Security announcements" page
|
||||
at http://drupal.org/security (available as an RSS feed). This page also
|
||||
describes how to subscribe to these announcements via e-mail.
|
||||
|
||||
- For information about the Drupal security process, or to find out how to report
|
||||
a potential security issue to the Drupal security team, see the "Security team"
|
||||
page at http://drupal.org/security-team.
|
||||
|
||||
- For information about the wide range of available support options, see the
|
||||
"Support" page at http://drupal.org/support.
|
||||
339
LICENSE.txt
Normal file
339
LICENSE.txt
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
85
MAINTAINERS.txt
Normal file
85
MAINTAINERS.txt
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
List of maintainers
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
LEGEND
|
||||
======
|
||||
|
||||
- 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?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
BLOG API
|
||||
M: James Walker <walkah@walkah.net>
|
||||
S: maintained
|
||||
|
||||
DISTRIBUTED AUTHENTICATION MODULES
|
||||
M: Moshe Weitzman <weitzman@tejasa.com>
|
||||
S: maintained
|
||||
|
||||
DOCUMENTATION COORDINATOR
|
||||
M: Steven Peck <speck@blkmtn.org>
|
||||
S: maintained
|
||||
|
||||
FILTER SYSTEM
|
||||
M: Steven Wittens <unconed@drupal.org>
|
||||
S: maintained
|
||||
|
||||
FORM SYSTEM
|
||||
M: Károly Négyesi <chx@mail.tvnet.hu>
|
||||
S: maintained
|
||||
|
||||
LOCALE MODULE
|
||||
M: Gabor Hojtsy <goba@php.net>
|
||||
S: maintained
|
||||
|
||||
LOGGING
|
||||
M: Khalid Baheyeldin <drupal@2bits.com>
|
||||
S: maintained
|
||||
|
||||
MENU SYSTEM
|
||||
M: Károly Négyesi <chx@mail.tvnet.hu>
|
||||
S: maintained
|
||||
|
||||
PATH MODULE
|
||||
M: Matt Westgate <drupal@asitis.org>
|
||||
S: maintained
|
||||
|
||||
POSTGRESQL
|
||||
M: Sammy Spets <sammys-drupal@synerger.com>
|
||||
S: maintained
|
||||
|
||||
SECURITY COORDINATOR
|
||||
M: Greg Knaddison <http://drupal.org/user/36762>
|
||||
S: maintained
|
||||
|
||||
STATISTICS MODULE
|
||||
M: Jeremy Andrews <jeremy@kerneltrap.com>
|
||||
S: maintained
|
||||
|
||||
THEME SYSTEM
|
||||
M: Earl Miles <merlin@logrus.com>
|
||||
Joon Park <joon@dvessel.com>
|
||||
S: maintained
|
||||
|
||||
UPDATE MODULE
|
||||
M: Derek Wright <http://drupal.org/user/46549/contact>
|
||||
S: maintained
|
||||
|
||||
XML-RPC SERVER/CLIENT
|
||||
M: John VanDyk <http://drupal.org/user/2375/contact>
|
||||
S: maintained
|
||||
|
||||
TRANSLATIONS COORDINATOR
|
||||
M: Gerhard Killesreiter <gerhard@killesreiter.de>
|
||||
S: maintained
|
||||
|
||||
THE REST:
|
||||
M: Dries <dries@drupal.org>
|
||||
107
UPGRADE.txt
Normal file
107
UPGRADE.txt
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
UPGRADING
|
||||
---------
|
||||
|
||||
Prior to upgrading, you should ensure that:
|
||||
|
||||
* Your system meets or exceeds Drupal's minimum requirements as shown at
|
||||
http://drupal.org/requirements.
|
||||
* You have a backup of all your relevant data (#1).
|
||||
* Custom and contributed modules have been checked for compatibility (#11).
|
||||
* Custom and contributed themes have been checked for compatibility (#11).
|
||||
* You have read through this entire document.
|
||||
|
||||
Let's begin!
|
||||
|
||||
1. Back up your Drupal database and site root directory. Be especially sure
|
||||
to back up your "sites" directory which contains your configuration file,
|
||||
added modules and themes, and your site's uploaded files. If other files
|
||||
have modifications, such as .htaccess or robots.txt, back those up as well.
|
||||
|
||||
Note: for a single site setup, the configuration file is the "settings.php"
|
||||
file located at sites/default/settings.php. The default.settings.php file
|
||||
contains a clean copy for restoration purposes, if required.
|
||||
|
||||
For multisite configurations, the configuration file is located in a
|
||||
structure like the following:
|
||||
|
||||
sites/default/settings.php
|
||||
sites/example.com/settings.php
|
||||
sites/sub.example.com/settings.php
|
||||
sites/sub.example.com.path/settings.php
|
||||
|
||||
More information on multisite configuration is located in INSTALL.txt.
|
||||
|
||||
2. If possible, log on as the user with user ID 1, which is the first account
|
||||
created and the main administrator account. User ID 1 will be able to
|
||||
automatically access update.php in step #10. There are special instructions
|
||||
in step #10 if you are unable to log on as user ID 1. Do not close your
|
||||
browser until the final step is complete.
|
||||
|
||||
3. Place the site in "Off-line" mode, to let the database updates run without
|
||||
interruption and avoid displaying errors to end users of the site. This
|
||||
option is at http://www.example.com/?q=admin/settings/site-maintenance
|
||||
(replace www.example.com with your installation's domain name and path).
|
||||
|
||||
4. If using a custom or contributed theme, switch
|
||||
to a core theme, such as Garland or Bluemarine.
|
||||
|
||||
5. Disable all custom and contributed modules.
|
||||
|
||||
6. Remove all old files and directories from the Drupal installation directory.
|
||||
|
||||
7. Unpack the new files and directories into the Drupal installation directory.
|
||||
|
||||
8. Copy your backed up "files" and "sites" directories to the Drupal
|
||||
installation directory. If other system files such as .htaccess or
|
||||
robots.txt were customized, re-create the modifications in the new
|
||||
versions of the files using the backups taken in step #1.
|
||||
|
||||
9. Verify the new configuration file to make sure it has correct information.
|
||||
|
||||
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 core database tables to the new Drupal installation.
|
||||
|
||||
Note: if you are unable to access update.php do the following:
|
||||
|
||||
- Open your settings.php with a text editor.
|
||||
|
||||
- There is a line that says $update_free_access = FALSE;
|
||||
Change it to $update_free_access = TRUE;
|
||||
|
||||
- Once update.php is done, you must change the settings.php file
|
||||
back to its original form with $update_free_access = FALSE;
|
||||
|
||||
11. Ensure that the versions of all custom and contributed modules match the
|
||||
new Drupal version to which you have updated. For a major update, such as
|
||||
from 5.x to 6.x, modules from previous versions will not be compatible
|
||||
and updated versions will be required.
|
||||
|
||||
- For contributed modules, check http://drupal.org/project/modules
|
||||
for the version of a module matching your version of Drupal.
|
||||
|
||||
- For custom modules, review http://drupal.org/update/modules to
|
||||
ensure that a custom module is compatible with the current version.
|
||||
|
||||
12. Re-enable custom and contributed modules and re-run update.php
|
||||
to update custom and contributed database tables.
|
||||
|
||||
13. Return the site to its original theme (if you switched to a core
|
||||
theme like Garland or Bluemarine in step #4). If your site uses a
|
||||
custom or contributed theme, make sure it is compatible with your
|
||||
version of Drupal.
|
||||
|
||||
- For contributed themes, check http://drupal.org/project/themes
|
||||
for the version of a theme matching your version of Drupal.
|
||||
|
||||
- For custom themes, review http://drupal.org/update/theme to ensure
|
||||
that a custom theme is compatible with the current version.
|
||||
|
||||
14. Finally, return your site to "Online" mode so your visitors may resume
|
||||
browsing. As in step #3, this option is available in your administration
|
||||
screens at http://www.example.com/?q=admin/settings/site-maintenance
|
||||
(replace www.example.com with your installation's domain name and path).
|
||||
|
||||
For more information on upgrading visit
|
||||
the Drupal handbook at http://drupal.org/upgrade
|
||||
10
cron.php
Normal file
10
cron.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
|
||||
*/
|
||||
|
||||
include_once './includes/bootstrap.inc';
|
||||
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
|
||||
drupal_cron_run();
|
||||
404
includes/actions.inc
Normal file
404
includes/actions.inc
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This is the actions engine for executing stored actions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup actions Actions
|
||||
* @{
|
||||
* Functions that perform an action on a certain system object.
|
||||
*
|
||||
* All modules should declare their action functions to be in this group and
|
||||
* each action function should reference its configuration form, validate, and
|
||||
* submit functions using \@see. Conversely, form, validate, and submit
|
||||
* functions should reference the action function using \@see. For examples of
|
||||
* this see comment_unpublish_by_keyword_action(), which has the following in
|
||||
* its doxygen documentation:
|
||||
*
|
||||
* \@ingroup actions
|
||||
* \@see comment_unpublish_by_keyword_action_form().
|
||||
* \@see comment_unpublish_by_keyword_action_submit().
|
||||
*
|
||||
* @} End of "defgroup actions".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform a given list of actions by executing their callback functions.
|
||||
*
|
||||
* Given the IDs of actions to perform, find out what the callbacks
|
||||
* for the actions are by querying the database. Then call each callback
|
||||
* using the function call $function($object, $context, $a1, $a2)
|
||||
* where $function is the name of a function written in compliance with
|
||||
* the action specification; that is, foo($object, $context).
|
||||
*
|
||||
* @param $action_ids
|
||||
* The ID of the action to perform. Can be a single action ID or an array
|
||||
* of IDs. IDs of instances will be numeric; IDs of singletons will be
|
||||
* function names.
|
||||
* @param $object
|
||||
* Parameter that will be passed along to the callback. Typically the
|
||||
* object that the action will act on; a node, user or comment object.
|
||||
* If the action does not act on an object, pass a dummy object. This
|
||||
* is necessary to support PHP 4 object referencing.
|
||||
* @param $context
|
||||
* Parameter that will be passed along to the callback. $context is a
|
||||
* keyed array containing extra information about what is currently
|
||||
* happening at the time of the call. Typically $context['hook'] and
|
||||
* $context['op'] will tell which hook-op combination resulted in this
|
||||
* call to actions_do().
|
||||
* @param $a1
|
||||
* Parameter that will be passed along to the callback.
|
||||
* @param $a2
|
||||
* Parameter that will be passed along to the callback.
|
||||
*
|
||||
* @return
|
||||
* An associative array containing the result of the function that
|
||||
* performs the action, keyed on action ID.
|
||||
*/
|
||||
function actions_do($action_ids, &$object, $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();
|
||||
$result = array();
|
||||
if (is_array($action_ids)) {
|
||||
$where = array();
|
||||
$where_values = array();
|
||||
foreach ($action_ids as $action_id) {
|
||||
if (is_numeric($action_id)) {
|
||||
$where[] = "OR aid = '%s'";
|
||||
$where_values[] = $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 ($where) {
|
||||
$where_clause = implode(' ', $where);
|
||||
// Strip off leading 'OR '.
|
||||
$where_clause = '('. strstr($where_clause, " ") .')';
|
||||
$result_db = db_query('SELECT * FROM {actions} WHERE '. $where_clause, $where_values);
|
||||
while ($action = db_fetch_object($result_db)) {
|
||||
$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) {
|
||||
if (is_numeric($action_id)) { // Configurable actions need parameters.
|
||||
$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 {
|
||||
$result[$action_id] = $action_id($object, $context, $a1, $a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optimized execution of single action.
|
||||
else {
|
||||
// If it's a configurable action, retrieve stored parameters.
|
||||
if (is_numeric($action_ids)) {
|
||||
$action = db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = '%s'", $action_ids));
|
||||
$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 {
|
||||
$result[$action_ids] = $action_ids($object, $context, $a1, $a2);
|
||||
}
|
||||
}
|
||||
$stack--;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discover all action functions by invoking hook_action_info().
|
||||
*
|
||||
* @code
|
||||
* mymodule_action_info() {
|
||||
* return array(
|
||||
* 'mymodule_functiondescription_action' => array(
|
||||
* 'type' => 'node',
|
||||
* 'description' => t('Save node'),
|
||||
* 'configurable' => FALSE,
|
||||
* 'hooks' => array(
|
||||
* 'nodeapi' => array('delete', 'insert', 'update', 'view'),
|
||||
* 'comment' => array('delete', 'insert', 'update', 'view'),
|
||||
* )
|
||||
* )
|
||||
* );
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* The description is used in presenting possible actions to the user for
|
||||
* configuration. The type is used to present these actions in a logical
|
||||
* grouping and to denote context. Some types are 'node', 'user', 'comment',
|
||||
* and 'system'. If an action is configurable it will provide form,
|
||||
* validation and submission functions. The hooks the action supports
|
||||
* are declared in the 'hooks' array.
|
||||
*
|
||||
* @param $reset
|
||||
* Reset the action info static cache.
|
||||
*
|
||||
* @return
|
||||
* An associative array keyed on function name. The value of each key is
|
||||
* an array containing information about the action, such as type of
|
||||
* action and description of the action, e.g.,
|
||||
*
|
||||
* @code
|
||||
* $actions['node_publish_action'] = array(
|
||||
* 'type' => 'node',
|
||||
* 'description' => t('Publish post'),
|
||||
* 'configurable' => FALSE,
|
||||
* 'hooks' => array(
|
||||
* 'nodeapi' => array('presave', 'insert', 'update', 'view'),
|
||||
* 'comment' => array('delete', 'insert', 'update', 'view'),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*/
|
||||
function actions_list($reset = FALSE) {
|
||||
static $actions;
|
||||
if (!isset($actions) || $reset) {
|
||||
$actions = module_invoke_all('action_info');
|
||||
drupal_alter('action_info', $actions);
|
||||
}
|
||||
|
||||
// See module_implements for explanations of this cast.
|
||||
return (array)$actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all action instances from the database.
|
||||
*
|
||||
* Compare with actions_list(), 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/settings/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 action ID. Each value is an
|
||||
* associative array with keys 'callback', 'description', 'type' and
|
||||
* 'configurable'.
|
||||
*/
|
||||
function actions_get_all_actions() {
|
||||
$actions = array();
|
||||
$result = db_query("SELECT * FROM {actions}");
|
||||
while ($action = db_fetch_object($result)) {
|
||||
$actions[$action->aid] = array(
|
||||
'callback' => $action->callback,
|
||||
'description' => $action->description,
|
||||
'type' => $action->type,
|
||||
'configurable' => (bool) $action->parameters,
|
||||
);
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an associative array keyed by md5 hashes of function names.
|
||||
*
|
||||
* 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 as keys and associative
|
||||
* arrays with keys 'description', 'type', etc. as values. Generally
|
||||
* the output of actions_list() or actions_get_all_actions() is given
|
||||
* as input to this function.
|
||||
*
|
||||
* @return
|
||||
* An associative array keyed on md5 hash of function name. The value of
|
||||
* each key is an associative array of function, description, and type
|
||||
* for the action.
|
||||
*/
|
||||
function actions_actions_map($actions) {
|
||||
$actions_map = array();
|
||||
foreach ($actions as $callback => $array) {
|
||||
$key = md5($callback);
|
||||
$actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback;
|
||||
$actions_map[$key]['description'] = $array['description'];
|
||||
$actions_map[$key]['type'] = $array['type'];
|
||||
$actions_map[$key]['configurable'] = $array['configurable'];
|
||||
}
|
||||
return $actions_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an md5 hash of a function name, return the function name.
|
||||
*
|
||||
* Faster than actions_actions_map() when you only need the function name.
|
||||
*
|
||||
* @param $hash
|
||||
* MD5 hash of a function name
|
||||
*
|
||||
* @return
|
||||
* Function name
|
||||
*/
|
||||
function actions_function_lookup($hash) {
|
||||
$actions_list = actions_list();
|
||||
foreach ($actions_list as $function => $array) {
|
||||
if (md5($function) == $hash) {
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
|
||||
// Must be an instance; must check database.
|
||||
$aid = db_result(db_query("SELECT aid FROM {actions} WHERE MD5(aid) = '%s' AND parameters <> ''", $hash));
|
||||
return $aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize actions that are provided by modules.
|
||||
*
|
||||
* They are synchronized with actions that are stored in the actions table.
|
||||
* This is necessary so that actions that do not require configuration can
|
||||
* receive action IDs. This is not necessarily the best approach,
|
||||
* but it is the most straightforward.
|
||||
*/
|
||||
function actions_synchronize($actions_in_code = array(), $delete_orphans = FALSE) {
|
||||
if (!$actions_in_code) {
|
||||
$actions_in_code = actions_list(TRUE);
|
||||
}
|
||||
$actions_in_db = array();
|
||||
$result = db_query("SELECT * FROM {actions} WHERE parameters = ''");
|
||||
while ($action = db_fetch_object($result)) {
|
||||
$actions_in_db[$action->callback] = array('aid' => $action->aid, 'description' => $action->description);
|
||||
}
|
||||
|
||||
// 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 (array_key_exists($callback, $actions_in_db)) {
|
||||
unset($actions_in_db[$callback]);
|
||||
}
|
||||
else {
|
||||
// This is a new singleton that we don't have an aid for; assign one.
|
||||
db_query("INSERT INTO {actions} (aid, type, callback, parameters, description) VALUES ('%s', '%s', '%s', '%s', '%s')", $callback, $array['type'], $callback, '', $array['description']);
|
||||
watchdog('actions', "Action '%action' added.", array('%action' => $array['description']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any actions that we have left in $actions_in_db are orphaned.
|
||||
if ($actions_in_db) {
|
||||
$orphaned = array();
|
||||
$placeholder = array();
|
||||
|
||||
foreach ($actions_in_db as $callback => $array) {
|
||||
$orphaned[] = $callback;
|
||||
$placeholder[] = "'%s'";
|
||||
}
|
||||
|
||||
$orphans = implode(', ', $orphaned);
|
||||
|
||||
if ($delete_orphans) {
|
||||
$placeholders = implode(', ', $placeholder);
|
||||
$results = db_query("SELECT a.aid, a.description FROM {actions} a WHERE callback IN ($placeholders)", $orphaned);
|
||||
while ($action = db_fetch_object($results)) {
|
||||
actions_delete($action->aid);
|
||||
watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => $action->description));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$link = l(t('Remove orphaned actions'), 'admin/settings/actions/orphan');
|
||||
$count = count($actions_in_db);
|
||||
watchdog('actions', format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link'), array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an action and its associated 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 $desc
|
||||
* A user-supplied description 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, $desc, $aid = NULL) {
|
||||
$serialized = serialize($params);
|
||||
if ($aid) {
|
||||
db_query("UPDATE {actions} SET callback = '%s', type = '%s', parameters = '%s', description = '%s' WHERE aid = '%s'", $function, $type, $serialized, $desc, $aid);
|
||||
watchdog('actions', 'Action %action saved.', array('%action' => $desc));
|
||||
}
|
||||
else {
|
||||
// aid is the callback for singleton actions so we need to keep a
|
||||
// separate table for numeric aids.
|
||||
db_query('INSERT INTO {actions_aid} VALUES (default)');
|
||||
$aid = db_last_insert_id('actions_aid', 'aid');
|
||||
db_query("INSERT INTO {actions} (aid, callback, type, parameters, description) VALUES ('%s', '%s', '%s', '%s', '%s')", $aid, $function, $type, $serialized, $desc);
|
||||
watchdog('actions', 'Action %action created.', array('%action' => $desc));
|
||||
}
|
||||
|
||||
return $aid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single action from the database.
|
||||
*
|
||||
* @param $aid
|
||||
* integer The ID of the action to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The appropriate action row from the database as an object.
|
||||
*/
|
||||
function actions_load($aid) {
|
||||
return db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = '%s'", $aid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single action from the database.
|
||||
*
|
||||
* @param $aid
|
||||
* integer The ID of the action to delete.
|
||||
*/
|
||||
function actions_delete($aid) {
|
||||
db_query("DELETE FROM {actions} WHERE aid = '%s'", $aid);
|
||||
module_invoke_all('actions_delete', $aid);
|
||||
}
|
||||
354
includes/batch.inc
Normal file
354
includes/batch.inc
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file Batch processing API for processes to run in multiple HTTP requests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* State-based dispatcher for the batch processing page.
|
||||
*/
|
||||
function _batch_page() {
|
||||
$batch =& batch_get();
|
||||
|
||||
// Retrieve the current state of batch from db.
|
||||
if (isset($_REQUEST['id']) && $data = db_result(db_query("SELECT batch FROM {batch} WHERE bid = %d AND token = '%s'", $_REQUEST['id'], drupal_get_token($_REQUEST['id'])))) {
|
||||
$batch = unserialize($data);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Register database update for end of processing.
|
||||
register_shutdown_function('_batch_shutdown');
|
||||
|
||||
$op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
|
||||
$output = NULL;
|
||||
switch ($op) {
|
||||
case 'start':
|
||||
$output = _batch_start();
|
||||
break;
|
||||
|
||||
case 'do':
|
||||
// JS-version AJAX callback.
|
||||
_batch_do();
|
||||
break;
|
||||
|
||||
case 'do_nojs':
|
||||
// Non-JS progress page.
|
||||
$output = _batch_progress_page_nojs();
|
||||
break;
|
||||
|
||||
case 'finished':
|
||||
$output = _batch_finished();
|
||||
break;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the batch processing
|
||||
*/
|
||||
function _batch_start() {
|
||||
// Choose between the JS and non-JS version.
|
||||
// JS-enabled users are identified through the 'has_js' cookie set in drupal.js.
|
||||
// If the user did not visit any JS enabled page during his browser session,
|
||||
// he gets the non-JS version...
|
||||
if (isset($_COOKIE['has_js']) && $_COOKIE['has_js']) {
|
||||
return _batch_progress_page_js();
|
||||
}
|
||||
else {
|
||||
return _batch_progress_page_nojs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing page with JavaScript support.
|
||||
*/
|
||||
function _batch_progress_page_js() {
|
||||
$batch = batch_get();
|
||||
|
||||
// The first batch set gets to set the page title
|
||||
// and the initialization and error messages.
|
||||
$current_set = _batch_current_set();
|
||||
drupal_set_title($current_set['title']);
|
||||
drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE);
|
||||
|
||||
$url = url($batch['url'], array('query' => array('id' => $batch['id'])));
|
||||
$js_setting = array(
|
||||
'batch' => array(
|
||||
'errorMessage' => $current_set['error_message'] .'<br/>'. $batch['error_message'],
|
||||
'initMessage' => $current_set['init_message'],
|
||||
'uri' => $url,
|
||||
),
|
||||
);
|
||||
drupal_add_js($js_setting, 'setting');
|
||||
drupal_add_js('misc/batch.js', 'core', 'header', FALSE, FALSE);
|
||||
|
||||
$output = '<div id="progress"></div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do one pass of execution and inform back the browser about progression
|
||||
* (used for JavaScript-mode only).
|
||||
*/
|
||||
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(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing page without JavaScript support.
|
||||
*/
|
||||
function _batch_progress_page_nojs() {
|
||||
$batch =& batch_get();
|
||||
$current_set = _batch_current_set();
|
||||
|
||||
drupal_set_title($current_set['title']);
|
||||
|
||||
$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. non-existant
|
||||
// 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'];
|
||||
drupal_maintenance_theme();
|
||||
$fallback = theme('maintenance_page', $fallback, FALSE, 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();
|
||||
}
|
||||
|
||||
$url = url($batch['url'], array('query' => array('id' => $batch['id'], 'op' => $new_op)));
|
||||
drupal_set_html_head('<meta http-equiv="Refresh" content="0; URL='. $url .'">');
|
||||
$output = theme('progress_bar', $percentage, $message);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance batch processing for 1 second (or process the whole batch if it
|
||||
* was not set for progressive execution - e.g forms submitted by drupal_execute).
|
||||
*/
|
||||
function _batch_process() {
|
||||
$batch =& batch_get();
|
||||
$current_set =& _batch_current_set();
|
||||
$set_changed = TRUE;
|
||||
|
||||
if ($batch['progressive']) {
|
||||
timer_start('batch_processing');
|
||||
}
|
||||
|
||||
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($current_set['file']);
|
||||
}
|
||||
|
||||
$finished = 1;
|
||||
$task_message = '';
|
||||
if ((list($function, $args) = reset($current_set['operations'])) && function_exists($function)) {
|
||||
// Build the 'context' array, execute the function call,
|
||||
// and retrieve the user message.
|
||||
$batch_context = array('sandbox' => &$current_set['sandbox'], 'results' => &$current_set['results'], 'finished' => &$finished, 'message' => &$task_message);
|
||||
// Process the current operation.
|
||||
call_user_func_array($function, array_merge($args, array(&$batch_context)));
|
||||
}
|
||||
|
||||
if ($finished >= 1) {
|
||||
// Make sure this step isn't counted double when computing $current.
|
||||
$finished = 0;
|
||||
// Remove the operation and clear the sandbox.
|
||||
array_shift($current_set['operations']);
|
||||
$current_set['sandbox'] = array();
|
||||
}
|
||||
|
||||
// If the batch set is completed, browse through the remaining sets,
|
||||
// executing 'control sets' (stored form submit handlers) along the way -
|
||||
// this might in turn insert new batch sets.
|
||||
// Stop when we find a set that actually has operations.
|
||||
$set_changed = FALSE;
|
||||
$old_set = $current_set;
|
||||
while (empty($current_set['operations']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
|
||||
$current_set =& _batch_current_set();
|
||||
$set_changed = TRUE;
|
||||
}
|
||||
// At this point, either $current_set is a 'real' batch set (has operations),
|
||||
// or all sets have been completed.
|
||||
|
||||
// If we're in progressive mode, stop after 1 second.
|
||||
if ($batch['progressive'] && timer_read('batch_processing') > 1000) {
|
||||
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) one.
|
||||
if ($set_changed && isset($current_set['operations'])) {
|
||||
// Processing will continue with a fresh batch set.
|
||||
$remaining = count($current_set['operations']);
|
||||
$total = $current_set['total'];
|
||||
$progress_message = $current_set['init_message'];
|
||||
$task_message = '';
|
||||
}
|
||||
else {
|
||||
$remaining = count($old_set['operations']);
|
||||
$total = $old_set['total'];
|
||||
$progress_message = $old_set['progress_message'];
|
||||
}
|
||||
|
||||
$current = $total - $remaining + $finished;
|
||||
$percentage = $total ? floor($current / $total * 100) : 100;
|
||||
$values = array(
|
||||
'@remaining' => $remaining,
|
||||
'@total' => $total,
|
||||
'@current' => floor($current),
|
||||
'@percentage' => $percentage,
|
||||
);
|
||||
$message = strtr($progress_message, $values) .'<br/>';
|
||||
$message .= $task_message ? $task_message : ' ';
|
||||
|
||||
return array($percentage, $message);
|
||||
}
|
||||
else {
|
||||
// If we're not in progressive mode, the whole batch has been processed by now.
|
||||
return _batch_finished();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the batch set being currently processed.
|
||||
*/
|
||||
function &_batch_current_set() {
|
||||
$batch =& batch_get();
|
||||
return $batch['sets'][$batch['current_set']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Move execution to the next batch set if any, executing the stored
|
||||
* form _submit handlers along the way (thus possibly inserting
|
||||
* additional batch sets).
|
||||
*/
|
||||
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 alteration by the submit handlers.
|
||||
$function($batch['form'], $batch['form_state']);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the batch processing:
|
||||
* Call the 'finished' callbacks to allow custom handling of results,
|
||||
* and resolve page redirection.
|
||||
*/
|
||||
function _batch_finished() {
|
||||
$batch =& batch_get();
|
||||
|
||||
// Execute the 'finished' callbacks for each batch set.
|
||||
foreach ($batch['sets'] as $key => $batch_set) {
|
||||
if (isset($batch_set['finished'])) {
|
||||
// Check if the set requires an additional file for functions definitions.
|
||||
if (isset($batch_set['file']) && is_file($batch_set['file'])) {
|
||||
include_once($batch_set['file']);
|
||||
}
|
||||
if (function_exists($batch_set['finished'])) {
|
||||
$batch_set['finished']($batch_set['success'], $batch_set['results'], $batch_set['operations']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the batch table and unset the global $batch variable.
|
||||
if ($batch['progressive']) {
|
||||
db_query("DELETE FROM {batch} WHERE bid = %d", $batch['id']);
|
||||
}
|
||||
$_batch = $batch;
|
||||
$batch = NULL;
|
||||
|
||||
// Redirect if needed.
|
||||
if ($_batch['progressive']) {
|
||||
// Put back the 'destination' that was saved in batch_process().
|
||||
if (isset($_batch['destination'])) {
|
||||
$_REQUEST['destination'] = $_batch['destination'];
|
||||
}
|
||||
|
||||
// Use $_batch['form_state']['redirect'], or $_batch['redirect'],
|
||||
// or $_batch['source_page'].
|
||||
if (isset($_batch['form_state']['redirect'])) {
|
||||
$redirect = $_batch['form_state']['redirect'];
|
||||
}
|
||||
elseif (isset($_batch['redirect'])) {
|
||||
$redirect = $_batch['redirect'];
|
||||
}
|
||||
else {
|
||||
$redirect = $_batch['source_page'];
|
||||
}
|
||||
|
||||
// Let drupal_redirect_form handle redirection logic.
|
||||
$form = isset($batch['form']) ? $batch['form'] : array();
|
||||
if (empty($_batch['form_state']['rebuild']) && empty($_batch['form_state']['storage'])) {
|
||||
drupal_redirect_form($form, $redirect);
|
||||
}
|
||||
|
||||
// We get here if $form['#redirect'] was FALSE, or if the form is a
|
||||
// multi-step form. We save the final $form_state value to be retrieved
|
||||
// by drupal_get_form, and we redirect to the originating page.
|
||||
$_SESSION['batch_form_state'] = $_batch['form_state'];
|
||||
drupal_goto($_batch['source_page']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown function: store the batch data for next request,
|
||||
* or clear the table if the batch is finished.
|
||||
*/
|
||||
function _batch_shutdown() {
|
||||
if ($batch = batch_get()) {
|
||||
db_query("UPDATE {batch} SET batch = '%s' WHERE bid = %d", serialize($batch), $batch['id']);
|
||||
}
|
||||
}
|
||||
1605
includes/bootstrap.inc
Normal file
1605
includes/bootstrap.inc
Normal file
File diff suppressed because it is too large
Load diff
23
includes/cache-install.inc
Normal file
23
includes/cache-install.inc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* A stub cache implementation to be used during the installation
|
||||
* process when database access is not yet available. Because Drupal's
|
||||
* caching system never requires that cached data be present, these
|
||||
* stub functions can short-circuit the process and sidestep the
|
||||
* need for any persistent storage. Obviously, using this cache
|
||||
* implementation during normal operations would have a negative impact
|
||||
* on performance.
|
||||
*/
|
||||
|
||||
function cache_get($key, $table = 'cache') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
|
||||
return;
|
||||
}
|
||||
186
includes/cache.inc
Normal file
186
includes/cache.inc
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Return data from the persistent cache. Data may be stored as either plain text or as serialized data.
|
||||
* cache_get will automatically return unserialized objects and arrays.
|
||||
*
|
||||
* @param $cid
|
||||
* The cache ID of the data to retrieve.
|
||||
* @param $table
|
||||
* The table $table to store the data in. Valid core values are 'cache_filter',
|
||||
* 'cache_menu', 'cache_page', or 'cache' for the default cache.
|
||||
*
|
||||
* @see cache_set()
|
||||
*/
|
||||
function cache_get($cid, $table = 'cache') {
|
||||
global $user;
|
||||
|
||||
// Garbage collection necessary when enforcing a minimum cache lifetime
|
||||
$cache_flush = variable_get('cache_flush_'. $table, 0);
|
||||
if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) {
|
||||
// Reset the variable immediately to prevent a meltdown in heavy load situations.
|
||||
variable_set('cache_flush_'. $table, 0);
|
||||
// Time to flush old cache data
|
||||
db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire <= %d", CACHE_PERMANENT, $cache_flush);
|
||||
}
|
||||
|
||||
$cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $cid));
|
||||
if (isset($cache->data)) {
|
||||
// If the data is permanent or we're not enforcing a minimum cache lifetime
|
||||
// always return the cached data.
|
||||
if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
|
||||
$cache->data = db_decode_blob($cache->data);
|
||||
if ($cache->serialized) {
|
||||
$cache->data = unserialize($cache->data);
|
||||
}
|
||||
}
|
||||
// If enforcing a minimum cache lifetime, validate that the data is
|
||||
// currently valid for this user before we return it by making sure the
|
||||
// cache entry was created before the timestamp in the current session's
|
||||
// cache timer. The cache variable is loaded into the $user object by
|
||||
// sess_read() in session.inc.
|
||||
else {
|
||||
if (isset($user->cache) && $user->cache > $cache->created) {
|
||||
// This cache data is too old and thus not valid for us, ignore it.
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
$cache->data = db_decode_blob($cache->data);
|
||||
if ($cache->serialized) {
|
||||
$cache->data = unserialize($cache->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store data in the persistent cache.
|
||||
*
|
||||
* The persistent cache is split up into four database
|
||||
* tables. Contributed modules can add additional tables.
|
||||
*
|
||||
* 'cache_page': This table stores generated pages for anonymous
|
||||
* users. This is the only table affected by the page cache setting on
|
||||
* the administrator panel.
|
||||
*
|
||||
* 'cache_menu': Stores the cachable part of the users' menus.
|
||||
*
|
||||
* 'cache_filter': Stores filtered pieces of content. This table is
|
||||
* periodically cleared of stale entries by cron.
|
||||
*
|
||||
* 'cache': Generic cache storage table.
|
||||
*
|
||||
* The reasons for having several tables are as follows:
|
||||
*
|
||||
* - smaller tables allow for faster selects and inserts
|
||||
* - we try to put fast changing cache items and rather static
|
||||
* ones into different tables. The effect is that only the fast
|
||||
* changing tables will need a lot of writes to disk. The more
|
||||
* static tables will also be better cachable with MySQL's query cache
|
||||
*
|
||||
* @param $cid
|
||||
* The cache ID of the data to store.
|
||||
* @param $data
|
||||
* The data to store in the cache. Complex data types will be automatically serialized before insertion.
|
||||
* Strings will be stored as plain text and not serialized.
|
||||
* @param $table
|
||||
* The table $table to store the data in. Valid core values are 'cache_filter',
|
||||
* 'cache_menu', 'cache_page', or 'cache'.
|
||||
* @param $expire
|
||||
* One of the following values:
|
||||
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
|
||||
* explicitly told to using cache_clear_all() with a cache ID.
|
||||
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
|
||||
* general cache wipe.
|
||||
* - A Unix timestamp: Indicates that the item should be kept at least until
|
||||
* the given time, after which it behaves like CACHE_TEMPORARY.
|
||||
* @param $headers
|
||||
* A string containing HTTP header information for cached pages.
|
||||
*
|
||||
* @see cache_get()
|
||||
*/
|
||||
function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
|
||||
$serialized = 0;
|
||||
if (is_object($data) || is_array($data)) {
|
||||
$data = serialize($data);
|
||||
$serialized = 1;
|
||||
}
|
||||
$created = time();
|
||||
db_query("UPDATE {". $table ."} SET data = %b, created = %d, expire = %d, headers = '%s', serialized = %d WHERE cid = '%s'", $data, $created, $expire, $headers, $serialized, $cid);
|
||||
if (!db_affected_rows()) {
|
||||
@db_query("INSERT INTO {". $table ."} (cid, data, created, expire, headers, serialized) VALUES ('%s', %b, %d, %d, '%s', %d)", $cid, $data, $created, $expire, $headers, $serialized);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Expire data from the cache. If called without arguments, expirable
|
||||
* entries will be cleared from the cache_page and cache_block tables.
|
||||
*
|
||||
* @param $cid
|
||||
* If set, the cache ID to delete. Otherwise, all cache entries that can
|
||||
* expire are deleted.
|
||||
*
|
||||
* @param $table
|
||||
* If set, the table $table to delete from. Mandatory
|
||||
* argument if $cid is set.
|
||||
*
|
||||
* @param $wildcard
|
||||
* If $wildcard is TRUE, cache IDs starting with $cid are deleted in
|
||||
* addition to the exact cache ID specified by $cid. If $wildcard is
|
||||
* TRUE and $cid is '*' then the entire table $table is emptied.
|
||||
*/
|
||||
function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
|
||||
global $user;
|
||||
|
||||
if (!isset($cid) && !isset($table)) {
|
||||
// Clear the block cache first, so stale data will
|
||||
// not end up in the page cache.
|
||||
cache_clear_all(NULL, 'cache_block');
|
||||
cache_clear_all(NULL, 'cache_page');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
if (variable_get('cache_lifetime', 0)) {
|
||||
// We store the time in the current user's $user->cache variable which
|
||||
// will be saved into the sessions table by sess_write(). We then
|
||||
// simulate that the cache was flushed for this user by not returning
|
||||
// cached data that was cached before the timestamp.
|
||||
$user->cache = time();
|
||||
|
||||
$cache_flush = variable_get('cache_flush_'. $table, 0);
|
||||
if ($cache_flush == 0) {
|
||||
// This is the first request to clear the cache, start a timer.
|
||||
variable_set('cache_flush_'. $table, time());
|
||||
}
|
||||
else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) {
|
||||
// Clear the cache for everyone, cache_lifetime seconds have
|
||||
// passed since the first request to clear the cache.
|
||||
db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
|
||||
variable_set('cache_flush_'. $table, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No minimum cache lifetime, flush all temporary cache entries now.
|
||||
db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($wildcard) {
|
||||
if ($cid == '*') {
|
||||
db_query("TRUNCATE TABLE {". $table ."}");
|
||||
}
|
||||
else {
|
||||
db_query("DELETE FROM {". $table ."} WHERE cid LIKE '%s%%'", $cid);
|
||||
}
|
||||
}
|
||||
else {
|
||||
db_query("DELETE FROM {". $table ."} WHERE cid = '%s'", $cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3882
includes/common.inc
Normal file
3882
includes/common.inc
Normal file
File diff suppressed because it is too large
Load diff
623
includes/database.inc
Normal file
623
includes/database.inc
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Wrapper for database interface code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A hash value to check when outputting database errors, md5('DB_ERROR').
|
||||
*
|
||||
* @see drupal_error_handler()
|
||||
*/
|
||||
define('DB_ERROR', 'a515ac9c2796ca0e23adbe92c68fc9fc');
|
||||
|
||||
/**
|
||||
* @defgroup database Database abstraction layer
|
||||
* @{
|
||||
* Allow the use of different database servers using the same code base.
|
||||
*
|
||||
* Drupal provides a slim database abstraction layer to provide developers with
|
||||
* the ability to support multiple database servers easily. The intent of this
|
||||
* layer is to preserve the syntax and power of SQL as much as possible, while
|
||||
* letting Drupal control the pieces of queries that need to be written
|
||||
* differently for different servers and provide basic security checks.
|
||||
*
|
||||
* Most Drupal database queries are performed by a call to db_query() or
|
||||
* db_query_range(). Module authors should also consider using pager_query() for
|
||||
* queries that return results that need to be presented on multiple pages, and
|
||||
* tablesort_sql() for generating appropriate queries for sortable tables.
|
||||
*
|
||||
* For example, one might wish to return a list of the most recent 10 nodes
|
||||
* authored by a given user. Instead of directly issuing the SQL query
|
||||
* @code
|
||||
* SELECT n.nid, n.title, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
|
||||
* @endcode
|
||||
* one would instead call the Drupal functions:
|
||||
* @code
|
||||
* $result = db_query_range('SELECT n.nid, n.title, n.created
|
||||
* FROM {node} n WHERE n.uid = %d', $uid, 0, 10);
|
||||
* while ($node = db_fetch_object($result)) {
|
||||
* // Perform operations on $node->body, etc. here.
|
||||
* }
|
||||
* @endcode
|
||||
* Curly braces are used around "node" to provide table prefixing via
|
||||
* db_prefix_tables(). The explicit use of a user ID is pulled out into an
|
||||
* argument passed to db_query() so that SQL injection attacks from user input
|
||||
* can be caught and nullified. The LIMIT syntax varies between database servers,
|
||||
* so that is abstracted into db_query_range() arguments. Finally, note the
|
||||
* common pattern of iterating over the result set using db_fetch_object().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform an SQL query and return success or failure.
|
||||
*
|
||||
* @param $sql
|
||||
* A string containing a complete SQL query. %-substitution
|
||||
* parameters are not supported.
|
||||
* @return
|
||||
* An array containing the keys:
|
||||
* success: a boolean indicating whether the query succeeded
|
||||
* query: the SQL query executed, passed through check_plain()
|
||||
*/
|
||||
function update_sql($sql) {
|
||||
$result = db_query($sql, true);
|
||||
return array('success' => $result !== FALSE, 'query' => check_plain($sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a database prefix to all tables in a query.
|
||||
*
|
||||
* Queries sent to Drupal should wrap all table names in curly brackets. This
|
||||
* function searches for this syntax and adds Drupal's table prefix to all
|
||||
* tables, allowing Drupal to coexist with other systems in the same database if
|
||||
* necessary.
|
||||
*
|
||||
* @param $sql
|
||||
* A string containing a partial or entire SQL query.
|
||||
* @return
|
||||
* The properly-prefixed string.
|
||||
*/
|
||||
function db_prefix_tables($sql) {
|
||||
global $db_prefix;
|
||||
|
||||
if (is_array($db_prefix)) {
|
||||
if (array_key_exists('default', $db_prefix)) {
|
||||
$tmp = $db_prefix;
|
||||
unset($tmp['default']);
|
||||
foreach ($tmp as $key => $val) {
|
||||
$sql = strtr($sql, array('{'. $key .'}' => $val . $key));
|
||||
}
|
||||
return strtr($sql, array('{' => $db_prefix['default'], '}' => ''));
|
||||
}
|
||||
else {
|
||||
foreach ($db_prefix as $key => $val) {
|
||||
$sql = strtr($sql, array('{'. $key .'}' => $val . $key));
|
||||
}
|
||||
return strtr($sql, array('{' => '', '}' => ''));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return strtr($sql, array('{' => $db_prefix, '}' => ''));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a database for future queries.
|
||||
*
|
||||
* If it is necessary to use external databases in a project, this function can
|
||||
* be used to change where database queries are sent. If the database has not
|
||||
* yet been used, it is initialized using the URL specified for that name in
|
||||
* Drupal's configuration file. If this name is not defined, a duplicate of the
|
||||
* default connection is made instead.
|
||||
*
|
||||
* Be sure to change the connection back to the default when done with custom
|
||||
* code.
|
||||
*
|
||||
* @param $name
|
||||
* The key in the $db_url global variable from settings.php. If omitted, the
|
||||
* default connection will be made active.
|
||||
*
|
||||
* @return
|
||||
* The name of the previously active database, or FALSE if none was found.
|
||||
*/
|
||||
function db_set_active($name = 'default') {
|
||||
global $db_url, $db_type, $active_db;
|
||||
static $db_conns, $active_name = FALSE;
|
||||
|
||||
if (empty($db_url)) {
|
||||
include_once 'includes/install.inc';
|
||||
install_goto('install.php');
|
||||
}
|
||||
|
||||
if (!isset($db_conns[$name])) {
|
||||
// Initiate a new connection, using the named DB URL specified.
|
||||
if (is_array($db_url)) {
|
||||
$connect_url = array_key_exists($name, $db_url) ? $db_url[$name] : $db_url['default'];
|
||||
}
|
||||
else {
|
||||
$connect_url = $db_url;
|
||||
}
|
||||
|
||||
$db_type = substr($connect_url, 0, strpos($connect_url, '://'));
|
||||
$handler = "./includes/database.$db_type.inc";
|
||||
|
||||
if (is_file($handler)) {
|
||||
include_once $handler;
|
||||
}
|
||||
else {
|
||||
_db_error_page("The database type '". $db_type ."' is unsupported. Please use either 'mysql' or 'mysqli' for MySQL, or 'pgsql' for PostgreSQL databases.");
|
||||
}
|
||||
|
||||
$db_conns[$name] = db_connect($connect_url);
|
||||
}
|
||||
|
||||
$previous_name = $active_name;
|
||||
// Set the active connection.
|
||||
$active_name = $name;
|
||||
$active_db = $db_conns[$name];
|
||||
|
||||
return $previous_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to show fatal database errors.
|
||||
*
|
||||
* Prints a themed maintenance page with the 'Site off-line' text,
|
||||
* adding the provided error message in the case of 'display_errors'
|
||||
* set to on. Ends the page request; no return.
|
||||
*
|
||||
* @param $error
|
||||
* The error message to be appended if 'display_errors' is on.
|
||||
*/
|
||||
function _db_error_page($error = '') {
|
||||
global $db_type;
|
||||
drupal_init_language();
|
||||
drupal_maintenance_theme();
|
||||
drupal_set_header($_SERVER['SERVER_PROTOCOL'] .' 503 Service Unavailable');
|
||||
drupal_set_title('Site off-line');
|
||||
|
||||
$message = '<p>The site is currently not available due to technical problems. Please try again later. Thank you for your understanding.</p>';
|
||||
$message .= '<hr /><p><small>If you are the maintainer of this site, please check your database settings in the <code>settings.php</code> file and ensure that your hosting provider\'s database server is running. For more help, see the <a href="http://drupal.org/node/258">handbook</a>, or contact your hosting provider.</small></p>';
|
||||
|
||||
if ($error && ini_get('display_errors')) {
|
||||
$message .= '<p><small>The '. theme('placeholder', $db_type) .' error was: '. theme('placeholder', $error) .'.</small></p>';
|
||||
}
|
||||
|
||||
print theme('maintenance_page', $message);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean depending on the availability of the database.
|
||||
*/
|
||||
function db_is_active() {
|
||||
global $active_db;
|
||||
return !empty($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for db_query().
|
||||
*/
|
||||
function _db_query_callback($match, $init = FALSE) {
|
||||
static $args = NULL;
|
||||
if ($init) {
|
||||
$args = $match;
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($match[1]) {
|
||||
case '%d': // We must use type casting to int to convert FALSE/NULL/(TRUE?)
|
||||
$value = array_shift($args);
|
||||
// Do we need special bigint handling?
|
||||
if ($value > PHP_INT_MAX) {
|
||||
$precision = ini_get('precision');
|
||||
@ini_set('precision', 16);
|
||||
$value = sprintf('%.0f', $value);
|
||||
@ini_set('precision', $precision);
|
||||
}
|
||||
else {
|
||||
$value = (int) $value;
|
||||
}
|
||||
// We don't need db_escape_string as numbers are db-safe.
|
||||
return $value;
|
||||
case '%s':
|
||||
return db_escape_string(array_shift($args));
|
||||
case '%n':
|
||||
// Numeric values have arbitrary precision, so can't be treated as float.
|
||||
// is_numeric() allows hex values (0xFF), but they are not valid.
|
||||
$value = trim(array_shift($args));
|
||||
return is_numeric($value) && !preg_match('/x/i', $value) ? $value : '0';
|
||||
case '%%':
|
||||
return '%';
|
||||
case '%f':
|
||||
return (float) array_shift($args);
|
||||
case '%b': // binary data
|
||||
return db_encode_blob(array_shift($args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate placeholders for an array of query arguments of a single type.
|
||||
*
|
||||
* Given a Schema API field type, return correct %-placeholders to
|
||||
* embed in a query
|
||||
*
|
||||
* @param $arguments
|
||||
* An array with at least one element.
|
||||
* @param $type
|
||||
* The Schema API type of a field (e.g. 'int', 'text', or 'varchar').
|
||||
*/
|
||||
function db_placeholders($arguments, $type = 'int') {
|
||||
$placeholder = db_type_placeholder($type);
|
||||
return implode(',', array_fill(0, count($arguments), $placeholder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the place holders that should be replaced in _db_query_callback().
|
||||
*/
|
||||
define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b|%n)/');
|
||||
|
||||
/**
|
||||
* Helper function for db_rewrite_sql.
|
||||
*
|
||||
* Collects JOIN and WHERE statements via hook_db_rewrite_sql()
|
||||
* Decides whether to select primary_key or DISTINCT(primary_key)
|
||||
*
|
||||
* @param $query
|
||||
* Query to be rewritten.
|
||||
* @param $primary_table
|
||||
* Name or alias of the table which has the primary key field for this query.
|
||||
* Typical table names would be: {blocks}, {comments}, {forum}, {node},
|
||||
* {menu}, {term_data} or {vocabulary}. However, in most cases the usual
|
||||
* table alias (b, c, f, n, m, t or v) is used instead of the table name.
|
||||
* @param $primary_field
|
||||
* Name of the primary field.
|
||||
* @param $args
|
||||
* Array of additional arguments.
|
||||
* @return
|
||||
* An array: join statements, where statements, field or DISTINCT(field).
|
||||
*/
|
||||
function _db_rewrite_sql($query = '', $primary_table = 'n', $primary_field = 'nid', $args = array()) {
|
||||
$where = array();
|
||||
$join = array();
|
||||
$distinct = FALSE;
|
||||
foreach (module_implements('db_rewrite_sql') as $module) {
|
||||
$result = module_invoke($module, 'db_rewrite_sql', $query, $primary_table, $primary_field, $args);
|
||||
if (isset($result) && is_array($result)) {
|
||||
if (isset($result['where'])) {
|
||||
$where[] = $result['where'];
|
||||
}
|
||||
if (isset($result['join'])) {
|
||||
$join[] = $result['join'];
|
||||
}
|
||||
if (isset($result['distinct']) && $result['distinct']) {
|
||||
$distinct = TRUE;
|
||||
}
|
||||
}
|
||||
elseif (isset($result)) {
|
||||
$where[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
$where = empty($where) ? '' : '('. implode(') AND (', $where) .')';
|
||||
$join = empty($join) ? '' : implode(' ', $join);
|
||||
|
||||
return array($join, $where, $distinct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites node, taxonomy and comment queries. Use it for listing queries. Do not
|
||||
* use FROM table1, table2 syntax, use JOIN instead.
|
||||
*
|
||||
* @param $query
|
||||
* Query to be rewritten.
|
||||
* @param $primary_table
|
||||
* Name or alias of the table which has the primary key field for this query.
|
||||
* Typical table names would be: {blocks}, {comments}, {forum}, {node},
|
||||
* {menu}, {term_data} or {vocabulary}. However, it is more common to use the
|
||||
* the usual table aliases: b, c, f, n, m, t or v.
|
||||
* @param $primary_field
|
||||
* Name of the primary field.
|
||||
* @param $args
|
||||
* An array of arguments, passed to the implementations of hook_db_rewrite_sql.
|
||||
* @return
|
||||
* The original query with JOIN and WHERE statements inserted from
|
||||
* hook_db_rewrite_sql implementations. nid is rewritten if needed.
|
||||
*/
|
||||
function db_rewrite_sql($query, $primary_table = 'n', $primary_field = 'nid', $args = array()) {
|
||||
list($join, $where, $distinct) = _db_rewrite_sql($query, $primary_table, $primary_field, $args);
|
||||
|
||||
if ($distinct) {
|
||||
$query = db_distinct_field($primary_table, $primary_field, $query);
|
||||
}
|
||||
|
||||
if (!empty($where) || !empty($join)) {
|
||||
$pattern = '{
|
||||
# Beginning of the string
|
||||
^
|
||||
((?P<anonymous_view>
|
||||
# Everything within this set of parentheses is named "anonymous view"
|
||||
(?:
|
||||
[^()]++ # anything not parentheses
|
||||
|
|
||||
\( (?P>anonymous_view) \) # an open parenthesis, more "anonymous view" and finally a close parenthesis.
|
||||
)*
|
||||
)[^()]+WHERE)
|
||||
}x';
|
||||
preg_match($pattern, $query, $matches);
|
||||
if (!$where) {
|
||||
$where = '1 = 1';
|
||||
}
|
||||
if ($matches) {
|
||||
$n = strlen($matches[1]);
|
||||
$second_part = substr($query, $n);
|
||||
$first_part = substr($matches[1], 0, $n - 5) ." $join WHERE $where AND ( ";
|
||||
// PHP 4 does not support strrpos for strings. We emulate it.
|
||||
$haystack_reverse = strrev($second_part);
|
||||
}
|
||||
else {
|
||||
$haystack_reverse = strrev($query);
|
||||
}
|
||||
// No need to use strrev on the needle, we supply GROUP, ORDER, LIMIT
|
||||
// reversed.
|
||||
foreach (array('PUORG', 'REDRO', 'TIMIL') as $needle_reverse) {
|
||||
$pos = strpos($haystack_reverse, $needle_reverse);
|
||||
if ($pos !== FALSE) {
|
||||
// All needles are five characters long.
|
||||
$pos += 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($matches) {
|
||||
if ($pos === FALSE) {
|
||||
$query = $first_part . $second_part .')';
|
||||
}
|
||||
else {
|
||||
$query = $first_part . substr($second_part, 0, -$pos) .')'. substr($second_part, -$pos);
|
||||
}
|
||||
}
|
||||
elseif ($pos === FALSE) {
|
||||
$query .= " $join WHERE $where";
|
||||
}
|
||||
else {
|
||||
$query = substr($query, 0, -$pos) . " $join WHERE $where " . substr($query, -$pos);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the DISTINCT flag to the supplied query and returns the altered query.
|
||||
*
|
||||
* The supplied query should not contain a DISTINCT flag. This will not, and
|
||||
* never did guarantee that you will obtain distinct values of $table.$field.
|
||||
*
|
||||
* @param $table
|
||||
* Unused. Kept to retain API compatibility.
|
||||
* @param $field
|
||||
* Unused. Kept to retain API compatibility.
|
||||
* @param $query
|
||||
* Query to which the DISTINCT flag should be applied.
|
||||
*
|
||||
* @return
|
||||
* SQL query with the DISTINCT flag set.
|
||||
*/
|
||||
function db_distinct_field($table, $field, $query) {
|
||||
$matches = array();
|
||||
if (!preg_match('/^SELECT\s*DISTINCT/i', $query, $matches)) {
|
||||
// Only add distinct to the outer SELECT to avoid messing up subqueries.
|
||||
$query = preg_replace('/^SELECT/i', 'SELECT DISTINCT', $query);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict a dynamic table, column or constraint name to safe characters.
|
||||
*
|
||||
* Only keeps alphanumeric and underscores.
|
||||
*/
|
||||
function db_escape_table($string) {
|
||||
return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup database".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup schemaapi Schema API
|
||||
* @{
|
||||
*
|
||||
* A Drupal schema definition is an array structure representing one or
|
||||
* more tables and their related keys and indexes. A schema is defined by
|
||||
* hook_schema(), which usually lives in a modulename.install file.
|
||||
*
|
||||
* By implementing hook_schema() and specifying the tables your module
|
||||
* declares, you can easily create and drop these tables on all
|
||||
* supported database engines. You don't have to deal with the
|
||||
* different SQL dialects for table creation and alteration of the
|
||||
* supported database engines.
|
||||
*
|
||||
* hook_schema() should return an array with a key for each table that
|
||||
* the module defines.
|
||||
*
|
||||
* The following keys are defined:
|
||||
*
|
||||
* - 'description': A string describing this table and its purpose.
|
||||
* References to other tables should be enclosed in
|
||||
* curly-brackets. For example, the node_revisions table
|
||||
* description field might contain "Stores per-revision title and
|
||||
* body data for each {node}."
|
||||
* - 'fields': An associative array ('fieldname' => specification)
|
||||
* that describes the table's database columns. The specification
|
||||
* is also an array. The following specification parameters are defined:
|
||||
* - 'description': A string describing this field and its purpose.
|
||||
* References to other tables should be enclosed in
|
||||
* curly-brackets. For example, the node table vid field
|
||||
* description might contain "Always holds the largest (most
|
||||
* recent) {node_revisions}.vid value for this nid."
|
||||
* - 'type': The generic datatype: 'varchar', 'int', 'serial'
|
||||
* 'float', 'numeric', 'text', 'blob' or 'datetime'. Most types
|
||||
* just map to the according database engine specific
|
||||
* datatypes. Use 'serial' for auto incrementing fields. This
|
||||
* will expand to 'int auto_increment' on mysql.
|
||||
* - 'serialize': A boolean indicating whether the field will be stored
|
||||
as a serialized string.
|
||||
* - 'size': The data size: 'tiny', 'small', 'medium', 'normal',
|
||||
* 'big'. This is a hint about the largest value the field will
|
||||
* store and determines which of the database engine specific
|
||||
* datatypes will be used (e.g. on MySQL, TINYINT vs. INT vs. BIGINT).
|
||||
* 'normal', the default, selects the base type (e.g. on MySQL,
|
||||
* INT, VARCHAR, BLOB, etc.).
|
||||
* Not all sizes are available for all data types. See
|
||||
* db_type_map() for possible combinations.
|
||||
* - 'not null': If true, no NULL values will be allowed in this
|
||||
* database column. Defaults to false.
|
||||
* - 'default': The field's default value. The PHP type of the
|
||||
* value matters: '', '0', and 0 are all different. If you
|
||||
* specify '0' as the default value for a type 'int' field it
|
||||
* will not work because '0' is a string containing the
|
||||
* character "zero", not an integer.
|
||||
* - 'length': The maximal length of a type 'char', 'varchar' or 'text'
|
||||
* field. Ignored for other field types.
|
||||
* - 'unsigned': A boolean indicating whether a type 'int', 'float'
|
||||
* and 'numeric' only is signed or unsigned. Defaults to
|
||||
* FALSE. Ignored for other field types.
|
||||
* - 'precision', 'scale': For type 'numeric' fields, indicates
|
||||
* the precision (total number of significant digits) and scale
|
||||
* (decimal digits right of the decimal point). Both values are
|
||||
* mandatory. Ignored for other field types.
|
||||
* All parameters apart from 'type' are optional except that type
|
||||
* 'numeric' columns must specify 'precision' and 'scale'.
|
||||
* - 'primary key': An array of one or more key column specifiers (see below)
|
||||
* that form the primary key.
|
||||
* - 'unique keys': An associative array of unique keys ('keyname' =>
|
||||
* specification). Each specification is an array of one or more
|
||||
* key column specifiers (see below) that form a unique key on the table.
|
||||
* - 'indexes': An associative array of indexes ('indexame' =>
|
||||
* specification). Each specification is an array of one or more
|
||||
* key column specifiers (see below) that form an index on the
|
||||
* table.
|
||||
*
|
||||
* A key column specifier is either a string naming a column or an
|
||||
* array of two elements, column name and length, specifying a prefix
|
||||
* of the named column.
|
||||
*
|
||||
* As an example, here is a SUBSET of the schema definition for
|
||||
* Drupal's 'node' table. It show four fields (nid, vid, type, and
|
||||
* title), the primary key on field 'nid', a unique key named 'vid' on
|
||||
* field 'vid', and two indexes, one named 'nid' on field 'nid' and
|
||||
* one named 'node_title_type' on the field 'title' and the first four
|
||||
* bytes of the field 'type':
|
||||
*
|
||||
* @code
|
||||
* $schema['node'] = array(
|
||||
* 'fields' => array(
|
||||
* 'nid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
* 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
|
||||
* 'type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
|
||||
* 'title' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
|
||||
* ),
|
||||
* 'primary key' => array('nid'),
|
||||
* 'unique keys' => array(
|
||||
* 'vid' => array('vid')
|
||||
* ),
|
||||
* 'indexes' => array(
|
||||
* 'nid' => array('nid'),
|
||||
* 'node_title_type' => array('title', array('type', 4)),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*
|
||||
* @see drupal_install_schema()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new table from a Drupal table definition.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $name
|
||||
* The name of the table to create.
|
||||
* @param $table
|
||||
* A Schema API table definition array.
|
||||
*/
|
||||
function db_create_table(&$ret, $name, $table) {
|
||||
$statements = db_create_table_sql($name, $table);
|
||||
foreach ($statements as $statement) {
|
||||
$ret[] = update_sql($statement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of field names from an array of key/index column specifiers.
|
||||
*
|
||||
* This is usually an identity function but if a key/index uses a column prefix
|
||||
* specification, this function extracts just the name.
|
||||
*
|
||||
* @param $fields
|
||||
* An array of key/index column specifiers.
|
||||
* @return
|
||||
* An array of field names.
|
||||
*/
|
||||
function db_field_names($fields) {
|
||||
$ret = array();
|
||||
foreach ($fields as $field) {
|
||||
if (is_array($field)) {
|
||||
$ret[] = $field[0];
|
||||
}
|
||||
else {
|
||||
$ret[] = $field;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a Schema API field type, return the correct %-placeholder.
|
||||
*
|
||||
* Embed the placeholder in a query to be passed to db_query and and pass as an
|
||||
* argument to db_query a value of the specified type.
|
||||
*
|
||||
* @param $type
|
||||
* The Schema API type of a field.
|
||||
* @return
|
||||
* The placeholder string to embed in a query for that type.
|
||||
*/
|
||||
function db_type_placeholder($type) {
|
||||
switch ($type) {
|
||||
case 'varchar':
|
||||
case 'char':
|
||||
case 'text':
|
||||
case 'datetime':
|
||||
return "'%s'";
|
||||
|
||||
case 'numeric':
|
||||
// Numeric values are arbitrary precision numbers. Syntacically, numerics
|
||||
// should be specified directly in SQL. However, without single quotes
|
||||
// the %s placeholder does not protect against non-numeric characters such
|
||||
// as spaces which would expose us to SQL injection.
|
||||
return '%n';
|
||||
|
||||
case 'serial':
|
||||
case 'int':
|
||||
return '%d';
|
||||
|
||||
case 'float':
|
||||
return '%f';
|
||||
|
||||
case 'blob':
|
||||
return '%b';
|
||||
}
|
||||
|
||||
// There is no safe value to return here, so return something that
|
||||
// will cause the query to fail.
|
||||
return 'unsupported type '. $type .'for db_type_placeholder';
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup schemaapi".
|
||||
*/
|
||||
542
includes/database.mysql-common.inc
Normal file
542
includes/database.mysql-common.inc
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions shared between mysql and mysqli database engines.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Runs a basic query in the active database.
|
||||
*
|
||||
* User-supplied arguments to the query should be passed in as separate
|
||||
* parameters so that they can be properly escaped to avoid SQL injection
|
||||
* attacks.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing an SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. Instead of a variable number of query arguments,
|
||||
* you may also pass a single array containing the query arguments.
|
||||
*
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
*
|
||||
* @return
|
||||
* Successful SELECT, SHOW, DESCRIBE, EXPLAIN, or other queries which return a
|
||||
* set of results will return a database query result resource. Other
|
||||
* successful queries will return TRUE and failing queries will return FALSE.
|
||||
*/
|
||||
function db_query($query) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
$query = db_prefix_tables($query);
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup schemaapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate SQL to create a new table from a Drupal schema definition.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the table to create.
|
||||
* @param $table
|
||||
* A Schema API table definition array.
|
||||
* @return
|
||||
* An array of SQL statements to create the table.
|
||||
*/
|
||||
function db_create_table_sql($name, $table) {
|
||||
|
||||
if (empty($table['mysql_suffix'])) {
|
||||
$table['mysql_suffix'] = '/*!40100 DEFAULT CHARACTER SET utf8';
|
||||
// By default, MySQL uses the default collation for new tables, which is
|
||||
// 'utf8_general_ci' for utf8. If an alternate collation has been set, it
|
||||
// needs to be explicitly specified.
|
||||
// @see db_connect()
|
||||
$collation = (!empty($table['collation']) ? $table['collation'] : (!empty($GLOBALS['db_collation']) ? $GLOBALS['db_collation'] : ''));
|
||||
if ($collation) {
|
||||
$table['mysql_suffix'] .= ' COLLATE ' . $collation;
|
||||
}
|
||||
$table['mysql_suffix'] .= ' */';
|
||||
}
|
||||
|
||||
$sql = "CREATE TABLE {". $name ."} (\n";
|
||||
|
||||
// Add the SQL statement for each field.
|
||||
foreach ($table['fields'] as $field_name => $field) {
|
||||
$sql .= _db_create_field_sql($field_name, _db_process_field($field)) .", \n";
|
||||
}
|
||||
|
||||
// Process keys & indexes.
|
||||
$keys = _db_create_keys_sql($table);
|
||||
if (count($keys)) {
|
||||
$sql .= implode(", \n", $keys) .", \n";
|
||||
}
|
||||
|
||||
// Remove the last comma and space.
|
||||
$sql = substr($sql, 0, -3) ."\n) ";
|
||||
|
||||
$sql .= $table['mysql_suffix'];
|
||||
|
||||
return array($sql);
|
||||
}
|
||||
|
||||
function _db_create_keys_sql($spec) {
|
||||
$keys = array();
|
||||
|
||||
if (!empty($spec['primary key'])) {
|
||||
$keys[] = 'PRIMARY KEY ('. _db_create_key_sql($spec['primary key']) .')';
|
||||
}
|
||||
if (!empty($spec['unique keys'])) {
|
||||
foreach ($spec['unique keys'] as $key => $fields) {
|
||||
$keys[] = 'UNIQUE KEY '. $key .' ('. _db_create_key_sql($fields) .')';
|
||||
}
|
||||
}
|
||||
if (!empty($spec['indexes'])) {
|
||||
foreach ($spec['indexes'] as $index => $fields) {
|
||||
$keys[] = 'INDEX '. $index .' ('. _db_create_key_sql($fields) .')';
|
||||
}
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
function _db_create_key_sql($fields) {
|
||||
$ret = array();
|
||||
foreach ($fields as $field) {
|
||||
if (is_array($field)) {
|
||||
$ret[] = $field[0] .'('. $field[1] .')';
|
||||
}
|
||||
else {
|
||||
$ret[] = $field;
|
||||
}
|
||||
}
|
||||
return implode(', ', $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database-engine specific properties for a field.
|
||||
*
|
||||
* @param $field
|
||||
* A field description array, as specified in the schema documentation.
|
||||
*/
|
||||
function _db_process_field($field) {
|
||||
|
||||
if (!isset($field['size'])) {
|
||||
$field['size'] = 'normal';
|
||||
}
|
||||
|
||||
// Set the correct database-engine specific datatype.
|
||||
if (!isset($field['mysql_type'])) {
|
||||
$map = db_type_map();
|
||||
$field['mysql_type'] = $map[$field['type'] .':'. $field['size']];
|
||||
}
|
||||
|
||||
if ($field['type'] == 'serial') {
|
||||
$field['auto_increment'] = TRUE;
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SQL string for a field to be used in table creation or alteration.
|
||||
*
|
||||
* Before passing a field out of a schema definition into this function it has
|
||||
* to be processed by _db_process_field().
|
||||
*
|
||||
* @param $name
|
||||
* Name of the field.
|
||||
* @param $spec
|
||||
* The field specification, as per the schema data structure format.
|
||||
*/
|
||||
function _db_create_field_sql($name, $spec) {
|
||||
$sql = "`". $name ."` ". $spec['mysql_type'];
|
||||
|
||||
if (in_array($spec['type'], array('varchar', 'char', 'text')) && isset($spec['length'])) {
|
||||
$sql .= '('. $spec['length'] .')';
|
||||
}
|
||||
elseif (isset($spec['precision']) && isset($spec['scale'])) {
|
||||
$sql .= '('. $spec['precision'] .', '. $spec['scale'] .')';
|
||||
}
|
||||
|
||||
if (!empty($spec['unsigned'])) {
|
||||
$sql .= ' unsigned';
|
||||
}
|
||||
|
||||
if (!empty($spec['not null'])) {
|
||||
$sql .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (!empty($spec['auto_increment'])) {
|
||||
$sql .= ' auto_increment';
|
||||
}
|
||||
|
||||
if (isset($spec['default'])) {
|
||||
if (is_string($spec['default'])) {
|
||||
$spec['default'] = "'". $spec['default'] ."'";
|
||||
}
|
||||
$sql .= ' DEFAULT '. $spec['default'];
|
||||
}
|
||||
|
||||
if (empty($spec['not null']) && !isset($spec['default'])) {
|
||||
$sql .= ' DEFAULT NULL';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* This maps a generic data type in combination with its data size
|
||||
* to the engine-specific data type.
|
||||
*/
|
||||
function db_type_map() {
|
||||
// Put :normal last so it gets preserved by array_flip. This makes
|
||||
// it much easier for modules (such as schema.module) to map
|
||||
// database types back into schema types.
|
||||
$map = array(
|
||||
'varchar:normal' => 'VARCHAR',
|
||||
'char:normal' => 'CHAR',
|
||||
|
||||
'text:tiny' => 'TINYTEXT',
|
||||
'text:small' => 'TINYTEXT',
|
||||
'text:medium' => 'MEDIUMTEXT',
|
||||
'text:big' => 'LONGTEXT',
|
||||
'text:normal' => 'TEXT',
|
||||
|
||||
'serial:tiny' => 'TINYINT',
|
||||
'serial:small' => 'SMALLINT',
|
||||
'serial:medium' => 'MEDIUMINT',
|
||||
'serial:big' => 'BIGINT',
|
||||
'serial:normal' => 'INT',
|
||||
|
||||
'int:tiny' => 'TINYINT',
|
||||
'int:small' => 'SMALLINT',
|
||||
'int:medium' => 'MEDIUMINT',
|
||||
'int:big' => 'BIGINT',
|
||||
'int:normal' => 'INT',
|
||||
|
||||
'float:tiny' => 'FLOAT',
|
||||
'float:small' => 'FLOAT',
|
||||
'float:medium' => 'FLOAT',
|
||||
'float:big' => 'DOUBLE',
|
||||
'float:normal' => 'FLOAT',
|
||||
|
||||
'numeric:normal' => 'DECIMAL',
|
||||
|
||||
'blob:big' => 'LONGBLOB',
|
||||
'blob:normal' => 'BLOB',
|
||||
|
||||
'datetime:normal' => 'DATETIME',
|
||||
);
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be renamed.
|
||||
* @param $new_name
|
||||
* The new name for the table.
|
||||
*/
|
||||
function db_rename_table(&$ret, $table, $new_name) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be dropped.
|
||||
*/
|
||||
function db_drop_table(&$ret, $table) {
|
||||
$ret[] = update_sql('DROP TABLE {'. $table .'}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new field to a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* Name of the table to be altered.
|
||||
* @param $field
|
||||
* Name of the field to be added.
|
||||
* @param $spec
|
||||
* The field specification array, as taken from a schema definition.
|
||||
* The specification may also contain the key 'initial', the newly
|
||||
* created field will be set to the value of the key in all rows.
|
||||
* This is most useful for creating NOT NULL columns with no default
|
||||
* value in existing tables.
|
||||
* @param $keys_new
|
||||
* (optional) Keys and indexes specification to be created on the
|
||||
* table along with adding the field. The format is the same as a
|
||||
* table specification but without the 'fields' element. If you are
|
||||
* adding a type 'serial' field, you MUST specify at least one key
|
||||
* or index including it in this array. See db_change_field() for more
|
||||
* explanation why.
|
||||
*/
|
||||
function db_add_field(&$ret, $table, $field, $spec, $keys_new = array()) {
|
||||
$fixnull = FALSE;
|
||||
if (!empty($spec['not null']) && !isset($spec['default'])) {
|
||||
$fixnull = TRUE;
|
||||
$spec['not null'] = FALSE;
|
||||
}
|
||||
$query = 'ALTER TABLE {'. $table .'} ADD ';
|
||||
$query .= _db_create_field_sql($field, _db_process_field($spec));
|
||||
if (count($keys_new)) {
|
||||
$query .= ', ADD '. implode(', ADD ', _db_create_keys_sql($keys_new));
|
||||
}
|
||||
$ret[] = update_sql($query);
|
||||
if (isset($spec['initial'])) {
|
||||
// All this because update_sql does not support %-placeholders.
|
||||
$sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']);
|
||||
$result = db_query($sql, $spec['initial']);
|
||||
$ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
|
||||
}
|
||||
if ($fixnull) {
|
||||
$spec['not null'] = TRUE;
|
||||
db_change_field($ret, $table, $field, $field, $spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a field.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be dropped.
|
||||
*/
|
||||
function db_drop_field(&$ret, $table, $field) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP '. $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value for a field.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be altered.
|
||||
* @param $default
|
||||
* Default value to be set. NULL for 'default NULL'.
|
||||
*/
|
||||
function db_field_set_default(&$ret, $table, $field, $default) {
|
||||
if ($default === NULL) {
|
||||
$default = 'NULL';
|
||||
}
|
||||
else {
|
||||
$default = is_string($default) ? "'$default'" : $default;
|
||||
}
|
||||
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' SET DEFAULT '. $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a field to have no default value.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be altered.
|
||||
*/
|
||||
function db_field_set_no_default(&$ret, $table, $field) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' DROP DEFAULT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a primary key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $fields
|
||||
* Fields for the primary key.
|
||||
*/
|
||||
function db_add_primary_key(&$ret, $table, $fields) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ADD PRIMARY KEY ('.
|
||||
_db_create_key_sql($fields) .')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the primary key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
*/
|
||||
function db_drop_primary_key(&$ret, $table) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP PRIMARY KEY');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a unique key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the key.
|
||||
* @param $fields
|
||||
* An array of field names.
|
||||
*/
|
||||
function db_add_unique_key(&$ret, $table, $name, $fields) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ADD UNIQUE KEY '.
|
||||
$name .' ('. _db_create_key_sql($fields) .')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a unique key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the key.
|
||||
*/
|
||||
function db_drop_unique_key(&$ret, $table, $name) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP KEY '. $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an index.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the index.
|
||||
* @param $fields
|
||||
* An array of field names.
|
||||
*/
|
||||
function db_add_index(&$ret, $table, $name, $fields) {
|
||||
$query = 'ALTER TABLE {'. $table .'} ADD INDEX '. $name .' ('. _db_create_key_sql($fields) .')';
|
||||
$ret[] = update_sql($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop an index.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the index.
|
||||
*/
|
||||
function db_drop_index(&$ret, $table, $name) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP INDEX '. $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a field definition.
|
||||
*
|
||||
* IMPORTANT NOTE: To maintain database portability, you have to explicitly
|
||||
* recreate all indices and primary keys that are using the changed field.
|
||||
*
|
||||
* That means that you have to drop all affected keys and indexes with
|
||||
* db_drop_{primary_key,unique_key,index}() before calling db_change_field().
|
||||
* To recreate the keys and indices, pass the key definitions as the
|
||||
* optional $keys_new argument directly to db_change_field().
|
||||
*
|
||||
* For example, suppose you have:
|
||||
* @code
|
||||
* $schema['foo'] = array(
|
||||
* 'fields' => array(
|
||||
* 'bar' => array('type' => 'int', 'not null' => TRUE)
|
||||
* ),
|
||||
* 'primary key' => array('bar')
|
||||
* );
|
||||
* @endcode
|
||||
* and you want to change foo.bar to be type serial, leaving it as the
|
||||
* primary key. The correct sequence is:
|
||||
* @code
|
||||
* db_drop_primary_key($ret, 'foo');
|
||||
* db_change_field($ret, 'foo', 'bar', 'bar',
|
||||
* array('type' => 'serial', 'not null' => TRUE),
|
||||
* array('primary key' => array('bar')));
|
||||
* @endcode
|
||||
*
|
||||
* The reasons for this are due to the different database engines:
|
||||
*
|
||||
* On PostgreSQL, changing a field definition involves adding a new field
|
||||
* and dropping an old one which* causes any indices, primary keys and
|
||||
* sequences (from serial-type fields) that use the changed field to be dropped.
|
||||
*
|
||||
* On MySQL, all type 'serial' fields must be part of at least one key
|
||||
* or index as soon as they are created. You cannot use
|
||||
* db_add_{primary_key,unique_key,index}() for this purpose because
|
||||
* the ALTER TABLE command will fail to add the column without a key
|
||||
* or index specification. The solution is to use the optional
|
||||
* $keys_new argument to create the key or index at the same time as
|
||||
* field.
|
||||
*
|
||||
* You could use db_add_{primary_key,unique_key,index}() in all cases
|
||||
* unless you are converting a field to be type serial. You can use
|
||||
* the $keys_new argument in all cases.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* Name of the table.
|
||||
* @param $field
|
||||
* Name of the field to change.
|
||||
* @param $field_new
|
||||
* New name for the field (set to the same as $field if you don't want to change the name).
|
||||
* @param $spec
|
||||
* The field specification for the new field.
|
||||
* @param $keys_new
|
||||
* (optional) Keys and indexes specification to be created on the
|
||||
* table along with changing the field. The format is the same as a
|
||||
* table specification but without the 'fields' element.
|
||||
*/
|
||||
|
||||
function db_change_field(&$ret, $table, $field, $field_new, $spec, $keys_new = array()) {
|
||||
$sql = 'ALTER TABLE {'. $table .'} CHANGE `'. $field .'` '.
|
||||
_db_create_field_sql($field_new, _db_process_field($spec));
|
||||
if (count($keys_new)) {
|
||||
$sql .= ', ADD '. implode(', ADD ', _db_create_keys_sql($keys_new));
|
||||
}
|
||||
$ret[] = update_sql($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last insert id.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table you inserted into.
|
||||
* @param $field
|
||||
* The name of the autoincrement field.
|
||||
*/
|
||||
function db_last_insert_id($table, $field) {
|
||||
return db_result(db_query('SELECT LAST_INSERT_ID()'));
|
||||
}
|
||||
376
includes/database.mysql.inc
Normal file
376
includes/database.mysql.inc
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Database interface code for MySQL database servers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup database
|
||||
* @{
|
||||
*/
|
||||
|
||||
// Include functions shared between mysql and mysqli.
|
||||
require_once './includes/database.mysql-common.inc';
|
||||
|
||||
/**
|
||||
* Report database status.
|
||||
*/
|
||||
function db_status_report($phase) {
|
||||
$t = get_t();
|
||||
|
||||
$version = db_version();
|
||||
|
||||
$form['mysql'] = array(
|
||||
'title' => $t('MySQL database'),
|
||||
'value' => ($phase == 'runtime') ? l($version, 'admin/reports/status/sql') : $version,
|
||||
);
|
||||
|
||||
if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) {
|
||||
$form['mysql']['severity'] = REQUIREMENT_ERROR;
|
||||
$form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL));
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the database server currently in use.
|
||||
*
|
||||
* @return Database server version
|
||||
*/
|
||||
function db_version() {
|
||||
list($version) = explode('-', mysql_get_server_info());
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a database connection.
|
||||
*/
|
||||
function db_connect($url) {
|
||||
$url = parse_url($url);
|
||||
|
||||
// Check if MySQL support is present in PHP
|
||||
if (!function_exists('mysql_connect')) {
|
||||
_db_error_page('Unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
|
||||
}
|
||||
|
||||
// Decode urlencoded information in the db connection string
|
||||
$url['user'] = urldecode($url['user']);
|
||||
// Test if database URL has a password.
|
||||
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
|
||||
$url['host'] = urldecode($url['host']);
|
||||
$url['path'] = urldecode($url['path']);
|
||||
|
||||
// Allow for non-standard MySQL port.
|
||||
if (isset($url['port'])) {
|
||||
$url['host'] = $url['host'] .':'. $url['port'];
|
||||
}
|
||||
|
||||
// - TRUE makes mysql_connect() always open a new link, even if
|
||||
// mysql_connect() was called before with the same parameters.
|
||||
// This is important if you are using two databases on the same
|
||||
// server.
|
||||
// - 2 means CLIENT_FOUND_ROWS: return the number of found
|
||||
// (matched) rows, not the number of affected rows.
|
||||
$connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2);
|
||||
if (!$connection || !mysql_select_db(substr($url['path'], 1))) {
|
||||
// Show error screen otherwise
|
||||
_db_error_page(mysql_error());
|
||||
}
|
||||
|
||||
// Force MySQL to use the UTF-8 character set. Also set the collation, if a
|
||||
// certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
|
||||
// for UTF-8.
|
||||
if (!empty($GLOBALS['db_collation'])) {
|
||||
mysql_query('SET NAMES utf8 COLLATE '. $GLOBALS['db_collation'], $connection);
|
||||
}
|
||||
else {
|
||||
mysql_query('SET NAMES utf8', $connection);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for db_query().
|
||||
*/
|
||||
function _db_query($query, $debug = 0) {
|
||||
global $active_db, $queries, $user;
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$timer = (float)$usec + (float)$sec;
|
||||
// If devel.module query logging is enabled, prepend a comment with the username and calling function
|
||||
// to the SQL string. This is useful when running mysql's SHOW PROCESSLIST to learn what exact
|
||||
// code is issueing the slow query.
|
||||
$bt = debug_backtrace();
|
||||
// t() may not be available yet so we don't wrap 'Anonymous'.
|
||||
$name = $user->uid ? $user->name : variable_get('anonymous', 'Anonymous');
|
||||
// str_replace() to prevent SQL injection via username or anonymous name.
|
||||
$name = str_replace(array('*', '/'), '', $name);
|
||||
$query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query;
|
||||
}
|
||||
|
||||
$result = mysql_query($query, $active_db);
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
$query = $bt[2]['function'] ."\n". $query;
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$stop = (float)$usec + (float)$sec;
|
||||
$diff = $stop - $timer;
|
||||
$queries[] = array($query, $diff);
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
print '<p>query: '. $query .'<br />error:'. mysql_error($active_db) .'</p>';
|
||||
}
|
||||
|
||||
if (!mysql_errno($active_db)) {
|
||||
return $result;
|
||||
}
|
||||
else {
|
||||
// Indicate to drupal_error_handler that this is a database error.
|
||||
${DB_ERROR} = TRUE;
|
||||
trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an object.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An object representing the next row of the result, or FALSE. The attributes
|
||||
* of this object are the table fields selected by the query.
|
||||
*/
|
||||
function db_fetch_object($result) {
|
||||
if ($result) {
|
||||
return mysql_fetch_object($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an array.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An associative array representing the next row of the result, or FALSE.
|
||||
* The keys of this object are the names of the table fields selected by the
|
||||
* query, and the values are the field values for this result row.
|
||||
*/
|
||||
function db_fetch_array($result) {
|
||||
if ($result) {
|
||||
return mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an individual result field from the previous query.
|
||||
*
|
||||
* Only use this function if exactly one field is being selected; otherwise,
|
||||
* use db_fetch_object() or db_fetch_array().
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
*
|
||||
* @return
|
||||
* The resulting field or FALSE.
|
||||
*/
|
||||
function db_result($result) {
|
||||
if ($result && mysql_num_rows($result) > 0) {
|
||||
// The mysql_fetch_row function has an optional second parameter $row
|
||||
// but that can't be used for compatibility with Oracle, DB2, etc.
|
||||
$array = mysql_fetch_row($result);
|
||||
return $array[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the previous query caused an error.
|
||||
*/
|
||||
function db_error() {
|
||||
global $active_db;
|
||||
return mysql_errno($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of rows changed by the preceding query.
|
||||
*/
|
||||
function db_affected_rows() {
|
||||
global $active_db;
|
||||
return mysql_affected_rows($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a limited-range query in the active database.
|
||||
*
|
||||
* Use this as a substitute for db_query() when a subset of the query is to be
|
||||
* returned.
|
||||
* User-supplied arguments to the query should be passed in as separate parameters
|
||||
* so that they can be properly escaped to avoid SQL injection attacks.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing an SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. The query arguments can be enclosed in one
|
||||
* array instead.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
*
|
||||
* @param $from
|
||||
* The first result row to return.
|
||||
* @param $count
|
||||
* The maximum number of result rows to return.
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_range($query) {
|
||||
$args = func_get_args();
|
||||
$count = array_pop($args);
|
||||
$from = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = db_prefix_tables($query);
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
$query .= ' LIMIT '. (int)$from .', '. (int)$count;
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a SELECT query and stores its results in a temporary table.
|
||||
*
|
||||
* Use this as a substitute for db_query() when the results need to be stored
|
||||
* in a temporary table.
|
||||
*
|
||||
* User-supplied arguments to the query should be passed in as separate parameters
|
||||
* so that they can be properly escaped to avoid SQL injection attacks.
|
||||
*
|
||||
* Note that if you need to know how many results were returned, you should do
|
||||
* a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does
|
||||
* not give consistent result across different database types in this case.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing a normal SELECT SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. The query arguments can be enclosed in one
|
||||
* array instead.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
* @param $table
|
||||
* The name of the temporary table to select into. This name will not be
|
||||
* prefixed as there is no risk of collision.
|
||||
*
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_temporary($query) {
|
||||
$args = func_get_args();
|
||||
$tablename = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', db_prefix_tables($query));
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a properly formatted Binary Large OBject value.
|
||||
*
|
||||
* @param $data
|
||||
* Data to encode.
|
||||
* @return
|
||||
* Encoded data.
|
||||
*/
|
||||
function db_encode_blob($data) {
|
||||
global $active_db;
|
||||
return "'". mysql_real_escape_string($data, $active_db) ."'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text from a Binary Large Object value.
|
||||
*
|
||||
* @param $data
|
||||
* Data to decode.
|
||||
* @return
|
||||
* Decoded data.
|
||||
*/
|
||||
function db_decode_blob($data) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare user input for use in a database query, preventing SQL injection attacks.
|
||||
*/
|
||||
function db_escape_string($text) {
|
||||
global $active_db;
|
||||
return mysql_real_escape_string($text, $active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock a table.
|
||||
*/
|
||||
function db_lock_table($table) {
|
||||
db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock all locked tables.
|
||||
*/
|
||||
function db_unlock_tables() {
|
||||
db_query('UNLOCK TABLES');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table exists.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the table exists, and FALSE if the table does not exist.
|
||||
*/
|
||||
function db_table_exists($table) {
|
||||
return (bool) db_fetch_object(db_query("SHOW TABLES LIKE '{". db_escape_table($table) ."}'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column exists in the given table.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
* @param $column
|
||||
* The name of the column.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the column exists, and FALSE if the column does not exist.
|
||||
*/
|
||||
function db_column_exists($table, $column) {
|
||||
return (bool) db_fetch_object(db_query("SHOW COLUMNS FROM {". db_escape_table($table) ."} LIKE '". db_escape_table($column) ."'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "ingroup database".
|
||||
*/
|
||||
377
includes/database.mysqli.inc
Normal file
377
includes/database.mysqli.inc
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Database interface code for MySQL database servers using the mysqli client libraries. mysqli is included in PHP 5 by default and allows developers to use the advanced features of MySQL 4.1.x, 5.0.x and beyond.
|
||||
*/
|
||||
|
||||
// Maintainers of this file should consult:
|
||||
// http://www.php.net/manual/en/ref.mysqli.php
|
||||
|
||||
/**
|
||||
* @ingroup database
|
||||
* @{
|
||||
*/
|
||||
|
||||
// Include functions shared between mysql and mysqli.
|
||||
require_once './includes/database.mysql-common.inc';
|
||||
|
||||
/**
|
||||
* Report database status.
|
||||
*/
|
||||
function db_status_report($phase) {
|
||||
$t = get_t();
|
||||
|
||||
$version = db_version();
|
||||
|
||||
$form['mysql'] = array(
|
||||
'title' => $t('MySQL database'),
|
||||
'value' => ($phase == 'runtime') ? l($version, 'admin/reports/status/sql') : $version,
|
||||
);
|
||||
|
||||
if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) {
|
||||
$form['mysql']['severity'] = REQUIREMENT_ERROR;
|
||||
$form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL));
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the database server currently in use.
|
||||
*
|
||||
* @return Database server version
|
||||
*/
|
||||
function db_version() {
|
||||
global $active_db;
|
||||
list($version) = explode('-', mysqli_get_server_info($active_db));
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise a database connection.
|
||||
*
|
||||
* Note that mysqli does not support persistent connections.
|
||||
*/
|
||||
function db_connect($url) {
|
||||
// Check if MySQLi support is present in PHP
|
||||
if (!function_exists('mysqli_init') && !extension_loaded('mysqli')) {
|
||||
_db_error_page('Unable to use the MySQLi database because the MySQLi extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
|
||||
}
|
||||
|
||||
$url = parse_url($url);
|
||||
|
||||
// Decode urlencoded information in the db connection string
|
||||
$url['user'] = urldecode($url['user']);
|
||||
// Test if database URL has a password.
|
||||
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
|
||||
$url['host'] = urldecode($url['host']);
|
||||
$url['path'] = urldecode($url['path']);
|
||||
if (!isset($url['port'])) {
|
||||
$url['port'] = NULL;
|
||||
}
|
||||
|
||||
$connection = mysqli_init();
|
||||
@mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS);
|
||||
|
||||
if (mysqli_connect_errno() > 0) {
|
||||
_db_error_page(mysqli_connect_error());
|
||||
}
|
||||
|
||||
// Force MySQL to use the UTF-8 character set. Also set the collation, if a
|
||||
// certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
|
||||
// for UTF-8.
|
||||
if (!empty($GLOBALS['db_collation'])) {
|
||||
mysqli_query($connection, 'SET NAMES utf8 COLLATE ' . $GLOBALS['db_collation']);
|
||||
}
|
||||
else {
|
||||
mysqli_query($connection, 'SET NAMES utf8');
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for db_query().
|
||||
*/
|
||||
function _db_query($query, $debug = 0) {
|
||||
global $active_db, $queries, $user;
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$timer = (float)$usec + (float)$sec;
|
||||
// If devel.module query logging is enabled, prepend a comment with the username and calling function
|
||||
// to the SQL string. This is useful when running mysql's SHOW PROCESSLIST to learn what exact
|
||||
// code is issueing the slow query.
|
||||
$bt = debug_backtrace();
|
||||
// t() may not be available yet so we don't wrap 'Anonymous'
|
||||
$name = $user->uid ? $user->name : variable_get('anonymous', 'Anonymous');
|
||||
// str_replace() to prevent SQL injection via username or anonymous name.
|
||||
$name = str_replace(array('*', '/'), '', $name);
|
||||
$query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query;
|
||||
}
|
||||
|
||||
$result = mysqli_query($active_db, $query);
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
$query = $bt[2]['function'] ."\n". $query;
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$stop = (float)$usec + (float)$sec;
|
||||
$diff = $stop - $timer;
|
||||
$queries[] = array($query, $diff);
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
print '<p>query: '. $query .'<br />error:'. mysqli_error($active_db) .'</p>';
|
||||
}
|
||||
|
||||
if (!mysqli_errno($active_db)) {
|
||||
return $result;
|
||||
}
|
||||
else {
|
||||
// Indicate to drupal_error_handler that this is a database error.
|
||||
${DB_ERROR} = TRUE;
|
||||
trigger_error(check_plain(mysqli_error($active_db) ."\nquery: ". $query), E_USER_WARNING);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an object.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An object representing the next row of the result, or FALSE. The attributes
|
||||
* of this object are the table fields selected by the query.
|
||||
*/
|
||||
function db_fetch_object($result) {
|
||||
if ($result) {
|
||||
$object = mysqli_fetch_object($result);
|
||||
return isset($object) ? $object : FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an array.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An associative array representing the next row of the result, or FALSE.
|
||||
* The keys of this object are the names of the table fields selected by the
|
||||
* query, and the values are the field values for this result row.
|
||||
*/
|
||||
function db_fetch_array($result) {
|
||||
if ($result) {
|
||||
$array = mysqli_fetch_array($result, MYSQLI_ASSOC);
|
||||
return isset($array) ? $array : FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an individual result field from the previous query.
|
||||
*
|
||||
* Only use this function if exactly one field is being selected; otherwise,
|
||||
* use db_fetch_object() or db_fetch_array().
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* The resulting field or FALSE.
|
||||
*/
|
||||
function db_result($result) {
|
||||
if ($result && mysqli_num_rows($result) > 0) {
|
||||
// The mysqli_fetch_row function has an optional second parameter $row
|
||||
// but that can't be used for compatibility with Oracle, DB2, etc.
|
||||
$array = mysqli_fetch_row($result);
|
||||
return $array[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the previous query caused an error.
|
||||
*/
|
||||
function db_error() {
|
||||
global $active_db;
|
||||
return mysqli_errno($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of rows changed by the preceding query.
|
||||
*/
|
||||
function db_affected_rows() {
|
||||
global $active_db; /* mysqli connection resource */
|
||||
return mysqli_affected_rows($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a limited-range query in the active database.
|
||||
*
|
||||
* Use this as a substitute for db_query() when a subset of the query is to be
|
||||
* returned.
|
||||
* User-supplied arguments to the query should be passed in as separate parameters
|
||||
* so that they can be properly escaped to avoid SQL injection attacks.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing an SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. The query arguments can be enclosed in one
|
||||
* array instead.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
*
|
||||
* @param $from
|
||||
* The first result row to return.
|
||||
* @param $count
|
||||
* The maximum number of result rows to return.
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_range($query) {
|
||||
$args = func_get_args();
|
||||
$count = array_pop($args);
|
||||
$from = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = db_prefix_tables($query);
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
$query .= ' LIMIT '. (int)$from .', '. (int)$count;
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a SELECT query and stores its results in a temporary table.
|
||||
*
|
||||
* Use this as a substitute for db_query() when the results need to be stored
|
||||
* in a temporary table.
|
||||
*
|
||||
* User-supplied arguments to the query should be passed in as separate parameters
|
||||
* so that they can be properly escaped to avoid SQL injection attacks.
|
||||
*
|
||||
* Note that if you need to know how many results were returned, you should do
|
||||
* a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does
|
||||
* not give consistent result across different database types in this case.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing a normal SELECT SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. The query arguments can be enclosed in one
|
||||
* array instead.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
* @param $table
|
||||
* The name of the temporary table to select into. This name will not be
|
||||
* prefixed as there is no risk of collision.
|
||||
*
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_temporary($query) {
|
||||
$args = func_get_args();
|
||||
$tablename = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', db_prefix_tables($query));
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a properly formatted Binary Large Object value.
|
||||
*
|
||||
* @param $data
|
||||
* Data to encode.
|
||||
* @return
|
||||
* Encoded data.
|
||||
*/
|
||||
function db_encode_blob($data) {
|
||||
global $active_db;
|
||||
return "'". mysqli_real_escape_string($active_db, $data) ."'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text from a Binary Large OBject value.
|
||||
*
|
||||
* @param $data
|
||||
* Data to decode.
|
||||
* @return
|
||||
* Decoded data.
|
||||
*/
|
||||
function db_decode_blob($data) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare user input for use in a database query, preventing SQL injection attacks.
|
||||
*/
|
||||
function db_escape_string($text) {
|
||||
global $active_db;
|
||||
return mysqli_real_escape_string($active_db, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock a table.
|
||||
*/
|
||||
function db_lock_table($table) {
|
||||
db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock all locked tables.
|
||||
*/
|
||||
function db_unlock_tables() {
|
||||
db_query('UNLOCK TABLES');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table exists.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the table exists, and FALSE if the table does not exist.
|
||||
*/
|
||||
function db_table_exists($table) {
|
||||
return (bool) db_fetch_object(db_query("SHOW TABLES LIKE '{". db_escape_table($table) ."}'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column exists in the given table.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
* @param $column
|
||||
* The name of the column.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the column exists, and FALSE if the column does not exist.
|
||||
*/
|
||||
function db_column_exists($table, $column) {
|
||||
return (bool) db_fetch_object(db_query("SHOW COLUMNS FROM {". db_escape_table($table) ."} LIKE '". db_escape_table($column) ."'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "ingroup database".
|
||||
*/
|
||||
|
||||
928
includes/database.pgsql.inc
Normal file
928
includes/database.pgsql.inc
Normal file
|
|
@ -0,0 +1,928 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Database interface code for PostgreSQL database servers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup database
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Report database status.
|
||||
*/
|
||||
function db_status_report() {
|
||||
$t = get_t();
|
||||
|
||||
$version = db_version();
|
||||
|
||||
$form['pgsql'] = array(
|
||||
'title' => $t('PostgreSQL database'),
|
||||
'value' => $version,
|
||||
);
|
||||
|
||||
if (version_compare($version, DRUPAL_MINIMUM_PGSQL) < 0) {
|
||||
$form['pgsql']['severity'] = REQUIREMENT_ERROR;
|
||||
$form['pgsql']['description'] = $t('Your PostgreSQL Server is too old. Drupal requires at least PostgreSQL %version.', array('%version' => DRUPAL_MINIMUM_PGSQL));
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the database server currently in use.
|
||||
*
|
||||
* @return Database server version
|
||||
*/
|
||||
function db_version() {
|
||||
return db_result(db_query("SHOW SERVER_VERSION"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a database connection.
|
||||
*/
|
||||
function db_connect($url) {
|
||||
// Check if PostgreSQL support is present in PHP
|
||||
if (!function_exists('pg_connect')) {
|
||||
_db_error_page('Unable to use the PostgreSQL database because the PostgreSQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
|
||||
}
|
||||
|
||||
$url = parse_url($url);
|
||||
$conn_string = '';
|
||||
|
||||
// Decode urlencoded information in the db connection string
|
||||
if (isset($url['user'])) {
|
||||
$conn_string .= ' user='. urldecode($url['user']);
|
||||
}
|
||||
if (isset($url['pass'])) {
|
||||
$conn_string .= ' password='. urldecode($url['pass']);
|
||||
}
|
||||
if (isset($url['host'])) {
|
||||
$conn_string .= ' host='. urldecode($url['host']);
|
||||
}
|
||||
if (isset($url['path'])) {
|
||||
$conn_string .= ' dbname='. substr(urldecode($url['path']), 1);
|
||||
}
|
||||
if (isset($url['port'])) {
|
||||
$conn_string .= ' port='. urldecode($url['port']);
|
||||
}
|
||||
|
||||
// pg_last_error() does not return a useful error message for database
|
||||
// connection errors. We must turn on error tracking to get at a good error
|
||||
// message, which will be stored in $php_errormsg.
|
||||
$track_errors_previous = ini_get('track_errors');
|
||||
ini_set('track_errors', 1);
|
||||
|
||||
$connection = @pg_connect($conn_string);
|
||||
if (!$connection) {
|
||||
require_once './includes/unicode.inc';
|
||||
_db_error_page(decode_entities($php_errormsg));
|
||||
}
|
||||
|
||||
// Restore error tracking setting
|
||||
ini_set('track_errors', $track_errors_previous);
|
||||
|
||||
pg_query($connection, "set client_encoding=\"UTF8\"");
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a basic query in the active database.
|
||||
*
|
||||
* User-supplied arguments to the query should be passed in as separate
|
||||
* parameters so that they can be properly escaped to avoid SQL injection
|
||||
* attacks.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing an SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. Instead of a variable number of query arguments,
|
||||
* you may also pass a single array containing the query arguments.
|
||||
*
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
*
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not
|
||||
* executed correctly.
|
||||
*/
|
||||
function db_query($query) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
$query = db_prefix_tables($query);
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for db_query().
|
||||
*/
|
||||
function _db_query($query, $debug = 0) {
|
||||
global $active_db, $last_result, $queries;
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$timer = (float)$usec + (float)$sec;
|
||||
}
|
||||
|
||||
$last_result = pg_query($active_db, $query);
|
||||
|
||||
if (variable_get('dev_query', 0)) {
|
||||
$bt = debug_backtrace();
|
||||
$query = $bt[2]['function'] ."\n". $query;
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$stop = (float)$usec + (float)$sec;
|
||||
$diff = $stop - $timer;
|
||||
$queries[] = array($query, $diff);
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
print '<p>query: '. $query .'<br />error:'. pg_last_error($active_db) .'</p>';
|
||||
}
|
||||
|
||||
if ($last_result !== FALSE) {
|
||||
return $last_result;
|
||||
}
|
||||
else {
|
||||
// Indicate to drupal_error_handler that this is a database error.
|
||||
${DB_ERROR} = TRUE;
|
||||
trigger_error(check_plain(pg_last_error($active_db) ."\nquery: ". $query), E_USER_WARNING);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an object.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An object representing the next row of the result, or FALSE. The attributes
|
||||
* of this object are the table fields selected by the query.
|
||||
*/
|
||||
function db_fetch_object($result) {
|
||||
if ($result) {
|
||||
return pg_fetch_object($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one result row from the previous query as an array.
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* An associative array representing the next row of the result, or FALSE.
|
||||
* The keys of this object are the names of the table fields selected by the
|
||||
* query, and the values are the field values for this result row.
|
||||
*/
|
||||
function db_fetch_array($result) {
|
||||
if ($result) {
|
||||
return pg_fetch_assoc($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an individual result field from the previous query.
|
||||
*
|
||||
* Only use this function if exactly one field is being selected; otherwise,
|
||||
* use db_fetch_object() or db_fetch_array().
|
||||
*
|
||||
* @param $result
|
||||
* A database query result resource, as returned from db_query().
|
||||
* @return
|
||||
* The resulting field or FALSE.
|
||||
*/
|
||||
function db_result($result) {
|
||||
if ($result && pg_num_rows($result) > 0) {
|
||||
$array = pg_fetch_row($result);
|
||||
return $array[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the previous query caused an error.
|
||||
*/
|
||||
function db_error() {
|
||||
global $active_db;
|
||||
return pg_last_error($active_db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last insert id. This function is thread safe.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table you inserted into.
|
||||
* @param $field
|
||||
* The name of the autoincrement field.
|
||||
*/
|
||||
function db_last_insert_id($table, $field) {
|
||||
return db_result(db_query("SELECT CURRVAL('{". db_escape_table($table) ."}_". db_escape_table($field) ."_seq')"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of rows changed by the preceding query.
|
||||
*/
|
||||
function db_affected_rows() {
|
||||
global $last_result;
|
||||
return empty($last_result) ? 0 : pg_affected_rows($last_result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a limited-range query in the active database.
|
||||
*
|
||||
* Use this as a substitute for db_query() when a subset of the query
|
||||
* is to be returned.
|
||||
* User-supplied arguments to the query should be passed in as separate
|
||||
* parameters so that they can be properly escaped to avoid SQL injection
|
||||
* attacks.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing an SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. Instead of a variable number of query arguments,
|
||||
* you may also pass a single array containing the query arguments.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
*
|
||||
* @param $from
|
||||
* The first result row to return.
|
||||
* @param $count
|
||||
* The maximum number of result rows to return.
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_range($query) {
|
||||
$args = func_get_args();
|
||||
$count = array_pop($args);
|
||||
$from = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = db_prefix_tables($query);
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
$query .= ' LIMIT '. (int)$count .' OFFSET '. (int)$from;
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a SELECT query and stores its results in a temporary table.
|
||||
*
|
||||
* Use this as a substitute for db_query() when the results need to be stored
|
||||
* in a temporary table.
|
||||
*
|
||||
* User-supplied arguments to the query should be passed in as separate parameters
|
||||
* so that they can be properly escaped to avoid SQL injection attacks.
|
||||
*
|
||||
* Note that if you need to know how many results were returned, you should do
|
||||
* a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does
|
||||
* not give consistent result across different database types in this case.
|
||||
*
|
||||
* @param $query
|
||||
* A string containing a normal SELECT SQL query.
|
||||
* @param ...
|
||||
* A variable number of arguments which are substituted into the query
|
||||
* using printf() syntax. The query arguments can be enclosed in one
|
||||
* array instead.
|
||||
* Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
|
||||
* in '') and %%.
|
||||
*
|
||||
* NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
|
||||
* and TRUE values to decimal 1.
|
||||
* @param $table
|
||||
* The name of the temporary table to select into. This name will not be
|
||||
* prefixed as there is no risk of collision.
|
||||
*
|
||||
* @return
|
||||
* A database query result resource, or FALSE if the query was not executed
|
||||
* correctly.
|
||||
*/
|
||||
function db_query_temporary($query) {
|
||||
$args = func_get_args();
|
||||
$tablename = array_pop($args);
|
||||
array_shift($args);
|
||||
|
||||
$query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' AS SELECT', db_prefix_tables($query));
|
||||
if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
|
||||
$args = $args[0];
|
||||
}
|
||||
_db_query_callback($args, TRUE);
|
||||
$query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
|
||||
return _db_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a properly formatted Binary Large OBject value.
|
||||
* In case of PostgreSQL encodes data for insert into bytea field.
|
||||
*
|
||||
* @param $data
|
||||
* Data to encode.
|
||||
* @return
|
||||
* Encoded data.
|
||||
*/
|
||||
function db_encode_blob($data) {
|
||||
return "'". pg_escape_bytea($data) ."'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text from a Binary Large OBject value.
|
||||
* In case of PostgreSQL decodes data after select from bytea field.
|
||||
*
|
||||
* @param $data
|
||||
* Data to decode.
|
||||
* @return
|
||||
* Decoded data.
|
||||
*/
|
||||
function db_decode_blob($data) {
|
||||
return pg_unescape_bytea($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare user input for use in a database query, preventing SQL injection attacks.
|
||||
* Note: This function requires PostgreSQL 7.2 or later.
|
||||
*/
|
||||
function db_escape_string($text) {
|
||||
return pg_escape_string($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock a table.
|
||||
* This function automatically starts a transaction.
|
||||
*/
|
||||
function db_lock_table($table) {
|
||||
db_query('BEGIN; LOCK TABLE {'. db_escape_table($table) .'} IN EXCLUSIVE MODE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock all locked tables.
|
||||
* This function automatically commits a transaction.
|
||||
*/
|
||||
function db_unlock_tables() {
|
||||
db_query('COMMIT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table exists.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the table exists, and FALSE if the table does not exist.
|
||||
*/
|
||||
function db_table_exists($table) {
|
||||
return (bool) db_result(db_query("SELECT COUNT(*) FROM pg_class WHERE relname = '{". db_escape_table($table) ."}'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a column exists in the given table.
|
||||
*
|
||||
* @param $table
|
||||
* The name of the table.
|
||||
* @param $column
|
||||
* The name of the column.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the column exists, and FALSE if the column does not exist.
|
||||
*/
|
||||
function db_column_exists($table, $column) {
|
||||
return (bool) db_result(db_query("SELECT COUNT(pg_attribute.attname) FROM pg_class, pg_attribute WHERE pg_attribute.attrelid = pg_class.oid AND pg_class.relname = '{". db_escape_table($table) ."}' AND attname = '". db_escape_table($column) ."'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the database is set up correctly.
|
||||
*/
|
||||
function db_check_setup() {
|
||||
$t = get_t();
|
||||
|
||||
$encoding = db_result(db_query('SHOW server_encoding'));
|
||||
if (!in_array(strtolower($encoding), array('unicode', 'utf8'))) {
|
||||
drupal_set_message($t('Your PostgreSQL database is set up with the wrong character encoding (%encoding). It is possible it will not work as expected. It is advised to recreate it with UTF-8/Unicode encoding. More information can be found in the <a href="@url">PostgreSQL documentation</a>.', array('%encoding' => $encoding, '@url' => 'http://www.postgresql.org/docs/7.4/interactive/multibyte.html')), 'status');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "ingroup database".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup schemaapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* This maps a generic data type in combination with its data size
|
||||
* to the engine-specific data type.
|
||||
*/
|
||||
function db_type_map() {
|
||||
// Put :normal last so it gets preserved by array_flip. This makes
|
||||
// it much easier for modules (such as schema.module) to map
|
||||
// database types back into schema types.
|
||||
$map = array(
|
||||
'varchar:normal' => 'varchar',
|
||||
'char:normal' => 'character',
|
||||
|
||||
'text:tiny' => 'text',
|
||||
'text:small' => 'text',
|
||||
'text:medium' => 'text',
|
||||
'text:big' => 'text',
|
||||
'text:normal' => 'text',
|
||||
|
||||
'int:tiny' => 'smallint',
|
||||
'int:small' => 'smallint',
|
||||
'int:medium' => 'int',
|
||||
'int:big' => 'bigint',
|
||||
'int:normal' => 'int',
|
||||
|
||||
'float:tiny' => 'real',
|
||||
'float:small' => 'real',
|
||||
'float:medium' => 'real',
|
||||
'float:big' => 'double precision',
|
||||
'float:normal' => 'real',
|
||||
|
||||
'numeric:normal' => 'numeric',
|
||||
|
||||
'blob:big' => 'bytea',
|
||||
'blob:normal' => 'bytea',
|
||||
|
||||
'datetime:normal' => 'timestamp without time zone',
|
||||
|
||||
'serial:tiny' => 'serial',
|
||||
'serial:small' => 'serial',
|
||||
'serial:medium' => 'serial',
|
||||
'serial:big' => 'bigserial',
|
||||
'serial:normal' => 'serial',
|
||||
);
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SQL to create a new table from a Drupal schema definition.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the table to create.
|
||||
* @param $table
|
||||
* A Schema API table definition array.
|
||||
* @return
|
||||
* An array of SQL statements to create the table.
|
||||
*/
|
||||
function db_create_table_sql($name, $table) {
|
||||
$sql_fields = array();
|
||||
foreach ($table['fields'] as $field_name => $field) {
|
||||
$sql_fields[] = _db_create_field_sql($field_name, _db_process_field($field));
|
||||
}
|
||||
|
||||
$sql_keys = array();
|
||||
if (isset($table['primary key']) && is_array($table['primary key'])) {
|
||||
$sql_keys[] = 'PRIMARY KEY ('. implode(', ', $table['primary key']) .')';
|
||||
}
|
||||
if (isset($table['unique keys']) && is_array($table['unique keys'])) {
|
||||
foreach ($table['unique keys'] as $key_name => $key) {
|
||||
$sql_keys[] = 'CONSTRAINT {'. $name .'}_'. $key_name .'_key UNIQUE ('. implode(', ', $key) .')';
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "CREATE TABLE {". $name ."} (\n\t";
|
||||
$sql .= implode(",\n\t", $sql_fields);
|
||||
if (count($sql_keys) > 0) {
|
||||
$sql .= ",\n\t";
|
||||
}
|
||||
$sql .= implode(",\n\t", $sql_keys);
|
||||
$sql .= "\n)";
|
||||
$statements[] = $sql;
|
||||
|
||||
if (isset($table['indexes']) && is_array($table['indexes'])) {
|
||||
foreach ($table['indexes'] as $key_name => $key) {
|
||||
$statements[] = _db_create_index_sql($name, $key_name, $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
function _db_create_index_sql($table, $name, $fields) {
|
||||
$query = 'CREATE INDEX {'. $table .'}_'. $name .'_idx ON {'. $table .'} (';
|
||||
$query .= _db_create_key_sql($fields) .')';
|
||||
return $query;
|
||||
}
|
||||
|
||||
function _db_create_key_sql($fields) {
|
||||
$ret = array();
|
||||
foreach ($fields as $field) {
|
||||
if (is_array($field)) {
|
||||
$ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')';
|
||||
}
|
||||
else {
|
||||
$ret[] = $field;
|
||||
}
|
||||
}
|
||||
return implode(', ', $ret);
|
||||
}
|
||||
|
||||
function _db_create_keys(&$ret, $table, $new_keys) {
|
||||
if (isset($new_keys['primary key'])) {
|
||||
db_add_primary_key($ret, $table, $new_keys['primary key']);
|
||||
}
|
||||
if (isset($new_keys['unique keys'])) {
|
||||
foreach ($new_keys['unique keys'] as $name => $fields) {
|
||||
db_add_unique_key($ret, $table, $name, $fields);
|
||||
}
|
||||
}
|
||||
if (isset($new_keys['indexes'])) {
|
||||
foreach ($new_keys['indexes'] as $name => $fields) {
|
||||
db_add_index($ret, $table, $name, $fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database-engine specific properties for a field.
|
||||
*
|
||||
* @param $field
|
||||
* A field description array, as specified in the schema documentation.
|
||||
*/
|
||||
function _db_process_field($field) {
|
||||
if (!isset($field['size'])) {
|
||||
$field['size'] = 'normal';
|
||||
}
|
||||
// Set the correct database-engine specific datatype.
|
||||
if (!isset($field['pgsql_type'])) {
|
||||
$map = db_type_map();
|
||||
$field['pgsql_type'] = $map[$field['type'] .':'. $field['size']];
|
||||
}
|
||||
if ($field['type'] == 'serial') {
|
||||
unset($field['not null']);
|
||||
}
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SQL string for a field to be used in table creation or alteration.
|
||||
*
|
||||
* Before passing a field out of a schema definition into this function it has
|
||||
* to be processed by _db_process_field().
|
||||
*
|
||||
* @param $name
|
||||
* Name of the field.
|
||||
* @param $spec
|
||||
* The field specification, as per the schema data structure format.
|
||||
*/
|
||||
function _db_create_field_sql($name, $spec) {
|
||||
$sql = $name .' '. $spec['pgsql_type'];
|
||||
|
||||
if ($spec['type'] == 'serial') {
|
||||
unset($spec['not null']);
|
||||
}
|
||||
|
||||
if (in_array($spec['type'], array('varchar', 'char', 'text')) && isset($spec['length'])) {
|
||||
$sql .= '('. $spec['length'] .')';
|
||||
}
|
||||
elseif (isset($spec['precision']) && isset($spec['scale'])) {
|
||||
$sql .= '('. $spec['precision'] .', '. $spec['scale'] .')';
|
||||
}
|
||||
|
||||
if (!empty($spec['unsigned'])) {
|
||||
$sql .= " CHECK ($name >= 0)";
|
||||
}
|
||||
|
||||
if (isset($spec['not null']) && $spec['not null']) {
|
||||
$sql .= ' NOT NULL';
|
||||
}
|
||||
if (isset($spec['default'])) {
|
||||
$default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default'];
|
||||
$sql .= " default $default";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be renamed.
|
||||
* @param $new_name
|
||||
* The new name for the table.
|
||||
*/
|
||||
function db_rename_table(&$ret, $table, $new_name) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be dropped.
|
||||
*/
|
||||
function db_drop_table(&$ret, $table) {
|
||||
$ret[] = update_sql('DROP TABLE {'. $table .'}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new field to a table.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* Name of the table to be altered.
|
||||
* @param $field
|
||||
* Name of the field to be added.
|
||||
* @param $spec
|
||||
* The field specification array, as taken from a schema definition.
|
||||
* The specification may also contain the key 'initial', the newly
|
||||
* created field will be set to the value of the key in all rows.
|
||||
* This is most useful for creating NOT NULL columns with no default
|
||||
* value in existing tables.
|
||||
* @param $new_keys
|
||||
* (optional) Keys and indexes specification to be created on the
|
||||
* table along with adding the field. The format is the same as a
|
||||
* table specification but without the 'fields' element. If you are
|
||||
* adding a type 'serial' field, you MUST specify at least one key
|
||||
* or index including it in this array. See db_change_field() for more
|
||||
* explanation why.
|
||||
*/
|
||||
function db_add_field(&$ret, $table, $field, $spec, $new_keys = array()) {
|
||||
$fixnull = FALSE;
|
||||
if (!empty($spec['not null']) && !isset($spec['default'])) {
|
||||
$fixnull = TRUE;
|
||||
$spec['not null'] = FALSE;
|
||||
}
|
||||
$query = 'ALTER TABLE {'. $table .'} ADD COLUMN ';
|
||||
$query .= _db_create_field_sql($field, _db_process_field($spec));
|
||||
$ret[] = update_sql($query);
|
||||
if (isset($spec['initial'])) {
|
||||
// All this because update_sql does not support %-placeholders.
|
||||
$sql = 'UPDATE {'. $table .'} SET '. $field .' = '. db_type_placeholder($spec['type']);
|
||||
$result = db_query($sql, $spec['initial']);
|
||||
$ret[] = array('success' => $result !== FALSE, 'query' => check_plain($sql .' ('. $spec['initial'] .')'));
|
||||
}
|
||||
if ($fixnull) {
|
||||
$ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field SET NOT NULL");
|
||||
}
|
||||
if (isset($new_keys)) {
|
||||
_db_create_keys($ret, $table, $new_keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a field.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be dropped.
|
||||
*/
|
||||
function db_drop_field(&$ret, $table, $field) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP COLUMN '. $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value for a field.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be altered.
|
||||
* @param $default
|
||||
* Default value to be set. NULL for 'default NULL'.
|
||||
*/
|
||||
function db_field_set_default(&$ret, $table, $field, $default) {
|
||||
if ($default == NULL) {
|
||||
$default = 'NULL';
|
||||
}
|
||||
else {
|
||||
$default = is_string($default) ? "'$default'" : $default;
|
||||
}
|
||||
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' SET DEFAULT '. $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a field to have no default value.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $field
|
||||
* The field to be altered.
|
||||
*/
|
||||
function db_field_set_no_default(&$ret, $table, $field) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' DROP DEFAULT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a primary key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $fields
|
||||
* Fields for the primary key.
|
||||
*/
|
||||
function db_add_primary_key(&$ret, $table, $fields) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ADD PRIMARY KEY ('.
|
||||
implode(',', $fields) .')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the primary key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
*/
|
||||
function db_drop_primary_key(&$ret, $table) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT {'. $table .'}_pkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a unique key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the key.
|
||||
* @param $fields
|
||||
* An array of field names.
|
||||
*/
|
||||
function db_add_unique_key(&$ret, $table, $name, $fields) {
|
||||
$name = '{'. $table .'}_'. $name .'_key';
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} ADD CONSTRAINT '.
|
||||
$name .' UNIQUE ('. implode(',', $fields) .')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a unique key.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the key.
|
||||
*/
|
||||
function db_drop_unique_key(&$ret, $table, $name) {
|
||||
$name = '{'. $table .'}_'. $name .'_key';
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT '. $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an index.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the index.
|
||||
* @param $fields
|
||||
* An array of field names.
|
||||
*/
|
||||
function db_add_index(&$ret, $table, $name, $fields) {
|
||||
$ret[] = update_sql(_db_create_index_sql($table, $name, $fields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop an index.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* The table to be altered.
|
||||
* @param $name
|
||||
* The name of the index.
|
||||
*/
|
||||
function db_drop_index(&$ret, $table, $name) {
|
||||
$name = '{'. $table .'}_'. $name .'_idx';
|
||||
$ret[] = update_sql('DROP INDEX '. $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a field definition.
|
||||
*
|
||||
* IMPORTANT NOTE: To maintain database portability, you have to explicitly
|
||||
* recreate all indices and primary keys that are using the changed field.
|
||||
*
|
||||
* That means that you have to drop all affected keys and indexes with
|
||||
* db_drop_{primary_key,unique_key,index}() before calling db_change_field().
|
||||
* To recreate the keys and indices, pass the key definitions as the
|
||||
* optional $new_keys argument directly to db_change_field().
|
||||
*
|
||||
* For example, suppose you have:
|
||||
* @code
|
||||
* $schema['foo'] = array(
|
||||
* 'fields' => array(
|
||||
* 'bar' => array('type' => 'int', 'not null' => TRUE)
|
||||
* ),
|
||||
* 'primary key' => array('bar')
|
||||
* );
|
||||
* @endcode
|
||||
* and you want to change foo.bar to be type serial, leaving it as the
|
||||
* primary key. The correct sequence is:
|
||||
* @code
|
||||
* db_drop_primary_key($ret, 'foo');
|
||||
* db_change_field($ret, 'foo', 'bar', 'bar',
|
||||
* array('type' => 'serial', 'not null' => TRUE),
|
||||
* array('primary key' => array('bar')));
|
||||
* @endcode
|
||||
*
|
||||
* The reasons for this are due to the different database engines:
|
||||
*
|
||||
* On PostgreSQL, changing a field definition involves adding a new field
|
||||
* and dropping an old one which* causes any indices, primary keys and
|
||||
* sequences (from serial-type fields) that use the changed field to be dropped.
|
||||
*
|
||||
* On MySQL, all type 'serial' fields must be part of at least one key
|
||||
* or index as soon as they are created. You cannot use
|
||||
* db_add_{primary_key,unique_key,index}() for this purpose because
|
||||
* the ALTER TABLE command will fail to add the column without a key
|
||||
* or index specification. The solution is to use the optional
|
||||
* $new_keys argument to create the key or index at the same time as
|
||||
* field.
|
||||
*
|
||||
* You could use db_add_{primary_key,unique_key,index}() in all cases
|
||||
* unless you are converting a field to be type serial. You can use
|
||||
* the $new_keys argument in all cases.
|
||||
*
|
||||
* @param $ret
|
||||
* Array to which query results will be added.
|
||||
* @param $table
|
||||
* Name of the table.
|
||||
* @param $field
|
||||
* Name of the field to change.
|
||||
* @param $field_new
|
||||
* New name for the field (set to the same as $field if you don't want to change the name).
|
||||
* @param $spec
|
||||
* The field specification for the new field.
|
||||
* @param $new_keys
|
||||
* (optional) Keys and indexes specification to be created on the
|
||||
* table along with changing the field. The format is the same as a
|
||||
* table specification but without the 'fields' element.
|
||||
*/
|
||||
function db_change_field(&$ret, $table, $field, $field_new, $spec, $new_keys = array()) {
|
||||
$ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME "'. $field .'" TO "'. $field .'_old"');
|
||||
$not_null = isset($spec['not null']) ? $spec['not null'] : FALSE;
|
||||
unset($spec['not null']);
|
||||
|
||||
if (!array_key_exists('size', $spec)) {
|
||||
$spec['size'] = 'normal';
|
||||
}
|
||||
db_add_field($ret, $table, "$field_new", $spec);
|
||||
|
||||
// We need to type cast the new column to best transfer the data
|
||||
// db_type_map will return possiblities that are not 'cast-able'
|
||||
// such as serial - they must be made 'int' instead.
|
||||
$map = db_type_map();
|
||||
$typecast = $map[$spec['type'] .':'. $spec['size']];
|
||||
if (in_array($typecast, array('serial', 'bigserial', 'numeric'))) {
|
||||
$typecast = 'int';
|
||||
}
|
||||
$ret[] = update_sql('UPDATE {'. $table .'} SET '. $field_new .' = CAST('. $field .'_old AS '. $typecast .')');
|
||||
|
||||
if ($not_null) {
|
||||
$ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field_new SET NOT NULL");
|
||||
}
|
||||
|
||||
db_drop_field($ret, $table, $field .'_old');
|
||||
|
||||
if (isset($new_keys)) {
|
||||
_db_create_keys($ret, $table, $new_keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "ingroup schemaapi".
|
||||
*/
|
||||
|
||||
1550
includes/file.inc
Normal file
1550
includes/file.inc
Normal file
File diff suppressed because it is too large
Load diff
2684
includes/form.inc
Normal file
2684
includes/form.inc
Normal file
File diff suppressed because it is too large
Load diff
220
includes/image.gd.inc
Normal file
220
includes/image.gd.inc
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* GD2 toolkit for image manipulation within Drupal.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup image
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve information about the toolkit.
|
||||
*/
|
||||
function image_gd_info() {
|
||||
return array('name' => 'gd', 'title' => t('GD2 image manipulation toolkit'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve settings for the GD2 toolkit.
|
||||
*/
|
||||
function image_gd_settings() {
|
||||
if (image_gd_check_settings()) {
|
||||
$form = array();
|
||||
$form['status'] = array(
|
||||
'#value' => t('The GD toolkit is installed and working properly.')
|
||||
);
|
||||
|
||||
$form['image_jpeg_quality'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('JPEG quality'),
|
||||
'#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
|
||||
'#size' => 10,
|
||||
'#maxlength' => 3,
|
||||
'#default_value' => variable_get('image_jpeg_quality', 75),
|
||||
'#field_suffix' => t('%'),
|
||||
);
|
||||
$form['#element_validate'] = array('image_gd_settings_validate');
|
||||
|
||||
return $form;
|
||||
}
|
||||
else {
|
||||
form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the submitted GD settings.
|
||||
*/
|
||||
function image_gd_settings_validate($form, &$form_state) {
|
||||
// Validate image quality range.
|
||||
$value = $form_state['values']['image_jpeg_quality'];
|
||||
if (!is_numeric($value) || $value < 0 || $value > 100) {
|
||||
form_set_error('image_jpeg_quality', t('JPEG quality must be a number between 0 and 100.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify GD2 settings (that the right version is actually installed).
|
||||
*
|
||||
* @return
|
||||
* A boolean indicating if the GD toolkit is avaiable on this machine.
|
||||
*/
|
||||
function image_gd_check_settings() {
|
||||
if ($check = get_extension_funcs('gd')) {
|
||||
if (in_array('imagegd2', $check)) {
|
||||
// GD2 support is available.
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale an image to the specified size using GD.
|
||||
*/
|
||||
function image_gd_resize($source, $destination, $width, $height) {
|
||||
if (!file_exists($source)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$info = image_get_info($source);
|
||||
if (!$info) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$im = image_gd_open($source, $info['extension']);
|
||||
if (!$im) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$res = imagecreatetruecolor($width, $height);
|
||||
if ($info['extension'] == 'png') {
|
||||
$transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
|
||||
imagealphablending($res, FALSE);
|
||||
imagefilledrectangle($res, 0, 0, $width, $height, $transparency);
|
||||
imagealphablending($res, TRUE);
|
||||
imagesavealpha($res, TRUE);
|
||||
}
|
||||
elseif ($info['extension'] == 'gif') {
|
||||
// If we have a specific transparent color.
|
||||
$transparency_index = imagecolortransparent($im);
|
||||
if ($transparency_index >= 0) {
|
||||
// Get the original image's transparent color's RGB values.
|
||||
$transparent_color = imagecolorsforindex($im, $transparency_index);
|
||||
// Allocate the same color in the new image resource.
|
||||
$transparency_index = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
|
||||
// Completely fill the background of the new image with allocated color.
|
||||
imagefill($res, 0, 0, $transparency_index);
|
||||
// Set the background color for new image to transparent.
|
||||
imagecolortransparent($res, $transparency_index);
|
||||
// Find number of colors in the images palette.
|
||||
$number_colors = imagecolorstotal($im);
|
||||
// Convert from true color to palette to fix transparency issues.
|
||||
imagetruecolortopalette($res, TRUE, $number_colors);
|
||||
}
|
||||
}
|
||||
imagecopyresampled($res, $im, 0, 0, 0, 0, $width, $height, $info['width'], $info['height']);
|
||||
$result = image_gd_close($res, $destination, $info['extension']);
|
||||
|
||||
imagedestroy($res);
|
||||
imagedestroy($im);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate an image the given number of degrees.
|
||||
*/
|
||||
function image_gd_rotate($source, $destination, $degrees, $background = 0x000000) {
|
||||
if (!function_exists('imageRotate')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$info = image_get_info($source);
|
||||
if (!$info) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$im = image_gd_open($source, $info['extension']);
|
||||
if (!$im) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$res = imageRotate($im, $degrees, $background);
|
||||
$result = image_gd_close($res, $destination, $info['extension']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop an image using the GD toolkit.
|
||||
*/
|
||||
function image_gd_crop($source, $destination, $x, $y, $width, $height) {
|
||||
$info = image_get_info($source);
|
||||
if (!$info) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$im = image_gd_open($source, $info['extension']);
|
||||
$res = imageCreateTrueColor($width, $height);
|
||||
imageCopy($res, $im, 0, 0, $x, $y, $width, $height);
|
||||
$result = image_gd_close($res, $destination, $info['extension']);
|
||||
|
||||
imageDestroy($res);
|
||||
imageDestroy($im);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* GD helper function to create an image resource from a file.
|
||||
*
|
||||
* @param $file
|
||||
* A string file path where the iamge should be saved.
|
||||
* @param $extension
|
||||
* A string containing one of the following extensions: gif, jpg, jpeg, png.
|
||||
* @return
|
||||
* An image resource, or FALSE on error.
|
||||
*/
|
||||
function image_gd_open($file, $extension) {
|
||||
$extension = str_replace('jpg', 'jpeg', $extension);
|
||||
$open_func = 'imageCreateFrom'. $extension;
|
||||
if (!function_exists($open_func)) {
|
||||
return FALSE;
|
||||
}
|
||||
return $open_func($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* GD helper to write an image resource to a destination file.
|
||||
*
|
||||
* @param $res
|
||||
* An image resource created with image_gd_open().
|
||||
* @param $destination
|
||||
* A string file path where the iamge should be saved.
|
||||
* @param $extension
|
||||
* A string containing one of the following extensions: gif, jpg, jpeg, png.
|
||||
* @return
|
||||
* Boolean indicating success.
|
||||
*/
|
||||
function image_gd_close($res, $destination, $extension) {
|
||||
$extension = str_replace('jpg', 'jpeg', $extension);
|
||||
$close_func = 'image'. $extension;
|
||||
if (!function_exists($close_func)) {
|
||||
return FALSE;
|
||||
}
|
||||
if ($extension == 'jpeg') {
|
||||
return $close_func($res, $destination, variable_get('image_jpeg_quality', 75));
|
||||
}
|
||||
else {
|
||||
return $close_func($res, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "ingroup image".
|
||||
*/
|
||||
271
includes/image.inc
Normal file
271
includes/image.inc
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for manipulating images.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup image Image toolkits
|
||||
* @{
|
||||
* Drupal's image toolkits provide an abstraction layer for common image file
|
||||
* manipulations like scaling, cropping, and rotating. The abstraction frees
|
||||
* module authors from the need to support multiple image libraries, and it
|
||||
* allows site administrators to choose the library that's best for them.
|
||||
*
|
||||
* PHP includes the GD library by default so a GD toolkit is installed with
|
||||
* Drupal. Other toolkits like ImageMagic are available from contrib modules.
|
||||
* GD works well for small images, but using it with larger files may cause PHP
|
||||
* to run out of memory. In contrast the ImageMagick library does not suffer
|
||||
* from this problem, but it requires the ISP to have installed additional
|
||||
* software.
|
||||
*
|
||||
* Image toolkits are installed by copying the image.ToolkitName.inc file into
|
||||
* Drupal's includes directory. The toolkit must then be enabled using the
|
||||
* admin/settings/image-toolkit form.
|
||||
*
|
||||
* Only one toolkit maybe selected at a time. If a module author wishes to call
|
||||
* a specific toolkit they can check that it is installed by calling
|
||||
* image_get_available_toolkits(), and then calling its functions directly.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return a list of available toolkits.
|
||||
*
|
||||
* @return
|
||||
* An array of toolkit name => descriptive title.
|
||||
*/
|
||||
function image_get_available_toolkits() {
|
||||
$toolkits = file_scan_directory('includes', 'image\..*\.inc$');
|
||||
|
||||
$output = array();
|
||||
foreach ($toolkits as $file => $toolkit) {
|
||||
include_once "./$file";
|
||||
$function = str_replace('.', '_', $toolkit->name) .'_info';
|
||||
if (function_exists($function)) {
|
||||
$info = $function();
|
||||
$output[$info['name']] = $info['title'];
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the currently used toolkit.
|
||||
*
|
||||
* @return
|
||||
* String containing the name of the selected toolkit, or FALSE on error.
|
||||
*/
|
||||
function image_get_toolkit() {
|
||||
static $toolkit;
|
||||
|
||||
if (!$toolkit) {
|
||||
$toolkit = variable_get('image_toolkit', 'gd');
|
||||
$toolkit_file = './includes/image.'. $toolkit .'.inc';
|
||||
if (isset($toolkit) && file_exists($toolkit_file)) {
|
||||
include_once $toolkit_file;
|
||||
}
|
||||
elseif (!image_gd_check_settings()) {
|
||||
$toolkit = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return $toolkit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given method using the currently selected toolkit.
|
||||
*
|
||||
* @param $method
|
||||
* A string containing the method to invoke.
|
||||
* @param $params
|
||||
* An optional array of parameters to pass to the toolkit method.
|
||||
* @return
|
||||
* Mixed values (typically Boolean indicating successful operation).
|
||||
*/
|
||||
function image_toolkit_invoke($method, $params = array()) {
|
||||
if ($toolkit = image_get_toolkit()) {
|
||||
$function = 'image_'. $toolkit .'_'. $method;
|
||||
if (function_exists($function)) {
|
||||
return call_user_func_array($function, $params);
|
||||
}
|
||||
else {
|
||||
watchdog('php', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $toolkit, '%function' => $function), WATCHDOG_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get details about an image.
|
||||
*
|
||||
* Drupal only supports GIF, JPG and PNG file formats.
|
||||
*
|
||||
* @return
|
||||
* FALSE, if the file could not be found or is not an image. Otherwise, a
|
||||
* keyed array containing information about the image:
|
||||
* 'width' - Width in pixels.
|
||||
* 'height' - Height in pixels.
|
||||
* 'extension' - Commonly used file extension for the image.
|
||||
* 'mime_type' - MIME type ('image/jpeg', 'image/gif', 'image/png').
|
||||
* 'file_size' - File size in bytes.
|
||||
*/
|
||||
function image_get_info($file) {
|
||||
// Proceed no further if this file doesn't exist. Some web servers (IIS) may
|
||||
// not pass is_file() for newly uploaded files, so we need two checks here.
|
||||
if (!is_file($file) && !is_uploaded_file($file)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$details = FALSE;
|
||||
$data = @getimagesize($file);
|
||||
$file_size = @filesize($file);
|
||||
|
||||
if (isset($data) && is_array($data)) {
|
||||
$extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
|
||||
$extension = array_key_exists($data[2], $extensions) ? $extensions[$data[2]] : '';
|
||||
$details = array('width' => $data[0],
|
||||
'height' => $data[1],
|
||||
'extension' => $extension,
|
||||
'file_size' => $file_size,
|
||||
'mime_type' => $data['mime']);
|
||||
}
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales an image to the exact width and height given. Achieves the
|
||||
* target aspect ratio by cropping the original image equally on both
|
||||
* sides, or equally on the top and bottom. This function is, for
|
||||
* example, useful to create uniform sized avatars from larger images.
|
||||
*
|
||||
* The resulting image always has the exact target dimensions.
|
||||
*
|
||||
* @param $source
|
||||
* The file path of the source image.
|
||||
* @param $destination
|
||||
* The file path of the destination image.
|
||||
* @param $width
|
||||
* The target width, in pixels.
|
||||
* @param $height
|
||||
* The target height, in pixels.
|
||||
* @return
|
||||
* TRUE or FALSE, based on success.
|
||||
*/
|
||||
function image_scale_and_crop($source, $destination, $width, $height) {
|
||||
$info = image_get_info($source);
|
||||
|
||||
$scale = max($width / $info['width'], $height / $info['height']);
|
||||
$x = round(($info['width'] * $scale - $width) / 2);
|
||||
$y = round(($info['height'] * $scale - $height) / 2);
|
||||
|
||||
if (image_toolkit_invoke('resize', array($source, $destination, $info['width'] * $scale, $info['height'] * $scale))) {
|
||||
return image_toolkit_invoke('crop', array($destination, $destination, $x, $y, $width, $height));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales an image to the given width and height while maintaining aspect
|
||||
* ratio.
|
||||
*
|
||||
* The resulting image can be smaller for one or both target dimensions.
|
||||
*
|
||||
* @param $source
|
||||
* The file path of the source image.
|
||||
* @param $destination
|
||||
* The file path of the destination image.
|
||||
* @param $width
|
||||
* The target width, in pixels.
|
||||
* @param $height
|
||||
* The target height, in pixels.
|
||||
* @return
|
||||
* TRUE or FALSE, based on success.
|
||||
*/
|
||||
function image_scale($source, $destination, $width, $height) {
|
||||
$info = image_get_info($source);
|
||||
|
||||
// Don't scale up.
|
||||
if ($width >= $info['width'] && $height >= $info['height']) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$aspect = $info['height'] / $info['width'];
|
||||
if ($aspect < $height / $width) {
|
||||
$width = (int)min($width, $info['width']);
|
||||
$height = (int)round($width * $aspect);
|
||||
}
|
||||
else {
|
||||
$height = (int)min($height, $info['height']);
|
||||
$width = (int)round($height / $aspect);
|
||||
}
|
||||
|
||||
return image_toolkit_invoke('resize', array($source, $destination, $width, $height));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize an image to the given dimensions (ignoring aspect ratio).
|
||||
*
|
||||
* @param $source
|
||||
* The file path of the source image.
|
||||
* @param $destination
|
||||
* The file path of the destination image.
|
||||
* @param $width
|
||||
* The target width, in pixels.
|
||||
* @param $height
|
||||
* The target height, in pixels.
|
||||
* @return
|
||||
* TRUE or FALSE, based on success.
|
||||
*/
|
||||
function image_resize($source, $destination, $width, $height) {
|
||||
return image_toolkit_invoke('resize', array($source, $destination, $width, $height));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate an image by the given number of degrees.
|
||||
*
|
||||
* @param $source
|
||||
* The file path of the source image.
|
||||
* @param $destination
|
||||
* The file path of the destination image.
|
||||
* @param $degrees
|
||||
* The number of (clockwise) degrees to rotate the image.
|
||||
* @param $background
|
||||
* An hexidecimal integer specifying the background color to use for the
|
||||
* uncovered area of the image after the rotation. E.g. 0x000000 for black,
|
||||
* 0xff00ff for magenta, and 0xffffff for white.
|
||||
* @return
|
||||
* TRUE or FALSE, based on success.
|
||||
*/
|
||||
function image_rotate($source, $destination, $degrees, $background = 0x000000) {
|
||||
return image_toolkit_invoke('rotate', array($source, $destination, $degrees, $background));
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop an image to the rectangle specified by the given rectangle.
|
||||
*
|
||||
* @param $source
|
||||
* The file path of the source image.
|
||||
* @param $destination
|
||||
* The file path of the destination image.
|
||||
* @param $x
|
||||
* The top left co-ordinate, in pixels, of the crop area (x axis value).
|
||||
* @param $y
|
||||
* The top left co-ordinate, in pixels, of the crop area (y axis value).
|
||||
* @param $width
|
||||
* The target width, in pixels.
|
||||
* @param $height
|
||||
* The target height, in pixels.
|
||||
* @return
|
||||
* TRUE or FALSE, based on success.
|
||||
*/
|
||||
function image_crop($source, $destination, $x, $y, $width, $height) {
|
||||
return image_toolkit_invoke('crop', array($source, $destination, $x, $y, $width, $height));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup image".
|
||||
*/
|
||||
736
includes/install.inc
Normal file
736
includes/install.inc
Normal file
|
|
@ -0,0 +1,736 @@
|
|||
<?php
|
||||
|
||||
define('SCHEMA_UNINSTALLED', -1);
|
||||
define('SCHEMA_INSTALLED', 0);
|
||||
|
||||
define('REQUIREMENT_INFO', -1);
|
||||
define('REQUIREMENT_OK', 0);
|
||||
define('REQUIREMENT_WARNING', 1);
|
||||
define('REQUIREMENT_ERROR', 2);
|
||||
|
||||
define('FILE_EXIST', 1);
|
||||
define('FILE_READABLE', 2);
|
||||
define('FILE_WRITABLE', 4);
|
||||
define('FILE_EXECUTABLE', 8);
|
||||
define('FILE_NOT_EXIST', 16);
|
||||
define('FILE_NOT_READABLE', 32);
|
||||
define('FILE_NOT_WRITABLE', 64);
|
||||
define('FILE_NOT_EXECUTABLE', 128);
|
||||
|
||||
/**
|
||||
* Initialize the update system by loading all installed module's .install files.
|
||||
*/
|
||||
function drupal_load_updates() {
|
||||
foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
|
||||
if ($schema_version > -1) {
|
||||
module_load_install($module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of available schema versions for a module.
|
||||
*
|
||||
* @param $module
|
||||
* A module name.
|
||||
* @return
|
||||
* If the module has updates, an array of available updates sorted by version.
|
||||
* Otherwise, FALSE.
|
||||
*/
|
||||
function drupal_get_schema_versions($module) {
|
||||
$updates = array();
|
||||
$functions = get_defined_functions();
|
||||
foreach ($functions['user'] as $function) {
|
||||
if (strpos($function, $module .'_update_') === 0) {
|
||||
$version = substr($function, strlen($module .'_update_'));
|
||||
if (is_numeric($version)) {
|
||||
$updates[] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($updates) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
sort($updates, SORT_NUMERIC);
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently installed schema version for a module.
|
||||
*
|
||||
* @param $module
|
||||
* A module name.
|
||||
* @param $reset
|
||||
* Set to TRUE after modifying the system table.
|
||||
* @param $array
|
||||
* Set to TRUE if you want to get information about all modules in the
|
||||
* system.
|
||||
* @return
|
||||
* The currently installed schema version.
|
||||
*/
|
||||
function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
|
||||
static $versions = array();
|
||||
|
||||
if ($reset) {
|
||||
$versions = array();
|
||||
}
|
||||
|
||||
if (!$versions) {
|
||||
$versions = array();
|
||||
$result = db_query("SELECT name, schema_version FROM {system} WHERE type = '%s'", 'module');
|
||||
while ($row = db_fetch_object($result)) {
|
||||
$versions[$row->name] = $row->schema_version;
|
||||
}
|
||||
}
|
||||
|
||||
return $array ? $versions : $versions[$module];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the installed version information for a module.
|
||||
*
|
||||
* @param $module
|
||||
* A module name.
|
||||
* @param $version
|
||||
* The new schema version.
|
||||
*/
|
||||
function drupal_set_installed_schema_version($module, $version) {
|
||||
db_query("UPDATE {system} SET schema_version = %d WHERE name = '%s'", $version, $module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the profile definition, extracting the profile's defined name.
|
||||
*
|
||||
* @return
|
||||
* The name defined in the profile's _profile_details() hook.
|
||||
*/
|
||||
function drupal_install_profile_name() {
|
||||
global $profile;
|
||||
static $name = NULL;
|
||||
|
||||
if (!isset($name)) {
|
||||
// Load profile details.
|
||||
$function = $profile .'_profile_details';
|
||||
if (function_exists($function)) {
|
||||
$details = $function();
|
||||
}
|
||||
$name = isset($details['name']) ? $details['name'] : 'Drupal';
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto detect the base_url with PHP predefined variables.
|
||||
*
|
||||
* @param $file
|
||||
* The name of the file calling this function so we can strip it out of
|
||||
* the URI when generating the base_url.
|
||||
*
|
||||
* @return
|
||||
* The auto-detected $base_url that should be configured in settings.php
|
||||
*/
|
||||
function drupal_detect_baseurl($file = 'install.php') {
|
||||
global $profile;
|
||||
$proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
|
||||
$host = $_SERVER['SERVER_NAME'];
|
||||
$port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':'. $_SERVER['SERVER_PORT']);
|
||||
$uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
|
||||
$dir = str_replace("/$file", '', $uri);
|
||||
|
||||
return "$proto$host$port$dir";
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect all databases supported by Drupal that are compiled into the current
|
||||
* PHP installation.
|
||||
*
|
||||
* @return
|
||||
* An array of database types compiled into PHP.
|
||||
*/
|
||||
function drupal_detect_database_types() {
|
||||
$databases = array();
|
||||
|
||||
foreach (array('mysql', 'mysqli', 'pgsql') as $type) {
|
||||
if (file_exists('./includes/install.'. $type .'.inc')) {
|
||||
include_once './includes/install.'. $type .'.inc';
|
||||
$function = $type .'_is_available';
|
||||
if ($function()) {
|
||||
$databases[$type] = $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $databases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read settings.php into a buffer line by line, changing values specified in
|
||||
* $settings array, then over-writing the old settings.php file.
|
||||
*
|
||||
* @param $settings
|
||||
* An array of settings that need to be updated.
|
||||
*/
|
||||
function drupal_rewrite_settings($settings = array(), $prefix = '') {
|
||||
$default_settings = './sites/default/default.settings.php';
|
||||
$settings_file = './'. conf_path(FALSE, TRUE) .'/'. $prefix .'settings.php';
|
||||
|
||||
// Build list of setting names and insert the values into the global namespace.
|
||||
$keys = array();
|
||||
foreach ($settings as $setting => $data) {
|
||||
$GLOBALS[$setting] = $data['value'];
|
||||
$keys[] = $setting;
|
||||
}
|
||||
|
||||
$buffer = NULL;
|
||||
$first = TRUE;
|
||||
if ($fp = fopen($default_settings, 'r')) {
|
||||
// Step line by line through settings.php.
|
||||
while (!feof($fp)) {
|
||||
$line = fgets($fp);
|
||||
if ($first && substr($line, 0, 5) != '<?php') {
|
||||
$buffer = "<?php\n\n";
|
||||
}
|
||||
$first = FALSE;
|
||||
// Check for constants.
|
||||
if (substr($line, 0, 7) == 'define(') {
|
||||
preg_match('/define\(\s*[\'"]([A-Z_-]+)[\'"]\s*,(.*?)\);/', $line, $variable);
|
||||
if (in_array($variable[1], $keys)) {
|
||||
$setting = $settings[$variable[1]];
|
||||
$buffer .= str_replace($variable[2], " '". $setting['value'] ."'", $line);
|
||||
unset($settings[$variable[1]]);
|
||||
unset($settings[$variable[2]]);
|
||||
}
|
||||
else {
|
||||
$buffer .= $line;
|
||||
}
|
||||
}
|
||||
// Check for variables.
|
||||
elseif (substr($line, 0, 1) == '$') {
|
||||
preg_match('/\$([^ ]*) /', $line, $variable);
|
||||
if (in_array($variable[1], $keys)) {
|
||||
// Write new value to settings.php in the following format:
|
||||
// $'setting' = 'value'; // 'comment'
|
||||
$setting = $settings[$variable[1]];
|
||||
$buffer .= '$'. $variable[1] ." = '". $setting['value'] ."';". (!empty($setting['comment']) ? ' // '. $setting['comment'] ."\n" : "\n");
|
||||
unset($settings[$variable[1]]);
|
||||
}
|
||||
else {
|
||||
$buffer .= $line;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$buffer .= $line;
|
||||
}
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
// Add required settings that were missing from settings.php.
|
||||
foreach ($settings as $setting => $data) {
|
||||
if ($data['required']) {
|
||||
$buffer .= "\$$setting = '". $data['value'] ."';\n";
|
||||
}
|
||||
}
|
||||
|
||||
$fp = fopen($settings_file, 'w');
|
||||
if ($fp && fwrite($fp, $buffer) === FALSE) {
|
||||
drupal_set_message(st('Failed to modify %settings, please verify the file permissions.', array('%settings' => $settings_file)), 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(st('Failed to open %settings, please verify the file permissions.', array('%settings' => $default_settings)), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of all .install files.
|
||||
*
|
||||
* @param $module_list
|
||||
* An array of modules to search for their .install files.
|
||||
*/
|
||||
function drupal_get_install_files($module_list = array()) {
|
||||
$installs = array();
|
||||
foreach ($module_list as $module) {
|
||||
$installs = array_merge($installs, drupal_system_listing($module .'.install$', 'modules'));
|
||||
}
|
||||
return $installs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a profile for installation.
|
||||
*
|
||||
* @param profile
|
||||
* Name of profile to verify.
|
||||
* @param locale
|
||||
* Name of locale used (if any).
|
||||
* @return
|
||||
* The list of modules to install.
|
||||
*/
|
||||
function drupal_verify_profile($profile, $locale) {
|
||||
include_once './includes/file.inc';
|
||||
include_once './includes/common.inc';
|
||||
|
||||
$profile_file = "./profiles/$profile/$profile.profile";
|
||||
|
||||
if (!isset($profile) || !file_exists($profile_file)) {
|
||||
install_no_profile_error();
|
||||
}
|
||||
|
||||
require_once($profile_file);
|
||||
|
||||
// Get a list of modules required by this profile.
|
||||
$function = $profile .'_profile_modules';
|
||||
$module_list = array_merge(drupal_required_modules(), $function(), ($locale != 'en' && !empty($locale) ? array('locale') : array()));
|
||||
|
||||
// Get a list of modules that exist in Drupal's assorted subdirectories.
|
||||
$present_modules = array();
|
||||
foreach (drupal_system_listing('\.module$', 'modules', 'name', 0) as $present_module) {
|
||||
$present_modules[] = $present_module->name;
|
||||
}
|
||||
|
||||
// Verify that all of the profile's required modules are present.
|
||||
$missing_modules = array_diff($module_list, $present_modules);
|
||||
if (count($missing_modules)) {
|
||||
foreach ($missing_modules as $module) {
|
||||
drupal_set_message(st('The %module module is required but was not found. Please move it into the <em>modules</em> subdirectory.', array('%module' => $module)), 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $module_list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the install function and updates the system table for a given list of
|
||||
* modules.
|
||||
*
|
||||
* @param module_list
|
||||
* The modules to install.
|
||||
*/
|
||||
function drupal_install_modules($module_list = array()) {
|
||||
$files = module_rebuild_cache();
|
||||
$module_list = array_flip(array_values($module_list));
|
||||
do {
|
||||
$moved = FALSE;
|
||||
foreach ($module_list as $module => $weight) {
|
||||
$file = $files[$module];
|
||||
if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
|
||||
foreach ($file->info['dependencies'] as $dependency) {
|
||||
if (isset($module_list[$dependency]) && $module_list[$module] < $module_list[$dependency] +1) {
|
||||
$module_list[$module] = $module_list[$dependency] +1;
|
||||
$moved = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($moved);
|
||||
asort($module_list);
|
||||
$module_list = array_keys($module_list);
|
||||
array_filter($module_list, '_drupal_install_module');
|
||||
module_enable($module_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to install an individual profile module.
|
||||
*
|
||||
* Used during installation to install modules one at a time and then
|
||||
* enable them, or to install a number of modules at one time
|
||||
* from admin/build/modules.
|
||||
*/
|
||||
function _drupal_install_module($module) {
|
||||
if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
|
||||
module_load_install($module);
|
||||
module_invoke($module, 'install');
|
||||
$versions = drupal_get_schema_versions($module);
|
||||
drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to install the system module.
|
||||
*
|
||||
* Separated from the installation of other modules so core system
|
||||
* functions can be made available while other modules are installed.
|
||||
*/
|
||||
function drupal_install_system() {
|
||||
$system_path = dirname(drupal_get_filename('module', 'system', NULL));
|
||||
require_once './'. $system_path .'/system.install';
|
||||
module_invoke('system', 'install');
|
||||
$system_versions = drupal_get_schema_versions('system');
|
||||
$system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
|
||||
db_query("INSERT INTO {system} (filename, name, type, owner, status, throttle, bootstrap, schema_version) VALUES('%s', '%s', '%s', '%s', %d, %d, %d, %d)", $system_path .'/system.module', 'system', 'module', '', 1, 0, 0, $system_version);
|
||||
// Now that we've installed things properly, bootstrap the full Drupal environment
|
||||
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
|
||||
module_rebuild_cache();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the uninstall function and updates the system table for a given module.
|
||||
*
|
||||
* @param $module
|
||||
* The module to uninstall.
|
||||
*/
|
||||
function drupal_uninstall_module($module) {
|
||||
// First, retrieve all the module's menu paths from db.
|
||||
drupal_load('module', $module);
|
||||
$paths = module_invoke($module, 'menu');
|
||||
|
||||
// Uninstall the module(s).
|
||||
module_load_install($module);
|
||||
module_invoke($module, 'uninstall');
|
||||
|
||||
// Now remove the menu links for all paths declared by this module.
|
||||
if (!empty($paths)) {
|
||||
$paths = array_keys($paths);
|
||||
// Clean out the names of load functions.
|
||||
foreach ($paths as $index => $path) {
|
||||
$parts = explode('/', $path, MENU_MAX_PARTS);
|
||||
foreach ($parts as $k => $part) {
|
||||
if (preg_match('/^%[a-z_]*$/', $part)) {
|
||||
$parts[$k] = '%';
|
||||
}
|
||||
}
|
||||
$paths[$index] = implode('/', $parts);
|
||||
}
|
||||
$placeholders = implode(', ', array_fill(0, count($paths), "'%s'"));
|
||||
|
||||
$result = db_query('SELECT * FROM {menu_links} WHERE router_path IN ('. $placeholders .') AND external = 0 ORDER BY depth DESC', $paths);
|
||||
// Remove all such items. Starting from those with the greatest depth will
|
||||
// minimize the amount of re-parenting done by menu_link_delete().
|
||||
while ($item = db_fetch_array($result)) {
|
||||
_menu_delete_item($item, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the state of the specified file.
|
||||
*
|
||||
* @param $file
|
||||
* The file to check for.
|
||||
* @param $mask
|
||||
* An optional bitmask created from various FILE_* constants.
|
||||
* @param $type
|
||||
* The type of file. Can be file (default), dir, or link.
|
||||
* @return
|
||||
* TRUE on success or FALSE on failure. A message is set for the latter.
|
||||
*/
|
||||
function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
|
||||
$return = TRUE;
|
||||
// Check for files that shouldn't be there.
|
||||
if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
|
||||
return FALSE;
|
||||
}
|
||||
// Verify that the file is the type of file it is supposed to be.
|
||||
if (isset($type) && file_exists($file)) {
|
||||
$check = 'is_'. $type;
|
||||
if (!function_exists($check) || !$check($file)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify file permissions.
|
||||
if (isset($mask)) {
|
||||
$masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
|
||||
foreach ($masks as $current_mask) {
|
||||
if ($mask & $current_mask) {
|
||||
switch ($current_mask) {
|
||||
case FILE_EXIST:
|
||||
if (!file_exists($file)) {
|
||||
if ($type == 'dir') {
|
||||
drupal_install_mkdir($file, $mask);
|
||||
}
|
||||
if (!file_exists($file)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FILE_READABLE:
|
||||
if (!is_readable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
case FILE_WRITABLE:
|
||||
if (!is_writable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
case FILE_EXECUTABLE:
|
||||
if (!is_executable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_READABLE:
|
||||
if (is_readable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_WRITABLE:
|
||||
if (is_writable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_EXECUTABLE:
|
||||
if (is_executable($file) && !drupal_install_fix_file($file, $mask)) {
|
||||
$return = FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory with specified permissions.
|
||||
*
|
||||
* @param file
|
||||
* The name of the directory to create;
|
||||
* @param mask
|
||||
* The permissions of the directory to create.
|
||||
* @param $message
|
||||
* (optional) Whether to output messages. Defaults to TRUE.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE whether or not the directory was successfully created.
|
||||
*/
|
||||
function drupal_install_mkdir($file, $mask, $message = TRUE) {
|
||||
$mod = 0;
|
||||
$masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
|
||||
foreach ($masks as $m) {
|
||||
if ($mask & $m) {
|
||||
switch ($m) {
|
||||
case FILE_READABLE:
|
||||
$mod += 444;
|
||||
break;
|
||||
case FILE_WRITABLE:
|
||||
$mod += 222;
|
||||
break;
|
||||
case FILE_EXECUTABLE:
|
||||
$mod += 111;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@mkdir($file, intval("0$mod", 8))) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to fix file permissions.
|
||||
*
|
||||
* The general approach here is that, because we do not know the security
|
||||
* setup of the webserver, we apply our permission changes to all three
|
||||
* digits of the file permission (i.e. user, group and all).
|
||||
*
|
||||
* To ensure that the values behave as expected (and numbers don't carry
|
||||
* from one digit to the next) we do the calculation on the octal value
|
||||
* using bitwise operations. This lets us remove, for example, 0222 from
|
||||
* 0700 and get the correct value of 0500.
|
||||
*
|
||||
* @param $file
|
||||
* The name of the file with permissions to fix.
|
||||
* @param $mask
|
||||
* The desired permissions for the file.
|
||||
* @param $message
|
||||
* (optional) Whether to output messages. Defaults to TRUE.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE whether or not we were able to fix the file's permissions.
|
||||
*/
|
||||
function drupal_install_fix_file($file, $mask, $message = TRUE) {
|
||||
$mod = fileperms($file) & 0777;
|
||||
$masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
|
||||
|
||||
// FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
|
||||
// can theoretically be 0400, 0200, and 0100 respectively, but to be safe
|
||||
// we set all three access types in case the administrator intends to
|
||||
// change the owner of settings.php after installation.
|
||||
foreach ($masks as $m) {
|
||||
if ($mask & $m) {
|
||||
switch ($m) {
|
||||
case FILE_READABLE:
|
||||
if (!is_readable($file)) {
|
||||
$mod |= 0444;
|
||||
}
|
||||
break;
|
||||
case FILE_WRITABLE:
|
||||
if (!is_writable($file)) {
|
||||
$mod |= 0222;
|
||||
}
|
||||
break;
|
||||
case FILE_EXECUTABLE:
|
||||
if (!is_executable($file)) {
|
||||
$mod |= 0111;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_READABLE:
|
||||
if (is_readable($file)) {
|
||||
$mod &= ~0444;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_WRITABLE:
|
||||
if (is_writable($file)) {
|
||||
$mod &= ~0222;
|
||||
}
|
||||
break;
|
||||
case FILE_NOT_EXECUTABLE:
|
||||
if (is_executable($file)) {
|
||||
$mod &= ~0111;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// chmod() will work if the web server is running as owner of the file.
|
||||
// If PHP safe_mode is enabled the currently executing script must also
|
||||
// have the same owner.
|
||||
if (@chmod($file, $mod)) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the user to a different installer page. This issues an on-site HTTP
|
||||
* redirect. Messages (and errors) are erased.
|
||||
*
|
||||
* @param $path
|
||||
* An installer path.
|
||||
*/
|
||||
function install_goto($path) {
|
||||
global $base_url;
|
||||
header('Location: '. $base_url .'/'. $path);
|
||||
header('Cache-Control: no-cache'); // Not a permanent redirect.
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardcoded function for doing the equivalent of t() during
|
||||
* the install process, when database, theme, and localization
|
||||
* system is possibly not yet available.
|
||||
*/
|
||||
function st($string, $args = array()) {
|
||||
static $locale_strings = NULL;
|
||||
global $profile, $install_locale;
|
||||
|
||||
if (!isset($locale_strings)) {
|
||||
$locale_strings = array();
|
||||
$filename = './profiles/'. $profile .'/translations/'. $install_locale .'.po';
|
||||
if (file_exists($filename)) {
|
||||
require_once './includes/locale.inc';
|
||||
$file = (object) array('filepath' => $filename);
|
||||
_locale_import_read_po('mem-store', $file);
|
||||
$locale_strings = _locale_import_one_string('mem-report');
|
||||
}
|
||||
}
|
||||
|
||||
require_once './includes/theme.inc';
|
||||
// Transform arguments before inserting them
|
||||
foreach ($args as $key => $value) {
|
||||
switch ($key[0]) {
|
||||
// Escaped only
|
||||
case '@':
|
||||
$args[$key] = check_plain($value);
|
||||
break;
|
||||
// Escaped and placeholder
|
||||
case '%':
|
||||
default:
|
||||
$args[$key] = '<em>'. check_plain($value) .'</em>';
|
||||
break;
|
||||
// Pass-through
|
||||
case '!':
|
||||
}
|
||||
}
|
||||
return strtr((!empty($locale_strings[$string]) ? $locale_strings[$string] : $string), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a profile's requirements.
|
||||
*
|
||||
* @param profile
|
||||
* Name of profile to check.
|
||||
*/
|
||||
function drupal_check_profile($profile) {
|
||||
include_once './includes/file.inc';
|
||||
|
||||
$profile_file = "./profiles/$profile/$profile.profile";
|
||||
|
||||
if (!isset($profile) || !file_exists($profile_file)) {
|
||||
install_no_profile_error();
|
||||
}
|
||||
|
||||
require_once($profile_file);
|
||||
|
||||
// Get a list of modules required by this profile.
|
||||
$function = $profile .'_profile_modules';
|
||||
$module_list = array_unique(array_merge(drupal_required_modules(), $function()));
|
||||
|
||||
// Get a list of all .install files.
|
||||
$installs = drupal_get_install_files($module_list);
|
||||
|
||||
// Collect requirement testing results
|
||||
$requirements = array();
|
||||
foreach ($installs as $install) {
|
||||
require_once $install->filename;
|
||||
if (module_hook($install->name, 'requirements')) {
|
||||
$requirements = array_merge($requirements, module_invoke($install->name, 'requirements', 'install'));
|
||||
}
|
||||
}
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract highest severity from requirements array.
|
||||
*/
|
||||
function drupal_requirements_severity(&$requirements) {
|
||||
$severity = REQUIREMENT_OK;
|
||||
foreach ($requirements as $requirement) {
|
||||
if (isset($requirement['severity'])) {
|
||||
$severity = max($severity, $requirement['severity']);
|
||||
}
|
||||
}
|
||||
return $severity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a module's requirements.
|
||||
*/
|
||||
function drupal_check_module($module) {
|
||||
// Include install file
|
||||
$install = drupal_get_install_files(array($module));
|
||||
if (isset($install[$module])) {
|
||||
require_once $install[$module]->filename;
|
||||
|
||||
// Check requirements
|
||||
$requirements = module_invoke($module, 'requirements', 'install');
|
||||
if (is_array($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
|
||||
// Print any error messages
|
||||
foreach ($requirements as $requirement) {
|
||||
if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
|
||||
$message = $requirement['description'];
|
||||
if (isset($requirement['value']) && $requirement['value']) {
|
||||
$message .= ' ('. t('Currently using !item !version', array('!item' => $requirement['title'], '!version' => $requirement['value'])) .')';
|
||||
}
|
||||
drupal_set_message($message, 'error');
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
116
includes/install.mysql.inc
Normal file
116
includes/install.mysql.inc
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
// MySQL specific install functions
|
||||
|
||||
/**
|
||||
* Check if MySQL is available.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function mysql_is_available() {
|
||||
return function_exists('mysql_connect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can connect to MySQL.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function drupal_test_mysql($url, &$success) {
|
||||
if (!mysql_is_available()) {
|
||||
drupal_set_message(st('PHP MySQL support not enabled.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$url = parse_url($url);
|
||||
|
||||
// Decode urlencoded information in the db connection string.
|
||||
$url['user'] = urldecode($url['user']);
|
||||
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
|
||||
$url['host'] = urldecode($url['host']);
|
||||
$url['path'] = urldecode($url['path']);
|
||||
|
||||
// Allow for non-standard MySQL port.
|
||||
if (isset($url['port'])) {
|
||||
$url['host'] = $url['host'] .':'. $url['port'];
|
||||
}
|
||||
|
||||
// Test connecting to the database.
|
||||
$connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2);
|
||||
if (!$connection) {
|
||||
drupal_set_message(st('Failed to connect to your MySQL database server. MySQL reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => mysql_error())), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Test selecting the database.
|
||||
if (!mysql_select_db(substr($url['path'], 1))) {
|
||||
drupal_set_message(st('Failed to select your database on your MySQL database server, which means the connection username and password are valid, but there is a problem accessing your data. MySQL reports the following message: %error.<ul><li>Are you sure you have the correct database name?</li><li>Are you sure the database exists?</li><li>Are you sure the username has permission to access the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => mysql_error())), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$success = array('CONNECT');
|
||||
|
||||
// Test CREATE.
|
||||
$query = 'CREATE TABLE drupal_install_test (id int NULL)';
|
||||
$result = mysql_query($query);
|
||||
if ($error = mysql_error()) {
|
||||
drupal_set_message(st('Failed to create a test table on your MySQL database server with the command %query. MySQL reports the following message: %error.<ul><li>Are you sure the configured username has the necessary MySQL permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
$err = FALSE;
|
||||
$success[] = 'SELECT';
|
||||
$success[] = 'CREATE';
|
||||
|
||||
// Test INSERT.
|
||||
$query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
|
||||
$result = mysql_query($query);
|
||||
if ($error = mysql_error()) {
|
||||
drupal_set_message(st('Failed to insert a value into a test table on your MySQL database server. We tried inserting a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'INSERT';
|
||||
}
|
||||
|
||||
// Test UPDATE.
|
||||
$query = 'UPDATE drupal_install_test SET id = 2';
|
||||
$result = mysql_query($query);
|
||||
if ($error = mysql_error()) {
|
||||
drupal_set_message(st('Failed to update a value in a test table on your MySQL database server. We tried updating a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'UPDATE';
|
||||
}
|
||||
|
||||
// Test DELETE.
|
||||
$query = 'DELETE FROM drupal_install_test';
|
||||
$result = mysql_query($query);
|
||||
if ($error = mysql_error()) {
|
||||
drupal_set_message(st('Failed to delete a value from a test table on your MySQL database server. We tried deleting a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DELETE';
|
||||
}
|
||||
|
||||
// Test DROP.
|
||||
$query = 'DROP TABLE drupal_install_test';
|
||||
$result = mysql_query($query);
|
||||
if ($error = mysql_error()) {
|
||||
drupal_set_message(st('Failed to drop a test table from your MySQL database server. We tried dropping a table with the command %query and MySQL reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DROP';
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mysql_close($connection);
|
||||
return TRUE;
|
||||
}
|
||||
111
includes/install.mysqli.inc
Normal file
111
includes/install.mysqli.inc
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
// MySQLi specific install functions
|
||||
|
||||
/**
|
||||
* Check if MySQLi is available.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function mysqli_is_available() {
|
||||
return function_exists('mysqli_connect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can connect to MySQL.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function drupal_test_mysqli($url, &$success) {
|
||||
if (!mysqli_is_available()) {
|
||||
drupal_set_message(st('PHP MySQLi support not enabled.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$url = parse_url($url);
|
||||
|
||||
// Decode urlencoded information in the db connection string.
|
||||
$url['user'] = urldecode($url['user']);
|
||||
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
|
||||
$url['host'] = urldecode($url['host']);
|
||||
$url['path'] = urldecode($url['path']);
|
||||
|
||||
$connection = mysqli_init();
|
||||
@mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS);
|
||||
if (mysqli_connect_errno() >= 2000 || mysqli_connect_errno() == 1045) {
|
||||
drupal_set_message(st('Failed to connect to your MySQL database server. MySQL reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => mysqli_connect_error())), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Test selecting the database.
|
||||
if (mysqli_connect_errno() > 0) {
|
||||
drupal_set_message(st('Failed to select your database on your MySQL database server, which means the connection username and password are valid, but there is a problem accessing your data. MySQL reports the following message: %error.<ul><li>Are you sure you have the correct database name?</li><li>Are you sure the database exists?</li><li>Are you sure the username has permission to access the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => mysqli_connect_error())), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$success = array('CONNECT');
|
||||
|
||||
// Test CREATE.
|
||||
$query = 'CREATE TABLE drupal_install_test (id int NULL)';
|
||||
$result = mysqli_query($connection, $query);
|
||||
if ($error = mysqli_error($connection)) {
|
||||
drupal_set_message(st('Failed to create a test table on your MySQL database server with the command %query. MySQL reports the following message: %error.<ul><li>Are you sure the configured username has the necessary MySQL permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
$err = FALSE;
|
||||
$success[] = 'SELECT';
|
||||
$success[] = 'CREATE';
|
||||
|
||||
// Test INSERT.
|
||||
$query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
|
||||
$result = mysqli_query($connection, $query);
|
||||
if ($error = mysqli_error($connection)) {
|
||||
drupal_set_message(st('Failed to insert a value into a test table on your MySQL database server. We tried inserting a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'INSERT';
|
||||
}
|
||||
|
||||
// Test UPDATE.
|
||||
$query = 'UPDATE drupal_install_test SET id = 2';
|
||||
$result = mysqli_query($connection, $query);
|
||||
if ($error = mysqli_error($connection)) {
|
||||
drupal_set_message(st('Failed to update a value in a test table on your MySQL database server. We tried updating a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'UPDATE';
|
||||
}
|
||||
|
||||
// Test DELETE.
|
||||
$query = 'DELETE FROM drupal_install_test';
|
||||
$result = mysqli_query($connection, $query);
|
||||
if ($error = mysqli_error($connection)) {
|
||||
drupal_set_message(st('Failed to delete a value from a test table on your MySQL database server. We tried deleting a value with the command %query and MySQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DELETE';
|
||||
}
|
||||
|
||||
// Test DROP.
|
||||
$query = 'DROP TABLE drupal_install_test';
|
||||
$result = mysqli_query($connection, $query);
|
||||
if ($error = mysqli_error($connection)) {
|
||||
drupal_set_message(st('Failed to drop a test table from your MySQL database server. We tried dropping a table with the command %query and MySQL reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DROP';
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mysqli_close($connection);
|
||||
return TRUE;
|
||||
}
|
||||
139
includes/install.pgsql.inc
Normal file
139
includes/install.pgsql.inc
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
// PostgreSQL specific install functions
|
||||
|
||||
/**
|
||||
* Check if PostgreSQL is available.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function pgsql_is_available() {
|
||||
return function_exists('pg_connect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can connect to PostgreSQL.
|
||||
*
|
||||
* @return
|
||||
* TRUE/FALSE
|
||||
*/
|
||||
function drupal_test_pgsql($url, &$success) {
|
||||
if (!pgsql_is_available()) {
|
||||
drupal_set_message(st('PHP PostgreSQL support not enabled.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$url = parse_url($url);
|
||||
$conn_string = '';
|
||||
|
||||
// Decode urlencoded information in the db connection string
|
||||
if (isset($url['user'])) {
|
||||
$conn_string .= ' user='. urldecode($url['user']);
|
||||
}
|
||||
if (isset($url['pass'])) {
|
||||
$conn_string .= ' password='. urldecode($url['pass']);
|
||||
}
|
||||
if (isset($url['host'])) {
|
||||
$conn_string .= ' host='. urldecode($url['host']);
|
||||
}
|
||||
if (isset($url['path'])) {
|
||||
$conn_string .= ' dbname='. substr(urldecode($url['path']), 1);
|
||||
}
|
||||
if (isset($url['port'])) {
|
||||
$conn_string .= ' port='. urldecode($url['port']);
|
||||
}
|
||||
|
||||
// Test connecting to the database.
|
||||
$connection = @pg_connect($conn_string);
|
||||
if (!$connection) {
|
||||
drupal_set_message(st('Failed to connect to your PostgreSQL database server. PostgreSQL reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li><li>Are you sure you typed the correct database name?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => 'Connection failed. See log file for failure reason')), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$success = array('CONNECT');
|
||||
|
||||
// Test CREATE.
|
||||
$query = 'CREATE TABLE drupal_install_test (id integer NOT NULL)';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error($result)) {
|
||||
drupal_set_message(st('Failed to create a test table on your PostgreSQL database server with the command %query. PostgreSQL reports the following message: %error.<ul><li>Are you sure the configured username has the necessary PostgreSQL permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
$err = FALSE;
|
||||
$success[] = 'SELECT';
|
||||
$success[] = 'CREATE';
|
||||
|
||||
// Test INSERT.
|
||||
$query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error($result)) {
|
||||
drupal_set_message(st('Failed to insert a value into a test table on your PostgreSQL database server. We tried inserting a value with the command %query and PostgreSQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'INSERT';
|
||||
}
|
||||
|
||||
// Test UPDATE.
|
||||
$query = 'UPDATE drupal_install_test SET id = 2';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error($result)) {
|
||||
drupal_set_message(st('Failed to update a value in a test table on your PostgreSQL database server. We tried updating a value with the command %query and PostgreSQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'UPDATE';
|
||||
}
|
||||
|
||||
// Test LOCK.
|
||||
$query = 'BEGIN; LOCK drupal_install_test IN SHARE ROW EXCLUSIVE MODE';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error($result)) {
|
||||
drupal_set_message(st('Failed to lock a test table on your PostgreSQL database server. We tried locking a table with the command %query and PostgreSQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'LOCK';
|
||||
}
|
||||
|
||||
// Test UNLOCK, which is done automatically upon transaction end in PostgreSQL
|
||||
$query = 'COMMIT';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error()) {
|
||||
drupal_set_message(st('Failed to unlock a test table on your PostgreSQL database server. We tried unlocking a table with the command %query and PostgreSQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'UNLOCK';
|
||||
}
|
||||
|
||||
// Test DELETE.
|
||||
$query = 'DELETE FROM drupal_install_test';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error()) {
|
||||
drupal_set_message(st('Failed to delete a value from a test table on your PostgreSQL database server. We tried deleting a value with the command %query and PostgreSQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DELETE';
|
||||
}
|
||||
|
||||
// Test DROP.
|
||||
$query = 'DROP TABLE drupal_install_test';
|
||||
$result = pg_query($connection, $query);
|
||||
if ($error = pg_result_error()) {
|
||||
drupal_set_message(st('Failed to drop a test table from your PostgreSQL database server. We tried dropping a table with the command %query and PostgreSQL reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
|
||||
$err = TRUE;
|
||||
}
|
||||
else {
|
||||
$success[] = 'DROP';
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pg_close($connection);
|
||||
return TRUE;
|
||||
}
|
||||
143
includes/language.inc
Normal file
143
includes/language.inc
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Multiple language handling functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Choose a language for the page, based on language negotiation settings.
|
||||
*/
|
||||
function language_initialize() {
|
||||
global $user;
|
||||
|
||||
// Configured presentation language mode.
|
||||
$mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
|
||||
// Get a list of enabled languages.
|
||||
$languages = language_list('enabled');
|
||||
$languages = $languages[1];
|
||||
|
||||
switch ($mode) {
|
||||
case LANGUAGE_NEGOTIATION_NONE:
|
||||
return language_default();
|
||||
|
||||
case LANGUAGE_NEGOTIATION_DOMAIN:
|
||||
foreach ($languages as $language) {
|
||||
$parts = parse_url($language->domain);
|
||||
if (!empty($parts['host']) && ($_SERVER['HTTP_HOST'] == $parts['host'])) {
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
return language_default();
|
||||
|
||||
case LANGUAGE_NEGOTIATION_PATH_DEFAULT:
|
||||
case LANGUAGE_NEGOTIATION_PATH:
|
||||
// $_GET['q'] might not be available at this time, because
|
||||
// path initialization runs after the language bootstrap phase.
|
||||
$args = isset($_GET['q']) ? explode('/', $_GET['q']) : array();
|
||||
$prefix = array_shift($args);
|
||||
// Search prefix within enabled languages.
|
||||
foreach ($languages as $language) {
|
||||
if (!empty($language->prefix) && $language->prefix == $prefix) {
|
||||
// Rebuild $GET['q'] with the language removed.
|
||||
$_GET['q'] = implode('/', $args);
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
if ($mode == LANGUAGE_NEGOTIATION_PATH_DEFAULT) {
|
||||
// If we did not found the language by prefix, choose the default.
|
||||
return language_default();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// User language.
|
||||
if ($user->uid && isset($languages[$user->language])) {
|
||||
return $languages[$user->language];
|
||||
}
|
||||
|
||||
// Browser accept-language parsing.
|
||||
if ($language = language_from_browser()) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
// Fall back on the default if everything else fails.
|
||||
return language_default();
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify language from the Accept-language HTTP header we got.
|
||||
*/
|
||||
function language_from_browser() {
|
||||
// Specified by the user via the browser's Accept Language setting
|
||||
// Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
|
||||
$browser_langs = array();
|
||||
|
||||
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$browser_accept = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
|
||||
for ($i = 0; $i < count($browser_accept); $i++) {
|
||||
// The language part is either a code or a code with a quality.
|
||||
// We cannot do anything with a * code, so it is skipped.
|
||||
// If the quality is missing, it is assumed to be 1 according to the RFC.
|
||||
if (preg_match("!([a-z-]+)(;q=([0-9\\.]+))?!", trim($browser_accept[$i]), $found)) {
|
||||
$browser_langs[$found[1]] = (isset($found[3]) ? (float) $found[3] : 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Order the codes by quality
|
||||
arsort($browser_langs);
|
||||
|
||||
// Try to find the first preferred language we have
|
||||
$languages = language_list('enabled');
|
||||
foreach ($browser_langs as $langcode => $q) {
|
||||
if (isset($languages['1'][$langcode])) {
|
||||
return $languages['1'][$langcode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite URL's with language based prefix. Parameters are the same
|
||||
* as those of the url() function.
|
||||
*/
|
||||
function language_url_rewrite(&$path, &$options) {
|
||||
global $language;
|
||||
|
||||
// Only modify relative (insite) URLs.
|
||||
if (empty($options['external'])) {
|
||||
|
||||
// Language can be passed as an option, or we go for current language.
|
||||
if (!isset($options['language'])) {
|
||||
$options['language'] = $language;
|
||||
}
|
||||
|
||||
switch (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE)) {
|
||||
case LANGUAGE_NEGOTIATION_NONE:
|
||||
// No language dependent path allowed in this mode.
|
||||
unset($options['language']);
|
||||
break;
|
||||
|
||||
case LANGUAGE_NEGOTIATION_DOMAIN:
|
||||
if ($options['language']->domain) {
|
||||
// Ask for an absolute URL with our modified base_url.
|
||||
$options['absolute'] = TRUE;
|
||||
$options['base_url'] = $options['language']->domain;
|
||||
}
|
||||
break;
|
||||
|
||||
case LANGUAGE_NEGOTIATION_PATH_DEFAULT:
|
||||
$default = language_default();
|
||||
if ($options['language']->language == $default->language) {
|
||||
break;
|
||||
}
|
||||
// Intentionally no break here.
|
||||
|
||||
case LANGUAGE_NEGOTIATION_PATH:
|
||||
if (!empty($options['language']->prefix)) {
|
||||
$options['prefix'] = $options['language']->prefix .'/';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
2658
includes/locale.inc
Normal file
2658
includes/locale.inc
Normal file
File diff suppressed because it is too large
Load diff
62
includes/lock-install.inc
Normal file
62
includes/lock-install.inc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* A stub lock implementation to be used during the installation
|
||||
* process when database access is not yet available. Because Drupal's
|
||||
* install system should never be running in more than on concurrant
|
||||
* request, we can bypass any need for locking.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize the locking system.
|
||||
*/
|
||||
function lock_init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire (or renew) a lock, but do not block if it fails.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the lock was acquired, FALSE if it failed.
|
||||
*/
|
||||
function lock_acquire($name, $timeout = 30.0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lock acquired by a different process may be available.
|
||||
*
|
||||
* @return
|
||||
* TRUE if there is no lock or it was removed, FALSE otherwise.
|
||||
*/
|
||||
function lock_may_be_available($name) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a lock to be available.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the lock holds, FALSE if it is available.
|
||||
*/
|
||||
function lock_wait($name, $delay = 30) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a lock previously acquired by lock_acquire().
|
||||
*
|
||||
* This will release the named lock if it is still held by the current request.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the lock.
|
||||
*/
|
||||
function lock_release($name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all previously acquired locks.
|
||||
*/
|
||||
function lock_release_all($lock_id = NULL) {
|
||||
}
|
||||
229
includes/lock.inc
Normal file
229
includes/lock.inc
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* A database-mediated implementation of a locking mechanism.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup lock Functions to coordinate long-running operations across requests.
|
||||
* @{
|
||||
* In most environments, multiple Drupal page requests (a.k.a. threads or
|
||||
* processes) will execute in parallel. This leads to potential conflicts or
|
||||
* race conditions when two requests execute the same code at the same time. A
|
||||
* common example of this is a rebuild like menu_rebuild() where we invoke many
|
||||
* hook implementations to get and process data from all active modules, and
|
||||
* then delete the current data in the database to insert the new afterwards.
|
||||
*
|
||||
* This is a cooperative, advisory lock system. Any long-running operation
|
||||
* that could potentially be attempted in parallel by multiple requests should
|
||||
* try to acquire a lock before proceeding. By obtaining a lock, one request
|
||||
* notifies any other requests that a specific opertation is in progress which
|
||||
* must not be executed in parallel.
|
||||
*
|
||||
* To use this API, pick a unique name for the lock. A sensible choice is the
|
||||
* name of the function performing the operation. A very simple example use of
|
||||
* this API:
|
||||
* @code
|
||||
* function mymodule_long_operation() {
|
||||
* if (lock_acquire('mymodule_long_operation')) {
|
||||
* // Do the long operation here.
|
||||
* // ...
|
||||
* lock_release('mymodule_long_operation');
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* If a function acquires a lock it should always release it when the
|
||||
* operation is complete by calling lock_release(), as in the example.
|
||||
*
|
||||
* A function that has acquired a lock may attempt to renew a lock (extend the
|
||||
* duration of the lock) by calling lock_acquire() again during the operation.
|
||||
* Failure to renew a lock is indicative that another request has acquired
|
||||
* the lock, and that the current operation may need to be aborted.
|
||||
*
|
||||
* If a function fails to acquire a lock it may either immediately return, or
|
||||
* it may call lock_wait() if the rest of the current page request requires
|
||||
* that the operation in question be complete. After lock_wait() returns,
|
||||
* the function may again attempt to acquire the lock, or may simply allow the
|
||||
* page request to proceed on the assumption that a parallel request completed
|
||||
* the operation.
|
||||
*
|
||||
* lock_acquire() and lock_wait() will automatically break (delete) a lock
|
||||
* whose duration has exceeded the timeout specified when it was acquired.
|
||||
*
|
||||
* Alternative implementations of this API (such as APC) may be substituted
|
||||
* by setting the 'lock_inc' variable to an alternate include filepath. Since
|
||||
* this is an API intended to support alternative implementations, code using
|
||||
* this API should never rely upon specific implementation details (for example
|
||||
* no code should look for or directly modify a lock in the {semaphore} table).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize the locking system.
|
||||
*/
|
||||
function lock_init() {
|
||||
global $locks;
|
||||
|
||||
$locks = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get this request's unique id.
|
||||
*/
|
||||
function _lock_id() {
|
||||
static $lock_id;
|
||||
|
||||
if (!isset($lock_id)) {
|
||||
// Assign a unique id.
|
||||
$lock_id = uniqid(mt_rand(), TRUE);
|
||||
// We only register a shutdown function if a lock is used.
|
||||
register_shutdown_function('lock_release_all', $lock_id);
|
||||
}
|
||||
return $lock_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire (or renew) a lock, but do not block if it fails.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the lock.
|
||||
* @param $timeout
|
||||
* A number of seconds (float) before the lock expires.
|
||||
* @return
|
||||
* TRUE if the lock was acquired, FALSE if it failed.
|
||||
*/
|
||||
function lock_acquire($name, $timeout = 30.0) {
|
||||
global $locks;
|
||||
|
||||
// Insure that the timeout is at least 1 ms.
|
||||
$timeout = max($timeout, 0.001);
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$expire = (float)$usec + (float)$sec + $timeout;
|
||||
if (isset($locks[$name])) {
|
||||
// Try to extend the expiration of a lock we already acquired.
|
||||
db_query("UPDATE {semaphore} SET expire = %f WHERE name = '%s' AND value = '%s'", $expire, $name, _lock_id());
|
||||
if (!db_affected_rows()) {
|
||||
// The lock was broken.
|
||||
unset($locks[$name]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Optimistically try to acquire the lock, then retry once if it fails.
|
||||
// The first time through the loop cannot be a retry.
|
||||
$retry = FALSE;
|
||||
// We always want to do this code at least once.
|
||||
do {
|
||||
if (@db_query("INSERT INTO {semaphore} (name, value, expire) VALUES ('%s', '%s', %f)", $name, _lock_id(), $expire)) {
|
||||
// We track all acquired locks in the global variable.
|
||||
$locks[$name] = TRUE;
|
||||
// We never need to try again.
|
||||
$retry = FALSE;
|
||||
}
|
||||
else {
|
||||
// Suppress the error. If this is our first pass through the loop,
|
||||
// then $retry is FALSE. In this case, the insert must have failed
|
||||
// meaning some other request acquired the lock but did not release it.
|
||||
// We decide whether to retry by checking lock_may_be_available()
|
||||
// Since this will break the lock in case it is expired.
|
||||
$retry = $retry ? FALSE : lock_may_be_available($name);
|
||||
}
|
||||
// We only retry in case the first attempt failed, but we then broke
|
||||
// an expired lock.
|
||||
} while ($retry);
|
||||
}
|
||||
return isset($locks[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lock acquired by a different process may be available.
|
||||
*
|
||||
* If an existing lock has expired, it is removed.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the lock.
|
||||
* @return
|
||||
* TRUE if there is no lock or it was removed, FALSE otherwise.
|
||||
*/
|
||||
function lock_may_be_available($name) {
|
||||
$lock = db_fetch_array(db_query("SELECT expire, value FROM {semaphore} WHERE name = '%s'", $name));
|
||||
if (!$lock) {
|
||||
return TRUE;
|
||||
}
|
||||
$expire = (float) $lock['expire'];
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$now = (float)$usec + (float)$sec;
|
||||
if ($now > $lock['expire']) {
|
||||
// We check two conditions to prevent a race condition where another
|
||||
// request acquired the lock and set a new expire time. We add a small
|
||||
// number to $expire to avoid errors with float to string conversion.
|
||||
db_query("DELETE FROM {semaphore} WHERE name = '%s' AND value = '%s' AND expire <= %f", $name, $lock['value'], 0.0001 + $expire);
|
||||
return (bool)db_affected_rows();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a lock to be available.
|
||||
*
|
||||
* This function may be called in a request that fails to acquire a desired
|
||||
* lock. This will block further execution until the lock is available or the
|
||||
* specified delay in seconds is reached. This should not be used with locks
|
||||
* that are acquired very frequently, since the lock is likely to be acquired
|
||||
* again by a different request during the sleep().
|
||||
*
|
||||
* @param $name
|
||||
* The name of the lock.
|
||||
* @param $delay
|
||||
* The maximum number of seconds to wait, as an integer.
|
||||
* @return
|
||||
* TRUE if the lock holds, FALSE if it is available.
|
||||
*/
|
||||
function lock_wait($name, $delay = 30) {
|
||||
|
||||
while ($delay--) {
|
||||
// This function should only be called by a request that failed to get a
|
||||
// lock, so we sleep first to give the parallel request a chance to finish
|
||||
// and release the lock.
|
||||
sleep(1);
|
||||
if (lock_may_be_available($name)) {
|
||||
// No longer need to wait.
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
// The caller must still wait longer to get the lock.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a lock previously acquired by lock_acquire().
|
||||
*
|
||||
* This will release the named lock if it is still held by the current request.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the lock.
|
||||
*/
|
||||
function lock_release($name) {
|
||||
global $locks;
|
||||
|
||||
unset($locks[$name]);
|
||||
db_query("DELETE FROM {semaphore} WHERE name = '%s' AND value = '%s'", $name, _lock_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all previously acquired locks.
|
||||
*/
|
||||
function lock_release_all($lock_id = NULL) {
|
||||
global $locks;
|
||||
|
||||
$locks = array();
|
||||
if (empty($lock_id)) {
|
||||
$lock_id = _lock_id();
|
||||
}
|
||||
|
||||
db_query("DELETE FROM {semaphore} WHERE value = '%s'", _lock_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup locks".
|
||||
*/
|
||||
482
includes/mail.inc
Normal file
482
includes/mail.inc
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Compose and optionally send an e-mail message.
|
||||
*
|
||||
* Sending an e-mail works with defining an e-mail template (subject, text
|
||||
* and possibly e-mail headers) and the replacement values to use in the
|
||||
* appropriate places in the template. Processed e-mail templates are
|
||||
* requested from hook_mail() from the module sending the e-mail. Any module
|
||||
* can modify the composed e-mail message array using hook_mail_alter().
|
||||
* Finally drupal_mail_send() sends the e-mail, which can be reused
|
||||
* if the exact same composed e-mail is to be sent to multiple recipients.
|
||||
*
|
||||
* Finding out what language to send the e-mail with needs some consideration.
|
||||
* If you send e-mail to a user, her preferred language should be fine, so
|
||||
* use user_preferred_language(). If you send email based on form values
|
||||
* filled on the page, there are two additional choices if you are not
|
||||
* sending the e-mail to a user on the site. You can either use the language
|
||||
* used to generate the page ($language global variable) or the site default
|
||||
* language. See language_default(). The former is good if sending e-mail to
|
||||
* the person filling the form, the later is good if you send e-mail to an
|
||||
* address previously set up (like contact addresses in a contact form).
|
||||
*
|
||||
* Taking care of always using the proper language is even more important
|
||||
* when sending e-mails in a row to multiple users. Hook_mail() abstracts
|
||||
* whether the mail text comes from an administrator setting or is
|
||||
* static in the source code. It should also deal with common mail tokens,
|
||||
* only receiving $params which are unique to the actual e-mail at hand.
|
||||
*
|
||||
* An example:
|
||||
*
|
||||
* @code
|
||||
* function example_notify($accounts) {
|
||||
* foreach ($accounts as $account) {
|
||||
* $params['account'] = $account;
|
||||
* // example_mail() will be called based on the first drupal_mail() parameter.
|
||||
* drupal_mail('example', 'notice', $account->mail, user_preferred_language($account), $params);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* function example_mail($key, &$message, $params) {
|
||||
* $language = $message['language'];
|
||||
* $variables = user_mail_tokens($params['account'], $language);
|
||||
* switch($key) {
|
||||
* case 'notice':
|
||||
* $message['subject'] = t('Notification from !site', $variables, $language->language);
|
||||
* $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, $language->language);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param $module
|
||||
* A module name to invoke hook_mail() on. The {$module}_mail() hook will be
|
||||
* called to complete the $message structure which will already contain common
|
||||
* defaults.
|
||||
* @param $key
|
||||
* A key to identify the e-mail sent. The final e-mail id for e-mail altering
|
||||
* will be {$module}_{$key}.
|
||||
* @param $to
|
||||
* The e-mail address or addresses where the message will be sent to. The
|
||||
* formatting of this string must comply with
|
||||
* @link http://tools.ietf.org/html/rfc5322 RFC 5322 @endlink.
|
||||
* Some examples are:
|
||||
* - user@example.com
|
||||
* - user@example.com, anotheruser@example.com
|
||||
* - User <user@example.com>
|
||||
* - User <user@example.com>, Another User <anotheruser@example.com>
|
||||
* @param $language
|
||||
* Language object to use to compose the e-mail.
|
||||
* @param $params
|
||||
* Optional parameters to build the e-mail.
|
||||
* @param $from
|
||||
* Sets From to this value, if given.
|
||||
* @param $send
|
||||
* Send the message directly, without calling drupal_mail_send() manually.
|
||||
*
|
||||
* @return
|
||||
* The $message array structure containing all details of the
|
||||
* message. If already sent ($send = TRUE), then the 'result' element
|
||||
* will contain the success indicator of the e-mail, failure being already
|
||||
* written to the watchdog. (Success means nothing more than the message being
|
||||
* accepted at php-level, which still doesn't guarantee it to be delivered.)
|
||||
*/
|
||||
function drupal_mail($module, $key, $to, $language, $params = array(), $from = NULL, $send = TRUE) {
|
||||
$default_from = variable_get('site_mail', ini_get('sendmail_from'));
|
||||
|
||||
// Bundle up the variables into a structured array for altering.
|
||||
$message = array(
|
||||
'id' => $module .'_'. $key,
|
||||
'to' => $to,
|
||||
'from' => isset($from) ? $from : $default_from,
|
||||
'language' => $language,
|
||||
'params' => $params,
|
||||
'subject' => '',
|
||||
'body' => array()
|
||||
);
|
||||
|
||||
// Build the default headers
|
||||
$headers = array(
|
||||
'MIME-Version' => '1.0',
|
||||
'Content-Type' => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
|
||||
'Content-Transfer-Encoding' => '8Bit',
|
||||
'X-Mailer' => 'Drupal'
|
||||
);
|
||||
if ($default_from) {
|
||||
// To prevent e-mail from looking like spam, the addresses in the Sender and
|
||||
// Return-Path headers should have a domain authorized to use the originating
|
||||
// SMTP server. Errors-To is redundant, but shouldn't hurt.
|
||||
$headers['From'] = $headers['Sender'] = $headers['Return-Path'] = $headers['Errors-To'] = $default_from;
|
||||
}
|
||||
if ($from) {
|
||||
$headers['From'] = $from;
|
||||
}
|
||||
$message['headers'] = $headers;
|
||||
|
||||
// Build the e-mail (get subject and body, allow additional headers) by
|
||||
// invoking hook_mail() on this module. We cannot use module_invoke() as
|
||||
// we need to have $message by reference in hook_mail().
|
||||
if (function_exists($function = $module .'_mail')) {
|
||||
$function($key, $message, $params);
|
||||
}
|
||||
|
||||
// Invoke hook_mail_alter() to allow all modules to alter the resulting e-mail.
|
||||
drupal_alter('mail', $message);
|
||||
|
||||
// Concatenate and wrap the e-mail body.
|
||||
$message['body'] = is_array($message['body']) ? drupal_wrap_mail(implode("\n\n", $message['body'])) : drupal_wrap_mail($message['body']);
|
||||
|
||||
// Optionally send e-mail.
|
||||
if ($send) {
|
||||
$message['result'] = drupal_mail_send($message);
|
||||
|
||||
// Log errors
|
||||
if (!$message['result']) {
|
||||
watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
|
||||
drupal_set_message(t('Unable to send e-mail. Please contact the site administrator if the problem persists.'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an e-mail message, using Drupal variables and default settings.
|
||||
* More information in the <a href="http://php.net/manual/en/function.mail.php">
|
||||
* PHP function reference for mail()</a>. See drupal_mail() for information on
|
||||
* how $message is composed.
|
||||
*
|
||||
* @param $message
|
||||
* Message array with at least the following elements:
|
||||
* - id: A unique identifier of the e-mail type. Examples:
|
||||
* 'contact_user_copy', 'user_password_reset'.
|
||||
* - to: The mail address or addresses where the message will be sent to. The
|
||||
* formatting of this string must comply with
|
||||
* @link http://tools.ietf.org/html/rfc5322 RFC 5322 @endlink.
|
||||
* Some examples are:
|
||||
* - user@example.com
|
||||
* - user@example.com, anotheruser@example.com
|
||||
* - User <user@example.com>
|
||||
* - User <user@example.com>, Another User <anotheruser@example.com>
|
||||
* - subject: Subject of the e-mail to be sent. This must not contain any
|
||||
* newline characters, or the mail may not be sent properly.
|
||||
* - body: Message to be sent. Accepts both CRLF and LF line-endings.
|
||||
* E-mail bodies must be wrapped. You can use drupal_wrap_mail() for
|
||||
* smart plain text wrapping.
|
||||
* - headers: Associative array containing all mail headers.
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the mail was successfully accepted for delivery,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
function drupal_mail_send($message) {
|
||||
// Allow for a custom mail backend.
|
||||
if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) {
|
||||
include_once './'. variable_get('smtp_library', '');
|
||||
return drupal_mail_wrapper($message);
|
||||
}
|
||||
else {
|
||||
$mimeheaders = array();
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
$mimeheaders[] = $name .': '. mime_header_encode($value);
|
||||
}
|
||||
return mail(
|
||||
$message['to'],
|
||||
mime_header_encode($message['subject']),
|
||||
// Note: e-mail uses CRLF for line-endings, but PHP's API requires LF.
|
||||
// They will appear correctly in the actual e-mail that is sent.
|
||||
str_replace("\r", '', $message['body']),
|
||||
// For headers, PHP's API suggests that we use CRLF normally,
|
||||
// but some MTAs incorrecly replace LF with CRLF. See #234403.
|
||||
join("\n", $mimeheaders)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform format=flowed soft wrapping for mail (RFC 3676).
|
||||
*
|
||||
* We use delsp=yes wrapping, but only break non-spaced languages when
|
||||
* absolutely necessary to avoid compatibility issues.
|
||||
*
|
||||
* We deliberately use LF rather than CRLF, see drupal_mail().
|
||||
*
|
||||
* @param $text
|
||||
* The plain text to process.
|
||||
* @param $indent (optional)
|
||||
* A string to indent the text with. Only '>' characters are repeated on
|
||||
* subsequent wrapped lines. Others are replaced by spaces.
|
||||
*/
|
||||
function drupal_wrap_mail($text, $indent = '') {
|
||||
// Convert CRLF into LF.
|
||||
$text = str_replace("\r", '', $text);
|
||||
// See if soft-wrapping is allowed.
|
||||
$clean_indent = _drupal_html_to_text_clean($indent);
|
||||
$soft = strpos($clean_indent, ' ') === FALSE;
|
||||
// Check if the string has line breaks.
|
||||
if (strpos($text, "\n") !== FALSE) {
|
||||
// Remove trailing spaces to make existing breaks hard.
|
||||
$text = preg_replace('/ +\n/m', "\n", $text);
|
||||
// Wrap each line at the needed width.
|
||||
$lines = explode("\n", $text);
|
||||
array_walk($lines, '_drupal_wrap_mail_line', array('soft' => $soft, 'length' => strlen($indent)));
|
||||
$text = implode("\n", $lines);
|
||||
}
|
||||
else {
|
||||
// Wrap this line.
|
||||
_drupal_wrap_mail_line($text, 0, array('soft' => $soft, 'length' => strlen($indent)));
|
||||
}
|
||||
// Empty lines with nothing but spaces.
|
||||
$text = preg_replace('/^ +\n/m', "\n", $text);
|
||||
// Space-stuff special lines.
|
||||
$text = preg_replace('/^(>| |From)/m', ' $1', $text);
|
||||
// Apply indentation. We only include non-'>' indentation on the first line.
|
||||
$text = $indent . substr(preg_replace('/^/m', $clean_indent, $text), strlen($indent));
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an HTML string into plain text, preserving the structure of the
|
||||
* markup. Useful for preparing the body of a node to be sent by e-mail.
|
||||
*
|
||||
* The output will be suitable for use as 'format=flowed; delsp=yes' text
|
||||
* (RFC 3676) and can be passed directly to drupal_mail() for sending.
|
||||
*
|
||||
* We deliberately use LF rather than CRLF, see drupal_mail().
|
||||
*
|
||||
* This function provides suitable alternatives for the following tags:
|
||||
* <a> <em> <i> <strong> <b> <br> <p> <blockquote> <ul> <ol> <li> <dl> <dt>
|
||||
* <dd> <h1> <h2> <h3> <h4> <h5> <h6> <hr>
|
||||
*
|
||||
* @param $string
|
||||
* The string to be transformed.
|
||||
* @param $allowed_tags (optional)
|
||||
* If supplied, a list of tags that will be transformed. If omitted, all
|
||||
* all supported tags are transformed.
|
||||
*
|
||||
* @return
|
||||
* The transformed string.
|
||||
*/
|
||||
function drupal_html_to_text($string, $allowed_tags = NULL) {
|
||||
// Cache list of supported tags.
|
||||
static $supported_tags;
|
||||
if (empty($supported_tags)) {
|
||||
$supported_tags = array('a', 'em', 'i', 'strong', 'b', 'br', 'p', 'blockquote', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr');
|
||||
}
|
||||
|
||||
// Make sure only supported tags are kept.
|
||||
$allowed_tags = isset($allowed_tags) ? array_intersect($supported_tags, $allowed_tags) : $supported_tags;
|
||||
|
||||
// Make sure tags, entities and attributes are well-formed and properly nested.
|
||||
$string = _filter_htmlcorrector(filter_xss($string, $allowed_tags));
|
||||
|
||||
// Apply inline styles.
|
||||
$string = preg_replace('!</?(em|i)((?> +)[^>]*)?>!i', '/', $string);
|
||||
$string = preg_replace('!</?(strong|b)((?> +)[^>]*)?>!i', '*', $string);
|
||||
|
||||
// Replace inline <a> tags with the text of link and a footnote.
|
||||
// 'See <a href="http://drupal.org">the Drupal site</a>' becomes
|
||||
// 'See the Drupal site [1]' with the URL included as a footnote.
|
||||
_drupal_html_to_mail_urls(NULL, TRUE);
|
||||
$pattern = '@(<a[^>]+?href="([^"]*)"[^>]*?>(.+?)</a>)@i';
|
||||
$string = preg_replace_callback($pattern, '_drupal_html_to_mail_urls', $string);
|
||||
$urls = _drupal_html_to_mail_urls();
|
||||
$footnotes = '';
|
||||
if (count($urls)) {
|
||||
$footnotes .= "\n";
|
||||
for ($i = 0, $max = count($urls); $i < $max; $i++) {
|
||||
$footnotes .= '['. ($i + 1) .'] '. $urls[$i] ."\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Split tags from text.
|
||||
$split = preg_split('/<([^>]+?)>/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
// Note: PHP ensures the array consists of alternating delimiters and literals
|
||||
// and begins and ends with a literal (inserting $null as required).
|
||||
|
||||
$tag = FALSE; // Odd/even counter (tag or no tag)
|
||||
$casing = NULL; // Case conversion function
|
||||
$output = '';
|
||||
$indent = array(); // All current indentation string chunks
|
||||
$lists = array(); // Array of counters for opened lists
|
||||
foreach ($split as $value) {
|
||||
$chunk = NULL; // Holds a string ready to be formatted and output.
|
||||
|
||||
// Process HTML tags (but don't output any literally).
|
||||
if ($tag) {
|
||||
list($tagname) = explode(' ', strtolower($value), 2);
|
||||
switch ($tagname) {
|
||||
// List counters
|
||||
case 'ul':
|
||||
array_unshift($lists, '*');
|
||||
break;
|
||||
case 'ol':
|
||||
array_unshift($lists, 1);
|
||||
break;
|
||||
case '/ul':
|
||||
case '/ol':
|
||||
array_shift($lists);
|
||||
$chunk = ''; // Ensure blank new-line.
|
||||
break;
|
||||
|
||||
// Quotation/list markers, non-fancy headers
|
||||
case 'blockquote':
|
||||
// Format=flowed indentation cannot be mixed with lists.
|
||||
$indent[] = count($lists) ? ' "' : '>';
|
||||
break;
|
||||
case 'li':
|
||||
$indent[] = is_numeric($lists[0]) ? ' '. $lists[0]++ .') ' : ' * ';
|
||||
break;
|
||||
case 'dd':
|
||||
$indent[] = ' ';
|
||||
break;
|
||||
case 'h3':
|
||||
$indent[] = '.... ';
|
||||
break;
|
||||
case 'h4':
|
||||
$indent[] = '.. ';
|
||||
break;
|
||||
case '/blockquote':
|
||||
if (count($lists)) {
|
||||
// Append closing quote for inline quotes (immediately).
|
||||
$output = rtrim($output, "> \n") ."\"\n";
|
||||
$chunk = ''; // Ensure blank new-line.
|
||||
}
|
||||
// Fall-through
|
||||
case '/li':
|
||||
case '/dd':
|
||||
array_pop($indent);
|
||||
break;
|
||||
case '/h3':
|
||||
case '/h4':
|
||||
array_pop($indent);
|
||||
case '/h5':
|
||||
case '/h6':
|
||||
$chunk = ''; // Ensure blank new-line.
|
||||
break;
|
||||
|
||||
// Fancy headers
|
||||
case 'h1':
|
||||
$indent[] = '======== ';
|
||||
$casing = 'drupal_strtoupper';
|
||||
break;
|
||||
case 'h2':
|
||||
$indent[] = '-------- ';
|
||||
$casing = 'drupal_strtoupper';
|
||||
break;
|
||||
case '/h1':
|
||||
case '/h2':
|
||||
$casing = NULL;
|
||||
// Pad the line with dashes.
|
||||
$output = _drupal_html_to_text_pad($output, ($tagname == '/h1') ? '=' : '-', ' ');
|
||||
array_pop($indent);
|
||||
$chunk = ''; // Ensure blank new-line.
|
||||
break;
|
||||
|
||||
// Horizontal rulers
|
||||
case 'hr':
|
||||
// Insert immediately.
|
||||
$output .= drupal_wrap_mail('', implode('', $indent)) ."\n";
|
||||
$output = _drupal_html_to_text_pad($output, '-');
|
||||
break;
|
||||
|
||||
// Paragraphs and definition lists
|
||||
case '/p':
|
||||
case '/dl':
|
||||
$chunk = ''; // Ensure blank new-line.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Process blocks of text.
|
||||
else {
|
||||
// Convert inline HTML text to plain text.
|
||||
$value = trim(preg_replace('/\s+/', ' ', decode_entities($value)));
|
||||
if (strlen($value)) {
|
||||
$chunk = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// See if there is something waiting to be output.
|
||||
if (isset($chunk)) {
|
||||
// Apply any necessary case conversion.
|
||||
if (isset($casing)) {
|
||||
$chunk = $casing($chunk);
|
||||
}
|
||||
// Format it and apply the current indentation.
|
||||
$output .= drupal_wrap_mail($chunk, implode('', $indent)) ."\n";
|
||||
// Remove non-quotation markers from indentation.
|
||||
$indent = array_map('_drupal_html_to_text_clean', $indent);
|
||||
}
|
||||
|
||||
$tag = !$tag;
|
||||
}
|
||||
|
||||
return $output . $footnotes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for array_walk in drupal_wrap_mail().
|
||||
*
|
||||
* Wraps words on a single line.
|
||||
*/
|
||||
function _drupal_wrap_mail_line(&$line, $key, $values) {
|
||||
// Use soft-breaks only for purely quoted or unindented text.
|
||||
$line = wordwrap($line, 77 - $values['length'], $values['soft'] ? " \n" : "\n");
|
||||
// Break really long words at the maximum width allowed.
|
||||
$line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for drupal_html_to_text().
|
||||
*
|
||||
* Keeps track of URLs and replaces them with placeholder tokens.
|
||||
*/
|
||||
function _drupal_html_to_mail_urls($match = NULL, $reset = FALSE) {
|
||||
global $base_url, $base_path;
|
||||
static $urls = array(), $regexp;
|
||||
|
||||
if ($reset) {
|
||||
// Reset internal URL list.
|
||||
$urls = array();
|
||||
}
|
||||
else {
|
||||
if (empty($regexp)) {
|
||||
$regexp = '@^'. preg_quote($base_path, '@') .'@';
|
||||
}
|
||||
if ($match) {
|
||||
list(, , $url, $label) = $match;
|
||||
// Ensure all URLs are absolute.
|
||||
$urls[] = strpos($url, '://') ? $url : preg_replace($regexp, $base_url .'/', $url);
|
||||
return $label .' ['. count($urls) .']';
|
||||
}
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for drupal_wrap_mail() and drupal_html_to_text().
|
||||
*
|
||||
* Replace all non-quotation markers from a given piece of indentation with spaces.
|
||||
*/
|
||||
function _drupal_html_to_text_clean($indent) {
|
||||
return preg_replace('/[^>]/', ' ', $indent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for drupal_html_to_text().
|
||||
*
|
||||
* Pad the last line with the given character.
|
||||
*/
|
||||
function _drupal_html_to_text_pad($text, $pad, $prefix = '') {
|
||||
// Remove last line break.
|
||||
$text = substr($text, 0, -1);
|
||||
// Calculate needed padding space and add it.
|
||||
if (($p = strrpos($text, "\n")) === FALSE) {
|
||||
$p = -1;
|
||||
}
|
||||
$n = max(0, 79 - (strlen($text) - $p) - strlen($prefix));
|
||||
// Add prefix and padding, and restore linebreak.
|
||||
return $text . $prefix . str_repeat($pad, $n) ."\n";
|
||||
}
|
||||
2543
includes/menu.inc
Normal file
2543
includes/menu.inc
Normal file
File diff suppressed because it is too large
Load diff
518
includes/module.inc
Normal file
518
includes/module.inc
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for loading and interacting with Drupal modules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Load all the modules that have been enabled in the system table.
|
||||
*/
|
||||
function module_load_all() {
|
||||
foreach (module_list(TRUE, FALSE) as $module) {
|
||||
drupal_load('module', $module);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function repeatedly with each module in turn as an argument.
|
||||
*/
|
||||
function module_iterate($function, $argument = '') {
|
||||
foreach (module_list() as $name) {
|
||||
$function($name, $argument);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a list of all loaded modules. During the bootstrap, return only
|
||||
* vital modules. See bootstrap.inc
|
||||
*
|
||||
* @param $refresh
|
||||
* Whether to force the module list to be regenerated (such as after the
|
||||
* administrator has changed the system settings).
|
||||
* @param $bootstrap
|
||||
* Whether to return the reduced set of modules loaded in "bootstrap mode"
|
||||
* for cached pages. See bootstrap.inc.
|
||||
* @param $sort
|
||||
* By default, modules are ordered by weight and filename, settings this option
|
||||
* to TRUE, module list will be ordered by module name.
|
||||
* @param $fixed_list
|
||||
* (Optional) Override the module list with the given modules. Stays until the
|
||||
* next call with $refresh = TRUE.
|
||||
* @return
|
||||
* An associative array whose keys and values are the names of all loaded
|
||||
* modules.
|
||||
*/
|
||||
function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE, $fixed_list = NULL) {
|
||||
static $list, $sorted_list;
|
||||
|
||||
if ($refresh || $fixed_list) {
|
||||
$list = array();
|
||||
$sorted_list = NULL;
|
||||
if ($fixed_list) {
|
||||
foreach ($fixed_list as $name => $module) {
|
||||
drupal_get_filename('module', $name, $module['filename']);
|
||||
$list[$name] = $name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($bootstrap) {
|
||||
$result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC");
|
||||
}
|
||||
else {
|
||||
$result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
|
||||
}
|
||||
while ($module = db_fetch_object($result)) {
|
||||
if (file_exists($module->filename)) {
|
||||
// Determine the current throttle status and see if the module should be
|
||||
// loaded based on server load. We have to directly access the throttle
|
||||
// variables, since throttle.module may not be loaded yet.
|
||||
$throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
|
||||
if (!$throttle) {
|
||||
drupal_get_filename('module', $module->name, $module->filename);
|
||||
$list[$module->name] = $module->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($sort) {
|
||||
if (!isset($sorted_list)) {
|
||||
$sorted_list = $list;
|
||||
ksort($sorted_list);
|
||||
}
|
||||
return $sorted_list;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the database cache of module files.
|
||||
*
|
||||
* @return
|
||||
* The array of filesystem objects used to rebuild the cache.
|
||||
*/
|
||||
function module_rebuild_cache() {
|
||||
$write_database = TRUE;
|
||||
// If lock not acquired, return $files data without writing to database.
|
||||
if (!lock_acquire('module_rebuild_cache')) {
|
||||
$write_database = FALSE;
|
||||
// Wait for the parallel thread to be done so we are more likely
|
||||
// to get updated and consistent data.
|
||||
lock_wait('module_rebuild_cache');
|
||||
}
|
||||
// Get current list of modules
|
||||
$files = drupal_system_listing('\.module$', 'modules', 'name', 0);
|
||||
|
||||
// Extract current files from database.
|
||||
system_get_files_database($files, 'module');
|
||||
|
||||
ksort($files);
|
||||
|
||||
// Set defaults for module info
|
||||
$defaults = array(
|
||||
'dependencies' => array(),
|
||||
'dependents' => array(),
|
||||
'description' => '',
|
||||
'version' => NULL,
|
||||
'php' => DRUPAL_MINIMUM_PHP,
|
||||
);
|
||||
|
||||
foreach ($files as $filename => $file) {
|
||||
// Look for the info file.
|
||||
$file->info = drupal_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
|
||||
|
||||
// Skip modules that don't provide info.
|
||||
if (empty($file->info)) {
|
||||
unset($files[$filename]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Invoke hook_system_info_alter() to give installed modules a chance to
|
||||
// modify the data in the .info files if necessary.
|
||||
drupal_alter('system_info', $files[$filename]->info, $files[$filename]);
|
||||
|
||||
// Merge in defaults and save.
|
||||
$files[$filename]->info = $file->info + $defaults;
|
||||
}
|
||||
|
||||
// If lock not acquired, return $files data without writing to database.
|
||||
if ($write_database) {
|
||||
foreach ($files as $filename => $file) {
|
||||
// Log the critical hooks implemented by this module.
|
||||
$bootstrap = 0;
|
||||
foreach (bootstrap_hooks() as $hook) {
|
||||
if (module_hook($file->name, $hook)) {
|
||||
$bootstrap = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the contents of the system table:
|
||||
if (isset($file->status)) {
|
||||
db_query("UPDATE {system} SET info = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", serialize($files[$filename]->info), $file->name, $file->filename, $bootstrap, $file->old_filename);
|
||||
}
|
||||
else {
|
||||
// This is a new module.
|
||||
$files[$filename]->status = 0;
|
||||
$files[$filename]->throttle = 0;
|
||||
db_query("INSERT INTO {system} (name, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, serialize($files[$filename]->info), 'module', $file->filename, 0, 0, $bootstrap);
|
||||
}
|
||||
}
|
||||
lock_release('module_rebuild_cache');
|
||||
}
|
||||
$files = _module_build_dependencies($files);
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find dependencies any level deep and fill in dependents information too.
|
||||
*
|
||||
* If module A depends on B which in turn depends on C then this function will
|
||||
* add C to the list of modules A depends on. This will be repeated until
|
||||
* module A has a list of all modules it depends on. If it depends on itself,
|
||||
* called a circular dependency, that's marked by adding a nonexistent module,
|
||||
* called -circular- to this list of modules. Because this does not exist,
|
||||
* it'll be impossible to switch module A on.
|
||||
*
|
||||
* Also we fill in a dependents array in $file->info. Using the names above,
|
||||
* the dependents array of module B lists A.
|
||||
*
|
||||
* @param $files
|
||||
* The array of filesystem objects used to rebuild the cache.
|
||||
* @return
|
||||
* The same array with dependencies and dependents added where applicable.
|
||||
*/
|
||||
function _module_build_dependencies($files) {
|
||||
do {
|
||||
$new_dependency = FALSE;
|
||||
foreach ($files as $filename => $file) {
|
||||
// We will modify this object (module A, see doxygen for module A, B, C).
|
||||
$file = &$files[$filename];
|
||||
if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
|
||||
foreach ($file->info['dependencies'] as $dependency_name) {
|
||||
// This is a nonexistent module.
|
||||
if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) {
|
||||
continue;
|
||||
}
|
||||
// $dependency_name is module B (again, see doxygen).
|
||||
$files[$dependency_name]->info['dependents'][$filename] = $filename;
|
||||
$dependency = $files[$dependency_name];
|
||||
if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) {
|
||||
// Let's find possible C modules.
|
||||
foreach ($dependency->info['dependencies'] as $candidate) {
|
||||
if (array_search($candidate, $file->info['dependencies']) === FALSE) {
|
||||
// Is this a circular dependency?
|
||||
if ($candidate == $filename) {
|
||||
// As a module name can not contain dashes, this makes
|
||||
// impossible to switch on the module.
|
||||
$candidate = '-circular-';
|
||||
// Do not display the message or add -circular- more than once.
|
||||
if (array_search($candidate, $file->info['dependencies']) !== FALSE) {
|
||||
continue;
|
||||
}
|
||||
drupal_set_message(t('%module is part of a circular dependency. This is not supported and you will not be able to switch it on.', array('%module' => $file->info['name'])), 'error');
|
||||
}
|
||||
else {
|
||||
// We added a new dependency to module A. The next loop will
|
||||
// be able to use this as "B module" thus finding even
|
||||
// deeper dependencies.
|
||||
$new_dependency = TRUE;
|
||||
}
|
||||
$file->info['dependencies'][] = $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't forget to break the reference.
|
||||
unset($file);
|
||||
}
|
||||
} while ($new_dependency);
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a given module exists.
|
||||
*
|
||||
* @param $module
|
||||
* The name of the module (without the .module extension).
|
||||
* @return
|
||||
* TRUE if the module is both installed and enabled.
|
||||
*/
|
||||
function module_exists($module) {
|
||||
$list = module_list();
|
||||
return array_key_exists($module, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a module's installation hooks.
|
||||
*/
|
||||
function module_load_install($module) {
|
||||
// Make sure the installation API is available
|
||||
include_once './includes/install.inc';
|
||||
|
||||
module_load_include('install', $module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a module include file.
|
||||
*
|
||||
* Examples:
|
||||
* @code
|
||||
* // Load node.admin.inc from the node module.
|
||||
* module_load_include('inc', 'node', 'node.admin');
|
||||
* // Load content_types.inc from the node module.
|
||||
* module_load_include('inc', 'node', 'content_types');
|
||||
* @endcode
|
||||
*
|
||||
* Do not use this function to load an install file. Use module_load_install()
|
||||
* instead.
|
||||
*
|
||||
* @param $type
|
||||
* The include file's type (file extension).
|
||||
* @param $module
|
||||
* The module to which the include file belongs.
|
||||
* @param $name
|
||||
* Optionally, specify the base file name (without the $type extension).
|
||||
* If not set, $module is used.
|
||||
*/
|
||||
function module_load_include($type, $module, $name | ||||