Compare commits

..
1 Commits
Author SHA1 Message Date
bachy b20b38f514 first import
Signed-off-by: bachy <git@g-u-i.net>
2013-01-09 10:53:26 +01:00
1576 changed files with 76549 additions and 310538 deletions
-13
View File
@@ -1,13 +0,0 @@
# Ignore configuration files that may contain sensitive information.
sites/*/settings*.php
# Ignore paths that contain user-generated content.
sites/*
#sites/*/files
#sites/*/private
_base_d7.tmproj
/_materio.sublime-project
/_materio.sublime-workspace
/_solr.sublime-project
/_solr.sublime-workspace
-236
View File
@@ -1,236 +0,0 @@
#
# Apache/PHP/Drupal settings:
#
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$">
Order allow,deny
</FilesMatch>
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php
# Set the default handler.
DirectoryIndex index.php index.html index.htm
# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_initialize_variables() in
# includes/bootstrap.inc for settings that can be changed at runtime.
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_flag magic_quotes_gpc off
php_flag magic_quotes_sybase off
php_flag register_globals off
php_flag session.auto_start off
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_flag mbstring.encoding_translation off
</IfModule>
# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
<FilesMatch \.php$>
# Do not allow PHP scripts to be cached unless they explicitly send cache
# headers themselves. Otherwise all scripts would have to overwrite the
# headers set by mod_expires if they want another caching behavior. This may
# fail if an error occurs early in the bootstrap process, and it may cause
# problems if a non-Drupal PHP file is installed in a subdirectory.
ExpiresActive Off
</FilesMatch>
</IfModule>
# Various rewrite rules.
<IfModule mod_rewrite.c>
RewriteEngine on
# Block access to "hidden" directories whose names begin with a period. This
# includes directories used by version control systems such as Subversion or
# Git to store control files. Files whose names begin with a period, as well
# as the control files used by CVS, are protected by the FilesMatch directive
# above.
#
# NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
# not possible to block access to entire directories from .htaccess, because
# <DirectoryMatch> is not allowed here.
#
# If you do not have mod_rewrite installed, you should remove these
# directories from your webroot or otherwise protect them from being
# downloaded.
RewriteRule "(^|/)\." - [F]
# custom
RewriteCond %{HTTP_HOST} ^basebeta\.materio\.com [OR]
RewriteCond %{HTTP_HOST} ^materio\.com [OR]
RewriteCond %{HTTP_HOST} ^www\.materio\.fr [OR]
RewriteCond %{HTTP_HOST} ^materio\.fr [OR]
RewriteCond %{HTTP_HOST} ^www\.materio\.net [OR]
RewriteCond %{HTTP_HOST} ^materio\.net [OR]
RewriteCond %{HTTP_HOST} ^www\.materio\.eu [OR]
RewriteCond %{HTTP_HOST} ^materio\.eu [NC]
RewriteRule ^ http://www.materio.com%{REQUEST_URI} [L,R=301]
# If your site can be accessed both with and without the 'www.' prefix, you
# can use one of the following settings to redirect users to your preferred
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
#
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# uncomment the following:
# RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
# VirtualDocumentRoot and the rewrite rules are not working properly.
# For example if your site is at http://example.com/drupal uncomment and
# modify the following line:
# RewriteBase /drupal
#
# If your site is running in a VirtualDocumentRoot at http://example.com/,
# uncomment the following line:
# RewriteBase /
# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^ index.php [L]
# Rules to correctly serve gzip compressed CSS and JS files.
# Requires both mod_rewrite and mod_headers to be enabled.
<IfModule mod_headers.c>
# Serve gzip compressed CSS files if they exist and the client accepts gzip.
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
# Serve gzip compressed JS files if they exist and the client accepts gzip.
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
# Serve correct content types, and prevent mod_deflate double gzip.
RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1]
<FilesMatch "(\.js\.gz|\.css\.gz)$">
# Serve correct encoding type.
Header set Content-Encoding gzip
# Force proxies to cache gzipped & non-gzipped css/js files separately.
Header append Vary Accept-Encoding
</FilesMatch>
</IfModule>
</IfModule>
# FROM Html5 BoilerPlate 4.0
# ----------------------------------------------------------------------
# Proper MIME type for all files
# ----------------------------------------------------------------------
# JavaScript
# Normalize to standard type (it's sniffed in IE anyways)
# tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript js jsonp
AddType application/json json
# Audio
AddType audio/ogg oga ogg
AddType audio/mp4 m4a f4a f4b
# Video
AddType video/ogg ogv
AddType video/mp4 mp4 m4v f4v f4p
AddType video/webm webm
AddType video/x-flv flv
# SVG
# Required for svg webfonts on iPad
# twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
# Webfonts
AddType application/vnd.ms-fontobject eot
AddType application/x-font-ttf ttf ttc
AddType font/opentype otf
AddType application/x-font-woff woff
# Assorted types
AddType image/x-icon ico
AddType image/webp webp
AddType text/cache-manifest appcache manifest
AddType text/x-component htc
AddType application/xml rss atom xml rdf
AddType application/x-chrome-extension crx
AddType application/x-opera-extension oex
AddType application/x-xpinstall xpi
AddType application/octet-stream safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard vcf
AddType application/x-shockwave-flash swf
AddType text/vtt vtt
# ----------------------------------------------------------------------
# UTF-8 encoding
# ----------------------------------------------------------------------
# Use UTF-8 encoding for anything served text/plain or text/html
AddDefaultCharset utf-8
# Force UTF-8 for a number of file formats
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml
# ----------------------------------------------------------------------
# A little more security
# ----------------------------------------------------------------------
# Block access to "hidden" directories or files whose names begin with a
# period. This includes directories used by version control systems such as
# Subversion or Git.
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
# Block access to backup and source files. These files may be left by some
# text/html editors and pose a great security danger, when anyone can access
# them.
<FilesMatch "(\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
# Increase cookie security
<IfModule php5_module>
php_value session.cookie_httponly true
</IfModule>
@@ -1,155 +0,0 @@
diff --git a/modules/user/user.module b/modules/user/user.module
index 47ac642..c204f6d 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2286,14 +2286,26 @@ function user_external_login_register($name, $module) {
*
* @param object $account
* An object containing the user account.
+ * @param array $options
+ * (optional) A keyed array of settings. Supported options are:
+ * - langcode: A language code to be used when generating locale-sensitive
+ * urls. If langcode is NULL the users preferred language is used.
*
* @return
* A unique URL that provides a one-time log in for the user, from which
* they can change their password.
*/
-function user_pass_reset_url($account) {
+function user_pass_reset_url($account, $options = array()) {
$timestamp = REQUEST_TIME;
- return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
+ $url_options = array('absolute' => TRUE);
+ if (isset($options['langcode'])) {
+ $languages = language_list();
+ $url_options['language'] = $languages[$options['langcode']];
+ }
+ else {
+ $url_options['language'] = user_preferred_language($account);
+ }
+ return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), $url_options);
}
/**
@@ -2305,6 +2317,10 @@ function user_pass_reset_url($account) {
* - uid: The user uid number.
* - pass: The hashed user password string.
* - login: The user login name.
+ * @param array $options
+ * (optional) A keyed array of settings. Supported options are:
+ * - langcode: A language code to be used when generating locale-sensitive
+ * urls. If langcode is NULL the users preferred language is used.
*
* @return
* A unique URL that may be used to confirm the cancellation of the user
@@ -2313,9 +2329,17 @@ function user_pass_reset_url($account) {
* @see user_mail_tokens()
* @see user_cancel_confirm()
*/
-function user_cancel_url($account) {
+function user_cancel_url($account, $options = array()) {
$timestamp = REQUEST_TIME;
- return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
+ $url_options = array('absolute' => TRUE);
+ if (isset($options['langcode'])) {
+ $languages = language_list();
+ $url_options['language'] = $languages[$options['langcode']];
+ }
+ else {
+ $url_options['language'] = user_preferred_language($account);
+ }
+ return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), $url_options);
}
/**
@@ -2752,7 +2776,7 @@ Your account on [site:name] has been canceled.
if ($replace) {
// We do not sanitize the token replacement, since the output of this
// replacement is intended for an e-mail message, not a web browser.
- return token_replace($text, $variables, array('language' => $language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+ return token_replace($text, $variables, array('langcode' => $langcode, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
}
return $text;
@@ -2779,8 +2803,8 @@ Your account on [site:name] has been canceled.
*/
function user_mail_tokens(&$replacements, $data, $options) {
if (isset($data['user'])) {
- $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']);
- $replacements['[user:cancel-url]'] = user_cancel_url($data['user']);
+ $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user'], $options);
+ $replacements['[user:cancel-url]'] = user_cancel_url($data['user'], $options);
}
}
diff --git a/modules/user/user.test b/modules/user/user.test
index b53db07..0417642 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -2012,6 +2012,26 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
);
}
+ public function setUp() {
+ parent::setUp('locale');
+
+ $account = $this->drupalCreateUser(array('access administration pages', 'administer languages'));
+ $this->drupalLogin($account);
+
+ // Add language.
+ $edit = array('langcode' => 'de');
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Enable URL language detection and selection.
+ $edit = array('language[enabled][locale-url]' => 1);
+ $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
+
+ // Reset static caching.
+ drupal_static_reset('language_list');
+ drupal_static_reset('locale_url_outbound_alter');
+ drupal_static_reset('locale_language_url_rewrite_url');
+ }
+
/**
* Creates a user, then tests the tokens generated from it.
*/
@@ -2062,6 +2082,39 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
$output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
$this->assertEqual($output, $expected, t('Unsanitized user token %token replaced.', array('%token' => $input)));
}
+
+ $languages = language_list();
+
+ // Generate login and cancel link.
+ $tests = array();
+ $tests['[user:one-time-login-url]'] = user_pass_reset_url($account);
+ $tests['[user:cancel-url]'] = user_cancel_url($account);
+
+ // Generate tokens with interface language.
+ $link = url('user', array('absolute' => TRUE));
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('user' => $account), array('langcode' => $language->language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+ $this->assertTrue(strpos($output, $link) === 0, 'Generated URL is in interface language.');
+ }
+
+ // Generate tokens with the user's preferred language.
+ $edit['language'] = 'de';
+ $account = user_save($account, $edit);
+ $link = url('user', array('language' => $languages[$account->language], 'absolute' => TRUE));
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('user' => $account), array('callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+ $this->assertTrue(strpos($output, $link) === 0, "Generated URL is in the user's preferred language.");
+ }
+
+ // Generate tokens with one specific language.
+ $link = url('user', array('language' => $languages['de'], 'absolute' => TRUE));
+ foreach ($tests as $input => $expected) {
+ foreach (array($user1, $user2) as $account) {
+ $output = token_replace($input, array('user' => $account), array('langcode' => 'de', 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE));
+ $this->assertTrue(strpos($output, $link) === 0, "Generated URL in in the requested language.");
+ }
+ }
+
}
}
-1648
View File
File diff suppressed because it is too large Load Diff
-44
View File
@@ -1,44 +0,0 @@
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; either version 2 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program as the file LICENSE.txt; if not, please see
http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
Drupal is a registered trademark of Dries Buytaert.
Drupal includes works under other copyright notices and distributed
according to the terms of the GNU General Public License or a compatible
license, including:
Javascript
Farbtastic - Copyright (c) 2010 Matt Farina
jQuery - Copyright (c) 2010 John Resig
jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman
jQuery Cookie - Copyright (c) 2006 Klaus Hartl
jQuery Form - Copyright (c) 2010 Mike Alsup
jQuery Once - Copyright (c) 2009 Konstantin Kfer
jQuery UI - Copyright (c) 2010 by the original authors
(http://jqueryui.com/about)
Sizzle.js - Copyright (c) 2010 The Dojo Foundation (http://sizzlejs.com/)
PHP
ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet
+2
View File
@@ -0,0 +1,2 @@
Information about upgrading existing views from Drupal 6 to Drupal 7 is located
in the module's advanced help under api upgrading.
-42
View File
@@ -1,42 +0,0 @@
CREATE THE MySQL DATABASE
--------------------------
This step is only necessary if you don't already have a database set up (e.g.,
by your host). In the following examples, 'username' is an example MySQL user
which has the CREATE and GRANT privileges. Use the appropriate user name for
your system.
First, you must create a new database for your Drupal site (here, 'databasename'
is the name of the new database):
mysqladmin -u username -p create databasename
MySQL will prompt for the 'username' database password and then create the
initial database files. Next you must log in and set the access database rights:
mysql -u username -p
Again, you will be asked for the 'username' database password. At the MySQL
prompt, enter the following command:
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
ON databasename.*
TO 'username'@'localhost' IDENTIFIED BY 'password';
where
'databasename' is the name of your database
'username@localhost' is the username of your MySQL account
'password' is the password required for that username
Note: Unless your database user has all of the privileges listed above, you will
not be able to run Drupal.
If successful, MySQL will reply with:
Query OK, 0 rows affected
If the InnoDB storage engine is available, it will be used for all database
tables. InnoDB provides features over MyISAM such as transaction support,
row-level locks, and consistent non-locking reads.
-44
View File
@@ -1,44 +0,0 @@
CREATE THE PostgreSQL DATABASE
------------------------------
Note that the database must be created with UTF-8 (Unicode) encoding.
1. CREATE DATABASE USER
This step is only necessary if you don't already have a user set up (e.g., by
your host), or want to create a new user for use with Drupal only. The
following command creates a new user named 'username' and asks for a password
for that user:
createuser --pwprompt --encrypted --no-createrole --no-createdb username
If there are no errors, then the command was successful.
2. CREATE DRUPAL DATABASE
This step is only necessary if you don't already have a database set up
(e.g., by your host) or want to create a new database for use with Drupal
only. The following command creates a new database named 'databasename',
which is owned by the previously created 'username':
createdb --encoding=UTF8 --owner=username databasename
If there are no errors, then the command was successful.
3. CREATE SCHEMA OR SCHEMAS (Optional advanced step)
Drupal will run across different schemas within your database if you so wish.
By default, Drupal runs inside the 'public' schema but you can use $db_prefix
inside settings.php to define a schema for Drupal to run inside of, or
specify tables that are shared inside of a separate schema. Drupal will not
create schemas for you. In fact, the user that Drupal runs as should not be
allowed to do this. You'll need to execute the SQL below as a superuser,
replace 'username' with the username that Drupal uses to connect to
PostgreSQL, and replace 'schema_name' with a schema name you wish to use,
such as 'shared':
CREATE SCHEMA schema_name AUTHORIZATION username;
Do this for as many schemas as you need. See default.settings.php for
instructions on how to set which tables use which schemas.
-31
View File
@@ -1,31 +0,0 @@
SQLITE REQUIREMENTS
-------------------
To use SQLite with your Drupal installation, the following requirements must be
met: Server has PHP 5.2 or later with PDO, and the PDO SQLite driver must be
enabled.
SQLITE DATABASE CREATION
------------------------
The Drupal installer will create the SQLite database for you. The only
requirement is that the installer must have write permissions to the directory
where the database file resides. This directory (not just the database file) also
has to remain writeable by the web server going forward for SQLite to continue to
be able to operate.
On the "Database configuration" form in the "Database file" field, you must
supply the exact path to where you wish your database file to reside. It is
strongly suggested that you choose a path that is outside of the webroot, yet
ensure that the directory is writeable by the web server.
If you must place your database file in your webroot, you could try using the
following in your "Database file" field:
sites/default/files/.ht.sqlite
Note: The .ht in the name will tell Apache to prevent the database from being
downloaded. Please check that the file is, indeed, protected by your webserver.
If not, please consult the documentation of your webserver on how to protect a
file from downloading.
-398
View File
@@ -1,398 +0,0 @@
CONTENTS OF THIS FILE
---------------------
* Requirements and notes
* Optional server requirements
* Installation
* Building and customizing your site
* Multisite configuration
* More information
REQUIREMENTS AND NOTES
----------------------
Drupal requires:
- A web server. Apache (version 2.0 or greater) is recommended.
- PHP 5.2.4 (or greater) (http://www.php.net/).
- One of the following databases:
- MySQL 5.0.15 (or greater) (http://www.mysql.com/).
- MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully
compatible drop-in replacement for MySQL.
- PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
- SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
For more detailed information about Drupal requirements, including a list of
PHP extensions and configurations that are required, see "System requirements"
(http://drupal.org/requirements) in the Drupal.org online documentation.
For detailed information on how to configure a test server environment using a
variety of operating systems and web servers, see "Local server setup"
(http://drupal.org/node/157602) in the Drupal.org online documentation.
Note that all directories mentioned in this document are always relative to the
directory of your Drupal installation, and commands are meant to be run from
this directory (except for the initial commands that create that directory).
OPTIONAL SERVER REQUIREMENTS
----------------------------
- If you want to use Drupal's "Clean URLs" feature on an Apache web server, you
will need the mod_rewrite module and the ability to use local .htaccess
files. For Clean URLs support on IIS, see "Clean URLs with IIS"
(http://drupal.org/node/3854) in the Drupal.org online documentation.
- If you plan to use XML-based services such as RSS aggregation, you will need
PHP's XML extension. This extension is enabled by default on most PHP
installations.
- To serve gzip compressed CSS and JS files on an Apache web server, you will
need the mod_headers module and the ability to use local .htaccess files.
- Some Drupal functionality (e.g., checking whether Drupal and contributed
modules need updates, RSS aggregation, etc.) require that the web server be
able to go out to the web and download information. If you want to use this
functionality, you need to verify that your hosting provider or server
configuration allows the web server to initiate outbound connections. Most web
hosting setups allow this.
INSTALLATION
------------
1. Download and extract Drupal.
You can obtain the latest Drupal release from http://drupal.org -- the files
are available in .tar.gz and .zip formats and can be extracted using most
compression tools.
To download and extract the files, on a typical Unix/Linux command line, use
the following commands (assuming you want version x.y of Drupal in .tar.gz
format):
wget http://drupal.org/files/projects/drupal-x.y.tar.gz
tar -zxvf drupal-x.y.tar.gz
This will create a new directory drupal-x.y/ containing all Drupal files and
directories. Then, to move the contents of that directory into a directory
within your web server's document root or your public HTML directory,
continue with this command:
mv drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
2. Optionally, download a translation.
By default, Drupal is installed in English, and further languages may be
installed later. If you prefer to install Drupal in another language
initially:
- Download a translation file for the correct Drupal version and language
from the translation server: http://localize.drupal.org/translate/downloads
- Place the file into your installation profile's translations directory.
For instance, if you are using the Standard installation profile,
move the .po file into the directory:
profiles/standard/translations/
For detailed instructions, visit http://drupal.org/localize
3. Create the Drupal database.
Because Drupal stores all site information in a database, you must create
this database in order to install Drupal, and grant Drupal certain database
privileges (such as the ability to create tables). For details, consult
INSTALL.mysql.txt, INSTALL.pgsql.txt, or INSTALL.sqlite.txt. You may also
need to consult your web hosting provider for instructions specific to your
web host.
Take note of the username, password, database name, and hostname as you
create the database. You will enter this information during the install.
4. Run the install script.
To run the install script, point your browser to the base URL of your
website (e.g., http://www.example.com).
You will be guided through several screens to set up the database, add the
site maintenance account (the first user, also known as user/1), and provide
basic web site settings.
During installation, several files and directories need to be created, which
the install script will try to do automatically. However, on some hosting
environments, manual steps are required, and the install script will tell
you that it cannot proceed until you fix certain issues. This is normal and
does not indicate a problem with your server.
The most common steps you may need to perform are:
a. Missing files directory.
The install script will attempt to create a file storage directory in
the default location at sites/default/files (the location of the files
directory may be changed after Drupal is installed).
If auto-creation fails, you can make it work by changing permissions on
the sites/default directory so that the web server can create the files
directory within it for you. (If you are creating a multisite
installation, substitute the correct sites directory for sites/default;
see the Multisite Configuration section of this file, below.)
For example, on a Unix/Linux command line, you can grant everyone
(including the web server) permission to write to the sites/default
directory with this command:
chmod a+w sites/default
Be sure to set the permissions back after the installation is finished!
Sample command:
chmod go-w sites/default
Alternatively, instead of allowing the web server to create the files
directory for you as described above, you can create it yourself. Sample
commands from a Unix/Linux command line:
mkdir sites/default/files
chmod a+w sites/default/files
b. Missing settings file.
Drupal will try to automatically create a settings.php configuration file,
which is normally in the directory sites/default (to avoid problems when
upgrading, Drupal is not packaged with this file). If auto-creation fails,
you will need to create this file yourself, using the file
sites/default/default.settings.php as a template.
For example, on a Unix/Linux command line, you can make a copy of the
default.settings.php file with the command:
cp sites/default/default.settings.php sites/default/settings.php
Next, grant write privileges to the file to everyone (including the web
server) with the command:
chmod a+w sites/default/settings.php
Be sure to set the permissions back after the installation is finished!
Sample command:
chmod go-w sites/default/settings.php
c. Write permissions after install.
The install script will attempt to write-protect the settings.php file and
the sites/default directory after saving your configuration. If this
fails, you will be notified, and you can do it manually. Sample commands
from a Unix/Linux command line:
chmod go-w sites/default/settings.php
chmod go-w sites/default
5. Verify that the site is working.
When the install script finishes, you will be logged in with the site
maintenance account on a "Welcome" page. If the default Drupal theme is not
displaying properly and links on the page result in "Page Not Found" errors,
you may be experiencing problems with clean URLs. Visit
http://drupal.org/getting-started/clean-urls to troubleshoot.
6. Change file system storage settings (optional).
The files directory created in step 4 is the default file system path used to
store all uploaded files, as well as some temporary files created by
Drupal. After installation, you can modify the file system path to store
uploaded files in a different location.
It is not necessary to modify this path, but you may wish to change it if:
- Your site runs multiple Drupal installations from a single codebase (modify
the file system path of each installation to a different directory so that
uploads do not overlap between installations).
- Your site runs on a number of web servers behind a load balancer or reverse
proxy (modify the file system path on each server to point to a shared file
repository).
- You want to restrict access to uploaded files.
To modify the file system path:
a. Ensure that the new location for the path exists and is writable by the
web server. For example, to create a new directory named uploads and grant
write permissions, use the following commands on a Unix/Linux command
line:
mkdir uploads
chmod a+w uploads
b. Navigate to Administration > Configuration > Media > File system, and
enter the desired path. Note that if you want to use private file storage,
you need to first enter the path for private files and save the
configuration, and then change the "Default download method" setting and
save again.
Changing the file system path after files have been uploaded may cause
unexpected problems on an existing site. If you modify the file system path
on an existing site, remember to copy all files from the original location
to the new location.
7. Revoke documentation file permissions (optional).
Some administrators suggest making the documentation files, especially
CHANGELOG.txt, non-readable so that the exact version of Drupal you are
running is slightly more difficult to determine. If you wish to implement
this optional security measure, from a Unix/Linux command line you can use
the following command:
chmod a-r CHANGELOG.txt
Note that the example only affects CHANGELOG.txt. To completely hide all
documentation files from public view, repeat this command for each of the
Drupal documentation files in the installation directory, substituting the
name of each file for CHANGELOG.txt in the example.
For more information on setting file permissions, see "Modifying Linux,
Unix, and Mac file permissions" (http://drupal.org/node/202483) or
"Modifying Windows file permissions" (http://drupal.org/node/202491) in the
Drupal.org online documentation.
8. Set up independent "cron" maintenance jobs.
Many Drupal modules have tasks that must be run periodically, including the
Search module (building and updating the index used for keyword searching),
the Aggregator module (retrieving feeds from other sites), and the System
module (performing routine maintenance and pruning of database tables). These
tasks are known as "cron maintenance tasks", named after the Unix/Linux
"cron" utility.
When you install Drupal, its built-in cron feature is enabled, which
automatically runs the cron tasks periodically, triggered by people visiting
pages of your site. You can configure the built-in cron feature by navigating
to Administration > Configuration > System > Cron.
It is also possible to run the cron tasks independent of site visits; this is
recommended for most sites. To do this, you will need to set up an automated
process to visit the page cron.php on your site, which executes the cron
tasks.
The URL of the cron.php page requires a "cron key" to protect against
unauthorized access. Your site's cron key is automatically generated during
installation and is specific to your site. The full URL of the page, with the
cron key, is available in the "Cron maintenance tasks" section of the Status
report page at Administration > Reports > Status report.
As an example for how to set up this automated process, you can use the
crontab utility on Unix/Linux systems. The following crontab line uses the
wget command to visit the cron.php page, and runs each hour, on the hour:
0 * * * * wget -O - -q -t 1 http://example.com/cron.php?cron_key=YOURKEY
Replace the text "http://example.com/cron.php?cron_key=YOURKEY" in the
example with the full URL displayed under "Cron maintenance tasks" on the
"Status report" page.
More information about cron maintenance tasks is available at
http://drupal.org/cron, and sample cron shell scripts can be found in the
scripts/ directory. (Note that these scripts must be customized like the
above example, to add your site-specific cron key and domain name.)
BUILDING AND CUSTOMIZING YOUR SITE
----------------------------------
A new installation of Drupal defaults to a very basic configuration. To extend
your site, you use "modules" and "themes". A module is a plugin that adds
functionality to Drupal, while a theme changes the look of your site. The core
of Drupal provides several optional modules and themes, and you can download
more at http://drupal.org/project/modules and http://drupal.org/project/themes
Do not mix downloaded or custom modules and themes with Drupal's core modules
and themes. Drupal's modules and themes are located in the top-level modules and
themes directories, while the modules and themes you add to Drupal are normally
placed in the sites/all/modules and sites/all/themes directories. If you run a
multisite installation, you can also place modules and themes in the
site-specific directories -- see the Multisite Configuration section, below.
Never edit Drupal's core modules and themes; instead, use the hooks available in
the Drupal API. To modify the behavior of Drupal, develop a module as described
at http://drupal.org/developing/modules. To modify the look of Drupal, create a
subtheme as described at http://drupal.org/node/225125, or a completely new
theme as described at http://drupal.org/documentation/theme
MULTISITE CONFIGURATION
-----------------------
A single Drupal installation can host several Drupal-powered sites, each with
its own individual configuration.
Additional site configurations are created in subdirectories within the 'sites'
directory. Each subdirectory must have a 'settings.php' file, which specifies
the configuration settings. The easiest way to create additional sites is to
copy the 'default' directory and modify the 'settings.php' file as appropriate.
The new directory name is constructed from the site's URL. The configuration for
www.example.com could be in 'sites/example.com/settings.php' (note that 'www.'
should be omitted if users can access your site at http://example.com/).
Sites do not have to have a different domain. You can also use subdomains and
subdirectories for Drupal sites. For example, example.com, sub.example.com, and
sub.example.com/site3 can all be defined as independent Drupal sites. The setup
for a configuration such as this would look like the following:
sites/default/settings.php
sites/example.com/settings.php
sites/sub.example.com/settings.php
sites/sub.example.com.site3/settings.php
When searching for a site configuration (for example www.sub.example.com/site3),
Drupal will search for configuration files in the following order, using the
first configuration it finds:
sites/www.sub.example.com.site3/settings.php
sites/sub.example.com.site3/settings.php
sites/example.com.site3/settings.php
sites/www.sub.example.com/settings.php
sites/sub.example.com/settings.php
sites/example.com/settings.php
sites/default/settings.php
If you are installing on a non-standard port, the port number is treated as the
deepest subdomain. For example: http://www.example.com:8080/ could be loaded
from sites/8080.www.example.com/. The port number will be removed according to
the pattern above if no port-specific configuration is found, just like a real
subdomain.
Each site configuration can have its own site-specific modules and themes in
addition to those installed in the standard 'modules' and 'themes' directories.
To use site-specific modules or themes, simply create a 'modules' or 'themes'
directory within the site configuration directory. For example, if
sub.example.com has a custom theme and a custom module that should not be
accessible to other sites, the setup would look like this:
sites/sub.example.com/
settings.php
themes/custom_theme
modules/custom_module
NOTE: for more information about multiple virtual hosts or the configuration
settings, consult http://drupal.org/getting-started/6/install/multi-site
For more information on configuring Drupal's file system path in a multisite
configuration, see step 6 above.
MORE INFORMATION
----------------
- See the Drupal.org online documentation:
http://drupal.org/documentation
- For a list of security announcements, see the "Security advisories" page at
http://drupal.org/security (available as an RSS feed). This page also
describes how to subscribe to these announcements via e-mail.
- For information about the Drupal security process, or to find out how to
report a potential security issue to the Drupal security team, see the
"Security team" page at http://drupal.org/security-team
- For information about the wide range of available support options, visit
http://drupal.org and click on Community and Support in the top or bottom
navigation.
-303
View File
@@ -1,303 +0,0 @@
Drupal core is built and maintained by the Drupal project community. Everyone is
encouraged to submit issues and changes (patches) to improve Drupal, and to
contribute in other ways -- see http://drupal.org/contribute to find out how.
Branch maintainers
------------------
The Drupal Core branch maintainers oversee the development of Drupal as a whole.
The branch maintainers for Drupal 7 are:
- Dries Buytaert 'dries' http://drupal.org/user/1
- Angela Byron 'webchick' http://drupal.org/user/24967
- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
Component maintainers
---------------------
The Drupal Core component maintainers oversee the development of Drupal
subsystems. See http://drupal.org/contribute/core-maintainers for more
information on their responsibilities, and to find out how to become a component
maintainer. Current component maintainers for Drupal 7:
Ajax system
- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
- Earl Miles 'merlinofchaos' http://drupal.org/user/26979
Base system
- Károly Négyesi 'chx' http://drupal.org/user/9446
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
Batch system
- Yves Chedemois 'yched' http://drupal.org/user/39567
Cache system
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
Cron system
- Károly Négyesi 'chx' http://drupal.org/user/9446
- Derek Wright 'dww' http://drupal.org/user/46549
Database system
- Larry Garfield 'Crell' http://drupal.org/user/26398
- MySQL driver
- Larry Garfield 'Crell' http://drupal.org/user/26398
- David Strauss 'David Strauss' http://drupal.org/user/93254
- PostgreSQL driver
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- Josh Waihi 'fiasco' http://drupal.org/user/188162
- Sqlite driver
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- Károly Négyesi 'chx' http://drupal.org/user/9446
Database update system
- Károly Négyesi 'chx' http://drupal.org/user/9446
- Ashok Modi 'BTMash' http://drupal.org/user/60422
Entity system
- Wolfgang Ziegler 'fago' http://drupal.org/user/16747
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
- Franz Heinzmann 'Frando' http://drupal.org/user/21850
File system
- Andrew Morton 'drewish' http://drupal.org/user/34869
- Aaron Winborn 'aaron' http://drupal.org/user/33420
Form system
- Károly Négyesi 'chx' http://drupal.org/user/9446
- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
- Wolfgang Ziegler 'fago' http://drupal.org/user/16747
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
- Franz Heinzmann 'Frando' http://drupal.org/user/21850
Image system
- Andrew Morton 'drewish' http://drupal.org/user/34869
- Nathan Haug 'quicksketch' http://drupal.org/user/35821
Install system
- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
JavaScript
- Théodore Biadala 'nod_' http://drupal.org/user/598310
- Steve De Jonghe 'seutje' http://drupal.org/user/264148
- Jesse Renée Beach 'jessebeach' http://drupal.org/user/748566
Language system
- Francesco Placella 'plach' http://drupal.org/user/183211
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
Lock system
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
Mail system
- ?
Markup
- Jacine Luisi 'Jacine' http://drupal.org/user/88931
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
Menu system
- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
- Károly Négyesi 'chx' http://drupal.org/user/9446
Path system
- Dave Reid 'davereid' http://drupal.org/user/53892
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
Render system
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
- Franz Heinzmann 'Frando' http://drupal.org/user/21850
Theme system
- Earl Miles 'merlinofchaos' http://drupal.org/user/26979
- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
- Joon Park 'dvessel' http://drupal.org/user/56782
- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
Token system
- Dave Reid 'davereid' http://drupal.org/user/53892
XML-RPC system
- Frederic G. Marand 'fgm' http://drupal.org/user/27985
Topic coordinators
------------------
Accessibility
- Everett Zufelt 'Everett Zufelt' http://drupal.org/user/406552
- Brandon Bowersox-Johnson 'bowersox' http://drupal.org/user/186415
Documentation
- Jennifer Hodgdon 'jhodgdon' http://drupal.org/user/155601
Security
- Greg Knaddison 'greggles' http://drupal.org/user/36762
Translations
- Gerhard Killesreiter 'killes' http://drupal.org/user/83
User experience and usability
- Roy Scholten 'yoroy' http://drupal.org/user/41502
- Bojhan Somers 'Bojhan' http://drupal.org/user/87969
Node Access
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
- Ken Rickard 'agentrickard' http://drupal.org/user/20975
- Jess Myrbo 'xjm' http://drupal.org/user/65776
Module maintainers
------------------
Aggregator module
- ?
Block module
- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
Blog module
- ?
Book module
- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
Color module
- ?
Comment module
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
Contact module
- Dave Reid 'davereid' http://drupal.org/user/53892
Contextual module
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
Dashboard module
- ?
Database logging module
- Khalid Baheyeldin 'kbahey' http://drupal.org/user/4063
Field module
- Yves Chedemois 'yched' http://drupal.org/user/39567
- Barry Jaspan 'bjaspan' http://drupal.org/user/46413
Field UI module
- Yves Chedemois 'yched' http://drupal.org/user/39567
File module
- Aaron Winborn 'aaron' http://drupal.org/user/33420
Filter module
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
Forum module
- Lee Rowlands 'larowlan' http://drupal.org/user/395439
Help module
- ?
Image module
- Nathan Haug 'quicksketch' http://drupal.org/user/35821
Locale module
- Gábor Hojtsy 'Gábor Hojtsy' http://drupal.org/user/4166
Menu module
- ?
Node module
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
- David Strauss 'David Strauss' http://drupal.org/user/93254
OpenID module
- Vojtech Kusy 'wojtha' http://drupal.org/user/56154
- Christian Schmidt 'c960657' http://drupal.org/user/216078
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
Overlay module
- Katherine Senzee 'ksenzee' http://drupal.org/user/139855
Path module
- Dave Reid 'davereid' http://drupal.org/user/53892
PHP module
- ?
Poll module
- Andrei Mateescu 'amateescu' http://drupal.org/user/729614
Profile module
- ?
RDF module
- Stéphane Corlosquet 'scor' http://drupal.org/user/52142
Search module
- Doug Green 'douggreen' http://drupal.org/user/29191
Shortcut module
- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
Simpletest module
- Jimmy Berry 'boombatower' http://drupal.org/user/214218
- Károly Négyesi 'chx' http://drupal.org/user/9446
Statistics module
- Tim Millwood 'timmillwood' http://drupal.org/user/227849
Syslog module
- Khalid Baheyeldin 'kbahey' http://drupal.org/user/4063
System module
- ?
Taxonomy module
- Jess Myrbo 'xjm' http://drupal.org/user/65776
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
- Benjamin Doherty 'bangpound' http://drupal.org/user/100456
Toolbar module
- ?
Tracker module
- David Strauss 'David Strauss' http://drupal.org/user/93254
Translation module
- Francesco Placella 'plach' http://drupal.org/user/183211
Trigger module
- ?
Update module
- Derek Wright 'dww' http://drupal.org/user/46549
User module
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
- David Strauss 'David Strauss' http://drupal.org/user/93254
Theme maintainers
-----------------
Bartik theme
- Jen Simmons 'jensimmons' http://drupal.org/user/140882
- Jeff Burns 'Jeff Burnz' http://drupal.org/user/61393
Garland theme
- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
Seven theme
- Jeff Burns 'Jeff Burnz' http://drupal.org/user/61393
Stark theme
- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
+12 -116
View File
@@ -1,123 +1,19 @@
CONTENTS OF THIS FILE
---------------------
Welcome to Views 3. Please see the advanced help for more information.
* About Drupal
* Configuration and features
* Installation profiles
* Appearance
* Developing for Drupal
If you're having trouble installing this module, please ensure that your
tar program is not flattening the directory tree, truncating filenames
or losing files.
ABOUT DRUPAL
------------
Installing Views:
Drupal is an open source content management platform supporting a variety of
websites ranging from personal weblogs to large community-driven websites. For
more information, see the Drupal website at http://drupal.org/, and join the
Drupal community at http://drupal.org/community.
Place the entirety of this directory in sites/all/modules/views
You must also install the CTools module (http://www.drupal.org/project/ctools) to use Views.
Legal information about Drupal:
* Know your rights when using Drupal:
See LICENSE.txt in the same directory as this document.
* Learn about the Drupal trademark and logo policy:
http://drupal.com/trademark
Navigate to administer >> build >> modules. Enable Views and Views UI.
CONFIGURATION AND FEATURES
--------------------------
If you're new to Views, try the Simple Views module which can create some
often used Views for you, this might save you some time.
Drupal core (what you get when you download and extract a drupal-x.y.tar.gz or
drupal-x.y.zip file from http://drupal.org/project/drupal) has what you need to
get started with your website. It includes several modules (extensions that add
functionality) for common website features, such as managing content, user
accounts, image uploading, and search. Core comes with many options that allow
site-specific configuration. In addition to the core modules, there are
thousands of contributed modules (for functionality not included with Drupal
core) available for download.
More about configuration:
* Install, upgrade, and maintain Drupal:
See INSTALL.txt and UPGRADE.txt in the same directory as this document.
* Learn about how to use Drupal to create your site:
http://drupal.org/documentation
* Download contributed modules to sites/all/modules to extend Drupal's
functionality:
http://drupal.org/project/modules
* See also: "Developing for Drupal" for writing your own modules, below.
INSTALLATION PROFILES
---------------------
Installation profiles define additional steps (such as enabling modules,
defining content types, etc.) that run after the base installation provided
by core when Drupal is first installed. There are two basic installation
profiles provided with Drupal core.
Installation profiles from the Drupal community modify the installation process
to provide a website for a specific use case, such as a CMS for media
publishers, a web-based project tracking tool, or a full-fledged CRM for
non-profit organizations raising money and accepting donations. They can be
distributed as bare installation profiles or as "distributions". Distributions
include Drupal core, the installation profile, and all other required
extensions, such as contributed and custom modules, themes, and third-party
libraries. Bare installation profiles require you to download Drupal Core and
the required extensions separately; place the downloaded profile in the
/profiles directory before you start the installation process. Note that the
contents of this directory may be overwritten during updates of Drupal core;
it is advised to keep code backups or use a version control system.
Additionally, modules and themes may be placed inside subdirectories in a
specific installation profile such as profiles/your_site_profile/modules and
profiles/your_site_profile/themes respectively to restrict their usage to only
sites that were installed with that specific profile.
More about installation profiles and distributions:
* Read about the difference between installation profiles and distributions:
http://drupal.org/node/1089736
* Download contributed installation profiles and distributions:
http://drupal.org/project/distributions
* Develop your own installation profile or distribution:
http://drupal.org/developing/distributions
APPEARANCE
----------
In Drupal, the appearance of your site is set by the theme (themes are
extensions that set fonts, colors, and layout). Drupal core comes with several
themes. More themes are available for download, and you can also create your own
custom theme.
More about themes:
* Download contributed themes to sites/all/themes to modify Drupal's
appearance:
http://drupal.org/project/themes
* Develop your own theme:
http://drupal.org/documentation/theme
DEVELOPING FOR DRUPAL
---------------------
Drupal contains an extensive API that allows you to add to and modify the
functionality of your site. The API consists of "hooks", which allow modules to
react to system events and customize Drupal's behavior, and functions that
standardize common operations such as database queries and form generation. The
flexible hook architecture means that you should never need to directly modify
the files that come with Drupal core to achieve the functionality you want;
instead, functionality modifications take the form of modules.
When you need new functionality for your Drupal site, search for existing
contributed modules. If you find a module that matches except for a bug or an
additional needed feature, change the module and contribute your improvements
back to the project in the form of a "patch". Create new custom modules only
when nothing existing comes close to what you need.
More about developing:
* Search for existing contributed modules:
http://drupal.org/project/modules
* Contribute a patch:
http://drupal.org/patch/submit
* Develop your own module:
http://drupal.org/developing/modules
* Follow best practices:
http://drupal.org/best-practices
* Refer to the API documentation:
http://api.drupal.org/api/drupal/7
Here you can find many modules extending the functionality of Views:
http://drupal.org/taxonomy/term/89
-236
View File
@@ -1,236 +0,0 @@
INTRODUCTION
------------
This document describes how to:
* Update your Drupal site from one minor 7.x version to another minor 7.x
version; for example, from 7.8 to 7.9, or from 7.6 to 7.10.
* Upgrade your Drupal site's major version from 6.x to 7.x.
First steps and definitions:
* If you are upgrading to Drupal version x.y, then x is known as the major
version number, and y is known as the minor version number. The download
file will be named drupal-x.y.tar.gz (or drupal-x.y.zip).
* All directories mentioned in this document are relative to the directory of
your Drupal installation.
* Make a full backup of all files, directories, and your database(s) before
starting, and save it outside your Drupal installation directory.
Instructions may be found at http://drupal.org/upgrade/backing-up-the-db
* It is wise to try an update or upgrade on a test copy of your site before
applying it to your live site. Even minor updates can cause your site's
behavior to change.
* Each new release of Drupal has release notes, which explain the changes made
since the previous version and any special instructions needed to update or
upgrade to the new version. You can find a link to the release notes for the
version you are upgrading or updating to on the Drupal project page
(http://drupal.org/project/drupal).
UPGRADE PROBLEMS
----------------
If you encounter errors during this process,
* Note any error messages you see.
* Restore your site to its previous state, using the file and database backups
you created before you started the upgrade process. Do not attempt to do
further upgrades on a site that had update problems.
* Consult one of the support options listed on http://drupal.org/support
More in-depth information on upgrading can be found at http://drupal.org/upgrade
MINOR VERSION UPDATES
---------------------
To update from one minor 7.x version of Drupal to any later 7.x version, after
following the instructions in the INTRODUCTION section at the top of this file:
1. Log in as a user with the permission "Administer software updates".
2. Go to Administration > Configuration > Development > Maintenance mode.
Enable the "Put site into maintenance mode" checkbox and save the
configuration.
3. Remove all old core files and directories, except for the 'sites' directory
and any custom files you added elsewhere.
If you made modifications to files like .htaccess or robots.txt, you will
need to re-apply them from your backup, after the new files are in place.
Sometimes an update includes changes to default.settings.php (this will be
noted in the release notes). If that's the case, follow these steps:
- Make a backup copy of your settings.php file, with a different file name.
- Make a copy of the new default.settings.php file, and name the copy
settings.php (overwriting your previous settings.php file).
- Copy the custom and site-specific entries from the backup you made into the
new settings.php file. You will definitely need the lines giving the
database information, and you will also want to copy in any other
customizations you have added.
4. Download the latest Drupal 7.x release from http://drupal.org to a
directory outside of your web root. Extract the archive and copy the files
into your Drupal directory.
On a typical Unix/Linux command line, use the following commands to download
and extract:
wget http://drupal.org/files/projects/drupal-x.y.tar.gz
tar -zxvf drupal-x.y.tar.gz
This creates a new directory drupal-x.y/ containing all Drupal files and
directories. Copy the files into your Drupal installation directory:
cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
If you do not have command line access to your server, download the archive
from http://drupal.org using your web browser, extract it, and then use an
FTP client to upload the files to your web root.
5. Re-apply any modifications to files such as .htaccess or robots.txt.
6. Run update.php by visiting http://www.example.com/update.php (replace
www.example.com with your domain name). This will update the core database
tables.
If you are unable to access update.php do the following:
- Open settings.php with a text editor.
- Find the line that says:
$update_free_access = FALSE;
- Change it into:
$update_free_access = TRUE;
- Once the upgrade is done, $update_free_access must be reverted to FALSE.
7. Go to Administration > Reports > Status report. Verify that everything is
working as expected.
8. Ensure that $update_free_access is FALSE in settings.php.
9. Go to Administration > Configuration > Development > Maintenance mode.
Disable the "Put site into maintenance mode" checkbox and save the
configuration.
MAJOR VERSION UPGRADE
---------------------
To upgrade from a previous major version of Drupal to Drupal 7.x, after
following the instructions in the INTRODUCTION section at the top of this file:
1. Check on the Drupal 7 status of your contributed and custom modules and
themes. See http://drupal.org/node/948216 for information on upgrading
contributed modules and themes. See http://drupal.org/node/895314 for a list
of modules that have been moved into core for Drupal 7, and instructions on
how to update them. See http://drupal.org/update/modules for information on
how to update your custom modules, and http://drupal.org/update/theme for
custom themes.
You may decide at this point that you cannot upgrade your site, because
needed modules or themes are not ready for Drupal 7.
2. Update to the latest available version of Drupal 6.x (if your current version
is Drupal 5.x, you have to upgrade to 6.x first). If you need to update,
download Drupal 6.x and follow the instructions in its UPGRADE.txt. This
document only applies for upgrades from 6.x to 7.x.
3. In addition to updating to the latest available version of Drupal 6.x core,
you must also upgrade all of your contributed modules for Drupal to their
latest Drupal 6.x versions.
4. Log in as user ID 1 (the site maintenance user).
5. Go to Administer > Site configuration > Site maintenance. Select
"Off-line" and save the configuration.
6. Go to Administer > Site building > Themes. Enable "Garland" and select it as
the default theme.
7. Go to Administer > Site building > Modules. Disable all modules that are not
listed under "Core - required" or "Core - optional". It is possible that some
modules cannot be disabled, because others depend on them. Repeat this step
until all non-core modules are disabled.
If you know that you will not re-enable some modules for Drupal 7.x and you
no longer need their data, then you can uninstall them under the Uninstall
tab after disabling them.
8. On the command line or in your FTP client, remove the file
sites/default/default.settings.php
9. Remove all old core files and directories, except for the 'sites' directory
and any custom files you added elsewhere.
If you made modifications to files like .htaccess or robots.txt, you will
need to re-apply them from your backup, after the new files are in place.
10. If you uninstalled any modules, remove them from the sites/all/modules and
other sites/*/modules directories. Leave other modules in place, even though
they are incompatible with Drupal 7.x.
11. Download the latest Drupal 7.x release from http://drupal.org to a
directory outside of your web root. Extract the archive and copy the files
into your Drupal directory.
On a typical Unix/Linux command line, use the following commands to download
and extract:
wget http://drupal.org/files/projects/drupal-x.y.tar.gz
tar -zxvf drupal-x.y.tar.gz
This creates a new directory drupal-x.y/ containing all Drupal files and
directories. Copy the files into your Drupal installation directory:
cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation
If you do not have command line access to your server, download the archive
from http://drupal.org using your web browser, extract it, and then use an
FTP client to upload the files to your web root.
12. Re-apply any modifications to files such as .htaccess or robots.txt.
13. Make your settings.php file writeable, so that the update process can
convert it to the format of Drupal 7.x. settings.php is usually located in
sites/default/settings.php
14. Run update.php by visiting http://www.example.com/update.php (replace
www.example.com with your domain name). This will update the core database
tables.
If you are unable to access update.php do the following:
- Open settings.php with a text editor.
- Find the line that says:
$update_free_access = FALSE;
- Change it into:
$update_free_access = TRUE;
- Once the upgrade is done, $update_free_access must be reverted to FALSE.
15. Backup your database after the core upgrade has run.
16. Replace and update your non-core modules and themes, following the
procedures at http://drupal.org/node/948216
17. Go to Administration > Reports > Status report. Verify that everything is
working as expected.
18. Ensure that $update_free_access is FALSE in settings.php.
19. Go to Administration > Configuration > Development > Maintenance mode.
Disable the "Put site into maintenance mode" checkbox and save the
configuration.
To get started with Drupal 7 administration, visit
http://drupal.org/getting-started/7/admin
-30
View File
@@ -1,30 +0,0 @@
diff --git a/misc/autocomplete.js b/misc/autocomplete.js
index 8f7ac60..5679081 100644
--- a/misc/autocomplete.js
+++ b/misc/autocomplete.js
@@ -114,6 +114,7 @@ Drupal.jsAC.prototype.onkeyup = function (input, e) {
*/
Drupal.jsAC.prototype.select = function (node) {
this.input.value = $(node).data('autocompleteValue');
+ $(this.input).trigger('autocompleteSelect', [node]);
};
/**
@@ -167,7 +168,7 @@ Drupal.jsAC.prototype.unhighlight = function (node) {
Drupal.jsAC.prototype.hidePopup = function (keycode) {
// Select item if the right key or mousebutton was pressed.
if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
- this.input.value = $(this.selected).data('autocompleteValue');
+ this.select(this.selected);
}
// Hide popup.
var popup = this.popup;
@@ -220,7 +221,7 @@ Drupal.jsAC.prototype.found = function (matches) {
for (key in matches) {
$('<li></li>')
.html($('<div></div>').html(matches[key]))
- .mousedown(function () { ac.select(this); })
+ .mousedown(function () { ac.hidePopup(this); })
.mouseover(function () { ac.highlight(this); })
.mouseout(function () { ac.unhighlight(this); })
.data('autocompleteValue', key)
-173
View File
@@ -1,173 +0,0 @@
<?php
/**
* @file
* Administrative script for running authorized file operations.
*
* Using this script, the site owner (the user actually owning the files on
* the webserver) can authorize certain file-related operations to proceed
* with elevated privileges, for example to deploy and upgrade modules or
* themes. Users should not visit this page directly, but instead use an
* administrative user interface which knows how to redirect the user to this
* script as part of a multistep process. This script actually performs the
* selected operations without loading all of Drupal, to be able to more
* gracefully recover from errors. Access to the script is controlled by a
* global killswitch in settings.php ('allow_authorize_operations') and via
* the 'administer software updates' permission.
*
* There are helper functions for setting up an operation to run via this
* system in modules/system/system.module. For more information, see:
* @link authorize Authorized operation helper functions @endlink
*/
/**
* Root directory of Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
/**
* Global flag to identify update.php and authorize.php runs, and so
* avoid various unwanted operations, such as hook_init() and
* hook_exit() invokes, css/js preprocessing and translation, and
* solve some theming issues. This flag is checked on several places
* in Drupal code (not just authorize.php).
*/
define('MAINTENANCE_MODE', 'update');
/**
* Renders a 403 access denied page for authorize.php.
*/
function authorize_access_denied_page() {
drupal_add_http_header('Status', '403 Forbidden');
watchdog('access denied', 'authorize.php', NULL, WATCHDOG_WARNING);
drupal_set_title('Access denied');
return t('You are not allowed to access this page.');
}
/**
* Determines if the current user is allowed to run authorize.php.
*
* The killswitch in settings.php overrides all else, otherwise, the user must
* have access to the 'administer software updates' permission.
*
* @return
* TRUE if the current user can run authorize.php, otherwise FALSE.
*/
function authorize_access_allowed() {
return variable_get('allow_authorize_operations', TRUE) && user_access('administer software updates');
}
// *** Real work of the script begins here. ***
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/file.inc';
require_once DRUPAL_ROOT . '/includes/module.inc';
require_once DRUPAL_ROOT . '/includes/ajax.inc';
// We prepare only a minimal bootstrap. This includes the database and
// variables, however, so we have access to the class autoloader registry.
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
// This must go after drupal_bootstrap(), which unsets globals!
global $conf;
// We have to enable the user and system modules, even to check access and
// display errors via the maintenance theme.
$module_list['system']['filename'] = 'modules/system/system.module';
$module_list['user']['filename'] = 'modules/user/user.module';
module_list(TRUE, FALSE, FALSE, $module_list);
drupal_load('module', 'system');
drupal_load('module', 'user');
// We also want to have the language system available, but we do *NOT* want to
// actually call drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE), since that would
// also force us through the DRUPAL_BOOTSTRAP_PAGE_HEADER phase, which loads
// all the modules, and that's exactly what we're trying to avoid.
drupal_language_initialize();
// Initialize the maintenance theme for this administrative script.
drupal_maintenance_theme();
$output = '';
$show_messages = TRUE;
if (authorize_access_allowed()) {
// Load both the Form API and Batch API.
require_once DRUPAL_ROOT . '/includes/form.inc';
require_once DRUPAL_ROOT . '/includes/batch.inc';
// Load the code that drives the authorize process.
require_once DRUPAL_ROOT . '/includes/authorize.inc';
// For the sake of Batch API and a few other low-level functions, we need to
// initialize the URL path into $_GET['q']. However, we do not want to raise
// our bootstrap level, nor do we want to call drupal_initialize_path(),
// since that is assuming that modules are loaded and invoking hooks.
// However, all we really care is if we're in the middle of a batch, in which
// case $_GET['q'] will already be set, we just initialize it to an empty
// string if it's not already defined.
if (!isset($_GET['q'])) {
$_GET['q'] = '';
}
if (isset($_SESSION['authorize_operation']['page_title'])) {
drupal_set_title($_SESSION['authorize_operation']['page_title']);
}
else {
drupal_set_title(t('Authorize file system changes'));
}
// See if we've run the operation and need to display a report.
if (isset($_SESSION['authorize_results']) && $results = $_SESSION['authorize_results']) {
// Clear the session out.
unset($_SESSION['authorize_results']);
unset($_SESSION['authorize_operation']);
unset($_SESSION['authorize_filetransfer_info']);
if (!empty($results['page_title'])) {
drupal_set_title($results['page_title']);
}
if (!empty($results['page_message'])) {
drupal_set_message($results['page_message']['message'], $results['page_message']['type']);
}
$output = theme('authorize_report', array('messages' => $results['messages']));
$links = array();
if (is_array($results['tasks'])) {
$links += $results['tasks'];
}
else {
$links = array_merge($links, array(
l(t('Administration pages'), 'admin'),
l(t('Front page'), '<front>'),
));
}
$output .= theme('item_list', array('items' => $links, 'title' => t('Next steps')));
}
// If a batch is running, let it run.
elseif (isset($_GET['batch'])) {
$output = _batch_page();
}
else {
if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_info'])) {
$output = t('It appears you have reached this page in error.');
}
elseif (!$batch = batch_get()) {
// We have a batch to process, show the filetransfer form.
$elements = drupal_get_form('authorize_filetransfer_form');
$output = drupal_render($elements);
}
}
// We defer the display of messages until all operations are done.
$show_messages = !(($batch = batch_get()) && isset($batch['running']));
}
else {
$output = authorize_access_denied_page();
}
if (!empty($output)) {
print theme('update_page', array('content' => $output, 'show_messages' => $show_messages));
}
-26
View File
@@ -1,26 +0,0 @@
<?php
/**
* @file
* Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
*/
/**
* Root directory of Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
drupal_access_denied();
}
elseif (variable_get('maintenance_mode', 0)) {
watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
drupal_access_denied();
}
else {
drupal_cron_run();
}
+91
View File
@@ -0,0 +1,91 @@
/**
* The query details collapsible divs are not visible in IE7 because has-layout
* is not being triggered. Trigger has-layout with height: 1%;
*/
.views-edit-view .collapsible > .fieldset-wrapper {
height: 1%;
}
/**
* The column width for the bucket containers in the query details section
* is not being calculated to 32% correctly. Give IE7 a slightly smaller
* width so that the three columns line up next to each other
*/
.views-edit-view .views-display-column {
width: 31.95%;
}
/**
* IE7 has no idea how large this container should be and it doesn't
* apply has-layout. Expand it's width to 100% and trigger has-layout.
*/
.views-edit-view .views-displays {
height: 1%;
width: 100%;
}
/**
* IE7 isn't positioning the span correctly as a display-inline element
*/
.views-edit-view .views-displays .icon {
display: block;
float: left;
}
.views-edit-view .views-displays .icon-add {
top: 2px;
}
/**
* The add display query dropdown needs a lot of help
*/
.views-edit-view .views-displays .tabs.secondary {
position: relative;
z-index: 100;
}
.views-edit-view .views-displays .secondary .open > a {
border-bottom: 1px solid #f1f1f1;
}
.views-edit-view .views-displays .secondary .action-list {
border-bottom: 1px solid #cbcbcb;
top: 30px;
}
.views-edit-view .views-displays .secondary input {
text-align: left;
}
/**
* IE7 does not interpret div > * correctly
*/
.page-admin-structure-views #content .views-ui-display-tab-bucket {
padding-left: 0;
padding-right: 0;
zoom: 1;
}
.page-admin-structure-views #content .views-display-column + .views-display-column {
margin-top: 0;
}
/**
* IE7 is interpreting a top margin of 18px from somewhere. remove it
*/
.page-admin-structure-views #content .views-display-setting {
margin-top: 0;
}
/**
* IE7 can't handle the + selector that indents form wrappers after a checkbox on the add page
* zoom is necessary to trigger has layout. !imporant is necessary because IE7 is precedent
* to the theme.css stylesheet, even though it is included before this file.
*/
.page-admin-structure-views #content .form-type-checkbox + .form-wrapper {
margin-left: 27px !important;
zoom: 1;
}
+98
View File
@@ -0,0 +1,98 @@
/**
* The .css file is intended to only contain positioning and size declarations
* For example: display, position, float, clear, and overflow.
*/
/* @group Inline lists */
.horizontal > * {
float: right;
}
.horizontal.right {
float: left;
}
/* @end */
/* @group Attachment details
*
* The attachment details section, its tabs for each section and the buttons
* to add a new section
*/
.form-actions {
float: left;
}
/* @end */
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .secondary > li {
float: right;
}
/* @end */
/* @group Attachment details new section button */
.views-displays .secondary .action-list {
left: auto;
right: 0;
}
/* @end */
/* @group Attachment details collapsible fieldset */
.views-display-tab .fieldset-legend {
left: auto;
right: -5px;
}
/* @end */
/* @group Attachment details actions
*
* Display the "Delete" and "Duplicate" buttons to the right.
*/
.views-display-tab .fieldset-wrapper > .views-ui-display-tab-bucket .actions {
left: 0;
right: auto;
}
/* @end */
/* @group Attachment configuration columns */
.views-display-columns > * {
float: right;
margin-left: 0;
margin-right: 1%;
padding-left: 0;
padding-right: 1%;
}
.views-display-columns > *:first-child {
margin-right: 0;
padding-right: 0;
}
/* @end */
/* @group Settings forms */
.views-dependent {
margin-right: 1.5em;
}
.views-display-setting .label,
.views-display-setting .views-ajax-link {
float: right;
}
/* @end */
+24
View File
@@ -0,0 +1,24 @@
/**
* The .advanced_help.css file is intended to contain styles that override declarations
* in the Advanced Help module.
*/
/**
* Adjust the advanced help icons
*/
.views-ui-display-tab-bucket .advanced-help-link {
padding: 0;
margin: 5px 3px 0px 6px; /* LTR */
}
.views-ui-display-tab-bucket .icon-text {
padding-left: 25px; /* LTR */
}
.views-ui-display-tab-bucket .icon-linked {
background-position: 6px -151px; /* LTR */
}
.views-ui-display-tab-bucket .icon-unlinked {
background-position: 6px -193px; /* LTR */
}
+12
View File
@@ -0,0 +1,12 @@
/**
* The .bartik.css file is intended to contain styles that override declarations
* in the Bartik theme.
*/
/* @group Lists */
.views-display-top .secondary .action-list {
padding-right: 0;
}
/* @end */
+233
View File
@@ -0,0 +1,233 @@
/**
* The .bartik.css file is intended to contain styles that override declarations
* in the Bartik theme.
*/
/* @group Lists */
.views-display-top .secondary .action-list {
padding-left: 0; /* LTR */
}
/* @end */
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .region-content .secondary,
.views-displays .region-content .secondary {
padding-bottom: 0;
padding-left: 0;
}
.views-displays .secondary a {
font-size: smaller;
}
.views-displays .secondary > li a {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.views-displays .secondary > li.open a {
-moz-border-radius: 5px 5px 0 0;
-webkit-border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
border-radius: 5px 5px 0 0;
}
.views-displays .secondary .open > a:hover {
color: #0071B3;
}
.views-displays .secondary input.form-submit {
font-size: smaller;
}
/* @end */
/* @group Modal dialog box
*
* The contents of the popup dialog on the views edit form.
*/
.views-filterable-options .even .form-type-checkbox {
background-color: #F9F9F9;
}
.views-ui-dialog .ui-dialog-titlebar-close,
.views-ui-dialog #views-ajax-title,
.views-ui-dialog .views-override,
.views-ui-dialog .form-buttons {
background-color: #f6f6f6;
}
.views-ui-dialog a {
color: #0071b3;
}
.views-ui-dialog a:hover,
.views-ui-dialog a:focus {
color: #018fe2;
}
/* @end */
/* @group CTools */
/* @group Buttons */
.ctools-button-processed {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding-bottom: 1px;
padding-top: 1px;
}
.ctools-button-processed:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(241, 241, 241, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
}
.ctools-button-processed li a,
.views-ui-display-tab-actions .ctools-button-processed input {
padding-left: 9px;
padding-right: 9px;
}
.ctools-content ul.actions {
padding-bottom: 0;
}
.ctools-dropbutton-processed.open:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
}
.ctools-dropbutton-processed.open {
-moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
-webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
}
.ctools-twisty {
top: 0.6667em;
}
.ctools-dropbutton-processed.open .ctools-twisty {
top: 0.3333em;
}
.ctools-dropbutton-processed li a,
.views-ui-display-tab-actions .ctools-dropbutton-processed input {
padding-right: 7px;
}
.views-ui-display-tab-actions .ctools-button-processed input.form-submit {
margin-right: 0;
margin-top: 0;
}
/* @end */
/* @group Collapsible */
.ctools-toggle {
margin-top: 0.9em;
}
.ctools-toggle.ctools-toggle-collapsed {
margin-top: 0.72em;
}
.views-display-column > .ctools-toggle {
margin-top: 14px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed {
margin-top: 12px;
}
.views-ui-display-tab-actions .ctools-button input {
color: #0071B3;
}
.views-ui-display-tab-actions .ctools-button input:hover,
.views-ui-display-tab-actions .ctools-button input:focus {
color: #018FE2;
}
/* @end */
/* @end */
+63
View File
@@ -0,0 +1,63 @@
/**
* The .contextual.css file is intended to contain styles that override declarations
* in the Contextual module.
*/
/* @group Wrapper */
#views-live-preview .contextual-links-region-active {
outline: medium none;
}
#views-live-preview div.contextual-links-wrapper {
right: auto;
top: auto;
}
html.js #views-live-preview div.contextual-links-wrapper {
display: inline;
}
/* @end */
/* @group Trigger */
#views-live-preview a.contextual-links-trigger {
display: block;
}
/* @end */
/* @group List */
div.contextual-links-wrapper ul.contextual-links {
-moz-border-radius: 0 4px 4px 4px;
-webkit-border-radius: 0 4px 4px 4px;
border-radius: 0 4px 4px 4px;
min-width: 10em;
padding: 6px 6px 9px 6px;
right: auto;
}
ul.contextual-links li a,
ul.contextual-links li span {
padding-bottom: 0.25em;
padding-right: 0.1667em;
padding-top: 0.25em;
}
ul.contextual-links li span {
font-weight: bold;
}
ul.contextual-links li a {
color: #666666 !important;
margin: 0.25em 0;
padding-left: 1em;
}
ul.contextual-links li a:hover {
background-color: #badbec;
}
/* @end */
+361
View File
@@ -0,0 +1,361 @@
/**
* The .css file is intended to only contain positioning and size declarations
* For example: display, position, float, clear, and overflow.
*/
/* @group Resets */
.views-admin ul,
.views-admin menu,
.views-admin dir {
padding-left: 0; /* LTR for IE */
/* padding-start is used so that RTL works out of the box */
-moz-padding-start: 0;
-webkit-padding-start: 0;
padding-start: 0;
}
.views-admin pre {
margin-bottom: 0;
margin-top: 0;
white-space: pre-wrap;
}
/* @end */
/* @group Inline lists */
.horizontal > * {
clear: none;
float: left; /* LTR */
}
.horizontal.right {
float: right;
}
.horizontal label {
position: absolute;
}
.horizontal .form-item > [class] {
margin-top: 25px;
}
.horizontal .form-item > [class] + [class] {
margin-top: 0;
}
/* @end */
/* @group Columns */
.views-left-25 {
float: left;
width: 25%;
}
.views-left-30 {
float: left;
width: 30%;
}
.views-left-40 {
float: left;
width: 40%;
}
.views-left-50 {
float: left;
width: 50%;
}
.views-left-75 {
float: left;
width: 75%;
}
.views-right-50 {
float: right;
width: 50%;
}
.views-right-60 {
float: right;
width: 60%;
}
.views-right-70 {
float: right;
width: 70%;
}
.views-group-box .form-item {
margin-left: 3px;
margin-right: 3px;
}
/* @end */
/* @group Attachment details
*
* The attachment details section, its tabs for each section and the buttons
* to add a new section
*/
.form-edit .form-actions {
float: right; /* LTR */
}
.views-displays {
clear: both;
}
/* @end */
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .secondary {
border-bottom: 0 none;
margin: 0;
overflow: visible;
padding: 0;
}
.views-displays .secondary > li {
border-right: 0 none;
display: inline-block;
float: left; /* LTR */
padding: 0;
}
.views-displays .secondary .open > a {
position: relative;
z-index: 51;
}
.views-displays .views-display-deleted-link {
text-decoration: line-through;
}
.views-display-deleted > fieldset > legend,
.views-display-deleted .fieldset-wrapper > .views-ui-display-tab-bucket > *,
.views-display-deleted .views-display-columns {
opacity: 0.25;
}
.views-display-disabled > fieldset > legend,
.views-display-disabled .fieldset-wrapper > .views-ui-display-tab-bucket > *,
.views-display-disabled .views-display-columns {
opacity: 0.5;
}
.views-display-tab .fieldset-wrapper > .views-ui-display-tab-bucket .actions {
opacity: 1.0;
}
/* @end */
/* @group Attachment details new section button */
.views-displays .secondary li.add {
position: relative;
}
.views-displays .secondary .action-list {
left: 0; /* LTR */
margin: 0;
position: absolute;
top: 23px;
z-index: 50;
}
.views-displays .secondary .action-list li {
display: block;
}
/* @end */
/* @group Attachment details collapsible fieldset */
.views-display-tab .fieldset-legend {
left: -5px; /* LTR */
position: relative;
}
.views-display-tab .fieldset-wrapper {
position: relative;
}
/* @end */
/* @group Attachment details actions
*
* Display the "Delete" and "Duplicate" buttons to the right.
*/
.views-display-tab .fieldset-wrapper > .views-ui-display-tab-bucket .actions {
position: absolute;
right: 0; /* LTR */
top: -5px;
}
/* @end */
/* @group Attachment configuration columns */
.views-display-columns > * {
float: left; /* LTR */
margin-left: 1%; /* LTR */
padding-left: 1%; /* LTR */
width: 32%;
}
.views-display-columns > *:first-child {
margin-left: 0; /* LTR */
padding-left: 0; /* LTR */
}
/* @end */
/* @group Modal dialog box */
.views-ui-dialog {
/* We need this so the button is visible. */
overflow: visible;
position: fixed;
}
.views-ui-dialog .ui-dialog-titlebar-close {
border: 1px solid transparent;
display: block;
margin: 0;
padding: 0;
position: absolute;
right: 0;
top: 2px;
/* Make sure this is in front of the modal backdrop. */
z-index: 1002;
}
.views-ui-dialog .ui-dialog-titlebar {
padding: 0;
margin: 0;
}
.views-ui-dialog .ui-dialog-title {
display: none;
}
.views-ui-dialog #views-ajax-popup {
padding: 0;
overflow: hidden;
}
.views-ui-dialog #views-ajax-title,
.views-ui-dialog #views-ajax-body {
margin: 0;
padding: 0;
}
.views-ui-dialog #views-ajax-popup {
overflow: hidden;
}
.views-ui-dialog .scroll {
max-height: 400px;
overflow: auto;
}
#views-filterable-options-controls {
display: none;
}
.views-ui-dialog #views-filterable-options-controls {
display: block;
}
/* Don't let the messages overwhelm the modal */
.views-ui-dialog .views-messages {
max-height: 200px;
overflow: auto;
}
/* @end */
/* @group Settings forms */
.views-display-setting .label,
.views-display-setting .views-ajax-link {
display: inline-block;
float: left; /* LTR */
}
/* @end */
/* @group Filter Settings form */
div.form-item-options-value-all {
display: none;
}
/* @end */
/* @group Drupal overrides */
/* The .progress-disabled class added to the form on submit floats the element
* left and causes the form width to shrink-wrap to the content. Setting the
* width to 100% prevents this.
*/
#views-ajax-body form {
width: 100%;
}
/* @end */
/* @group Javascript dependent styling */
.js-only {
display: none;
}
html.js .js-only {
display: inherit;
}
html.js span.js-only {
display: inline;
}
/* @end */
/* @group AJAX throbber */
/* Base Page */
#views-ui-list-page .ajax-progress-throbber,
.views-admin .ajax-progress-throbber {
/* Can't do center:50% middle: 50%, so approximate it for a typical window size. */
left: 49%;
position: fixed;
top: 48.5%;
z-index: 1000;
}
#views-ui-list-page .ajax-progress-throbber .message,
.views-admin .ajax-progress-throbber .message {
display: none;
}
/* Modal */
#views-ajax-popup .ajax-progress-throbber {
/* Can't do center:50% middle: 50%, so approximate it for a typical window size. */
left: 49%;
position: fixed;
top: 48.5%;
z-index: 1000;
}
#views-ajax-popup .ajax-progress-throbber .message {
display: none;
}
/* @end */
+82
View File
@@ -0,0 +1,82 @@
/* @group Buttons */
.ctools-dropbutton .ctools-content {
border-left: 1px solid #e8e8e8;
}
.ctools-content ul.actions {
padding-left: auto;
padding-right: 0;
}
.ctools-dropbutton .ctools-link {
border-right: 1px solid #ffffff;
}
.ctools-dropbutton li {
padding-left: 9px;
padding-left: auto;
}
.views-display-top .ctools-button {
left: 12px;
right: auto;
}
.views-ui-display-tab-bucket .ctools-button {
left: 5px;
right: auto;
}
/* @end */
/* @group Collapsible */
.ctools-toggle {
float: right;
margin-left: 2px;
margin-right: 0;
}
.ctools-toggle.ctools-toggle-collapsed {
border-left: 0;
border-right: 4px solid;
border-left-color: transparent;
border-width: 5px 5px 5px 0;
margin-left: 5px;
margin-right: 2px;
}
.ctools-export-ui-row label {
float: right;
}
.views-display-column > .ctools-toggle {
margin-left: 3px;
margin-right: 6px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed {
margin-left: 5px;
margin-right: 9px;
}
/* @end */
/* @group Dependent */
.dependent-options {
margin-left: 0;
margin-right: 18px;
}
/* @end */
/* @group Export */
/* Override for filter button on the views list screen */
#ctools-export-ui-list-form .form-submit {
margin-left: 0em;
margin-right: auto;
}
/* @end */
+232
View File
@@ -0,0 +1,232 @@
/* @group Buttons */
.ctools-button-processed {
background-color: #ffffff;
border-color: #cccccc;
font-size: 11px;
padding-bottom: 2px;
padding-top: 2px;
}
.ctools-button-processed:hover {
border-color: #b8b8b8;
}
.ctools-button-processed:active {
border-color: #a0a0a0;
}
.ctools-button-processed .ctools-content {
padding-bottom: 0;
padding-top: 0;
}
.ctools-dropbutton-processed {
position: absolute;
}
.ctools-dropbutton-processed .ctools-content {
border-right: 1px solid #e8e8e8;
}
.ctools-dropbutton-processed .ctools-content ul {
margin: 0;
padding: 0;
}
.ctools-content ul.actions {
margin-top: 0;
margin-bottom: 0;
padding-left: 0;
}
.ctools-button-processed .ctools-content a {
background-image: none;
border: medium none;
}
.ctools-dropbutton-processed.open:hover {
border-color: #D0D0D0;
}
.ctools-dropbutton-processed.open {
z-index: 100;
}
.ctools-dropbutton-processed .ctools-link {
border-left: 1px solid #ffffff;
}
.ctools-dropbutton-processed.open .ctools-content {
padding-bottom: 4px;
}
.ctools-dropbutton-processed li a,
.ctools-dropbutton-processed li input {
padding-right: 9px;
}
.ctools-dropbutton-processed.open li + li {
border-top: 1px solid #efefef;
margin-top: 4px;
padding-bottom: 0;
padding-top: 4px;
}
.ctools-twisty:focus {
outline: medium none;
}
.ctools-no-js .ctools-content ul {
margin-bottom: 0;
margin-top: 0;
padding-left: 0;
}
.views-display-top .ctools-button-processed {
font-size: 12px;
position: absolute;
right: 12px;
top: 7px;
}
.views-ui-display-tab-bucket .ctools-button-processed {
position: absolute;
right: 5px;
top: 4px;
}
.views-ui-display-tab-actions .ctools-button-processed li a,
.views-ui-display-tab-actions .ctools-button-processed input {
background: none;
border: medium;
font-family: inherit;
font-size: 12px;
padding-bottom: 0;
padding-left: 12px;
padding-top: 0;
margin-bottom: 0;
}
.views-ui-display-tab-actions .ctools-button-processed input:hover {
background: none;
}
/* @end */
/* @group Collapsible */
.ctools-toggle {
border-bottom-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
border-style: solid;
border-width: 5px 5px 0;
display: inline-block;
float: left;
height: 0;
margin-right: 2px;
margin-top: 0.4545em;
width: 0;
}
.ctools-toggle.ctools-toggle-collapsed {
border-bottom-color: transparent;
border-left: 4px solid;
border-right-color: transparent;
border-top-color: transparent;
border-width: 5px 0 5px 5px;
margin-left: 2px;
margin-right: 5px;
margin-top: 0.3333em;
}
.ctools-toggle:hover,
.ctools-collapsible-handle:hover {
cursor: pointer;
}
.ctools-export-ui-row {
margin-bottom: 0;
padding-top: 0;
}
.ctools-export-ui-row label {
display: block;
float: left;
width: 55px;
}
.views-display-settings .ctools-toggle {
color: #000000;
}
.views-display-column > .ctools-toggle {
margin-left: 6px;
margin-right: 3px;
margin-top: 10px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed {
margin-left: 9px;
margin-right: 5px;
margin-top: 8px;
}
.views-display-column > .ctools-collapsible-handle {
border-color: #F3F3F3;
border-style: solid;
border-width: 1px 1px 0;
font-size: 13px;
font-weight: normal;
margin: 0;
padding: 6px 3px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed + .ctools-collapsible-handle {
border-width: 1px;
}
.views-display-column > .ctools-collapsible-content > .views-ui-display-tab-bucket:first-child {
border-top: medium none;
}
h2.ctools-collapsible-handle {
display: inline;
clear: both;
}
/* @end */
/* @group Dependent */
.dependent-options {
margin-left: 18px; /* LTR */
}
/* @end */
/* @group Export */
/* Override for filter button on the views list screen */
#ctools-export-ui-list-form .form-submit {
margin-top: 0em !important;
margin-right: 0em;
}
.ctools-export-ui-row + .ctools-export-ui-row {
margin-top: 1em;
}
.ctools-export-ui-fourth-row input {
margin-top: 5px !important;
}
/* @end */
/* @group Jump list */
#views-live-preview .ctools-jump-menu-select{
max-width: 450px;
}
/* @end */
+13
View File
@@ -0,0 +1,13 @@
/**
* The .garland.css file is intended to contain styles that override declarations
* in the Garland theme.
*/
/* @group Lists */
.views-displays .secondary .action-list {
left: auto;
right: 1px;
}
/* @end */
+263
View File
@@ -0,0 +1,263 @@
/**
* The .garland.css file is intended to contain styles that override declarations
* in the Garland theme.
*/
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .region-content .secondary,
.views-displays .region-content .secondary {
padding-bottom: 0;
padding-left: 0;
}
.views-displays .secondary .action-list {
left: 1px;
top: 20px;
}
.views-displays .secondary .action-list li,
.views-displays .secondary a {
border-color: #e9e9e9;
}
.views-displays .secondary a {
font-size: 12px;
padding: 2px 7px;
}
.views-displays ul.secondary li a:hover,
.views-displays ul.secondary li.active a {
border: 1px solid transparent;
padding: 2px 7px;
}
.views-displays .secondary > li a {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.views-displays .secondary > li.open a {
background-image: none;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
border-radius: 5px 5px 0 0;
}
.views-displays .secondary .open > a:hover {
border-color: #e9e9e9 #e9e9e9 #f1f1f1 #e9e9e9;
border-width: 1px 1px 1px 1px;
color: #0071B3;
}
.views-displays .secondary input.form-submit {
font-size: 11px;
}
/* @end */
/* @group Attachment buckets
*
* These are the individual "buckets," or boxes, inside the display settings area
*/
.views-ui-display-tab-bucket h3 {
font-weight: bold;
}
/* @end */
/* @group Modal dialog box
*
* The contents of the popup dialog on the views edit form.
*/
.views-filterable-options .form-type-checkbox {
margin: 0;
}
.views-filterable-options .even .form-type-checkbox {
background-color: #F9F9F9;
}
.views-ui-dialog .ui-dialog-titlebar-close,
.views-ui-dialog #views-ajax-title,
.views-ui-dialog .views-override,
.views-ui-dialog .form-buttons {
background-color: #f6f6f6;
}
/* @end */
/* @group CTools */
/* @group Buttons */
.ctools-button-processed {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding-bottom: 1px;
padding-top: 1px;
}
.ctools-button-processed:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(241, 241, 241, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
}
.ctools-button-processed ol li,
.ctools-button-processed ul li {
margin: 0;
padding-bottom: 0;
}
.views-ui-display-tab-actions .ctools-button-processed input {
margin-right: 0;
}
.ctools-content ul.actions {
padding-bottom: 0;
}
.ctools-dropbutton-processed.open:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
}
.ctools-dropbutton-processed.open {
-moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
-webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
}
.ctools-twisty {
top: 0.6667em;
}
.ctools-dropbutton-processed.open .ctools-twisty {
top: 0.3333em;
}
/* @end */
/* @group Dependent */
.dependent-options,
.form-checkboxes.dependent-options,
.form-radios.dependent-options,
.form-checkboxes .form-item.dependent-options,
.form-radios .form-item.dependent-options {
margin-left: 25px;
}
/* @end */
/* @group Collapsible */
.ctools-toggle {
margin-top: 0.5em;
}
.ctools-toggle,
.views-display-settings .ctools-toggle {
color: #494949;
}
.ctools-toggle.ctools-toggle-collapsed {
margin-top: 0.25em;
}
.views-display-column > .ctools-toggle {
margin-top: 14px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed {
margin-top: 12px;
}
.views-ui-display-tab-actions .ctools-button input {
color: #027AC6;
font-size: 12px;
}
.views-ui-display-tab-actions .ctools-button input:hover,
.views-ui-display-tab-actions .ctools-button input:focus {
color: #0062A0;
}
/* @end */
/* @end */
+43
View File
@@ -0,0 +1,43 @@
/**
* The .seven.css file is intended to contain styles that override declarations
* in the Seven admin theme.
*/
/* @group Forms */
.views-admin .form-submit,
.views-admin a.button {
margin-left: 0;
}
/* @end */
/* @group Lists */
.views-admin .links li {
padding-left: 0;
}
/* @end */
/* @group Attachments */
.views-displays .secondary {
text-align: right;
}
/* @end */
/* @group Attachment details tabs */
.views-display-top ul.secondary {
float: right;
}
.views-displays .secondary .action-list li:first-child {
-moz-border-radius: 7px 0 0 0;
-webkit-border-radius: 7px 0 0 0;
border-radius: 7px 0 0 0;
}
/* @end */
+552
View File
@@ -0,0 +1,552 @@
/**
* The .seven.css file is intended to contain styles that override declarations
* in the Seven admin theme.
*/
/* @group Content */
.views-ui-display-tab-bucket h1,
.views-ui-display-tab-bucket h2,
.views-ui-display-tab-bucket h3,
.views-ui-display-tab-bucket h4,
.views-ui-display-tab-bucket h5 {
margin-bottom: 0;
margin-top: 0;
}
/* @end */
/* @group Forms */
.views-ui-dialog fieldset {
padding-top: 2.5em;
}
fieldset fieldset {
border: medium none;
}
/**
* Seven positions the legend absolutely, but does not have a way to ignore
* fieldsets without a legend so we make one up.
*/
fieldset.fieldset-no-legend {
padding-top: 0;
}
/**
* Being extra safe here and scoping this to the add view wizard form (where
* a layout problem occurs for the Display format fieldset if we don't fix its
* padding), but it's probably safe to just let it apply everywhere.
*/
#views-ui-add-form fieldset fieldset .fieldset-wrapper {
padding-left: 0;
padding-right: 0;
}
.views-display-tab fieldset {
padding: 0 12px;
}
.views-display-tab .fieldset-wrapper {
padding: 10px 12px 12px;
}
.views-display-tab fieldset.box-padding .fieldset-wrapper {
padding: 0;
}
.views-display-tab legend + .fieldset-wrapper {
padding-top: 2.5em;
}
.views-admin .form-item label.option,
#views-ui-preview-form .form-item label.option {
font-size: 1em;
}
#views-ui-preview-form .form-submit {
margin-top: 3px;
}
.views-admin input.form-submit,
.views-ui-dialog input.form-submit,
.views-admin a.button,
.views-ui-dialog a.button {
margin-bottom: 0;
margin-right: 0; /* LTR */
margin-top: 0;
}
/* Override for a button on the edit display screen */
#edit-displays-preview-controls .form-submit {
display: inline-block;
margin-right: 1em;
}
/* Override for filter button on the views list screen */
#ctools-export-ui-list-form .form-submit {
margin-bottom: 0;
}
#ctools-export-ui-list-form .ctools-export-ui-first-row .form-item {
margin-top: 3px;
margin-right: 5px; /* LTR */
}
.form-item,
.form-item .form-item {
margin-bottom: 0;
margin-top: 9px;
padding-bottom: 0;
padding-top: 0;
}
.form-actions {
margin-bottom: 0;
margin-top: 0;
}
.form-item .form-item {
padding-bottom: 0;
padding-top: 0;
}
.form-radios > .form-item {
margin-top: 3px;
}
/* @group Dependent options
*
* Dependent options are identified in CTools dependent.js
*/
/* The .dependent-options.form-item is necessary to supercede the Seven .form-item
* reset declaration that sets the margin to zero.
*/
.dependent-options,
.dependent-options.form-item,
.form-item-options-expose-required,
.form-item-options-expose-label,
.form-item-options-expose-description {
margin-left: 1.5em;
}
.views-admin-dependent .form-item .form-item,
.views-admin-dependent .form-type-checkboxes,
.views-admin-dependent .form-type-radios,
.views-admin-dependent .dependent-options,
.views-admin-dependent .form-item .form-item,
.views-admin-dependent .dependent-options .form-type-select,
.views-admin-dependent .dependent-options .form-type-textfield,
.form-item-options-expose-required,
.form-item-options-expose-label,
.form-item-options-expose-description {
margin-bottom: 6px;
margin-top: 6px;
}
.views-admin-dependent .form-type-radio,
.views-admin-dependent .form-radios .form-item {
margin-bottom: 2px;
margin-top: 2px;
}
/* @end */
/* @end */
/* @group Lists */
.views-admin ul.secondary,
.views-admin .item-list ul {
margin: 0;
padding: 0;
}
.views-admin ul.secondary {
clear: none;
}
.views-displays ul.secondary li a,
.views-displays ul.secondary li.active a,
.views-displays ul.secondary li.active a.active {
padding: 2px 7px 3px;
}
.views-displays ul.secondary li.active a,
.views-displays ul.secondary li.active a.active {
border: 1px solid transparent;
}
.views-admin .links li {
padding-right: 0; /* LTR */
}
.views-admin .button .links li {
padding-right: 12px; /* LTR */
}
.page-admin-structure-views #content ul.action-links {
padding-left: 0;
padding-right: 0;
}
.views-display-top ul.secondary {
background-color: transparent;
float: left
}
.views-display-top .secondary .action-list li {
float: none;
margin: 0;
}
/* @end */
/* @group Buttons */
.ctools-button-processed ul {
margin: 0;
}
/* Override for input elements that are themed like ctools-buttons */
.ctools-button-processed input.form-submit:hover {
background-image: none;
color: #0074BD;
text-shadow: none;
}
.ctools-button-processed input.form-submit:active {
background: none;
border: medium none;
color: #0074BD;
text-shadow: none;
}
/* @end */
/* @group Tables */
table td,
table th {
vertical-align: top;
}
/* @end */
/* @group Attachment details */
#edit-display-settings-title {
color: #008BCB;
}
/* @end */
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .secondary {
text-align: left; /* LTR */
}
.views-displays .secondary > li:first-child {
padding-left: 0;
}
.views-admin .icon.add {
background-position: center 3px;
}
.views-displays .secondary a {
background-color: #f1f1f1;
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border-radius: 7px;
color: #008BCB;
}
.views-displays .secondary a:hover > .icon.add {
background-position: center -25px;
}
.views-displays .secondary .open > a {
-moz-border-radius: 7px 7px 0 0;
-webkit-border-radius: 7px 7px 0 0;
border-radius: 7px 7px 0 0;
}
.views-displays .secondary .open > a:hover {
background-color: #f1f1f1;
color: #008BCB;
}
.views-displays .secondary .action-list li:first-child {
-moz-border-radius: 0 7px 0 0;
-webkit-border-radius: 0 7px 0 0;
border-radius: 0 7px 0 0;
}
.views-displays .secondary .action-list li:last-child {
-moz-border-radius: 0 0 7px 7px;
-webkit-border-radius: 0 0 7px 7px;
border-radius: 0 0 7px 7px;
}
.views-displays .secondary .action-list input.form-submit {
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
color: #008BCB;
}
/* @end */
/* @group Attachment buckets
*
* These are the individual "buckets," or boxes, inside the display settings area
*/
.views-ui-display-tab-bucket h3 {
font-size: 12px;
text-transform: uppercase;
}
.views-ui-display-tab-bucket .links {
padding: 2px 6px 4px;
}
.views-ui-display-tab-bucket .links li + li {
margin-left: 3px;
}
/* @end */
/* @group Rearrange filter criteria */
#views-ui-rearrange-filter-form .action-links {
margin: 0;
padding: 0;
}
#views-ui-rearrange-filter-form table {
border: medium none;
}
#views-ui-rearrange-filter-form [id^="views-row"] {
border: medium none;
}
#views-ui-rearrange-filter-form tr td:last-child {
border-right: medium none;
}
#views-ui-rearrange-filter-form .filter-group-operator-row {
border-left: 1px solid transparent !important;
border-right: 1px solid transparent !important;
}
#views-ui-rearrange-filter-form tr.drag td {
background-color: #FFEE77 !important;
}
#views-ui-rearrange-filter-form tr.drag-previous td {
background-color: #FFFFBB !important;
}
/* @end */
/* @group Live preview elements */
.views-query-info pre {
margin-bottom: 0;
margin-top: 0;
}
/* @group Query info table */
.views-query-info table {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border-radius: 7px;
-webkit-border-horizontal-spacing: 1px;
-webkit-border-vertical-spacing: 1px;
}
.views-query-info table tr td:last-child {
/* Fixes a Seven style that bleeds down into this table unnecessarily */
border-right: 0 none;
}
/* @end */
/* @end */
/* @group Add view */
.form-item-page-create,
.form-item-block-create {
margin-top: 13px;
}
/* @end */
/* @group Modal dialog box
*
* The contents of the popup dialog on the views edit form.
*/
.views-ui-dialog .ui-dialog-titlebar-close {
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
border-color: #cccccc;
right: -27px;
top: -1px;
}
.views-ui-dialog fieldset.collapsible {
padding-top: 1.5em;
}
.views-ui-dialog fieldset.collapsed {
padding-top: 2.5em;
}
.filterable-option .form-item.form-type-checkbox {
/* This selector is aggressive because Seven's reset for .form-items is aggressive. */
padding-bottom: 4px;
padding-left: 4px;
padding-top: 4px;
}
/* @end */
/* @group CTools */
/* @group Buttons */
.ctools-button-processed {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
-moz-border-radius: 11px 11px 11px 11px;
-webkit-border-radius: 11px 11px 11px 11px;
border-radius: 11px 11px 11px 11px;
}
.ctools-button-processed:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(241, 241, 241, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f1f1f1 100%);
}
.ctools-dropbutton-processed.open:hover {
background-image:
-moz-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, rgba(255, 255, 255, 1.0)),
color-stop(1.0, rgba(249, 249, 249, 1.0))
);
background-image:
-webkit-linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
background-image:
linear-gradient(
-90deg,
#ffffff 0px,
#f9f9f9 100%);
}
.ctools-dropbutton-processed.open {
-moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
-webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
box-shadow: 1px 1px 2px rgba(0,0,0,0.25);
}
/* @end */
/* @group Collapsible */
.ctools-toggle {
margin-top: 0.6667em;
}
.ctools-toggle.ctools-toggle-collapsed {
margin-top: 0.5em;
}
.views-display-settings .ctools-toggle {
color: #008BCB;
}
.views-display-column > .ctools-toggle {
margin-top: 14px;
}
.views-display-column > .ctools-toggle.ctools-toggle-collapsed {
margin-top: 12px;
}
.views-display-column > .ctools-collapsible-handle {
color: #008BCB;
}
.views-ui-display-tab-actions .ctools-button-processed input {
color: #0074BD;
}
/* @end */
/* @end */
+208
View File
@@ -0,0 +1,208 @@
/**
* The .theme.css file is intended to contain presentation declarations including
* images, borders, colors, and fonts.
*/
/* @end */
/* @group Icons */
.actions a,
.views-admin .icon,
.views-admin .icon-text {
background-position: right top;
}
/* Targets any element with an icon -> text combo */
.views-admin .icon-text {
padding-right: 19px;
}
.views-admin .icon-linked {
background-position: right -153px;
}
.views-admin .icon-unlinked {
background-position: right -195px;
}
.actions .views-button-add {
background-position: right -39px;
}
.actions .views-button-rearrange {
background-position: right -96px;
}
.actions .views-button-add:hover {
background-position: right -58px;
}
.actions .views-button-rearrange:hover {
background-position: right -115px;
}
.actions .views-button-add:active {
background-position: right -77px;
}
.actions .views-button-rearrange:active {
background-position: right -134px;
}
.views-displays .icon-add {
background-position: right -3px;
}
.views-displays .secondary a:hover > .icon-add {
background-position: right -21px;
}
.views-displays .secondary .open a:hover > .icon-add {
background-position: right -3px;
}
/* @end */
/* @group Forms */
.form-submit + .form-submit,
.views-admin a.button + a.button {
margin-right: 1em;
}
.container-inline > * + *,
.container-inline .fieldset-wrapper > * + * {
padding-left: 0;
padding-right: 4pt;
}
.views-admin .form-type-checkbox + .form-wrapper {
margin-right: 16px;
}
/* @end */
/* @group Lists */
.horizontal > * + * {
margin-right: 9px;
padding-right: 9px;
}
/* @end */
/* @group Attachments */
.views-displays .secondary {
padding: 6px 8px 8px;
}
.views-displays .views-display-top > ul > li + li {
margin-right: 3px;
}
.views-displays .views-extra-actions {
left: 10px;
}
/* @end */
/* @group Attachment details tabs
*
* The tabs that switch between sections
*/
.views-displays .secondary .action-list li:first-child {
-moz-border-radius: 7px 0 0 0;
-webkit-border-top-left-radius: 7px;
-webkit-border-top-right-radius: 0;
border-radius: 7px 0 0 0;
}
/* @end */
/* @group Attachment details collapsible fieldset
*
* The attachment details section is a collapsible fieldset, but should not
* have a border around it.
*/
.views-display-tab .fieldset-legend {
left: auto;
right: -5px;
}
/* @end */
/* @group Auto preview
*
* The auto-preview checkbox line. This may have more stuff added to it.
*/
div.form-item-displays-live-preview {
text-align: left;
}
/* @end */
/* @group Attachment buckets
*
* These are the individual "buckets," or boxes, inside the three columns in the
* attachment details section.
*/
.views-ui-display-tab-bucket .icon-text {
padding-right: 25px;
}
/* @end */
/* @group Attachment bucket rows
*
* This is each row within one of the "boxes."
*/
.views-display-setting .label {
margin-left: 3pt;
}
/* @end */
/* @group Modal dialog box
*
* The contents of the popup dialog on the views edit form.
*/
#views-filterable-options-controls .form-item {
margin-left: 2%;
}
.views-ui-dialog #views-progress-indicator {
left: 10px;
right: auto;
}
/* @end */
/* @group Rearrange filters
*
* Styling for the form that allows views filters to be rearranged.
*/
.views-operator-label {
padding-right: 0.5em;
}
/* @end */
/* @group Live preview elements */
/* @group HTML list */
#views-live-preview .view-content > .item-list > ul {
padding-right: 21px;
}
/* @end */
/* @end */
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
.views-exposed-form .views-exposed-widget {
float: right; /* RTL */
padding: .5em 1em 0 0; /* RTL */
}
+42
View File
@@ -0,0 +1,42 @@
.views-exposed-form .views-exposed-widget {
float: left; /* LTR */
padding: .5em 1em 0 0; /* LTR */
}
.views-exposed-form .views-exposed-widget .form-submit {
margin-top: 1.6em;
}
.views-exposed-form .form-item,
.views-exposed-form .form-submit {
margin-top: 0;
margin-bottom: 0;
}
.views-exposed-form label {
font-weight: bold;
}
.views-exposed-widgets {
margin-bottom: .5em;
}
/* table style column align */
.views-align-left {
text-align: left;
}
.views-align-right {
text-align: right;
}
.views-align-center {
text-align: center;
}
/* Remove the border on tbody that system puts in */
.views-view-grid tbody {
border-top: none;
}
.view .progress-disabled {
float: none;
}
+5
View File
@@ -0,0 +1,5 @@
- If the interface text is *bolded*, it got strong tags.
- If it's a button they need to click, that's *bold* too.
- If the text is not bolded (ex: links to click, options to check), it
got /italicized/.
- If it's user-entered text it got 'single quotes'.
+496
View File
@@ -0,0 +1,496 @@
<?php
/**
* @file
* Drush integration of views.
*
* drush cache-clear views - Clears the views specific caches.
* views-revert - Drush command to revert views overridden in the system.
*/
/**
* Implement hook_drush_help().
*/
function views_drush_help($section) {
switch ($section) {
case 'drush:views-revert':
$help = dt('Reverts views in the drupal installation that have been overriden. ');
$help .= dt('If no view names are specified, you will be presented with a list of overridden views to choose from. ');
$help .= dt('To revert all views, do not specify any view names, and choose the option "All" from the options presented.');
return $help;
case 'drush:views-list':
return dt('Show a list of available views with information about them.');
case 'drush:views-enable':
return dt('Enable the specified views. Follow the command with a space delimited list of view names');
case 'drush:views-disable':
return dt('Disable the specified views. Follow the command with a space delimited list of view names');
}
}
/**
* Implement hook_drush_command().
*/
function views_drush_command() {
$items = array();
$items['views-revert'] = array(
'callback' => 'views_revert_views',
'drupal dependencies' => array('views'),
'description' => 'Revert overridden views to their default state. Make sure to backup first.',
'arguments' => array(
'views' => 'A space delimited list of view names.',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
'aliases' => array('vr'),
'examples' => array(
'drush vr archive' => 'Reverts the "archive" view.',
'drush rln archive frontpage' => 'Reverts the "archive" and "frontpage" view.',
'drush vr' => 'Will present you with a list of overridden views to choose from, and an option to revert all overridden views.',
),
);
$items['views-dev'] = array(
'callback' => 'views_development_settings',
'drupal dependencies' => array('views'),
'description' => 'Set the Views settings to more developer-oriented values.',
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
'aliases' => array('vd'),
);
$items['views-list'] = array(
'drupal dependencies' => array('views'),
'description' => 'Get a list of all views in the system.',
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
'aliases' => array('vl'),
'options' => array(
'name' => 'String contained in view\'s name by which filter the results.',
'tags' => 'A comma-separated list of views tags by which to filter the results.',
'status' => 'Status of the views by which to filter the results. Choices: enabled, disabled.',
'type' => 'Type of the views by which to filter the results. Choices: normal, default or overridden.',
),
'examples' => array(
'drush vl' => 'Show a list of all available views.',
'drush vl --name=blog' => 'Show a list of views which names contain "blog".',
'drush vl --tags=tag1,tag2' => 'Show a list of views tagged with "tag1" or "tag2".',
'drush vl --status=enabled' => 'Show a list of enabled views.',
'drush vl --type=overridden' => 'Show a list of overridden views.',
),
);
$items['views-analyze'] = array(
'drupal dependencies' => array('views', 'views_ui'),
'description' => 'Get a list of all Views analyze warnings',
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
'aliases' => array('va'),
);
$items['views-enable'] = array(
'drupal dependencies' => array('views'),
'description' => 'Enable the specified views.',
'arguments' => array(
'views' => 'A space delimited list of view names.',
),
'aliases' => array('ven'),
'examples' => array(
'drush ven frontpage taxonomy_term' => 'Enable the frontpage and taxonomy_term views.',
),
);
$items['views-disable'] = array(
'drupal dependencies' => array('views'),
'description' => 'Disable the specified views.',
'arguments' => array(
'views' => 'A space delimited list of view names.',
),
'aliases' => array('vdis'),
'examples' => array(
'drush vdis frontpage taxonomy_term' => 'Disable the frontpage and taxonomy_term views.',
),
);
return $items;
}
/**
* Callback function for views-revert command.
*/
function views_revert_views() {
$views = views_get_all_views();
$i = 0;
// The provided views names specified in the command.
$viewnames = _convert_csv_to_array(func_get_args());
// Find all overridden views.
foreach ($views as $view) {
if ($view->disabled) {
continue;
}
if ($view->type == dt('Overridden')) {
$overridden[$view->name] = $view->name;
}
}
// Return early if there are no overridden views in the system.
if (empty($overridden)) {
return drush_set_error(dt('There are no overridden views in the system.'));
}
// If the user specified in the command the views to be overridden.
if (!empty($viewnames)) {
foreach ($viewnames as $key => $viewname) {
$is_overridden = key_exists($viewname, $overridden);
// Check if the provided view name is in the system
if ($viewname && !key_exists($viewname, $views)) {
drush_set_error(dt("'@viewname' view is not present in the system.", array('@viewname' => $viewname)));
}
// Check if the provided view is overridden.
elseif (!$is_overridden) {
drush_set_error(dt("The view specified '@viewname' is not overridden.", array('@viewname' => $viewname)));
}
// If the view is overriden, revert it.
elseif ($is_overridden){
views_revert_view($views[$viewname]);
$i++;
}
// We should never get here but well...
else {
drush_set_error(dt("The view specified '@viewname' is not provided in code, and thus cannot be reverted.", array('@viewname' => $viewname)));
}
}
}
// The user did not specify any views in the command, prompt the user
else {
// list of choices for the user
$overridden['all'] = dt('Revert all overridden views'); // add a choice at the end
$choice = drush_choice($overridden, 'Enter a number to choose which view to revert.', '!key'); // prompt the user
if ($choice !== FALSE) {
// revert all views option
if ($choice == 'all') {
$i = views_revert_allviews($views);
}
// else the user specified a single view
else {
views_revert_view($views[$choice]);
$i++;
}
}
}
// final results output
if ($i == 0) {
drush_log(dt('No views were reverted.'), 'ok');
}
else {
drush_log(dt('Reverted a total of @count views.', array('@count' => $i)), 'ok');
}
}
/**
* Reverts all views
* @param $views
* All views in the system as provided by views_get_all_views().
*/
function views_revert_allviews($views) {
$i = 0;
foreach ($views as $view) {
if ($view->disabled) {
continue;
}
if ($view->type == t('Overridden')) {
views_revert_view($view);
$i++;
}
}
return $i;
}
/**
* Revert a specified view
* @param $view
* The view object to be reverted
*
* Checks on wether or not the view is overridden is handled in views_revert_views_revert()
* We perform a check here anyway in case someone somehow calls this function on their own...
*/
function views_revert_view($view) {
// check anyway just in case
if ($view->type == t('Overridden')) {
// Revert the view.
$view->delete();
// Clear its cache.
ctools_include('object-cache');
ctools_object_cache_clear('view', $view->name);
// Give feedback.
$message = dt("Reverted the view '@viewname'", array('@viewname' => $view->name));
drush_log($message, 'success');
// Reverted one more view.
}
else {
drush_set_error(dt("The view '@viewname' is not overridden.", array('@viewname' => $view->name)));
}
}
/**
* Change the settings to a more developer oriented value.
*/
function views_development_settings() {
variable_set('views_ui_show_listing_filters', TRUE);
variable_set('views_ui_show_master_display', TRUE);
variable_set('views_ui_show_advanced_column', TRUE);
variable_set('views_ui_always_live_preview', FALSE);
variable_set('views_ui_always_live_preview_button', TRUE);
variable_set('views_ui_show_preview_information', TRUE);
variable_set('views_ui_show_sql_query', TRUE);
variable_set('views_ui_show_performance_statistics', TRUE);
variable_set('views_show_additional_queries', TRUE);
variable_set('views_devel_output', TRUE);
variable_set('views_devel_region', 'message');
variable_set('views_ui_display_embed', TRUE);
$message = dt("Setup the new views settings.");
drush_log($message, 'success');
}
/**
* Callback function for views-list command.
*/
function drush_views_list() {
// Initialize stuf
$rows = array();
$disabled_views = array();
$enabled_views = array();
$overridden = 0;
$indb = 0;
$incode = 0;
$disabled = 0;
$total = 0;
$views = views_get_all_views();
// get the --name option
// TODO : take into account the case off a comma-separated list of names
$name = drush_get_option_list('name');
$with_name = !empty($name) ? TRUE : FALSE;
// get the --tags option
$tags = drush_get_option_list('tags');
$with_tags = !empty($tags) ? TRUE : FALSE;
// get the --status option
// store user input appart to reuse it after
$status_opt = drush_get_option_list('status');
// use the same logic than $view->disabled
if (in_array('disabled', $status_opt)) {
$status = TRUE;
$with_status = TRUE;
}
elseif (in_array('enabled', $status_opt)) {
$status = FALSE;
$with_status = TRUE;
}
else {
$status = NULL;
// wrong or empty --status option
$with_status = FALSE;
}
// get the --type option
$type = drush_get_option_list('type');
// use the same logic than $view->type
$with_type = FALSE;
if (in_array('normal', $type) || in_array('default', $type)|| in_array('overridden', $type)) {
$with_type = TRUE;
}
// set the table headers
$header = array(
dt('Machine name'),
dt('Description'),
dt('Type'),
dt('Status'),
dt('Tag'),
);
// setup a row for each view
foreach($views as $id => $view){
// if options were specified, check that first
// mismatch push the loop to the next view
if ($with_tags && !in_array($view->tag, $tags)) {
continue;
}
if ($with_status && !$view->disabled == $status) {
continue;
}
if ($with_type && strtolower($view->type) !== $type[0]) {
continue;
}
if ($with_name && !stristr($view->name, $name[0])) {
continue;
}
$row = array();
// each row entry should be in the same order as the header
$row[] = $view->name;
$row[] = $view->description;
$row[] = $view->type;
$row[] = $view->disabled ? dt('Disabled') : dt('Enabled');
$row[] = $view->tag;
// place the row in the appropiate array,
// so we can have disabled views at the bottom
if($view->disabled) {
$disabled_views[] = $row;
}
else{
$enabled_views[] = $row;
}
unset($row);
// gather some statistics
switch($view->type) {
case dt('Normal'):
$indb++;
break;
case dt('Overridden'):
$overridden++;
break;
case dt('Default'):
$incode++;
break;
}
$total++;
}
$disabled = count($disabled_views);
// sort alphabeticaly
asort($disabled_views);
asort($enabled_views);
// if options were used
$summary = "";
if ($with_name || $with_tags || $with_status || $with_type) {
$summary = "Views";
if ($with_name) {
$summary .= " named $name[0]";
}
if ($with_tags) {
$tags = implode(" or ", $tags);
$summary .= " tagged $tags";
}
if ($with_status) {
$status_opt = implode("", $status_opt);
$summary .= " which status is '$status_opt'";
}
if ($with_type) {
$type = ucfirst($type[0]);
$summary .= " of type '$type'";
}
}
if (!empty($summary)) {
drush_print($summary . "\n");
}
// print all rows as a table
if ($total > 0) {
$rows = array_merge($enabled_views, $disabled_views);
// put the headers as first row
array_unshift($rows, $header);
drush_print_table($rows, TRUE);
}
// print the statistics messages
drush_print(dt("A total of @total views were found in this Drupal installation:", array('@total' => $total)));
drush_print(dt(" @indb views reside only in the database", array('@indb' => $indb )));
drush_print(dt(" @over views are overridden", array('@over' => $overridden)));
drush_print(dt(" @incode views are in their default state", array('@incode' => $incode)));
drush_print(dt(" @dis views are disabled\n", array('@dis' => $disabled)));
}
function drush_views_analyze() {
views_include('analyze');
$messages_count = 0;
$total = 0;
foreach (views_get_all_views() as $view_name => $view) {
$total++;
if ($messages = views_analyze_view($view)) {
drush_print($view_name);
foreach ($messages as $message) {
$messages_count++;
drush_print($message['type'] .': '. $message['message'], 2);
}
}
}
drush_log(dt('A total of @total views were analyzed and @messages problems were found.', array('@total' => $total, '@messages' => $messages_count)), 'ok');
}
/**
* Enables views
*/
function drush_views_enable() {
$viewnames = _convert_csv_to_array(func_get_args());
// Return early if no view names were specified.
if (empty($viewnames)) {
return drush_set_error(dt('Please specify a space delimited list of view names to enable'));
}
_views_drush_changestatus($viewnames, FALSE);
}
/**
* Disables views
*/
function drush_views_disable() {
$viewnames = _convert_csv_to_array(func_get_args());
// Return early if no view names were specified.
if (empty($viewnames)) {
return drush_set_error(dt('Please specify a space delimited list of view names to disable'));
}
_views_drush_changestatus($viewnames, TRUE);
}
/**
* Helper function to enable / disable views
* @param $viewnames: array of viewnames to process
* @param $status: TRUE to disable or FALSE to enable the view
*/
function _views_drush_changestatus($viewnames = array(), $status = NULL) {
if ($status !== NULL && !empty($viewnames)) {
$changed = FALSE;
$processed = $status ? dt('disabled') : dt('enabled');
$views_status = variable_get('views_defaults', array());
foreach ($viewnames as $key => $viewname) {
if ($views_status[$viewname] !== $status) {
$views_status[$viewname] = $status;
$changed = TRUE;
drush_log(dt("The view '!name' has been !processed", array('!name' => $viewname, '!processed' => $processed)), 'success');
}
else {
drush_set_error(dt("The view '!name' is already !processed", array('!name' => $viewname, '!processed' => $processed)));
}
}
// If we made changes to views status, save them and clear caches
if ($changed) {
variable_set('views_defaults', $views_status);
views_invalidate_cache();
drush_log(dt("Views cache was cleared"), 'ok');
drush_log(dt("Menu cache is set to be rebuilt on the next request."), 'ok');
}
}
}
/**
* Adds a cache clear option for views.
*/
function views_drush_cache_clear(&$types) {
$types['views'] = 'views_invalidate_cache';
}
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* @file
* Views area handlers.
*/
/**
* @defgroup views_area_handlers Views area handlers
* @{
* Handlers to tell Views what can display in header, footer
* and empty text in a view.
*/
/**
* Base class for area handlers.
*
* @ingroup views_area_handlers
*/
class views_handler_area extends views_handler {
/**
* Overrides views_handler::init().
*
* Make sure that no result area handlers are set to be shown when the result
* is empty.
*/
function init(&$view, &$options) {
parent::init($view, $options);
if ($this->handler_type == 'empty') {
$this->options['empty'] = TRUE;
}
}
/**
* Get this field's label.
*/
function label() {
if (!isset($this->options['label'])) {
return $this->ui_name();
}
return $this->options['label'];
}
function option_definition() {
$options = parent::option_definition();
$this->definition['field'] = !empty($this->definition['field']) ? $this->definition['field'] : '';
$label = !empty($this->definition['label']) ? $this->definition['label'] : $this->definition['field'];
$options['label'] = array('default' => $label, 'translatable' => TRUE);
$options['empty'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* Provide extra data to the administration form
*/
function admin_summary() {
return $this->label();
}
/**
* Default options form that provides the label widget that all fields
* should have.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['label'] = array(
'#type' => 'textfield',
'#title' => t('Label'),
'#default_value' => isset($this->options['label']) ? $this->options['label'] : '',
'#description' => t('The label for this area that will be displayed only administratively.'),
);
if ($form_state['type'] != 'empty') {
$form['empty'] = array(
'#type' => 'checkbox',
'#title' => t('Display even if view has no result'),
'#default_value' => isset($this->options['empty']) ? $this->options['empty'] : 0,
);
}
}
/**
* Don't run a query
*/
function query() { }
/**
* Render the area
*/
function render($empty = FALSE) {
return '';
}
/**
* Area handlers shouldn't have groupby.
*/
function use_group_by() {
return FALSE;
}
}
/**
* A special handler to take the place of missing or broken handlers.
*
* @ingroup views_area_handlers
*/
class views_handler_area_broken extends views_handler_area {
function ui_name($short = FALSE) {
return t('Broken/missing handler');
}
function ensure_my_table() { /* No table to ensure! */ }
function query($group_by = FALSE) { /* No query to run */ }
function render($empty = FALSE) { return ''; }
function options_form(&$form, &$form_state) {
$form['markup'] = array(
'#prefix' => '<div class="form-item description">',
'#value' => t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.'),
);
}
/**
* Determine if the handler is considered 'broken'
*/
function broken() { return TRUE; }
}
/**
* @}
*/
+96
View File
@@ -0,0 +1,96 @@
<?php
/**
* @file
* Definition of views_handler_area_result.
*/
/**
* Views area handler to display some configurable result summary.
*
* @ingroup views_area_handlers
*/
class views_handler_area_result extends views_handler_area {
function option_definition() {
$options = parent::option_definition();
$options['content'] = array(
'default' => 'Displaying @start - @end of @total',
'translatable' => TRUE,
);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$variables = array(
'items' => array(
'@start -- the initial record number in the set',
'@end -- the last record number in the set',
'@total -- the total records in the set',
'@name -- the human-readable name of the view',
'@per_page -- the number of items per page',
'@current_page -- the current page number',
'@current_record_count -- the current page record count',
'@page_count -- the total page count',
),
);
$list = theme('item_list', $variables);
$form['content'] = array(
'#title' => t('Display'),
'#type' => 'textarea',
'#rows' => 3,
'#default_value' => $this->options['content'],
'#description' => t('You may use HTML code in this field. The following tokens are supported:') . $list,
);
}
/**
* Find out the information to render.
*/
function render($empty = FALSE) {
// Must have options and does not work on summaries.
if (!isset($this->options['content']) || $this->view->plugin_name == 'default_summary') {
return;
}
$output = '';
$format = $this->options['content'];
// Calculate the page totals.
$current_page = (int) $this->view->get_current_page() + 1;
$per_page = (int) $this->view->get_items_per_page();
$count = count($this->view->result);
// @TODO: Maybe use a possible is views empty functionality.
// Not every view has total_rows set, use view->result instead.
$total = isset($this->view->total_rows) ? $this->view->total_rows : count($this->view->result);
$name = check_plain($this->view->human_name);
if ($per_page === 0) {
$page_count = 1;
$start = 1;
$end = $total;
}
else {
$page_count = (int) ceil($total / $per_page);
$total_count = $current_page * $per_page;
if ($total_count > $total) {
$total_count = $total;
}
$start = ($current_page - 1) * $per_page + 1;
$end = $total_count;
}
$current_record_count = ($end - $start) + 1;
// Get the search information.
$items = array('start', 'end', 'total', 'name', 'per_page', 'current_page', 'current_record_count', 'page_count');
$replacements = array();
foreach ($items as $item) {
$replacements["@$item"] = ${$item};
}
// Send the output.
if (!empty($total)) {
$output .= filter_xss_admin(str_replace(array_keys($replacements), array_values($replacements), $format));
}
return $output;
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
/**
* @file
* Definition of views_handler_area_text.
*/
/**
* Views area text handler.
*
* @ingroup views_area_handlers
*/
class views_handler_area_text extends views_handler_area {
function option_definition() {
$options = parent::option_definition();
$options['content'] = array('default' => '', 'translatable' => TRUE, 'format_key' => 'format');
$options['format'] = array('default' => NULL);
$options['tokenize'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['content'] = array(
'#type' => 'text_format',
'#default_value' => $this->options['content'],
'#rows' => 6,
'#format' => isset($this->options['format']) ? $this->options['format'] : filter_default_format(),
'#wysiwyg' => FALSE,
);
// @TODO: Refactor token handling into a base class.
$form['tokenize'] = array(
'#type' => 'checkbox',
'#title' => t('Use replacement tokens from the first row'),
'#default_value' => $this->options['tokenize'],
);
// Get a list of the available fields and arguments for token replacement.
$options = array();
foreach ($this->view->display_handler->get_handlers('field') as $field => $handler) {
$options[t('Fields')]["[$field]"] = $handler->ui_name();
}
$count = 0; // This lets us prepare the key as we want it printed.
foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
$options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
$options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
}
if (!empty($options)) {
$output = '<p>' . t('The following tokens are available. If you would like to have the characters \'[\' and \']\' please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.' . '</p>');
foreach (array_keys($options) as $type) {
if (!empty($options[$type])) {
$items = array();
foreach ($options[$type] as $key => $value) {
$items[] = $key . ' == ' . $value;
}
$output .= theme('item_list',
array(
'items' => $items,
'type' => $type
));
}
}
$form['token_help'] = array(
'#type' => 'fieldset',
'#title' => t('Replacement patterns'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#value' => $output,
'#id' => 'edit-options-token-help',
'#dependency' => array(
'edit-options-tokenize' => array(1),
),
'#prefix' => '<div>',
'#suffix' => '</div>',
);
}
}
function options_submit(&$form, &$form_state) {
$form_state['values']['options']['format'] = $form_state['values']['options']['content']['format'];
$form_state['values']['options']['content'] = $form_state['values']['options']['content']['value'];
parent::options_submit($form, $form_state);
}
function render($empty = FALSE) {
$format = isset($this->options['format']) ? $this->options['format'] : filter_default_format();
if (!$empty || !empty($this->options['empty'])) {
return $this->render_textarea($this->options['content'], $format);
}
return '';
}
/**
* Render a text area, using the proper format.
*/
function render_textarea($value, $format) {
if ($value) {
if ($this->options['tokenize']) {
$value = $this->view->style_plugin->tokenize_value($value, 0);
}
return check_markup($value, $format, '', FALSE);
}
}
}
@@ -0,0 +1,56 @@
<?php
/**
* @file
* Definition of views_handler_area_text_custom.
*/
/**
* Views area text custom handler.
*
* @ingroup views_area_handlers
*/
class views_handler_area_text_custom extends views_handler_area_text {
function option_definition() {
$options = parent::option_definition();
unset($options['format']);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// Alter the form element, to be a regular text area.
$form['content']['#type'] = 'textarea';
unset($form['content']['#format']);
unset($form['content']['#wysiwyg']);
// @TODO: Use the token refactored base class.
}
// Empty, so we don't inherit options_submit from the parent.
function options_submit(&$form, &$form_state) {
}
function render($empty = FALSE) {
if (!$empty || !empty($this->options['empty'])) {
return $this->render_textarea_custom($this->options['content']);
}
return '';
}
/**
* Render a text area with filter_xss_admin.
*/
function render_textarea_custom($value) {
if ($value) {
if ($this->options['tokenize']) {
$value = $this->view->style_plugin->tokenize_value($value, 0);
}
return $this->sanitize_value($value, 'xss_admin');
}
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
/**
* @file
* Definition of views_handler_area_view.
*/
/**
* Views area handlers. Insert a view inside of an area.
*
* @ingroup views_area_handlers
*/
class views_handler_area_view extends views_handler_area {
function option_definition() {
$options = parent::option_definition();
$options['view_to_insert'] = array('default' => '');
$options['inherit_arguments'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* Default options form that provides the label widget that all fields
* should have.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$view_display = $this->view->name . ':' . $this->view->current_display;
$options = array('' => t('-Select-'));
$options += views_get_views_as_options(FALSE, 'all', $view_display, FALSE, TRUE);
$form['view_to_insert'] = array(
'#type' => 'select',
'#title' => t('View to insert'),
'#default_value' => $this->options['view_to_insert'],
'#description' => t('The view to insert into this area.'),
'#options' => $options,
);
$form['inherit_arguments'] = array(
'#type' => 'checkbox',
'#title' => t('Inherit contextual filters'),
'#default_value' => $this->options['inherit_arguments'],
'#description' => t('If checked, this view will receive the same contextual filters as its parent.'),
);
}
/**
* Render the area
*/
function render($empty = FALSE) {
if (!empty($this->options['view_to_insert'])) {
list($view_name, $display_id) = explode(':', $this->options['view_to_insert']);
$view = views_get_view($view_name);
if (empty($view) || !$view->access($display_id)) {
return;
}
$view->set_display($display_id);
// Avoid recursion
$view->parent_views += $this->view->parent_views;
$view->parent_views[] = "$view_name:$display_id";
// Check if the view is part of the parent views of this view
$search = "$view_name:$display_id";
if (in_array($search, $this->view->parent_views)) {
drupal_set_message(t("Recursion detected in view @view display @display.", array('@view' => $view_name, '@display' => $display_id)), 'error');
}
else {
if (!empty($this->options['inherit_arguments']) && !empty($this->view->args)) {
return $view->preview($display_id, $this->view->args);
}
else {
return $view->preview($display_id);
}
}
}
return '';
}
}
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
<?php
/**
* @file
* Definition of views_handler_argument_date.
*/
/**
* Abstract argument handler for dates.
*
* Adds an option to set a default argument based on the current date.
*
* @param $arg_format
* The format string to use on the current time when
* creating a default date argument.
*
* Definitions terms:
* - many to one: If true, the "many to one" helper will be used.
* - invalid input: A string to give to the user for obviously invalid input.
* This is deprecated in favor of argument validators.
*
* @see views_many_to_one_helper()
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_date extends views_handler_argument_formula {
var $option_name = 'default_argument_date';
var $arg_format = 'Y-m-d';
/**
* Add an option to set the default value to the current date.
*/
function default_argument_form(&$form, &$form_state) {
parent::default_argument_form($form, $form_state);
$form['default_argument_type']['#options'] += array('date' => t('Current date'));
$form['default_argument_type']['#options'] += array('node_created' => t("Current node's creation time"));
$form['default_argument_type']['#options'] += array('node_changed' => t("Current node's update time")); }
/**
* Set the empty argument value to the current date,
* formatted appropriately for this argument.
*/
function get_default_argument($raw = FALSE) {
if (!$raw && $this->options['default_argument_type'] == 'date') {
return date($this->arg_format, REQUEST_TIME);
}
else if (!$raw && in_array($this->options['default_argument_type'], array('node_created', 'node_changed'))) {
foreach (range(1, 3) as $i) {
$node = menu_get_object('node', $i);
if (!empty($node)) {
continue;
}
}
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
}
if (empty($node)) {
return parent::get_default_argument();
}
elseif ($this->options['default_argument_type'] == 'node_created') {
return date($this->arg_format, $node->created);
}
elseif ($this->options['default_argument_type'] == 'node_changed') {
return date($this->arg_format, $node->changed);
}
}
return parent::get_default_argument($raw);
}
/**
* The date handler provides some default argument types, which aren't argument default plugins,
* so addapt the export mechanism.
*/
function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
// Only use a special behaviour for the special argument types, else just
// use the default behaviour.
if ($option == 'default_argument_type') {
$type = 'argument default';
$option_name = 'default_argument_options';
$plugin = $this->get_plugin($type);
$name = $this->options[$option];
if (in_array($name, array('date', 'node_created', 'node_changed'))) {
// Write which plugin to use.
$output = $indent . $prefix . "['$option'] = '$name';\n";
return $output;
}
}
return parent::export_plugin($indent, $prefix, $storage, $option, $definition, $parents);
}
function get_sort_name() {
return t('Date', array(), array('context' => 'Sort order'));
}
}
@@ -0,0 +1,63 @@
<?php
/**
* @file
* Definition of views_handler_argument_formula.
*/
/**
* Abstract argument handler for simple formulae.
*
* Child classes of this object should implement summary_argument, at least.
*
* Definition terms:
* - formula: The formula to use for this handler.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_formula extends views_handler_argument {
var $formula = NULL;
/**
* Constructor
*/
function construct() {
parent::construct();
if (!empty($this->definition['formula'])) {
$this->formula = $this->definition['formula'];
}
}
function get_formula() {
return str_replace('***table***', $this->table_alias, $this->formula);
}
/**
* Build the summary query based on a formula
*/
function summary_query() {
$this->ensure_my_table();
// Now that our table is secure, get our formula.
$formula = $this->get_formula();
// Add the field.
$this->base_alias = $this->name_alias = $this->query->add_field(NULL, $formula, $this->field);
$this->query->set_count_field(NULL, $formula, $this->field);
return $this->summary_basics(FALSE);
}
/**
* Build the query based upon the formula
*/
function query($group_by = FALSE) {
$this->ensure_my_table();
// Now that our table is secure, get our formula.
$placeholder = $this->placeholder();
$formula = $this->get_formula() .' = ' . $placeholder;
$placeholders = array(
$placeholder => $this->argument,
);
$this->query->add_where(0, $formula, $placeholders, 'formula');
}
}
@@ -0,0 +1,29 @@
<?php
/**
* @file
* Definition of views_handler_argument_group_by_numeric.
*/
/**
* Simple handler for arguments using group by.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_group_by_numeric extends views_handler_argument {
function query($group_by = FALSE) {
$this->ensure_my_table();
$field = $this->get_field();
$placeholder = $this->placeholder();
$this->query->add_having_expression(0, "$field = $placeholder", array($placeholder => $this->argument));
}
function ui_name($short = FALSE) {
return $this->get_field(parent::ui_name($short));
}
function get_sort_name() {
return t('Numerical', array(), array('context' => 'Sort order'));
}
}
@@ -0,0 +1,185 @@
<?php
/**
* @file
* Definition of views_handler_argument_many_to_one.
*/
/**
* An argument handler for use in fields that have a many to one relationship
* with the table(s) to the left. This adds a bunch of options that are
* reasonably common with this type of relationship.
* Definition terms:
* - numeric: If true, the field will be considered numeric. Probably should
* always be set TRUE as views_handler_argument_string has many to one
* capabilities.
* - zero is null: If true, a 0 will be handled as empty, so for example
* a default argument can be provided or a summary can be shown.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_many_to_one extends views_handler_argument {
function init(&$view, &$options) {
parent::init($view, $options);
$this->helper = new views_many_to_one_helper($this);
// Ensure defaults for these, during summaries and stuff:
$this->operator = 'or';
$this->value = array();
}
function option_definition() {
$options = parent::option_definition();
if (!empty($this->definition['numeric'])) {
$options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
}
$options['add_table'] = array('default' => FALSE, 'bool' => TRUE);
$options['require_value'] = array('default' => FALSE, 'bool' => TRUE);
if (isset($this->helper)) {
$this->helper->option_definition($options);
}
else {
$helper = new views_many_to_one_helper($this);
$helper->option_definition($options);
}
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// allow + for or, , for and
if (!empty($this->definition['numeric'])) {
$form['break_phrase'] = array(
'#type' => 'checkbox',
'#title' => t('Allow multiple values'),
'#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
'#default_value' => !empty($this->options['break_phrase']),
'#fieldset' => 'more',
);
}
$form['add_table'] = array(
'#type' => 'checkbox',
'#title' => t('Allow multiple filter values to work together'),
'#description' => t('If selected, multiple instances of this filter can work together, as though multiple values were supplied to the same filter. This setting is not compatible with the "Reduce duplicates" setting.'),
'#default_value' => !empty($this->options['add_table']),
'#fieldset' => 'more',
);
$form['require_value'] = array(
'#type' => 'checkbox',
'#title' => t('Do not display items with no value in summary'),
'#default_value' => !empty($this->options['require_value']),
'#fieldset' => 'more',
);
$this->helper->options_form($form, $form_state);
}
/**
* Override ensure_my_table so we can control how this joins in.
* The operator actually has influence over joining.
*/
function ensure_my_table() {
$this->helper->ensure_my_table();
}
function query($group_by = FALSE) {
$empty = FALSE;
if (isset($this->definition['zero is null']) && $this->definition['zero is null']) {
if (empty($this->argument)) {
$empty = TRUE;
}
}
else {
if (!isset($this->argument)) {
$empty = TRUE;
}
}
if ($empty) {
parent::ensure_my_table();
$this->query->add_where(0, "$this->table_alias.$this->real_field", NULL, 'IS NULL');
return;
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
$this->operator = 'or';
}
$this->helper->add_filter();
}
function title() {
if (!$this->argument) {
return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
$this->operator = 'or';
}
// @todo -- both of these should check definition for alternate keywords.
if (empty($this->value)) {
return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
}
if ($this->value === array(-1)) {
return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
}
return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
}
function summary_query() {
$field = $this->table . '.' . $this->field;
$join = $this->get_join();
if (!empty($this->options['require_value'])) {
$join->type = 'INNER';
}
if (empty($this->options['add_table']) || empty($this->view->many_to_one_tables[$field])) {
$this->table_alias = $this->query->ensure_table($this->table, $this->relationship, $join);
}
else {
$this->table_alias = $this->helper->summary_join();
}
// Add the field.
$this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
$this->summary_name_field();
return $this->summary_basics();
}
function summary_argument($data) {
$value = $data->{$this->base_alias};
if (empty($value)) {
$value = 0;
}
return $value;
}
/**
* Override for specific title lookups.
*/
function title_query() {
return $this->value;
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
/**
* @file
* Definition of views_handler_argument_null.
*/
/**
* Argument handler that ignores the argument.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_null extends views_handler_argument {
function option_definition() {
$options = parent::option_definition();
$options['must_not_be'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* Override options_form() so that only the relevant options
* are displayed to the user.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['must_not_be'] = array(
'#type' => 'checkbox',
'#title' => t('Fail basic validation if any argument is given'),
'#default_value' => !empty($this->options['must_not_be']),
'#description' => t('By checking this field, you can use this to make sure views with more arguments than necessary fail validation.'),
'#fieldset' => 'more',
);
unset($form['exception']);
}
/**
* Override default_actions() to remove actions that don't
* make sense for a null argument.
*/
function default_actions($which = NULL) {
if ($which) {
if (in_array($which, array('ignore', 'not found', 'empty', 'default'))) {
return parent::default_actions($which);
}
return;
}
$actions = parent::default_actions();
unset($actions['summary asc']);
unset($actions['summary desc']);
return $actions;
}
function validate_argument_basic($arg) {
if (!empty($this->options['must_not_be'])) {
return !isset($arg);
}
return parent::validate_argument_basic($arg);
}
/**
* Override the behavior of query() to prevent the query
* from being changed in any way.
*/
function query($group_by = FALSE) {}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
/**
* @file
* Definition of views_handler_argument_numeric.
*/
/**
* Basic argument handler for arguments that are numeric. Incorporates
* break_phrase.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_numeric extends views_handler_argument {
/**
* The operator used for the query: or|and.
* @var string
*/
var $operator;
/**
* The actual value which is used for querying.
* @var array
*/
var $value;
function option_definition() {
$options = parent::option_definition();
$options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
$options['not'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// allow + for or, , for and
$form['break_phrase'] = array(
'#type' => 'checkbox',
'#title' => t('Allow multiple values'),
'#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
'#default_value' => !empty($this->options['break_phrase']),
'#fieldset' => 'more',
);
$form['not'] = array(
'#type' => 'checkbox',
'#title' => t('Exclude'),
'#description' => t('If selected, the numbers entered for the filter will be excluded rather than limiting the view.'),
'#default_value' => !empty($this->options['not']),
'#fieldset' => 'more',
);
}
function title() {
if (!$this->argument) {
return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
$this->operator = 'or';
}
if (empty($this->value)) {
return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
}
if ($this->value === array(-1)) {
return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
}
return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
}
/**
* Override for specific title lookups.
* @return array
* Returns all titles, if it's just one title it's an array with one entry.
*/
function title_query() {
return $this->value;
}
function query($group_by = FALSE) {
$this->ensure_my_table();
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
}
$placeholder = $this->placeholder();
$null_check = empty($this->options['not']) ? '' : "OR $this->table_alias.$this->real_field IS NULL";
if (count($this->value) > 1) {
$operator = empty($this->options['not']) ? 'IN' : 'NOT IN';
$this->query->add_where_expression(0, "$this->table_alias.$this->real_field $operator($placeholder) $null_check", array($placeholder => $this->value));
}
else {
$operator = empty($this->options['not']) ? '=' : '!=';
$this->query->add_where_expression(0, "$this->table_alias.$this->real_field $operator $placeholder $null_check", array($placeholder => $this->argument));
}
}
function get_sort_name() {
return t('Numerical', array(), array('context' => 'Sort order'));
}
}
+274
View File
@@ -0,0 +1,274 @@
<?php
/**
* @file
* Definition of views_handler_argument_string.
*/
/**
* Basic argument handler to implement string arguments that may have length
* limits.
*
* @ingroup views_argument_handlers
*/
class views_handler_argument_string extends views_handler_argument {
function init(&$view, &$options) {
parent::init($view, $options);
if (!empty($this->definition['many to one'])) {
$this->helper = new views_many_to_one_helper($this);
// Ensure defaults for these, during summaries and stuff:
$this->operator = 'or';
$this->value = array();
}
}
function option_definition() {
$options = parent::option_definition();
$options['glossary'] = array('default' => FALSE, 'bool' => TRUE);
$options['limit'] = array('default' => 0);
$options['case'] = array('default' => 'none');
$options['path_case'] = array('default' => 'none');
$options['transform_dash'] = array('default' => FALSE, 'bool' => TRUE);
$options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
if (!empty($this->definition['many to one'])) {
$options['add_table'] = array('default' => FALSE, 'bool' => TRUE);
$options['require_value'] = array('default' => FALSE, 'bool' => TRUE);
}
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['glossary'] = array(
'#type' => 'checkbox',
'#title' => t('Glossary mode'),
'#description' => t('Glossary mode applies a limit to the number of characters used in the filter value, which allows the summary view to act as a glossary.'),
'#default_value' => $this->options['glossary'],
'#fieldset' => 'more',
);
$form['limit'] = array(
'#type' => 'textfield',
'#title' => t('Character limit'),
'#description' => t('How many characters of the filter value to filter against. If set to 1, all fields starting with the first letter in the filter value would be matched.'),
'#default_value' => $this->options['limit'],
'#dependency' => array('edit-options-glossary' => array(TRUE)),
'#fieldset' => 'more',
);
$form['case'] = array(
'#type' => 'select',
'#title' => t('Case'),
'#description' => t('When printing the title and summary, how to transform the case of the filter value.'),
'#options' => array(
'none' => t('No transform'),
'upper' => t('Upper case'),
'lower' => t('Lower case'),
'ucfirst' => t('Capitalize first letter'),
'ucwords' => t('Capitalize each word'),
),
'#default_value' => $this->options['case'],
'#fieldset' => 'more',
);
$form['path_case'] = array(
'#type' => 'select',
'#title' => t('Case in path'),
'#description' => t('When printing url paths, how to transform the case of the filter value. Do not use this unless with Postgres as it uses case sensitive comparisons.'),
'#options' => array(
'none' => t('No transform'),
'upper' => t('Upper case'),
'lower' => t('Lower case'),
'ucfirst' => t('Capitalize first letter'),
'ucwords' => t('Capitalize each word'),
),
'#default_value' => $this->options['path_case'],
'#fieldset' => 'more',
);
$form['transform_dash'] = array(
'#type' => 'checkbox',
'#title' => t('Transform spaces to dashes in URL'),
'#default_value' => $this->options['transform_dash'],
'#fieldset' => 'more',
);
if (!empty($this->definition['many to one'])) {
$form['add_table'] = array(
'#type' => 'checkbox',
'#title' => t('Allow multiple filter values to work together'),
'#description' => t('If selected, multiple instances of this filter can work together, as though multiple values were supplied to the same filter. This setting is not compatible with the "Reduce duplicates" setting.'),
'#default_value' => !empty($this->options['add_table']),
'#fieldset' => 'more',
);
$form['require_value'] = array(
'#type' => 'checkbox',
'#title' => t('Do not display items with no value in summary'),
'#default_value' => !empty($this->options['require_value']),
'#fieldset' => 'more',
);
}
// allow + for or, , for and
$form['break_phrase'] = array(
'#type' => 'checkbox',
'#title' => t('Allow multiple values'),
'#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
'#default_value' => !empty($this->options['break_phrase']),
'#fieldset' => 'more',
);
}
/**
* Build the summary query based on a string
*/
function summary_query() {
if (empty($this->definition['many to one'])) {
$this->ensure_my_table();
}
else {
$this->table_alias = $this->helper->summary_join();
}
if (empty($this->options['glossary'])) {
// Add the field.
$this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
$this->query->set_count_field($this->table_alias, $this->real_field);
}
else {
// Add the field.
$formula = $this->get_formula();
$this->base_alias = $this->query->add_field(NULL, $formula, $this->field . '_truncated');
$this->query->set_count_field(NULL, $formula, $this->field, $this->field . '_truncated');
}
$this->summary_name_field();
return $this->summary_basics(FALSE);
}
/**
* Get the formula for this argument.
*
* $this->ensure_my_table() MUST have been called prior to this.
*/
function get_formula() {
return "SUBSTRING($this->table_alias.$this->real_field, 1, " . intval($this->options['limit']) . ")";
}
/**
* Build the query based upon the formula
*/
function query($group_by = FALSE) {
$argument = $this->argument;
if (!empty($this->options['transform_dash'])) {
$argument = strtr($argument, '-', ' ');
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase_string($argument, $this);
}
else {
$this->value = array($argument);
$this->operator = 'or';
}
if (!empty($this->definition['many to one'])) {
if (!empty($this->options['glossary'])) {
$this->helper->formula = TRUE;
}
$this->helper->ensure_my_table();
$this->helper->add_filter();
return;
}
$this->ensure_my_table();
$formula = FALSE;
if (empty($this->options['glossary'])) {
$field = "$this->table_alias.$this->real_field";
}
else {
$formula = TRUE;
$field = $this->get_formula();
}
if (count($this->value) > 1) {
$operator = 'IN';
$argument = $this->value;
}
else {
$operator = '=';
}
if ($formula) {
$placeholder = $this->placeholder();
if ($operator == 'IN') {
$field .= " IN($placeholder)";
}
else {
$field .= ' = ' . $placeholder;
}
$placeholders = array(
$placeholder => $argument,
);
$this->query->add_where_expression(0, $field, $placeholders);
}
else {
$this->query->add_where(0, $field, $argument, $operator);
}
}
function summary_argument($data) {
$value = $this->case_transform($data->{$this->base_alias}, $this->options['path_case']);
if (!empty($this->options['transform_dash'])) {
$value = strtr($value, ' ', '-');
}
return $value;
}
function get_sort_name() {
return t('Alphabetical', array(), array('context' => 'Sort order'));
}
function title() {
$this->argument = $this->case_transform($this->argument, $this->options['case']);
if (!empty($this->options['transform_dash'])) {
$this->argument = strtr($this->argument, '-', ' ');
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase_string($this->argument, $this);
}
else {
$this->value = array($this->argument);
$this->operator = 'or';
}
if (empty($this->value)) {
return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
}
if ($this->value === array(-1)) {
return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
}
return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
}
/**
* Override for specific title lookups.
*/
function title_query() {
return drupal_map_assoc($this->value, 'check_plain');
}
function summary_name($data) {
return $this->case_transform(parent::summary_name($data), $this->options['case']);
}
}
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* @file
* Definition of views_handler_field_boolean.
*/
/**
* A handler to provide proper displays for booleans.
*
* Allows for display of true/false, yes/no, on/off, enabled/disabled.
*
* Definition terms:
* - output formats: An array where the first entry is displayed on boolean true
* and the second is displayed on boolean false. An example for sticky is:
* @code
* 'output formats' => array(
* 'sticky' => array(t('Sticky'), ''),
* ),
* @endcode
*
* @ingroup views_field_handlers
*/
class views_handler_field_boolean extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['type'] = array('default' => 'yes-no');
$options['not'] = array('definition bool' => 'reverse');
return $options;
}
function init(&$view, &$options) {
parent::init($view, $options);
$default_formats = array(
'yes-no' => array(t('Yes'), t('No')),
'true-false' => array(t('True'), t('False')),
'on-off' => array(t('On'), t('Off')),
'enabled-disabled' => array(t('Enabled'), t('Disabled')),
'boolean' => array(1, 0),
'unicode-yes-no' => array('✔', '✖'),
);
$output_formats = isset($this->definition['output formats']) ? $this->definition['output formats'] : array();
$this->formats = array_merge($default_formats, $output_formats);
}
function options_form(&$form, &$form_state) {
foreach ($this->formats as $key => $item) {
$options[$key] = implode('/', $item);
}
$form['type'] = array(
'#type' => 'select',
'#title' => t('Output format'),
'#options' => $options,
'#default_value' => $this->options['type'],
);
$form['not'] = array(
'#type' => 'checkbox',
'#title' => t('Reverse'),
'#description' => t('If checked, true will be displayed as false.'),
'#default_value' => $this->options['not'],
);
parent::options_form($form, $form_state);
}
function render($values) {
$value = $this->get_value($values);
if (!empty($this->options['not'])) {
$value = !$value;
}
if (isset($this->formats[$this->options['type']])) {
return $value ? $this->formats[$this->options['type']][0] : $this->formats[$this->options['type']][1];
}
else {
return $value ? $this->formats['yes-no'][0] : $this->formats['yes-no'][1];
}
}
}
@@ -0,0 +1,102 @@
<?php
/**
* @file
* Definition of views_handler_field_contextual_links.
*/
/**
* Provides a handler that adds contextual links.
*
* @ingroup views_field_handlers
*/
class views_handler_field_contextual_links extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['fields'] = array('default' => array());
$options['destination'] = array('default' => TRUE, 'bool' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
$all_fields = $this->view->display_handler->get_field_labels();
// Offer to include only those fields that follow this one.
$field_options = array_slice($all_fields, 0, array_search($this->options['id'], array_keys($all_fields)));
$form['fields'] = array(
'#type' => 'checkboxes',
'#title' => t('Fields'),
'#description' => t('Fields to be included as contextual links.'),
'#options' => $field_options,
'#default_value' => $this->options['fields'],
);
$form['destination'] = array(
'#type' => 'checkbox',
'#title' => t('Include destination'),
'#description' => t('Include a "destination" parameter in the link to return the user to the original view upon completing the contextual action.'),
'#default_value' => $this->options['destination'],
);
}
function pre_render(&$values) {
// Add a row plugin css class for the contextual link.
$class = 'contextual-links-region';
if (!empty($this->view->style_plugin->options['row_class'])) {
$this->view->style_plugin->options['row_class'] .= " $class";
}
else {
$this->view->style_plugin->options['row_class'] = $class;
}
}
/**
* Render the contextual fields.
*/
function render($values) {
$links = array();
foreach ($this->options['fields'] as $field) {
if (empty($this->view->style_plugin->rendered_fields[$this->view->row_index][$field])) {
continue;
}
$title = $this->view->field[$field]->last_render_text;
$path = '';
if (!empty($this->view->field[$field]->options['alter']['path'])) {
$path = $this->view->field[$field]->options['alter']['path'];
}
if (!empty($title) && !empty($path)) {
// Make sure that tokens are replaced for this paths as well.
$tokens = $this->get_render_tokens(array());
$path = strip_tags(decode_entities(strtr($path, $tokens)));
$links[$field] = array(
'href' => $path,
'title' => $title,
);
if (!empty($this->options['destination'])) {
$links[$field]['query'] = drupal_get_destination();
}
}
}
if (!empty($links)) {
$build = array(
'#prefix' => '<div class="contextual-links-wrapper">',
'#suffix' => '</div>',
'#theme' => 'links__contextual',
'#links' => $links,
'#attributes' => array('class' => array('contextual-links')),
'#attached' => array(
'library' => array(array('contextual', 'contextual-links')),
),
'#access' => user_access('access contextual links'),
);
return drupal_render($build);
}
else {
return '';
}
}
function query() { }
}
+50
View File
@@ -0,0 +1,50 @@
<?php
/**
* @file
* Definition of views_handler_field_counter.
*/
/**
* Field handler to show a counter of the current row.
*
* @ingroup views_field_handlers
*/
class views_handler_field_counter extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['counter_start'] = array('default' => 1);
return $options;
}
function options_form(&$form, &$form_state) {
$form['counter_start'] = array(
'#type' => 'textfield',
'#title' => t('Starting value'),
'#default_value' => $this->options['counter_start'],
'#description' => t('Specify the number the counter should start at.'),
'#size' => 2,
);
parent::options_form($form, $form_state);
}
function query() {
// do nothing -- to override the parent query.
}
function render($values) {
// Note: 1 is subtracted from the counter start value below because the
// counter value is incremented by 1 at the end of this function.
$count = is_numeric($this->options['counter_start']) ? $this->options['counter_start'] - 1 : 0;
$pager = $this->view->query->pager;
// Get the base count of the pager.
if ($pager->use_pager()) {
$count += ($pager->get_items_per_page() * $pager->get_current_page() + $pager->get_offset());
}
// Add the counter for the current site.
$count += $this->view->row_index + 1;
return $count;
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* @file
* Definition of views_handler_field_custom.
*/
/**
* A handler to provide a field that is completely custom by the administrator.
*
* @ingroup views_field_handlers
*/
class views_handler_field_custom extends views_handler_field {
function query() {
// do nothing -- to override the parent query.
}
function option_definition() {
$options = parent::option_definition();
// Override the alter text option to always alter the text.
$options['alter']['contains']['alter_text'] = array('default' => TRUE, 'bool' => TRUE);
$options['hide_alter_empty'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// Remove the checkbox
unset($form['alter']['alter_text']);
unset($form['alter']['text']['#dependency']);
unset($form['alter']['text']['#process']);
unset($form['alter']['help']['#dependency']);
unset($form['alter']['help']['#process']);
$form['#pre_render'][] = 'views_handler_field_custom_pre_render_move_text';
}
function render($values) {
// Return the text, so the code never thinks the value is empty.
return $this->options['alter']['text'];
}
}
/**
* Prerender function to move the textarea to the top.
*/
function views_handler_field_custom_pre_render_move_text($form) {
$form['text'] = $form['alter']['text'];
$form['help'] = $form['alter']['help'];
unset($form['alter']['text']);
unset($form['alter']['help']);
return $form;
}
+101
View File
@@ -0,0 +1,101 @@
<?php
/**
* @file
* Definition of views_handler_field_date.
*/
/**
* A handler to provide proper displays for dates.
*
* @ingroup views_field_handlers
*/
class views_handler_field_date extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['date_format'] = array('default' => 'small');
$options['custom_date_format'] = array('default' => '');
$options['timezone'] = array('default' => '');
return $options;
}
function options_form(&$form, &$form_state) {
$date_formats = array();
$date_types = system_get_date_types();
foreach ($date_types as $key => $value) {
$date_formats[$value['type']] = t('@date_format format', array('@date_format' => $value['title'])) . ': ' . format_date(REQUEST_TIME, $value['type']);
}
$form['date_format'] = array(
'#type' => 'select',
'#title' => t('Date format'),
'#options' => $date_formats + array(
'custom' => t('Custom'),
'raw time ago' => t('Time ago'),
'time ago' => t('Time ago (with "ago" appended)'),
'raw time hence' => t('Time hence'),
'time hence' => t('Time hence (with "hence" appended)'),
'raw time span' => t('Time span (future dates have "-" prepended)'),
'inverse time span' => t('Time span (past dates have "-" prepended)'),
'time span' => t('Time span (with "ago/hence" appended)'),
),
'#default_value' => isset($this->options['date_format']) ? $this->options['date_format'] : 'small',
);
$form['custom_date_format'] = array(
'#type' => 'textfield',
'#title' => t('Custom date format'),
'#description' => t('If "Custom", see the <a href="@url" target="_blank">PHP manual</a> for date formats. Otherwise, enter the number of different time units to display, which defaults to 2.', array('@url' => 'http://php.net/manual/function.date.php')),
'#default_value' => isset($this->options['custom_date_format']) ? $this->options['custom_date_format'] : '',
'#dependency' => array('edit-options-date-format' => array('custom', 'raw time ago', 'time ago', 'raw time hence', 'time hence', 'raw time span', 'time span', 'raw time span', 'inverse time span', 'time span')),
);
$form['timezone'] = array(
'#type' => 'select',
'#title' => t('Timezone'),
'#description' => t('Timezone to be used for date output.'),
'#options' => array('' => t('- Default site/user timezone -')) + system_time_zones(FALSE),
'#default_value' => $this->options['timezone'],
'#dependency' => array('edit-options-date-format' => array_merge(array('custom'), array_keys($date_formats))),
);
parent::options_form($form, $form_state);
}
function render($values) {
$value = $this->get_value($values);
$format = $this->options['date_format'];
if (in_array($format, array('custom', 'raw time ago', 'time ago', 'raw time hence', 'time hence', 'raw time span', 'time span', 'raw time span', 'inverse time span', 'time span'))) {
$custom_format = $this->options['custom_date_format'];
}
if ($value) {
$timezone = !empty($this->options['timezone']) ? $this->options['timezone'] : NULL;
$time_diff = REQUEST_TIME - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
switch ($format) {
case 'raw time ago':
return format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2);
case 'time ago':
return t('%time ago', array('%time' => format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2)));
case 'raw time hence':
return format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2);
case 'time hence':
return t('%time hence', array('%time' => format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2)));
case 'raw time span':
return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
case 'inverse time span':
return ($time_diff > 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
case 'time span':
return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2)));
case 'custom':
if ($custom_format == 'r') {
return format_date($value, $format, $custom_format, $timezone, 'en');
}
return format_date($value, $format, $custom_format, $timezone);
default:
return format_date($value, $format, '', $timezone);
}
}
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
/**
* @file
* Definition of views_handler_field_entity.
*/
/**
* A handler to display data from entity objects.
*
* Fields based upon this handler work with all query-backends if the tables
* used by the query backend have an 'entity type' specified. In order to
* make fields based upon this handler automatically available to all compatible
* query backends, the views field can be defined in the table
* @code views_entity_{ENTITY_TYPE} @endcode.
*
* @ingroup views_field_handlers
*/
class views_handler_field_entity extends views_handler_field {
/**
* Stores the entity type which is loaded by this field.
*/
public $entity_type;
/**
* Stores all entites which are in the result.
*/
public $entities;
/**
* The base field of the entity type assosiated with this field.
*/
public $base_field;
/**
* Initialize the entity type.
*/
public function init(&$view, &$options) {
parent::init($view, $options);
// Initialize the entity-type used.
$table_data = views_fetch_data($this->table);
$this->entity_type = $table_data['table']['entity type'];
}
/**
* Overriden to add the field for the entity id.
*/
function query() {
$this->table_alias = $base_table = $this->view->base_table;
$this->base_field = $this->view->base_field;
if (!empty($this->relationship)) {
foreach ($this->view->relationship as $relationship) {
if ($relationship->alias == $this->relationship) {
$base_table = $relationship->definition['base'];
$this->table_alias = $relationship->alias;
$table_data = views_fetch_data($base_table);
$this->base_field = empty($relationship->definition['base field']) ? $table_data['table']['base']['field'] : $relationship->definition['base field'];
}
}
}
// Add the field if the query back-end implements an add_field() method,
// just like the default back-end.
if (method_exists($this->query, 'add_field')) {
$this->field_alias = $this->query->add_field($this->table_alias, $this->base_field, '');
}
$this->add_additional_fields();
}
/**
* Load the entities for all rows that are about to be displayed.
*/
function pre_render(&$values) {
if (!empty($values)) {
list($this->entity_type, $this->entities) = $this->query->get_result_entities($values, !empty($this->relationship) ? $this->relationship : NULL, $this->field_alias);
}
}
/**
* Overridden to return the entity object, or a certain property of the entity.
*/
function get_value($values, $field = NULL) {
if (isset($this->entities[$this->view->row_index])) {
$entity = $this->entities[$this->view->row_index];
// Support to get a certain part of the entity.
if (isset($field) && isset($entity->{$field})) {
return $entity->{$field};
}
// Support to get a part of the values as the normal get_value.
elseif (isset($field) && isset($values->{$this->aliases[$field]})) {
return $values->{$this->aliases[$field]};
}
else {
return $entity;
}
}
return FALSE;
}
}
@@ -0,0 +1,73 @@
<?php
/**
* @file
* Definition of views_handler_field_machine_name.
*/
/**
* Field handler whichs allows to show machine name content as human name.
* @ingroup views_field_handlers
*
* Definition items:
* - options callback: The function to call in order to generate the value options. If omitted, the options 'Yes' and 'No' will be used.
* - options arguments: An array of arguments to pass to the options callback.
*/
class views_handler_field_machine_name extends views_handler_field {
/**
* @var array Stores the available options.
*/
var $value_options;
function get_value_options() {
if (isset($this->value_options)) {
return;
}
if (isset($this->definition['options callback']) && is_callable($this->definition['options callback'])) {
if (isset($this->definition['options arguments']) && is_array($this->definition['options arguments'])) {
$this->value_options = call_user_func_array($this->definition['options callback'], $this->definition['options arguments']);
}
else {
$this->value_options = call_user_func($this->definition['options callback']);
}
}
else {
$this->value_options = array();
}
}
function option_definition() {
$options = parent::option_definition();
$options['machine_name'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['machine_name'] = array(
'#title' => t('Output machine name'),
'#description' => t('Display field as machine name.'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['machine_name']),
);
}
function pre_render(&$values) {
$this->get_value_options();
}
function render($values) {
$value = $values->{$this->field_alias};
if (!empty($this->options['machine_name']) || !isset($this->value_options[$value])) {
$result = check_plain($value);
}
else {
$result = $this->value_options[$value];
}
return $result;
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* @file
* Definition of views_handler_field_markup.
*/
/**
* A handler to run a field through check_markup, using a companion
* format field.
*
* - format: (REQUIRED) Either a string format id to use for this field or an
* array('field' => {$field}) where $field is the field in this table
* used to control the format such as the 'format' field in the node,
* which goes with the 'body' field.
*
* @ingroup views_field_handlers
*/
class views_handler_field_markup extends views_handler_field {
/**
* Constructor; calls to base object constructor.
*/
function construct() {
parent::construct();
$this->format = $this->definition['format'];
$this->additional_fields = array();
if (is_array($this->format)) {
$this->additional_fields['format'] = $this->format;
}
}
function render($values) {
$value = $this->get_value($values);
if (is_array($this->format)) {
$format = $this->get_value($values, 'format');
}
else {
$format = $this->format;
}
if ($value) {
$value = str_replace('<!--break-->', '', $value);
return check_markup($value, $format, '');
}
}
function element_type($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) {
if ($inline) {
return 'span';
}
if (isset($this->definition['element type'])) {
return $this->definition['element type'];
}
return 'div';
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
/**
* @file
* Definition of views_handler_field_math.
*/
/**
* Render a mathematical expression as a numeric value
*
* Definition terms:
* - float: If true this field contains a decimal value. If unset this field
* will be assumed to be integer.
*
* @ingroup views_field_handlers
*/
class views_handler_field_math extends views_handler_field_numeric {
function option_definition() {
$options = parent::option_definition();
$options['expression'] = array('default' => '');
return $options;
}
function options_form(&$form, &$form_state) {
$form['expression'] = array(
'#type' => 'textarea',
'#title' => t('Expression'),
'#description' => t('Enter mathematical expressions such as 2 + 2 or sqrt(5). You may assign variables and create mathematical functions and evaluate them. Use the ; to separate these. For example: f(x) = x + 2; f(2).'),
'#default_value' => $this->options['expression'],
);
// Create a place for the help
$form['expression_help'] = array();
parent::options_form($form, $form_state);
// Then move the existing help:
$form['expression_help'] = $form['alter']['help'];
unset($form['expression_help']['#dependency']);
unset($form['alter']['help']);
}
function render($values) {
ctools_include('math-expr');
$tokens = array_map('floatval', $this->get_render_tokens(array()));
$value = strtr($this->options['expression'], $tokens);
$expressions = explode(';', $value);
$math = new ctools_math_expr;
foreach ($expressions as $expression) {
if ($expression !== '') {
$value = $math->evaluate($expression);
}
}
// The rest is directly from views_handler_field_numeric but because it
// does not allow the value to be passed in, it is copied.
if (!empty($this->options['set_precision'])) {
$value = number_format($value, $this->options['precision'], $this->options['decimal'], $this->options['separator']);
}
else {
$remainder = abs($value) - intval(abs($value));
$value = $value > 0 ? floor($value) : ceil($value);
$value = number_format($value, 0, '', $this->options['separator']);
if ($remainder) {
// The substr may not be locale safe.
$value .= $this->options['decimal'] . substr($remainder, 2);
}
}
// Check to see if hiding should happen before adding prefix and suffix.
if ($this->options['hide_empty'] && empty($value) && ($value !== 0 || $this->options['empty_zero'])) {
return '';
}
// Should we format as a plural.
if (!empty($this->options['format_plural']) && ($value != 0 || !$this->options['empty_zero'])) {
$value = format_plural($value, $this->options['format_plural_singular'], $this->options['format_plural_plural']);
}
return $this->sanitize_value($this->options['prefix'] . $value . $this->options['suffix']);
}
function query() { }
}
+137
View File
@@ -0,0 +1,137 @@
<?php
/**
* @file
* Definition of views_handler_field_numeric.
*/
/**
* Render a field as a numeric value
*
* Definition terms:
* - float: If true this field contains a decimal value. If unset this field
* will be assumed to be integer.
*
* @ingroup views_field_handlers
*/
class views_handler_field_numeric extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['set_precision'] = array('default' => FALSE, 'bool' => TRUE);
$options['precision'] = array('default' => 0);
$options['decimal'] = array('default' => '.', 'translatable' => TRUE);
$options['separator'] = array('default' => ',', 'translatable' => TRUE);
$options['format_plural'] = array('default' => FALSE, 'bool' => TRUE);
$options['format_plural_singular'] = array('default' => '1');
$options['format_plural_plural'] = array('default' => '@count');
$options['prefix'] = array('default' => '', 'translatable' => TRUE);
$options['suffix'] = array('default' => '', 'translatable' => TRUE);
return $options;
}
function options_form(&$form, &$form_state) {
if (!empty($this->definition['float'])) {
$form['set_precision'] = array(
'#type' => 'checkbox',
'#title' => t('Round'),
'#description' => t('If checked, the number will be rounded.'),
'#default_value' => $this->options['set_precision'],
);
$form['precision'] = array(
'#type' => 'textfield',
'#title' => t('Precision'),
'#default_value' => $this->options['precision'],
'#description' => t('Specify how many digits to print after the decimal point.'),
'#dependency' => array('edit-options-set-precision' => array(TRUE)),
'#size' => 2,
);
$form['decimal'] = array(
'#type' => 'textfield',
'#title' => t('Decimal point'),
'#default_value' => $this->options['decimal'],
'#description' => t('What single character to use as a decimal point.'),
'#size' => 2,
);
}
$form['separator'] = array(
'#type' => 'select',
'#title' => t('Thousands marker'),
'#options' => array(
'' => t('- None -'),
',' => t('Comma'),
' ' => t('Space'),
'.' => t('Decimal'),
'\'' => t('Apostrophe'),
),
'#default_value' => $this->options['separator'],
'#description' => t('What single character to use as the thousands separator.'),
'#size' => 2,
);
$form['format_plural'] = array(
'#type' => 'checkbox',
'#title' => t('Format plural'),
'#description' => t('If checked, special handling will be used for plurality.'),
'#default_value' => $this->options['format_plural'],
);
$form['format_plural_singular'] = array(
'#type' => 'textfield',
'#title' => t('Singular form'),
'#default_value' => $this->options['format_plural_singular'],
'#description' => t('Text to use for the singular form.'),
'#dependency' => array('edit-options-format-plural' => array(TRUE)),
);
$form['format_plural_plural'] = array(
'#type' => 'textfield',
'#title' => t('Plural form'),
'#default_value' => $this->options['format_plural_plural'],
'#description' => t('Text to use for the plural form, @count will be replaced with the value.'),
'#dependency' => array('edit-options-format-plural' => array(TRUE)),
);
$form['prefix'] = array(
'#type' => 'textfield',
'#title' => t('Prefix'),
'#default_value' => $this->options['prefix'],
'#description' => t('Text to put before the number, such as currency symbol.'),
);
$form['suffix'] = array(
'#type' => 'textfield',
'#title' => t('Suffix'),
'#default_value' => $this->options['suffix'],
'#description' => t('Text to put after the number, such as currency symbol.'),
);
parent::options_form($form, $form_state);
}
function render($values) {
$value = $this->get_value($values);
if (!empty($this->options['set_precision'])) {
$value = number_format($value, $this->options['precision'], $this->options['decimal'], $this->options['separator']);
}
else {
$remainder = abs($value) - intval(abs($value));
$value = $value > 0 ? floor($value) : ceil($value);
$value = number_format($value, 0, '', $this->options['separator']);
if ($remainder) {
// The substr may not be locale safe.
$value .= $this->options['decimal'] . substr($remainder, 2);
}
}
// Check to see if hiding should happen before adding prefix and suffix.
if ($this->options['hide_empty'] && empty($value) && ($value !== 0 || $this->options['empty_zero'])) {
return '';
}
// Should we format as a plural.
if (!empty($this->options['format_plural'])) {
$value = format_plural($value, $this->options['format_plural_singular'], $this->options['format_plural_plural']);
}
return $this->sanitize_value($this->options['prefix'], 'xss')
. $this->sanitize_value($value)
. $this->sanitize_value($this->options['suffix'], 'xss');
}
}
@@ -0,0 +1,158 @@
<?php
/**
* @file
* Definition of views_handler_field_prerender_list.
*/
/**
* Field handler to provide a list of items.
*
* The items are expected to be loaded by a child object during pre_render,
* and 'my field' is expected to be the pointer to the items in the list.
*
* Items to render should be in a list in $this->items
*
* @ingroup views_field_handlers
*/
class views_handler_field_prerender_list extends views_handler_field {
/**
* Stores all items which are used to render the items.
* It should be keyed first by the id of the base table, for example nid.
* The second key is the id of the thing which is displayed multiple times
* per row, for example the tid.
*
* @var array
*/
var $items = array();
function option_definition() {
$options = parent::option_definition();
$options['type'] = array('default' => 'separator');
$options['separator'] = array('default' => ', ');
return $options;
}
function options_form(&$form, &$form_state) {
$form['type'] = array(
'#type' => 'radios',
'#title' => t('Display type'),
'#options' => array(
'ul' => t('Unordered list'),
'ol' => t('Ordered list'),
'separator' => t('Simple separator'),
),
'#default_value' => $this->options['type'],
);
$form['separator'] = array(
'#type' => 'textfield',
'#title' => t('Separator'),
'#default_value' => $this->options['separator'],
'#dependency' => array('radio:options[type]' => array('separator')),
);
parent::options_form($form, $form_state);
}
/**
* Render the field.
*
* This function is deprecated, but left in for older systems that have not
* yet or won't update their prerender list fields. If a render_item method
* exists, this will not get used by advanced_render.
*/
function render($values) {
$field = $this->get_value($values);
if (!empty($this->items[$field])) {
if ($this->options['type'] == 'separator') {
return implode($this->sanitize_value($this->options['separator']), $this->items[$field]);
}
else {
return theme('item_list',
array(
'items' => $this->items[$field],
'title' => NULL,
'type' => $this->options['type']
));
}
}
}
/**
* Render all items in this field together.
*
* When using advanced render, each possible item in the list is rendered
* individually. Then the items are all pasted together.
*/
function render_items($items) {
if (!empty($items)) {
if ($this->options['type'] == 'separator') {
return implode($this->sanitize_value($this->options['separator'], 'xss_admin'), $items);
}
else {
return theme('item_list',
array(
'items' => $items,
'title' => NULL,
'type' => $this->options['type']
));
}
}
}
/**
* Return an array of items for the field.
*
* Items should be stored in the result array, if possible, as an array
* with 'value' as the actual displayable value of the item, plus
* any items that might be found in the 'alter' options array for
* creating links, such as 'path', 'fragment', 'query' etc, such a thing
* is to be made. Additionally, items that might be turned into tokens
* should also be in this array.
*/
function get_items($values) {
// Only the parent get_value returns a single field.
$field = parent::get_value($values);
if (!empty($this->items[$field])) {
return $this->items[$field];
}
return array();
}
/**
* Get the value that's supposed to be rendered.
*
* @param $values
* An object containing all retrieved values.
* @param $field
* Optional name of the field where the value is stored.
* @param $raw
* Use the raw data and not the data defined in pre_render
*/
function get_value($values, $field = NULL, $raw = FALSE) {
if ($raw) {
return parent::get_value($values, $field);
}
$item = $this->get_items($values);
$item = (array) $item;
if (isset($field) && isset($item[$field])) {
return $item[$field];
}
return $item;
}
/**
* Determine if advanced rendering is allowed.
*
* By default, advanced rendering will NOT be allowed if the class
* inheriting from this does not implement a 'render_items' method.
*/
function allow_advanced_render() {
// Note that the advanced render bits also use the presence of
// this method to determine if it needs to render items as a list.
return method_exists($this, 'render_item');
}
}
@@ -0,0 +1,65 @@
<?php
/**
* @file
* Definition of views_handler_field_serialized.
*/
/**
* Field handler to show data of serialized fields.
*
* @ingroup views_field_handlers
*/
class views_handler_field_serialized extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['format'] = array('default' => 'unserialized');
$options['key'] = array('default' => '');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['format'] = array(
'#type' => 'select',
'#title' => t('Display format'),
'#description' => t('How should the serialized data be displayed. You can choose a custom array/object key or a print_r on the full output.'),
'#options' => array(
'unserialized' => t('Full data (unserialized)'),
'serialized' => t('Full data (serialized)'),
'key' => t('A certain key'),
),
'#default_value' => $this->options['format'],
);
$form['key'] = array(
'#type' => 'textfield',
'#title' => t('Which key should be displayed'),
'#default_value' => $this->options['key'],
'#dependency' => array('edit-options-format' => array('key')),
);
}
function options_validate(&$form, &$form_state) {
// Require a key if the format is key.
if ($form_state['values']['options']['format'] == 'key' && $form_state['values']['options']['key'] == '') {
form_error($form['key'], t('You have to enter a key if you want to display a key of the data.'));
}
}
function render($values) {
$value = $values->{$this->field_alias};
if ($this->options['format'] == 'unserialized') {
return check_plain(print_r(unserialize($value), TRUE));
}
elseif ($this->options['format'] == 'key' && !empty($this->options['key'])) {
$value = (array) unserialize($value);
return check_plain($value[$this->options['key']]);
}
return $value;
}
}
@@ -0,0 +1,37 @@
<?php
/**
* @file
* Definition of views_handler_field_time_interval.
*/
/**
* A handler to provide proper displays for time intervals.
*
* @ingroup views_field_handlers
*/
class views_handler_field_time_interval extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['granularity'] = array('default' => 2);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['granularity'] = array(
'#type' => 'textfield',
'#title' => t('Granularity'),
'#description' => t('How many different units to display in the string.'),
'#default_value' => $this->options['granularity'],
);
}
function render($values) {
$value = $values->{$this->field_alias};
return format_interval($value, isset($this->options['granularity']) ? $this->options['granularity'] : 2);
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* @file
* Definition of views_handler_field_url.
*/
/**
* Field handler to provide simple renderer that turns a URL into a clickable link.
*
* @ingroup views_field_handlers
*/
class views_handler_field_url extends views_handler_field {
function option_definition() {
$options = parent::option_definition();
$options['display_as_link'] = array('default' => TRUE, 'bool' => TRUE);
return $options;
}
/**
* Provide link to the page being visited.
*/
function options_form(&$form, &$form_state) {
$form['display_as_link'] = array(
'#title' => t('Display as link'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['display_as_link']),
);
parent::options_form($form, $form_state);
}
function render($values) {
$value = $this->get_value($values);
if (!empty($this->options['display_as_link'])) {
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = $value;
$text = !empty($this->options['text']) ? $this->sanitize_value($this->options['text']) : $this->sanitize_value($value, 'url');
return $text;
}
else {
return $this->sanitize_value($value, 'url');
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,179 @@
<?php
/**
* @file
* Definition of views_handler_filter_boolean_operator.
*/
/**
* Simple filter to handle matching of boolean values
*
* Definition items:
* - label: (REQUIRED) The label for the checkbox.
* - type: For basic 'true false' types, an item can specify the following:
* - true-false: True/false (this is the default)
* - yes-no: Yes/No
* - on-off: On/Off
* - enabled-disabled: Enabled/Disabled
* - accept null: Treat a NULL value as false.
* - use equal: If you use this flag the query will use = 1 instead of <> 0.
* This might be helpful for performance reasons.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_boolean_operator extends views_handler_filter {
// exposed filter options
var $always_multiple = TRUE;
// Don't display empty space where the operator would be.
var $no_operator = TRUE;
// Whether to accept NULL as a false value or not
var $accept_null = FALSE;
function construct() {
$this->value_value = t('True');
if (isset($this->definition['label'])) {
$this->value_value = $this->definition['label'];
}
if (isset($this->definition['accept null'])) {
$this->accept_null = (bool) $this->definition['accept null'];
}
else if (isset($this->definition['accept_null'])) {
$this->accept_null = (bool) $this->definition['accept_null'];
}
$this->value_options = NULL;
parent::construct();
}
/**
* Return the possible options for this filter.
*
* Child classes should override this function to set the possible values
* for the filter. Since this is a boolean filter, the array should have
* two possible keys: 1 for "True" and 0 for "False", although the labels
* can be whatever makes sense for the filter. These values are used for
* configuring the filter, when the filter is exposed, and in the admin
* summary of the filter. Normally, this should be static data, but if it's
* dynamic for some reason, child classes should use a guard to reduce
* database hits as much as possible.
*/
function get_value_options() {
if (isset($this->definition['type'])) {
if ($this->definition['type'] == 'yes-no') {
$this->value_options = array(1 => t('Yes'), 0 => t('No'));
}
if ($this->definition['type'] == 'on-off') {
$this->value_options = array(1 => t('On'), 0 => t('Off'));
}
if ($this->definition['type'] == 'enabled-disabled') {
$this->value_options = array(1 => t('Enabled'), 0 => t('Disabled'));
}
}
// Provide a fallback if the above didn't set anything.
if (!isset($this->value_options)) {
$this->value_options = array(1 => t('True'), 0 => t('False'));
}
}
function option_definition() {
$options = parent::option_definition();
$options['value']['default'] = FALSE;
return $options;
}
function operator_form(&$form, &$form_state) {
$form['operator'] = array();
}
function value_form(&$form, &$form_state) {
if (empty($this->value_options)) {
// Initialize the array of possible values for this filter.
$this->get_value_options();
}
if (!empty($form_state['exposed'])) {
// Exposed filter: use a select box to save space.
$filter_form_type = 'select';
}
else {
// Configuring a filter: use radios for clarity.
$filter_form_type = 'radios';
}
$form['value'] = array(
'#type' => $filter_form_type,
'#title' => $this->value_value,
'#options' => $this->value_options,
'#default_value' => $this->value,
);
if (!empty($this->options['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = $this->value;
}
// If we're configuring an exposed filter, add an <Any> option.
if (empty($form_state['exposed']) || empty($this->options['expose']['required'])) {
$any_label = variable_get('views_exposed_filter_any_label', 'new_any') == 'old_any' ? '<Any>' : t('- Any -');
if ($form['value']['#type'] != 'select') {
$any_label = check_plain($any_label);
}
$form['value']['#options'] = array('All' => $any_label) + $form['value']['#options'];
}
}
}
function value_validate($form, &$form_state) {
if ($form_state['values']['options']['value'] == 'All' && !empty($form_state['values']['options']['expose']['required'])) {
form_set_error('value', t('You must select a value unless this is an non-required exposed filter.'));
}
}
function admin_summary() {
if ($this->is_a_group()) {
return t('grouped');
}
if (!empty($this->options['exposed'])) {
return t('exposed');
}
if (empty($this->value_options)) {
$this->get_value_options();
}
// Now that we have the valid options for this filter, just return the
// human-readable label based on the current value. The value_options
// array is keyed with either 0 or 1, so if the current value is not
// empty, use the label for 1, and if it's empty, use the label for 0.
return $this->value_options[!empty($this->value)];
}
function expose_options() {
parent::expose_options();
$this->options['expose']['operator_id'] = '';
$this->options['expose']['label'] = $this->value_value;
$this->options['expose']['required'] = TRUE;
}
function query() {
$this->ensure_my_table();
$field = "$this->table_alias.$this->real_field";
if (empty($this->value)) {
if ($this->accept_null) {
$or = db_or()
->condition($field, 0, '=')
->condition($field, NULL, 'IS NULL');
$this->query->add_where($this->options['group'], $or);
}
else {
$this->query->add_where($this->options['group'], $field, 0, '=');
}
}
else {
if (!empty($this->definition['use equal'])) {
$this->query->add_where($this->options['group'], $field, 1, '=');
}
else {
$this->query->add_where($this->options['group'], $field, 0, '<>');
}
}
}
}
@@ -0,0 +1,35 @@
<?php
/**
* @file
* Definition of views_handler_filter_boolean_operator_string.
*/
/**
* Simple filter to handle matching of boolean values.
*
* This handler checks to see if a string field is empty (equal to '') or not.
* It is otherwise identical to the parent operator.
*
* Definition items:
* - label: (REQUIRED) The label for the checkbox.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_boolean_operator_string extends views_handler_filter_boolean_operator {
function query() {
$this->ensure_my_table();
$where = "$this->table_alias.$this->real_field ";
if (empty($this->value)) {
$where .= "= ''";
if ($this->accept_null) {
$where = '(' . $where . " OR $this->table_alias.$this->real_field IS NULL)";
}
}
else {
$where .= "<> ''";
}
$this->query->add_where($this->options['group'], $where);
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
/**
* @file
* Definition of views_handler_filter_combine.
*/
/**
* Filter handler which allows to search on multiple fields.
*
* @ingroup views_field_handlers
*/
class views_handler_filter_combine extends views_handler_filter_string {
/**
* @var views_plugin_query_default
*/
public $query;
function option_definition() {
$options = parent::option_definition();
$options['fields'] = array('default' => array());
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$this->view->init_style();
// Allow to choose all fields as possible.
if ($this->view->style_plugin->uses_fields()) {
$options = array();
foreach ($this->view->display_handler->get_handlers('field') as $name => $field) {
$options[$name] = $field->ui_name(TRUE);
}
if ($options) {
$form['fields'] = array(
'#type' => 'select',
'#title' => t('Choose fields to combine for filtering'),
'#description' => t("This filter doesn't work for very special field handlers."),
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => $this->options['fields'],
);
}
else {
form_set_error('', t('You have to add some fields to be able to use this filter.'));
}
}
}
function query() {
$this->view->_build('field');
$fields = array();
// Only add the fields if they have a proper field and table alias.
foreach ($this->options['fields'] as $id) {
$field = $this->view->field[$id];
// Always add the table of the selected fields to be sure a table alias
// exists.
$field->ensure_my_table();
if (!empty($field->field_alias) && !empty($field->field_alias)) {
$fields[] = "$field->table_alias.$field->real_field";
}
}
if ($fields) {
$count = count($fields);
$separated_fields = array();
foreach ($fields as $key => $field) {
$separated_fields[] = $field;
if ($key < $count - 1) {
$separated_fields[] = "' '";
}
}
$expression = implode(', ', $separated_fields);
$expression = "CONCAT_WS(' ', $expression)";
$info = $this->operators();
if (!empty($info[$this->operator]['method'])) {
$this->{$info[$this->operator]['method']}($expression);
}
}
}
// By default things like op_equal uses add_where, that doesn't support
// complex expressions, so override all operators.
function op_equal($field) {
$placeholder = $this->placeholder();
$operator = $this->operator();
$this->query->add_where_expression($this->options['group'], "$field $operator $placeholder", array($placeholder => $this->value));
}
function op_contains($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field LIKE $placeholder", array($placeholder => '%' . db_like($this->value) . '%'));
}
function op_word($field) {
$where = $this->operator == 'word' ? db_or() : db_and();
// Don't filter on empty strings.
if (empty($this->value)) {
return;
}
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$phrase = FALSE;
// Strip off phrase quotes.
if ($match[2]{0} == '"') {
$match[2] = substr($match[2], 1, -1);
$phrase = TRUE;
}
$words = trim($match[2], ',?!();:-');
$words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
$placeholder = $this->placeholder();
foreach ($words as $word) {
$where->where($field . " LIKE $placeholder", array($placeholder => '%' . db_like(trim($word, " ,!?")) . '%'));
}
}
if (!$where) {
return;
}
// Previously this was a call_user_func_array() but that's unnecessary
// as views will unpack an array that is a single arg.
$this->query->add_where($this->options['group'], $where);
}
function op_starts($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field LIKE $placeholder", array($placeholder => db_like($this->value) . '%'));
}
function op_not_starts($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field NOT LIKE $placeholder", array($placeholder => db_like($this->value) . '%'));
}
function op_ends($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field LIKE $placeholder", array($placeholder => '%' . db_like($this->value)));
}
function op_not_ends($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field NOT LIKE $placeholder", array($placeholder => '%' . db_like($this->value)));
}
function op_not($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field NOT LIKE $placeholder", array($placeholder => '%' . db_like($this->value) . '%'));
}
function op_regex($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field RLIKE $placeholder", array($placeholder => $this->value));
}
function op_empty($field) {
if ($this->operator == 'empty') {
$operator = "IS NULL";
}
else {
$operator = "IS NOT NULL";
}
$this->query->add_where_expression($this->options['group'], "$field $operator");
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
/**
* @file
* Definition of views_handler_filter_date.
*/
/**
* Filter to handle dates stored as a timestamp.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_date extends views_handler_filter_numeric {
function option_definition() {
$options = parent::option_definition();
// value is already set up properly, we're just adding our new field to it.
$options['value']['contains']['type']['default'] = 'date';
return $options;
}
/**
* Add a type selector to the value form
*/
function value_form(&$form, &$form_state) {
if (empty($form_state['exposed'])) {
$form['value']['type'] = array(
'#type' => 'radios',
'#title' => t('Value type'),
'#options' => array(
'date' => t('A date in any machine readable format. CCYY-MM-DD HH:MM:SS is preferred.'),
'offset' => t('An offset from the current time such as "!example1" or "!example2"', array('!example1' => '+1 day', '!example2' => '-2 hours -30 minutes')),
),
'#default_value' => !empty($this->value['type']) ? $this->value['type'] : 'date',
);
}
parent::value_form($form, $form_state);
}
function options_validate(&$form, &$form_state) {
parent::options_validate($form, $form_state);
if (!empty($this->options['exposed']) && empty($form_state['values']['options']['expose']['required'])) {
// Who cares what the value is if it's exposed and non-required.
return;
}
$this->validate_valid_time($form['value'], $form_state['values']['options']['operator'], $form_state['values']['options']['value']);
}
function exposed_validate(&$form, &$form_state) {
if (empty($this->options['exposed'])) {
return;
}
if (empty($this->options['expose']['required'])) {
// Who cares what the value is if it's exposed and non-required.
return;
}
$value = &$form_state['values'][$this->options['expose']['identifier']];
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id'])) {
$operator = $form_state['values'][$this->options['expose']['operator_id']];
}
else {
$operator = $this->operator;
}
$this->validate_valid_time($this->options['expose']['identifier'], $operator, $value);
}
/**
* Validate that the time values convert to something usable.
*/
function validate_valid_time(&$form, $operator, $value) {
$operators = $this->operators();
if ($operators[$operator]['values'] == 1) {
$convert = strtotime($value['value']);
if (!empty($form['value']) && ($convert == -1 || $convert === FALSE)) {
form_error($form['value'], t('Invalid date format.'));
}
}
elseif ($operators[$operator]['values'] == 2) {
$min = strtotime($value['min']);
if ($min == -1 || $min === FALSE) {
form_error($form['min'], t('Invalid date format.'));
}
$max = strtotime($value['max']);
if ($max == -1 || $max === FALSE) {
form_error($form['max'], t('Invalid date format.'));
}
}
}
/**
* Validate the build group options form.
*/
function build_group_validate($form, &$form_state) {
// Special case to validate grouped date filters, this is because the
// $group['value'] array contains the type of filter (date or offset)
// and therefore the number of items the comparission has to be done
// against 'one' instead of 'zero'.
foreach ($form_state['values']['options']['group_info']['group_items'] as $id => $group) {
if (empty($group['remove'])) {
// Check if the title is defined but value wasn't defined.
if (!empty($group['title'])) {
if ((!is_array($group['value']) && empty($group['value'])) || (is_array($group['value']) && count(array_filter($group['value'])) == 1)) {
form_error($form['group_info']['group_items'][$id]['value'], t('The value is required if title for this item is defined.'));
}
}
// Check if the value is defined but title wasn't defined.
if ((!is_array($group['value']) && !empty($group['value'])) || (is_array($group['value']) && count(array_filter($group['value'])) > 1)) {
if (empty($group['title'])) {
form_error($form['group_info']['group_items'][$id]['title'], t('The title is required if value for this item is defined.'));
}
}
}
}
}
function accept_exposed_input($input) {
if (empty($this->options['exposed'])) {
return TRUE;
}
// Store this because it will get overwritten.
$type = $this->value['type'];
$rc = parent::accept_exposed_input($input);
// Don't filter if value(s) are empty.
$operators = $this->operators();
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id'])) {
$operator = $input[$this->options['expose']['operator_id']];
}
else {
$operator = $this->operator;
}
if ($operators[$operator]['values'] == 1) {
if ($this->value['value'] == '') {
return FALSE;
}
}
else {
if ($this->value['min'] == '' || $this->value['max'] == '') {
return FALSE;
}
}
// restore what got overwritten by the parent.
$this->value['type'] = $type;
return $rc;
}
function op_between($field) {
$a = intval(strtotime($this->value['min'], 0));
$b = intval(strtotime($this->value['max'], 0));
if ($this->value['type'] == 'offset') {
$a = '***CURRENT_TIME***' . sprintf('%+d', $a); // keep sign
$b = '***CURRENT_TIME***' . sprintf('%+d', $b); // keep sign
}
// This is safe because we are manually scrubbing the values.
// It is necessary to do it this way because $a and $b are formulas when using an offset.
$operator = strtoupper($this->operator);
$this->query->add_where_expression($this->options['group'], "$field $operator $a AND $b");
}
function op_simple($field) {
$value = intval(strtotime($this->value['value'], 0));
if (!empty($this->value['type']) && $this->value['type'] == 'offset') {
$value = '***CURRENT_TIME***' . sprintf('%+d', $value); // keep sign
}
// This is safe because we are manually scrubbing the value.
// It is necessary to do it this way because $value is a formula when using an offset.
$this->query->add_where_expression($this->options['group'], "$field $this->operator $value");
}
}
@@ -0,0 +1,122 @@
<?php
/**
* @file
* Definition of views_handler_filter_entity_bundle
*/
/**
* Filter class which allows to filter by certain bundles of an entity.
*
* This class provides workarounds for taxonomy and comment.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_entity_bundle extends views_handler_filter_in_operator {
/**
* Stores the entity type on which the filter filters.
*
* @var string
*/
public $entity_type;
function init(&$view, &$options) {
parent::init($view, $options);
$this->get_entity_type();
}
/**
* Set and returns the entity_type.
*
* @return string
* The entity type on the filter.
*/
function get_entity_type() {
if (!isset($this->entity_type)) {
$data = views_fetch_data($this->table);
if (isset($data['table']['entity type'])) {
$this->entity_type = $data['table']['entity type'];
}
// If the current filter is under a relationship you can't be sure that the
// entity type of the view is the entity type of the current filter
// For example a filter from a node author on a node view does have users as entity type.
if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
$relationships = $this->view->display_handler->get_option('relationships');
if (!empty($relationships[$this->options['relationship']])) {
$options = $relationships[$this->options['relationship']];
$data = views_fetch_data($options['table']);
$this->entity_type = $data['table']['entity type'];
}
}
}
return $this->entity_type;
}
function get_value_options() {
if (!isset($this->value_options)) {
$info = entity_get_info($this->entity_type);
$types = $info['bundles'];
$this->value_title = t('@entity types', array('@entity' => $info['label']));
$options = array();
foreach ($types as $type => $info) {
$options[$type] = t($info['label']);
}
asort($options);
$this->value_options = $options;
}
}
/**
* All entity types beside comment and taxonomy terms have a proper implement
* bundle, though these two need an additional join to node/vocab table
* to work as required.
*/
function query() {
$this->ensure_my_table();
// Adjust the join for the comment case.
if ($this->entity_type == 'comment') {
$join = new views_join();
$def = array(
'table' => 'node',
'field' => 'nid',
'left_table' => $this->table_alias,
'left_field' => 'nid',
);
$join->definition = $def;
$join->construct();
$join->adjusted = TRUE;
$this->table_alias = $this->query->add_table('node', $this->relationship, $join);
$this->real_field = 'type';
// Replace the value to match the node type column.
foreach ($this->value as &$value) {
$value = str_replace('comment_node_', '', $value);
}
}
elseif ($this->entity_type == 'taxonomy_term') {
$join = new views_join();
$def = array(
'table' => 'taxonomy_vocabulary',
'field' => 'vid',
'left_table' => $this->table_alias,
'left_field' => 'vid',
);
$join->definition = $def;
$join->construct();
$join->adjusted = TRUE;
$this->table_alias = $this->query->add_table('taxonomy_vocabulary', $this->relationship, $join);
$this->real_field = 'machine_name';
}
else {
$entity_info = entity_get_info($this->entity_type);
$this->real_field = $entity_info['bundle keys']['bundle'];
}
parent::query();
}
}
@@ -0,0 +1,45 @@
<?php
/**
* @file
* Definition of views_handler_filter_equality.
*/
/**
* Simple filter to handle equal to / not equal to filters
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_equality extends views_handler_filter {
// exposed filter options
var $always_multiple = TRUE;
/**
* Provide simple equality operator
*/
function operator_options() {
return array(
'=' => t('Is equal to'),
'!=' => t('Is not equal to'),
);
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
$form['value'] = array(
'#type' => 'textfield',
'#title' => t('Value'),
'#size' => 30,
'#default_value' => $this->value,
);
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (!isset($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = $this->value;
}
}
}
}
@@ -0,0 +1,56 @@
<?php
/**
* @file
* Definition of views_handler_filter_group_by_numeric.
*/
/**
* Simple filter to handle greater than/less than filters
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_group_by_numeric extends views_handler_filter_numeric {
function query() {
$this->ensure_my_table();
$field = $this->get_field();
$info = $this->operators();
if (!empty($info[$this->operator]['method'])) {
$this->{$info[$this->operator]['method']}($field);
}
}
function op_between($field) {
$placeholder_min = $this->placeholder();
$placeholder_max = $this->placeholder();
if ($this->operator == 'between') {
$this->query->add_having_expression($this->options['group'], "$field >= $placeholder_min", array($placeholder_min => $this->value['min']));
$this->query->add_having_expression($this->options['group'], "$field <= $placeholder_max", array($placeholder_max => $this->value['max']));
}
else {
$this->query->add_having_expression($this->options['group'], "$field <= $placeholder_min OR $field >= $placeholder_max", array($placeholder_min => $this->value['min'], $placeholder_max => $this->value['max']));
}
}
function op_simple($field) {
$placeholder = $this->placeholder();
$this->query->add_having_expression($this->options['group'], "$field $this->operator $placeholder", array($placeholder => $this->value['value']));
}
function op_empty($field) {
if ($this->operator == 'empty') {
$operator = "IS NULL";
}
else {
$operator = "IS NOT NULL";
}
$this->query->add_having_expression($this->options['group'], "$field $operator");
}
function ui_name($short = FALSE) {
return $this->get_field(parent::ui_name($short));
}
function can_group() { return FALSE; }
}
@@ -0,0 +1,426 @@
<?php
/**
* @file
* Definition of views_handler_filter_in_operator.
*/
/**
* Simple filter to handle matching of multiple options selectable via checkboxes
*
* Definition items:
* - options callback: The function to call in order to generate the value options. If omitted, the options 'Yes' and 'No' will be used.
* - options arguments: An array of arguments to pass to the options callback.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_in_operator extends views_handler_filter {
var $value_form_type = 'checkboxes';
/**
* @var array
* Stores all operations which are available on the form.
*/
var $value_options = NULL;
function construct() {
parent::construct();
$this->value_title = t('Options');
$this->value_options = NULL;
}
/**
* Child classes should be used to override this function and set the
* 'value options', unless 'options callback' is defined as a valid function
* or static public method to generate these values.
*
* This can use a guard to be used to reduce database hits as much as
* possible.
*
* @return
* Return the stored values in $this->value_options if someone expects it.
*/
function get_value_options() {
if (isset($this->value_options)) {
return;
}
if (isset($this->definition['options callback']) && is_callable($this->definition['options callback'])) {
if (isset($this->definition['options arguments']) && is_array($this->definition['options arguments'])) {
$this->value_options = call_user_func_array($this->definition['options callback'], $this->definition['options arguments']);
}
else {
$this->value_options = call_user_func($this->definition['options callback']);
}
}
else {
$this->value_options = array(t('Yes'), t('No'));
}
return $this->value_options;
}
function expose_options() {
parent::expose_options();
$this->options['expose']['reduce'] = FALSE;
}
function expose_form(&$form, &$form_state) {
parent::expose_form($form, $form_state);
$form['expose']['reduce'] = array(
'#type' => 'checkbox',
'#title' => t('Limit list to selected items'),
'#description' => t('If checked, the only items presented to the user will be the ones selected here.'),
'#default_value' => !empty($this->options['expose']['reduce']), // safety
);
}
function option_definition() {
$options = parent::option_definition();
$options['operator']['default'] = 'in';
$options['value']['default'] = array();
$options['expose']['contains']['reduce'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* This kind of construct makes it relatively easy for a child class
* to add or remove functionality by overriding this function and
* adding/removing items from this array.
*/
function operators() {
$operators = array(
'in' => array(
'title' => t('Is one of'),
'short' => t('in'),
'short_single' => t('='),
'method' => 'op_simple',
'values' => 1,
),
'not in' => array(
'title' => t('Is not one of'),
'short' => t('not in'),
'short_single' => t('<>'),
'method' => 'op_simple',
'values' => 1,
),
);
// if the definition allows for the empty operator, add it.
if (!empty($this->definition['allow empty'])) {
$operators += array(
'empty' => array(
'title' => t('Is empty (NULL)'),
'method' => 'op_empty',
'short' => t('empty'),
'values' => 0,
),
'not empty' => array(
'title' => t('Is not empty (NOT NULL)'),
'method' => 'op_empty',
'short' => t('not empty'),
'values' => 0,
),
);
}
return $operators;
}
/**
* Build strings from the operators() for 'select' options
*/
function operator_options($which = 'title') {
$options = array();
foreach ($this->operators() as $id => $info) {
$options[$id] = $info[$which];
}
return $options;
}
function operator_values($values = 1) {
$options = array();
foreach ($this->operators() as $id => $info) {
if (isset($info['values']) && $info['values'] == $values) {
$options[] = $id;
}
}
return $options;
}
function value_form(&$form, &$form_state) {
$form['value'] = array();
$options = array();
if (empty($form_state['exposed'])) {
// Add a select all option to the value form.
$options = array('all' => t('Select all'));
}
$this->get_value_options();
$options += $this->value_options;
$default_value = (array) $this->value;
$which = 'all';
if (!empty($form['operator'])) {
$source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
}
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
// exposed and locked.
$which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
}
else {
$source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']);
}
if (!empty($this->options['expose']['reduce'])) {
$options = $this->reduce_value_options();
if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
$default_value = array();
}
}
if (empty($this->options['expose']['multiple'])) {
if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
$default_value = 'All';
}
elseif (empty($default_value)) {
$keys = array_keys($options);
$default_value = array_shift($keys);
}
else {
$copy = $default_value;
$default_value = array_shift($copy);
}
}
}
if ($which == 'all' || $which == 'value') {
$form['value'] = array(
'#type' => $this->value_form_type,
'#title' => $this->value_title,
'#options' => $options,
'#default_value' => $default_value,
// These are only valid for 'select' type, but do no harm to checkboxes.
'#multiple' => TRUE,
'#size' => count($options) > 8 ? 8 : count($options),
);
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = $default_value;
}
if ($which == 'all') {
if (empty($form_state['exposed']) && (in_array($this->value_form_type, array('checkbox', 'checkboxes', 'radios', 'select')))) {
$form['value']['#prefix'] = '<div id="edit-options-value-wrapper">';
$form['value']['#suffix'] = '</div>';
}
$form['value']['#dependency'] = array($source => $this->operator_values(1));
}
}
}
/**
* When using exposed filters, we may be required to reduce the set.
*/
function reduce_value_options($input = NULL) {
if (!isset($input)) {
$input = $this->value_options;
}
// Because options may be an array of strings, or an array of mixed arrays
// and strings (optgroups) or an array of objects, we have to
// step through and handle each one individually.
$options = array();
foreach ($input as $id => $option) {
if (is_array($option)) {
$options[$id] = $this->reduce_value_options($option);
continue;
}
elseif (is_object($option)) {
$keys = array_keys($option->option);
$key = array_shift($keys);
if (isset($this->options['value'][$key])) {
$options[$id] = $option;
}
}
elseif (isset($this->options['value'][$id])) {
$options[$id] = $option;
}
}
return $options;
}
function accept_exposed_input($input) {
// A very special override because the All state for this type of
// filter could have a default:
if (empty($this->options['exposed'])) {
return TRUE;
}
// If this is non-multiple and non-required, then this filter will
// participate, but using the default settings, *if* 'limit is true.
if (empty($this->options['expose']['multiple']) && empty($this->options['expose']['required']) && !empty($this->options['expose']['limit'])) {
$identifier = $this->options['expose']['identifier'];
if ($input[$identifier] == 'All') {
return TRUE;
}
}
return parent::accept_exposed_input($input);
}
function value_submit($form, &$form_state) {
// Drupal's FAPI system automatically puts '0' in for any checkbox that
// was not set, and the key to the checkbox if it is set.
// Unfortunately, this means that if the key to that checkbox is 0,
// we are unable to tell if that checkbox was set or not.
// Luckily, the '#value' on the checkboxes form actually contains
// *only* a list of checkboxes that were set, and we can use that
// instead.
$form_state['values']['options']['value'] = $form['value']['#value'];
}
function admin_summary() {
if ($this->is_a_group()) {
return t('grouped');
}
if (!empty($this->options['exposed'])) {
return t('exposed');
}
$info = $this->operators();
$this->get_value_options();
if (!is_array($this->value)) {
return;
}
$operator = check_plain($info[$this->operator]['short']);
$values = '';
if (in_array($this->operator, $this->operator_values(1))) {
// Remove every element which is not known.
foreach ($this->value as $value) {
if (!isset($this->value_options[$value])) {
unset($this->value[$value]);
}
}
// Choose different kind of ouput for 0, a single and multiple values.
if (count($this->value) == 0) {
$values = t('Unknown');
}
else if (count($this->value) == 1) {
// If any, use the 'single' short name of the operator instead.
if (isset($info[$this->operator]['short_single'])) {
$operator = check_plain($info[$this->operator]['short_single']);
}
$keys = $this->value;
$value = array_shift($keys);
if (isset($this->value_options[$value])) {
$values = check_plain($this->value_options[$value]);
}
else {
$values = '';
}
}
else {
foreach ($this->value as $value) {
if ($values !== '') {
$values .= ', ';
}
if (drupal_strlen($values) > 8) {
$values .= '...';
break;
}
if (isset($this->value_options[$value])) {
$values .= check_plain($this->value_options[$value]);
}
}
}
}
return $operator . (($values !== '') ? ' ' . $values : '');
}
function query() {
$info = $this->operators();
if (!empty($info[$this->operator]['method'])) {
$this->{$info[$this->operator]['method']}();
}
}
function op_simple() {
if (empty($this->value)) {
return;
}
$this->ensure_my_table();
// We use array_values() because the checkboxes keep keys and that can cause
// array addition problems.
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", array_values($this->value), $this->operator);
}
function op_empty() {
$this->ensure_my_table();
if ($this->operator == 'empty') {
$operator = "IS NULL";
}
else {
$operator = "IS NOT NULL";
}
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", NULL, $operator);
}
function validate() {
$this->get_value_options();
$errors = array();
// If the operator is an operator which doesn't require a value, there is
// no need for additional validation.
if (in_array($this->operator, $this->operator_values(0))) {
return array();
}
if (!in_array($this->operator, $this->operator_values(1))) {
$errors[] = t('The operator is invalid on filter: @filter.', array('@filter' => $this->ui_name(TRUE)));
}
if (is_array($this->value)) {
if (!isset($this->value_options)) {
// Don't validate if there are none value options provided, for example for special handlers.
return $errors;
}
if ($this->options['exposed'] && !$this->options['expose']['required'] && empty($this->value)) {
// Don't validate if the field is exposed and no default value is provided.
return $errors;
}
// Some filter_in_operator usage uses optgroups forms, so flatten it.
$flat_options = form_options_flatten($this->value_options, TRUE);
// Remove every element which is not known.
foreach ($this->value as $value) {
if (!isset($flat_options[$value])) {
unset($this->value[$value]);
}
}
// Choose different kind of ouput for 0, a single and multiple values.
if (count($this->value) == 0) {
$errors[] = t('No valid values found on filter: @filter.', array('@filter' => $this->ui_name(TRUE)));
}
}
elseif (!empty($this->value) && ($this->operator == 'in' || $this->operator == 'not in')) {
$errors[] = t('The value @value is not an array for @operator on filter: @filter', array('@value' => views_var_export($this->value), '@operator' => $this->operator, '@filter' => $this->ui_name(TRUE)));
}
return $errors;
}
}
@@ -0,0 +1,125 @@
<?php
/**
* @file
* Definition of views_handler_filter_many_to_one.
*/
/**
* Complex filter to handle filtering for many to one relationships,
* such as terms (many terms per node) or roles (many roles per user).
*
* The construct method needs to be overridden to provide a list of options;
* alternately, the value_form and admin_summary methods need to be overriden
* to provide something that isn't just a select list.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_many_to_one extends views_handler_filter_in_operator {
/**
* @var views_many_to_one_helper
*
* Stores the Helper object which handles the many_to_one complexity.
*/
var $helper = NULL;
function init(&$view, &$options) {
parent::init($view, $options);
$this->helper = new views_many_to_one_helper($this);
}
function option_definition() {
$options = parent::option_definition();
$options['operator']['default'] = 'or';
$options['value']['default'] = array();
if (isset($this->helper)) {
$this->helper->option_definition($options);
}
else {
$helper = new views_many_to_one_helper($this);
$helper->option_definition($options);
}
return $options;
}
function operators() {
$operators = array(
'or' => array(
'title' => t('Is one of'),
'short' => t('or'),
'short_single' => t('='),
'method' => 'op_helper',
'values' => 1,
'ensure_my_table' => 'helper',
),
'and' => array(
'title' => t('Is all of'),
'short' => t('and'),
'short_single' => t('='),
'method' => 'op_helper',
'values' => 1,
'ensure_my_table' => 'helper',
),
'not' => array(
'title' => t('Is none of'),
'short' => t('not'),
'short_single' => t('<>'),
'method' => 'op_helper',
'values' => 1,
'ensure_my_table' => 'helper',
),
);
// if the definition allows for the empty operator, add it.
if (!empty($this->definition['allow empty'])) {
$operators += array(
'empty' => array(
'title' => t('Is empty (NULL)'),
'method' => 'op_empty',
'short' => t('empty'),
'values' => 0,
),
'not empty' => array(
'title' => t('Is not empty (NOT NULL)'),
'method' => 'op_empty',
'short' => t('not empty'),
'values' => 0,
),
);
}
return $operators;
}
var $value_form_type = 'select';
function value_form(&$form, &$form_state) {
parent::value_form($form, $form_state);
if (empty($form_state['exposed'])) {
$this->helper->options_form($form, $form_state);
}
}
/**
* Override ensure_my_table so we can control how this joins in.
* The operator actually has influence over joining.
*/
function ensure_my_table() {
// Defer to helper if the operator specifies it.
$info = $this->operators();
if (isset($info[$this->operator]['ensure_my_table']) && $info[$this->operator]['ensure_my_table'] == 'helper') {
return $this->helper->ensure_my_table();
}
return parent::ensure_my_table();
}
function op_helper() {
if (empty($this->value)) {
return;
}
$this->helper->add_filter();
}
}
+325
View File
@@ -0,0 +1,325 @@
<?php
/**
* @file
* Definition of views_handler_filter_numeric.
*/
/**
* Simple filter to handle greater than/less than filters
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_numeric extends views_handler_filter {
var $always_multiple = TRUE;
function option_definition() {
$options = parent::option_definition();
$options['value'] = array(
'contains' => array(
'min' => array('default' => ''),
'max' => array('default' => ''),
'value' => array('default' => ''),
),
);
return $options;
}
function operators() {
$operators = array(
'<' => array(
'title' => t('Is less than'),
'method' => 'op_simple',
'short' => t('<'),
'values' => 1,
),
'<=' => array(
'title' => t('Is less than or equal to'),
'method' => 'op_simple',
'short' => t('<='),
'values' => 1,
),
'=' => array(
'title' => t('Is equal to'),
'method' => 'op_simple',
'short' => t('='),
'values' => 1,
),
'!=' => array(
'title' => t('Is not equal to'),
'method' => 'op_simple',
'short' => t('!='),
'values' => 1,
),
'>=' => array(
'title' => t('Is greater than or equal to'),
'method' => 'op_simple',
'short' => t('>='),
'values' => 1,
),
'>' => array(
'title' => t('Is greater than'),
'method' => 'op_simple',
'short' => t('>'),
'values' => 1,
),
'between' => array(
'title' => t('Is between'),
'method' => 'op_between',
'short' => t('between'),
'values' => 2,
),
'not between' => array(
'title' => t('Is not between'),
'method' => 'op_between',
'short' => t('not between'),
'values' => 2,
),
);
// if the definition allows for the empty operator, add it.
if (!empty($this->definition['allow empty'])) {
$operators += array(
'empty' => array(
'title' => t('Is empty (NULL)'),
'method' => 'op_empty',
'short' => t('empty'),
'values' => 0,
),
'not empty' => array(
'title' => t('Is not empty (NOT NULL)'),
'method' => 'op_empty',
'short' => t('not empty'),
'values' => 0,
),
);
}
// Add regexp support for MySQL.
if (Database::getConnection()->databaseType() == 'mysql') {
$operators += array(
'regular_expression' => array(
'title' => t('Regular expression'),
'short' => t('regex'),
'method' => 'op_regex',
'values' => 1,
),
);
}
return $operators;
}
/**
* Provide a list of all the numeric operators
*/
function operator_options($which = 'title') {
$options = array();
foreach ($this->operators() as $id => $info) {
$options[$id] = $info[$which];
}
return $options;
}
function operator_values($values = 1) {
$options = array();
foreach ($this->operators() as $id => $info) {
if ($info['values'] == $values) {
$options[] = $id;
}
}
return $options;
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
$form['value']['#tree'] = TRUE;
// We have to make some choices when creating this as an exposed
// filter form. For example, if the operator is locked and thus
// not rendered, we can't render dependencies; instead we only
// render the form items we need.
$which = 'all';
if (!empty($form['operator'])) {
$source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
}
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
// exposed and locked.
$which = in_array($this->operator, $this->operator_values(2)) ? 'minmax' : 'value';
}
else {
$source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']);
}
}
if ($which == 'all') {
$form['value']['value'] = array(
'#type' => 'textfield',
'#title' => empty($form_state['exposed']) ? t('Value') : '',
'#size' => 30,
'#default_value' => $this->value['value'],
'#dependency' => array($source => $this->operator_values(1)),
);
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['value'])) {
$form_state['input'][$identifier]['value'] = $this->value['value'];
}
}
elseif ($which == 'value') {
// When exposed we drop the value-value and just do value if
// the operator is locked.
$form['value'] = array(
'#type' => 'textfield',
'#title' => empty($form_state['exposed']) ? t('Value') : '',
'#size' => 30,
'#default_value' => $this->value['value'],
);
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = $this->value['value'];
}
}
if ($which == 'all' || $which == 'minmax') {
$form['value']['min'] = array(
'#type' => 'textfield',
'#title' => empty($form_state['exposed']) ? t('Min') : '',
'#size' => 30,
'#default_value' => $this->value['min'],
);
$form['value']['max'] = array(
'#type' => 'textfield',
'#title' => empty($form_state['exposed']) ? t('And max') : t('And'),
'#size' => 30,
'#default_value' => $this->value['max'],
);
if ($which == 'all') {
$dependency = array(
'#dependency' => array($source => $this->operator_values(2)),
);
$form['value']['min'] += $dependency;
$form['value']['max'] += $dependency;
}
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min'])) {
$form_state['input'][$identifier]['min'] = $this->value['min'];
}
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['max'])) {
$form_state['input'][$identifier]['max'] = $this->value['max'];
}
if (!isset($form['value'])) {
// Ensure there is something in the 'value'.
$form['value'] = array(
'#type' => 'value',
'#value' => NULL
);
}
}
}
function query() {
$this->ensure_my_table();
$field = "$this->table_alias.$this->real_field";
$info = $this->operators();
if (!empty($info[$this->operator]['method'])) {
$this->{$info[$this->operator]['method']}($field);
}
}
function op_between($field) {
if ($this->operator == 'between') {
$this->query->add_where($this->options['group'], $field, array($this->value['min'], $this->value['max']), 'BETWEEN');
}
else {
$this->query->add_where($this->options['group'], db_or()->condition($field, $this->value['min'], '<=')->condition($field, $this->value['max'], '>='));
}
}
function op_simple($field) {
$this->query->add_where($this->options['group'], $field, $this->value['value'], $this->operator);
}
function op_empty($field) {
if ($this->operator == 'empty') {
$operator = "IS NULL";
}
else {
$operator = "IS NOT NULL";
}
$this->query->add_where($this->options['group'], $field, NULL, $operator);
}
function op_regex($field) {
$this->query->add_where($this->options['group'], $field, $this->value, 'RLIKE');
}
function admin_summary() {
if ($this->is_a_group()) {
return t('grouped');
}
if (!empty($this->options['exposed'])) {
return t('exposed');
}
$options = $this->operator_options('short');
$output = check_plain($options[$this->operator]);
if (in_array($this->operator, $this->operator_values(2))) {
$output .= ' ' . t('@min and @max', array('@min' => $this->value['min'], '@max' => $this->value['max']));
}
elseif (in_array($this->operator, $this->operator_values(1))) {
$output .= ' ' . check_plain($this->value['value']);
}
return $output;
}
/**
* Do some minor translation of the exposed input
*/
function accept_exposed_input($input) {
if (empty($this->options['exposed'])) {
return TRUE;
}
// rewrite the input value so that it's in the correct format so that
// the parent gets the right data.
if (!empty($this->options['expose']['identifier'])) {
$value = &$input[$this->options['expose']['identifier']];
if (!is_array($value)) {
$value = array(
'value' => $value,
);
}
}
$rc = parent::accept_exposed_input($input);
if (empty($this->options['expose']['required'])) {
// We have to do some of our own checking for non-required filters.
$info = $this->operators();
if (!empty($info[$this->operator]['values'])) {
switch ($info[$this->operator]['values']) {
case 1:
if ($value['value'] === '') {
return FALSE;
}
break;
case 2:
if ($value['min'] === '' && $value['max'] === '') {
return FALSE;
}
break;
}
}
}
return $rc;
}
}
+338
View File
@@ -0,0 +1,338 @@
<?php
/**
* @file
* Definition of views_handler_filter_string.
*/
/**
* Basic textfield filter to handle string filtering commands
* including equality, like, not like, etc.
*
* @ingroup views_filter_handlers
*/
class views_handler_filter_string extends views_handler_filter {
// exposed filter options
var $always_multiple = TRUE;
function option_definition() {
$options = parent::option_definition();
$options['expose']['contains']['required'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* This kind of construct makes it relatively easy for a child class
* to add or remove functionality by overriding this function and
* adding/removing items from this array.
*/
function operators() {
$operators = array(
'=' => array(
'title' => t('Is equal to'),
'short' => t('='),
'method' => 'op_equal',
'values' => 1,
),
'!=' => array(
'title' => t('Is not equal to'),
'short' => t('!='),
'method' => 'op_equal',
'values' => 1,
),
'contains' => array(
'title' => t('Contains'),
'short' => t('contains'),
'method' => 'op_contains',
'values' => 1,
),
'word' => array(
'title' => t('Contains any word'),
'short' => t('has word'),
'method' => 'op_word',
'values' => 1,
),
'allwords' => array(
'title' => t('Contains all words'),
'short' => t('has all'),
'method' => 'op_word',
'values' => 1,
),
'starts' => array(
'title' => t('Starts with'),
'short' => t('begins'),
'method' => 'op_starts',
'values' => 1,
),
'not_starts' => array(
'title' => t('Does not start with'),
'short' => t('not_begins'),
'method' => 'op_not_starts',
'values' => 1,
),
'ends' => array(
'title' => t('Ends with'),
'short' => t('ends'),
'method' => 'op_ends',
'values' => 1,
),
'not_ends' => array(
'title' => t('Does not end with'),
'short' => t('not_ends'),
'method' => 'op_not_ends',
'values' => 1,
),
'not' => array(
'title' => t('Does not contain'),
'short' => t('!has'),
'method' => 'op_not',
'values' => 1,
),
'shorterthan' => array(
'title' => t('Length is shorter than'),
'short' => t('shorter than'),
'method' => 'op_shorter',
'values' => 1,
),
'longerthan' => array(
'title' => t('Length is longer than'),
'short' => t('longer than'),
'method' => 'op_longer',
'values' => 1,
),
);
// if the definition allows for the empty operator, add it.
if (!empty($this->definition['allow empty'])) {
$operators += array(
'empty' => array(
'title' => t('Is empty (NULL)'),
'method' => 'op_empty',
'short' => t('empty'),
'values' => 0,
),
'not empty' => array(
'title' => t('Is not empty (NOT NULL)'),
'method' => 'op_empty',
'short' => t('not empty'),
'values' => 0,
),
);
}
// Add regexp support for MySQL.
if (Database::getConnection()->databaseType() == 'mysql') {
$operators += array(
'regular_expression' => array(
'title' => t('Regular expression'),
'short' => t('regex'),
'method' => 'op_regex',
'values' => 1,
),
);
}
return $operators;
}
/**
* Build strings from the operators() for 'select' options
*/
function operator_options($which = 'title') {
$options = array();
foreach ($this->operators() as $id => $info) {
$options[$id] = $info[$which];
}
return $options;
}
function admin_summary() {
if ($this->is_a_group()) {
return t('grouped');
}
if (!empty($this->options['exposed'])) {
return t('exposed');
}
$options = $this->operator_options('short');
$output = '';
if(!empty($options[$this->operator])) {
$output = check_plain($options[$this->operator]);
}
if (in_array($this->operator, $this->operator_values(1))) {
$output .= ' ' . check_plain($this->value);
}
return $output;
}
function operator_values($values = 1) {
$options = array();
foreach ($this->operators() as $id => $info) {
if (isset($info['values']) && $info['values'] == $values) {
$options[] = $id;
}
}
return $options;
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
// We have to make some choices when creating this as an exposed
// filter form. For example, if the operator is locked and thus
// not rendered, we can't render dependencies; instead we only
// render the form items we need.
$which = 'all';
if (!empty($form['operator'])) {
$source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
}
if (!empty($form_state['exposed'])) {
$identifier = $this->options['expose']['identifier'];
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
// exposed and locked.
$which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
}
else {
$source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']);
}
}
if ($which == 'all' || $which == 'value') {
$form['value'] = array(
'#type' => 'textfield',
'#title' => t('Value'),
'#size' => 30,
'#default_value' => $this->value,
);
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = $this->value;
}
if ($which == 'all') {
$form['value'] += array(
'#dependency' => array($source => $this->operator_values(1)),
);
}
}
if (!isset($form['value'])) {
// Ensure there is something in the 'value'.
$form['value'] = array(
'#type' => 'value',
'#value' => NULL
);
}
}
function operator() {
return $this->operator == '=' ? 'LIKE' : 'NOT LIKE';
}
/**
* Add this filter to the query.
*
* Due to the nature of fapi, the value and the operator have an unintended
* level of indirection. You will find them in $this->operator
* and $this->value respectively.
*/
function query() {
$this->ensure_my_table();
$field = "$this->table_alias.$this->real_field";
$info = $this->operators();
if (!empty($info[$this->operator]['method'])) {
$this->{$info[$this->operator]['method']}($field);
}
}
function op_equal($field) {
$this->query->add_where($this->options['group'], $field, $this->value, $this->operator());
}
function op_contains($field) {
$this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'LIKE');
}
function op_word($field) {
$where = $this->operator == 'word' ? db_or() : db_and();
// Don't filter on empty strings.
if (empty($this->value)) {
return;
}
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$phrase = false;
// Strip off phrase quotes
if ($match[2]{0} == '"') {
$match[2] = substr($match[2], 1, -1);
$phrase = true;
}
$words = trim($match[2], ',?!();:-');
$words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
foreach ($words as $word) {
$placeholder = $this->placeholder();
$where->condition($field, '%' . db_like(trim($word, " ,!?")) . '%', 'LIKE');
}
}
if (!$where) {
return;
}
// previously this was a call_user_func_array but that's unnecessary
// as views will unpack an array that is a single arg.
$this->query->add_where($this->options['group'], $where);
}
function op_starts($field) {
$this->query->add_where($this->options['group'], $field, db_like($this->value) . '%', 'LIKE');
}
function op_not_starts($field) {
$this->query->add_where($this->options['group'], $field, db_like($this->value) . '%', 'NOT LIKE');
}
function op_ends($field) {
$this->query->add_where($this->options['group'], $field, '%' . db_like($this->value), 'LIKE');
}
function op_not_ends($field) {
$this->query->add_where($this->options['group'], $field, '%' . db_like($this->value), 'NOT LIKE');
}
function op_not($field) {
$this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'NOT LIKE');
}
function op_shorter($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "LENGTH($field) < $placeholder", array($placeholder => $this->value));
}
function op_longer($field) {
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "LENGTH($field) > $placeholder", array($placeholder => $this->value));
}
function op_regex($field) {
$this->query->add_where($this->options['group'], $field, $this->value, 'RLIKE');
}
function op_empty($field) {
if ($this->operator == 'empty') {
$operator = "IS NULL";
}
else {
$operator = "IS NOT NULL";
}
$this->query->add_where($this->options['group'], $field, NULL, $operator);
}
}
+186
View File
@@ -0,0 +1,186 @@
<?php
/**
* @file
* Views' relationship handlers.
*/
/**
* @defgroup views_relationship_handlers Views relationship handlers
* @{
* Handlers to tell Views how to create alternate relationships.
*/
/**
* Simple relationship handler that allows a new version of the primary table
* to be linked in.
*
* The base relationship handler can only handle a single join. Some relationships
* are more complex and might require chains of joins; for those, you must
* utilize a custom relationship handler.
*
* Definition items:
* - base: The new base table this relationship will be adding. This does not
* have to be a declared base table, but if there are no tables that
* utilize this base table, it won't be very effective.
* - base field: The field to use in the relationship; if left out this will be
* assumed to be the primary field.
* - relationship table: The actual table this relationship operates against.
* This is analogous to using a 'table' override.
* - relationship field: The actual field this relationship operates against.
* This is analogous to using a 'real field' override.
* - label: The default label to provide for this relationship, which is
* shown in parentheses next to any field/sort/filter/argument that uses
* the relationship.
*
* @ingroup views_relationship_handlers
*/
class views_handler_relationship extends views_handler {
/**
* Init handler to let relationships live on tables other than
* the table they operate on.
*/
function init(&$view, &$options) {
parent::init($view, $options);
if (isset($this->definition['relationship table'])) {
$this->table = $this->definition['relationship table'];
}
if (isset($this->definition['relationship field'])) {
// Set both real_field and field so custom handler
// can rely on the old field value.
$this->real_field = $this->field = $this->definition['relationship field'];
}
}
/**
* Get this field's label.
*/
function label() {
if (!isset($this->options['label'])) {
return $this->ui_name();
}
return $this->options['label'];
}
function option_definition() {
$options = parent::option_definition();
// Relationships definitions should define a default label, but if they aren't get another default value.
if (!empty($this->definition['label'])) {
$label = $this->definition['label'];
}
else {
$label = !empty($this->definition['field']) ? $this->definition['field'] : $this->definition['base field'];
}
$options['label'] = array('default' => $label, 'translatable' => TRUE);
$options['required'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
/**
* Default options form that provides the label widget that all fields
* should have.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['label'] = array(
'#type' => 'textfield',
'#title' => t('Identifier'),
'#default_value' => isset($this->options['label']) ? $this->options['label'] : '',
'#description' => t('Edit the administrative label displayed when referencing this relationship from filters, etc.'),
'#required' => TRUE,
);
$form['required'] = array(
'#type' => 'checkbox',
'#title' => t('Require this relationship'),
'#description' => t('Enable to hide items that do not contain this relationship'),
'#default_value' => !empty($this->options['required']),
);
}
/**
* Called to implement a relationship in a query.
*/
function query() {
// Figure out what base table this relationship brings to the party.
$table_data = views_fetch_data($this->definition['base']);
$base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
$this->ensure_my_table();
$def = $this->definition;
$def['table'] = $this->definition['base'];
$def['field'] = $base_field;
$def['left_table'] = $this->table_alias;
$def['left_field'] = $this->real_field;
if (!empty($this->options['required'])) {
$def['type'] = 'INNER';
}
if (!empty($this->definition['extra'])) {
$def['extra'] = $this->definition['extra'];
}
if (!empty($def['join_handler']) && class_exists($def['join_handler'])) {
$join = new $def['join_handler'];
}
else {
$join = new views_join();
}
$join->definition = $def;
$join->options = $this->options;
$join->construct();
$join->adjusted = TRUE;
// use a short alias for this:
$alias = $def['table'] . '_' . $this->table;
$this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship);
// Add access tags if the base table provide it.
if (empty($this->query->options['disable_sql_rewrite']) && isset($table_data['table']['base']['access query tag'])) {
$access_tag = $table_data['table']['base']['access query tag'];
$this->query->add_tag($access_tag);
}
}
/**
* You can't groupby a relationship.
*/
function use_group_by() {
return FALSE;
}
}
/**
* A special handler to take the place of missing or broken handlers.
*
* @ingroup views_relationship_handlers
*/
class views_handler_relationship_broken extends views_handler_relationship {
function ui_name($short = FALSE) {
return t('Broken/missing handler');
}
function ensure_my_table() { /* No table to ensure! */ }
function query() { /* No query to run */ }
function options_form(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
);
}
/**
* Determine if the handler is considered 'broken'
*/
function broken() { return TRUE; }
}
/**
* @}
*/
@@ -0,0 +1,382 @@
<?php
/**
* @file
* Relationship for groupwise maximum handler.
*/
/**
* Relationship handler that allows a groupwise maximum of the linked in table.
* For a definition, see:
* http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
* In lay terms, instead of joining to get all matching records in the linked
* table, we get only one record, a 'representative record' picked according
* to a given criteria.
*
* Example:
* Suppose we have a term view that gives us the terms: Horse, Cat, Aardvark.
* We wish to show for each term the most recent node of that term.
* What we want is some kind of relationship from term to node.
* But a regular relationship will give us all the nodes for each term,
* giving the view multiple rows per term. What we want is just one
* representative node per term, the node that is the 'best' in some way:
* eg, the most recent, the most commented on, the first in alphabetical order.
*
* This handler gives us that kind of relationship from term to node.
* The method of choosing the 'best' implemented with a sort
* that the user selects in the relationship settings.
*
* So if we want our term view to show the most commented node for each term,
* add the relationship and in its options, pick the 'Comment count' sort.
*
* Relationship definition
* - 'outer field': The outer field to substitute into the correlated subquery.
* This must be the full field name, not the alias.
* Eg: 'term_data.tid'.
* - 'argument table',
* 'argument field': These options define a views argument that the subquery
* must add to itself to filter by the main view.
* Example: the main view shows terms, this handler is being used to get to
* the nodes base table. Your argument must be 'term_node', 'tid', as this
* is the argument that should be added to a node view to filter on terms.
*
* A note on performance:
* This relationship uses a correlated subquery, which is expensive.
* Subsequent versions of this handler could also implement the alternative way
* of doing this, with a join -- though this looks like it could be pretty messy
* to implement. This is also an expensive method, so providing both methods and
* allowing the user to choose which one works fastest for their data might be
* the best way.
* If your use of this relationship handler is likely to result in large
* data sets, you might want to consider storing statistics in a separate table,
* in the same way as node_comment_statistics.
*
* @ingroup views_relationship_handlers
*/
class views_handler_relationship_groupwise_max extends views_handler_relationship {
/**
* Defines default values for options.
*/
function option_definition() {
$options = parent::option_definition();
$options['subquery_sort'] = array('default' => NULL);
// Descending more useful.
$options['subquery_order'] = array('default' => 'DESC');
$options['subquery_regenerate'] = array('default' => FALSE, 'bool' => TRUE);
$options['subquery_view'] = array('default' => FALSE);
$options['subquery_namespace'] = array('default' => FALSE);
return $options;
}
/**
* Extends the relationship's basic options, allowing the user to pick
* a sort and an order for it.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// Get the sorts that apply to our base.
$sorts = views_fetch_fields($this->definition['base'], 'sort');
foreach ($sorts as $sort_id => $sort) {
$sort_options[$sort_id] = "$sort[group]: $sort[title]";
}
$base_table_data = views_fetch_data($this->definition['base']);
$form['subquery_sort'] = array(
'#type' => 'select',
'#title' => t('Representative sort criteria'),
// Provide the base field as sane default sort option.
'#default_value' => !empty($this->options['subquery_sort']) ? $this->options['subquery_sort'] : $this->definition['base'] . '.' . $base_table_data['table']['base']['field'],
'#options' => $sort_options,
'#description' => theme('advanced_help_topic', array('module' => 'views', 'topic' => 'relationship-representative')) .
t("The sort criteria is applied to the data brought in by the relationship to determine how a representative item is obtained for each row. For example, to show the most recent node for each user, pick 'Content: Updated date'."),
);
$form['subquery_order'] = array(
'#type' => 'radios',
'#title' => t('Representative sort order'),
'#description' => t("The ordering to use for the sort criteria selected above."),
'#options' => array('ASC' => t('Ascending'), 'DESC' => t('Descending')),
'#default_value' => $this->options['subquery_order'],
);
$form['subquery_namespace'] = array(
'#type' => 'textfield',
'#title' => t('Subquery namespace'),
'#description' => t('Advanced. Enter a namespace for the subquery used by this relationship.'),
'#default_value' => $this->options['subquery_namespace'],
);
// WIP: This stuff doens't work yet: namespacing issues.
// A list of suitable views to pick one as the subview.
$views = array('' => '<none>');
$all_views = views_get_all_views();
foreach ($all_views as $view) {
// Only get views that are suitable:
// - base must the base that our relationship joins towards
// - must have fields.
if ($view->base_table == $this->definition['base'] && !empty($view->display['default']->display_options['fields'])) {
// TODO: check the field is the correct sort?
// or let users hang themselves at this stage and check later?
if ($view->type == 'Default') {
$views[t('Default Views')][$view->name] = $view->name;
}
else {
$views[t('Existing Views')][$view->name] = $view->name;
}
}
}
$form['subquery_view'] = array(
'#type' => 'select',
'#title' => t('Representative view'),
'#default_value' => $this->options['subquery_view'],
'#options' => $views,
'#description' => t('Advanced. Use another view to generate the relationship subquery. This allows you to use filtering and more than one sort. If you pick a view here, the sort options above are ignored. Your view must have the ID of its base as its only field, and should have some kind of sorting.'),
);
$form['subquery_regenerate'] = array(
'#type' => 'checkbox',
'#title' => t('Generate subquery each time view is run.'),
'#default_value' => $this->options['subquery_regenerate'],
'#description' => t('Will re-generate the subquery for this relationship every time the view is run, instead of only when these options are saved. Use for testing if you are making changes elsewhere. WARNING: seriously impairs performance.'),
);
}
/**
* Helper function to create a pseudo view.
*
* We use this to obtain our subquery SQL.
*/
function get_temporary_view() {
views_include('view');
$view = new view();
$view->vid = 'new'; // @todo: what's this?
$view->base_table = $this->definition['base'];
$view->add_display('default');
return $view;
}
/**
* When the form is submitted, take sure to clear the subquery string cache.
*/
function options_form_submit(&$form, &$form_state) {
$cid = 'views_relationship_groupwise_max:' . $this->view->name . ':' . $this->view->current_display . ':' . $this->options['id'];
cache_clear_all($cid, 'cache_views_data');
}
/**
* Generate a subquery given the user options, as set in the options.
* These are passed in rather than picked up from the object because we
* generate the subquery when the options are saved, rather than when the view
* is run. This saves considerable time.
*
* @param $options
* An array of options:
* - subquery_sort: the id of a views sort.
* - subquery_order: either ASC or DESC.
* @return
* The subquery SQL string, ready for use in the main query.
*/
function left_query($options) {
// Either load another view, or create one on the fly.
if ($options['subquery_view']) {
$temp_view = views_get_view($options['subquery_view']);
// Remove all fields from default display
unset($temp_view->display['default']->display_options['fields']);
}
else {
// Create a new view object on the fly, which we use to generate a query
// object and then get the SQL we need for the subquery.
$temp_view = $this->get_temporary_view();
// Add the sort from the options to the default display.
// This is broken, in that the sort order field also gets added as a
// select field. See http://drupal.org/node/844910.
// We work around this further down.
$sort = $options['subquery_sort'];
list($sort_table, $sort_field) = explode('.', $sort);
$sort_options = array('order' => $options['subquery_order']);
$temp_view->add_item('default', 'sort', $sort_table, $sort_field, $sort_options);
}
// Get the namespace string.
$temp_view->namespace = (!empty($options['subquery_namespace'])) ? '_'. $options['subquery_namespace'] : '_INNER';
$this->subquery_namespace = (!empty($options['subquery_namespace'])) ? '_'. $options['subquery_namespace'] : 'INNER';
// The value we add here does nothing, but doing this adds the right tables
// and puts in a WHERE clause with a placeholder we can grab later.
$temp_view->args[] = '**CORRELATED**';
// Add the base table ID field.
$views_data = views_fetch_data($this->definition['base']);
$base_field = $views_data['table']['base']['field'];
$temp_view->add_item('default', 'field', $this->definition['base'], $this->definition['field']);
// Add the correct argument for our relationship's base
// ie the 'how to get back to base' argument.
// The relationship definition tells us which one to use.
$temp_view->add_item(
'default',
'argument',
$this->definition['argument table'], // eg 'term_node',
$this->definition['argument field'] // eg 'tid'
);
// Build the view. The creates the query object and produces the query
// string but does not run any queries.
$temp_view->build();
// Now take the SelectQuery object the View has built and massage it
// somewhat so we can get the SQL query from it.
$subquery = $temp_view->build_info['query'];
// Workaround until http://drupal.org/node/844910 is fixed:
// Remove all fields from the SELECT except the base id.
$fields =& $subquery->getFields();
foreach (array_keys($fields) as $field_name) {
// The base id for this subquery is stored in our definition.
if ($field_name != $this->definition['field']) {
unset($fields[$field_name]);
}
}
// Make every alias in the subquery safe within the outer query by
// appending a namespace to it, '_inner' by default.
$tables =& $subquery->getTables();
foreach (array_keys($tables) as $table_name) {
$tables[$table_name]['alias'] .= $this->subquery_namespace;
// Namespace the join on every table.
if (isset($tables[$table_name]['condition'])) {
$tables[$table_name]['condition'] = $this->condition_namespace($tables[$table_name]['condition']);
}
}
// Namespace fields.
foreach (array_keys($fields) as $field_name) {
$fields[$field_name]['table'] .= $this->subquery_namespace;
$fields[$field_name]['alias'] .= $this->subquery_namespace;
}
// Namespace conditions.
$where =& $subquery->conditions();
$this->alter_subquery_condition($subquery, $where);
// Not sure why, but our sort order clause doesn't have a table.
// TODO: the call to add_item() above to add the sort handler is probably
// wrong -- needs attention from someone who understands it.
// In the meantime, this works, but with a leap of faith...
$orders =& $subquery->getOrderBy();
foreach ($orders as $order_key => $order) {
// But if we're using a whole view, we don't know what we have!
if ($options['subquery_view']) {
list($sort_table, $sort_field) = explode('.', $order_key);
}
$orders[$sort_table . $this->subquery_namespace . '.' . $sort_field] = $order;
unset($orders[$order_key]);
}
// The query we get doesn't include the LIMIT, so add it here.
$subquery->range(0, 1);
// Extract the SQL the temporary view built.
$subquery_sql = $subquery->__toString();
// Replace the placeholder with the outer, correlated field.
// Eg, change the placeholder ':users_uid' into the outer field 'users.uid'.
// We have to work directly with the SQL, because putting a name of a field
// into a SelectQuery that it does not recognize (because it's outer) just
// makes it treat it as a string.
$outer_placeholder = ':' . str_replace('.', '_', $this->definition['outer field']);
$subquery_sql = str_replace($outer_placeholder, $this->definition['outer field'], $subquery_sql);
return $subquery_sql;
}
/**
* Recursive helper to add a namespace to conditions.
*
* Similar to _views_query_tag_alter_condition().
*
* (Though why is the condition we get in a simple query 3 levels deep???)
*/
function alter_subquery_condition(QueryAlterableInterface $query, &$conditions) {
foreach ($conditions as $condition_id => &$condition) {
// Skip the #conjunction element.
if (is_numeric($condition_id)) {
if (is_string($condition['field'])) {
$condition['field'] = $this->condition_namespace($condition['field']);
}
elseif (is_object($condition['field'])) {
$sub_conditions =& $condition['field']->conditions();
$this->alter_subquery_condition($query, $sub_conditions);
}
}
}
}
/**
* Helper function to namespace query pieces.
*
* Turns 'foo.bar' into 'foo_NAMESPACE.bar'.
*/
function condition_namespace($string) {
return str_replace('.', $this->subquery_namespace . '.', $string);
}
/**
* Called to implement a relationship in a query.
* This is mostly a copy of our parent's query() except for this bit with
* the join class.
*/
function query() {
// Figure out what base table this relationship brings to the party.
$table_data = views_fetch_data($this->definition['base']);
$base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
$this->ensure_my_table();
$def = $this->definition;
$def['table'] = $this->definition['base'];
$def['field'] = $base_field;
$def['left_table'] = $this->table_alias;
$def['left_field'] = $this->field;
if (!empty($this->options['required'])) {
$def['type'] = 'INNER';
}
if ($this->options['subquery_regenerate']) {
// For testing only, regenerate the subquery each time.
$def['left_query'] = $this->left_query($this->options);
}
else {
// Get the stored subquery SQL string.
$cid = 'views_relationship_groupwise_max:' . $this->view->name . ':' . $this->view->current_display . ':' . $this->options['id'];
$cache = cache_get($cid, 'cache_views_data');
if (isset($cache->data)) {
$def['left_query'] = $cache->data;
}
else {
$def['left_query'] = $this->left_query($this->options);
cache_set($cid, $def['left_query'], 'cache_views_data');
}
}
if (!empty($def['join_handler']) && class_exists($def['join_handler'])) {
$join = new $def['join_handler'];
}
else {
$join = new views_join_subquery();
}
$join->definition = $def;
$join->construct();
$join->adjusted = TRUE;
// use a short alias for this:
$alias = $def['table'] . '_' . $this->table;
$this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship);
}
}
+240
View File
@@ -0,0 +1,240 @@
<?php
/**
* @file
* @todo.
*/
/**
* @defgroup views_sort_handlers Views sort handlers
* @{
* Handlers to tell Views how to sort queries.
*/
/**
* Base sort handler that has no options and performs a simple sort.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort extends views_handler {
/**
* Determine if a sort can be exposed.
*/
function can_expose() { return TRUE; }
/**
* Called to add the sort to a query.
*/
function query() {
$this->ensure_my_table();
// Add the field.
$this->query->add_orderby($this->table_alias, $this->real_field, $this->options['order']);
}
function option_definition() {
$options = parent::option_definition();
$options['order'] = array('default' => 'ASC');
$options['exposed'] = array('default' => FALSE, 'bool' => TRUE);
$options['expose'] = array(
'contains' => array(
'label' => array('default' => '', 'translatable' => TRUE),
),
);
return $options;
}
/**
* Display whether or not the sort order is ascending or descending
*/
function admin_summary() {
if (!empty($this->options['exposed'])) {
return t('Exposed');
}
switch ($this->options['order']) {
case 'ASC':
case 'asc':
default:
return t('asc');
break;
case 'DESC';
case 'desc';
return t('desc');
break;
}
}
/**
* Basic options for all sort criteria
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
if ($this->can_expose()) {
$this->show_expose_button($form, $form_state);
}
$form['op_val_start'] = array('#value' => '<div class="clearfix">');
$this->show_sort_form($form, $form_state);
$form['op_val_end'] = array('#value' => '</div>');
if ($this->can_expose()) {
$this->show_expose_form($form, $form_state);
}
}
/**
* Shortcut to display the expose/hide button.
*/
function show_expose_button(&$form, &$form_state) {
$form['expose_button'] = array(
'#prefix' => '<div class="views-expose clearfix">',
'#suffix' => '</div>',
// Should always come first
'#weight' => -1000,
);
// Add a checkbox for JS users, which will have behavior attached to it
// so it can replace the button.
$form['expose_button']['checkbox'] = array(
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('js-only')),
);
$form['expose_button']['checkbox']['checkbox'] = array(
'#title' => t('Expose this sort to visitors, to allow them to change it'),
'#type' => 'checkbox',
);
// Then add the button itself.
if (empty($this->options['exposed'])) {
$form['expose_button']['markup'] = array(
'#markup' => '<div class="description exposed-description" style="float: left; margin-right:10px">' . t('This sort is not exposed. Expose it to allow the users to change it.') . '</div>',
);
$form['expose_button']['button'] = array(
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Expose sort'),
'#submit' => array('views_ui_config_item_form_expose'),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 0;
}
else {
$form['expose_button']['markup'] = array(
'#markup' => '<div class="description exposed-description">' . t('This sort is exposed. If you hide it, users will not be able to change it.') . '</div>',
);
$form['expose_button']['button'] = array(
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Hide sort'),
'#submit' => array('views_ui_config_item_form_expose'),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 1;
}
}
/**
* Simple validate handler
*/
function options_validate(&$form, &$form_state) {
$this->sort_validate($form, $form_state);
if (!empty($this->options['exposed'])) {
$this->expose_validate($form, $form_state);
}
}
/**
* Simple submit handler
*/
function options_submit(&$form, &$form_state) {
unset($form_state['values']['expose_button']); // don't store this.
$this->sort_submit($form, $form_state);
if (!empty($this->options['exposed'])) {
$this->expose_submit($form, $form_state);
}
}
/**
* Shortcut to display the value form.
*/
function show_sort_form(&$form, &$form_state) {
$options = $this->sort_options();
if (!empty($options)) {
$form['order'] = array(
'#type' => 'radios',
'#options' => $options,
'#default_value' => $this->options['order'],
);
}
}
function sort_validate(&$form, &$form_state) { }
function sort_submit(&$form, &$form_state) { }
/**
* Provide a list of options for the default sort form.
* Should be overridden by classes that don't override sort_form
*/
function sort_options() {
return array(
'ASC' => t('Sort ascending'),
'DESC' => t('Sort descending'),
);
}
function expose_form(&$form, &$form_state) {
// #flatten will move everything from $form['expose'][$key] to $form[$key]
// prior to rendering. That's why the pre_render for it needs to run first,
// so that when the next pre_render (the one for fieldsets) runs, it gets
// the flattened data.
array_unshift($form['#pre_render'], 'views_ui_pre_render_flatten_data');
$form['expose']['#flatten'] = TRUE;
$form['expose']['label'] = array(
'#type' => 'textfield',
'#default_value' => $this->options['expose']['label'],
'#title' => t('Label'),
'#required' => TRUE,
'#size' => 40,
'#weight' => -1,
);
}
/**
* Provide default options for exposed sorts.
*/
function expose_options() {
$this->options['expose'] = array(
'order' => $this->options['order'],
'label' => $this->definition['title'],
);
}
}
/**
* A special handler to take the place of missing or broken handlers.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort_broken extends views_handler_sort {
function ui_name($short = FALSE) {
return t('Broken/missing handler');
}
function ensure_my_table() { /* No table to ensure! */ }
function query($group_by = FALSE) { /* No query to run */ }
function options_form(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
);
}
/**
* Determine if the handler is considered 'broken'
*/
function broken() { return TRUE; }
}
/**
* @}
*/
+74
View File
@@ -0,0 +1,74 @@
<?php
/**
* @file
* Definition of views_handler_sort_date.
*/
/**
* Basic sort handler for dates.
*
* This handler enables granularity, which is the ability to make dates
* equivalent based upon nearness.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort_date extends views_handler_sort {
function option_definition() {
$options = parent::option_definition();
$options['granularity'] = array('default' => 'second');
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['granularity'] = array(
'#type' => 'radios',
'#title' => t('Granularity'),
'#options' => array(
'second' => t('Second'),
'minute' => t('Minute'),
'hour' => t('Hour'),
'day' => t('Day'),
'month' => t('Month'),
'year' => t('Year'),
),
'#description' => t('The granularity is the smallest unit to use when determining whether two dates are the same; for example, if the granularity is "Year" then all dates in 1999, regardless of when they fall in 1999, will be considered the same date.'),
'#default_value' => $this->options['granularity'],
);
}
/**
* Called to add the sort to a query.
*/
function query() {
$this->ensure_my_table();
switch ($this->options['granularity']) {
case 'second':
default:
$this->query->add_orderby($this->table_alias, $this->real_field, $this->options['order']);
return;
case 'minute':
$formula = views_date_sql_format('YmdHi', "$this->table_alias.$this->real_field");
break;
case 'hour':
$formula = views_date_sql_format('YmdH', "$this->table_alias.$this->real_field");
break;
case 'day':
$formula = views_date_sql_format('Ymd', "$this->table_alias.$this->real_field");
break;
case 'month':
$formula = views_date_sql_format('Ym', "$this->table_alias.$this->real_field");
break;
case 'year':
$formula = views_date_sql_format('Y', "$this->table_alias.$this->real_field");
break;
}
// Add the field.
$this->query->add_orderby(NULL, $formula, $this->options['order'], $this->table_alias . '_' . $this->field . '_' . $this->options['granularity']);
}
}
@@ -0,0 +1,38 @@
<?php
/**
* @file
* Definition of views_handler_sort_group_by_numeric.
*/
/**
* Handler for GROUP BY on simple numeric fields.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort_group_by_numeric extends views_handler_sort {
function init(&$view, &$options) {
parent::init($view, $options);
// Initialize the original handler.
$this->handler = views_get_handler($options['table'], $options['field'], 'sort');
$this->handler->init($view, $options);
}
/**
* Called to add the field to a query.
*/
function query() {
$this->ensure_my_table();
$params = array(
'function' => $this->options['group_type'],
);
$this->query->add_orderby($this->table_alias, $this->real_field, $this->options['order'], NULL, $params);
}
function ui_name($short = FALSE) {
return $this->get_field(parent::ui_name($short));
}
}
@@ -0,0 +1,54 @@
<?php
/**
* @file
* Definition of views_handler_sort_menu_hierarchy.
*/
/**
* Sort in menu hierarchy order.
*
* Given a field name of 'p' this produces an ORDER BY on p1, p2, ..., p9;
* and optionally injects multiple joins to {menu_links} to sort by weight
* and title as well.
*
* This is only really useful for the {menu_links} table.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort_menu_hierarchy extends views_handler_sort {
function option_definition() {
$options = parent::option_definition();
$options['sort_within_level'] = array('default' => FALSE);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['sort_within_level'] = array(
'#type' => 'checkbox',
'#title' => t('Sort within each hierarchy level'),
'#description' => t('Enable this to sort the items within each level of the hierarchy by weight and title. Warning: this may produce a slow query.'),
'#default_value' => $this->options['sort_within_level'],
);
}
function query() {
$this->ensure_my_table();
$max_depth = isset($this->definition['max depth']) ? $this->definition['max depth'] : MENU_MAX_DEPTH;
for ($i = 1; $i <= $max_depth; ++$i) {
if ($this->options['sort_within_level']) {
$join = new views_join();
$join->construct('menu_links', $this->table_alias, $this->field . $i, 'mlid');
$menu_links = $this->query->add_table('menu_links', NULL, $join);
$this->query->add_orderby($menu_links, 'weight', $this->options['order']);
$this->query->add_orderby($menu_links, 'link_title', $this->options['order']);
}
// We need this even when also sorting by weight and title, to make sure
// that children of two parents with the same weight and title are
// correctly separated.
$this->query->add_orderby($this->table_alias, $this->field . $i, $this->options['order']);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
/**
* @file
* Definition of views_handler_sort_random.
*/
/**
* Handle a random sort.
*
* @ingroup views_sort_handlers
*/
class views_handler_sort_random extends views_handler_sort {
function query() {
$this->query->add_orderby('rand');
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['order']['#access'] = FALSE;
}
}
+62
View File
@@ -0,0 +1,62 @@
The views module allows administrators and site designers to create, manage, and display lists of content. Each list managed by the views module is known as a "view", and the output of a view is known as a "display". Displays are provided in either block or page form, and a single view may have multiple displays. Optional navigation aids, including a system path and menu item, can be set for each page-based display of a view. By default, views may be created that list content (a <em>Node</em> view type), content revisions (a <em>Node revisions</em> view type) or users (a <em>User</em> view type). A view may be restricted to members of specific user roles, and may be added, edited or deleted at the <a href="base_url:admin/structure/views">views administration page</a>.
For more technical users, views can be understood as a user interface to compose SQL-queries and to pull information (Content, Users, etc.) from the database and show it on a screen as desired.
The "building block" design of the views system provides power and flexibility, allowing parameters to be specified only when needed. While an advanced view may use all of available parameters to create complex and highly interactive applications, a simple content listing may specify only a few options. All views rely on a conceptual framework that includes:
<ul>
<li><a href="topic:views/field">Fields</a>, or the individual pieces of data being displayed. Adding the fields <em>Node: Title</em>, <em>Node: Type</em>, and <em>Node: Post date</em> to a node view, for example, includes the title, content type and creation date in the displayed results </li>
<li><a href="topic:views/relationship">Relationships</a>, or information about how data elements relate to one another. If relationship data is available, like that provided by a CCK <em>nodereference</em> field, items from a related node may be included in the view </li>
<li><a href="topic:views/argument">Arguments</a>, or additional parameters that dynamically refine the view results, passed as part of the path. Adding an argument of <em>Node: Type</em> to a node view with a path of "content", for example, dynamically filters the displayed items by content type. In this example (shown with Clean URLs enabled), accessing the view through the path "<em>http://www.example.com/content/page</em>" displays all posts of the type "page", the path "<em>http://www.example.com/content/story</em>" displays all posts of the type "story", and "<em>http://www.example.com/content</em>" displays all posts regardless of type) </li>
<li><a href="topic:views/sort">Sort criteria</a>, which determine the order of items displayed in the view results. Adding the sort criteria <em>Node: Post date</em> (in descending order) to a node <em>view</em>, for example, sorts the displayed posts in descending order by creation date </li>
<li><a href="topic:views/filter">Filters</a>, which limit items displayed in the results. Adding the filter <em>Node: Published</em> (and setting it equal to "Published") to a node view, for example, prevents unpublished items from being displayed</li>
<li><a href="topic:views/display">Displays</a>, which control where the output will be seen. Every view has a default display, which doesn't actually display the view anywhere, but is used to hold the default settings for the view, and is used when the view is called programmatically if another display is not specified. Much more useful to users are the <a href="topic:views/display-page">page</a> display, which gives a view a path and allows it to be the primary content of a page, or the <a href="topic:views/display-block">block</a> display which allows it to appear as secondary content on other pages.</li>
<li><a href="topic:views/header">Header</a>, which allow you to add by default one or more text area above the views output. </li>
<li><a href="topic:views/footer">Footer</a>, which allow you to add by default one or more text area beneath the views output. </li>
<li>The <a href="topic:views/footer">Emtpy Text</a> content will be displayed, when you choose in the Arguments Section "Action to take if argument is not present" the option "Display empty text".</li>
</ul>
Parallels between the components in the Views interface and an sql query:
<table>
<thead>
<tr>
<th>Sql Query</th>
<th>Views Component</th>
<tr>
</thead>
<tr>
<td>SELECT n.title, u.name</td>
<td>fields</td>
</tr>
<tr>
<td>FROM {node} n base table</td>
<td>view type</td>
</tr>
<tr>
<td>INNER JOIN {users} u ON n.uid = u.uid</td>
<td>relationship</td>
</tr>
<tr>
<td>WHERE n.status = 1</td>
<td>filter</td>
</tr>
<tr>
<td>AND u.uid = arg(1)</td>
<td>argument</td>
</tr>
<tr>
<td>ORDER BY n.changed DESC</td>
<td>sort</td>
</tr>
</table>
+43
View File
@@ -0,0 +1,43 @@
In the category <strong>Other</strong> you have different options to set Advanced configurations in your View.
<strong>Machine Name:</strong>
You can change the default machine name of the view.
<strong>Comment: No comment</strong>
You can Use the comment option to write comments for your Views, the comments are only shown in the Views UI. Comment your Display for other Maintainer
<strong>Use AJAX: No</strong>
If set, this view will use an AJAX mechanism for paging, table sorting and exposed filters. This prevents the entire page from refreshing. It is not recommended that you use this if this view is the main content of the page as it will prevent deep linking to specific pages, but it is very useful for side content. Block displays require this setting to be ON if exposed filters are being used.
<strong>Hide attachments in summary: No</strong>
<strong>Use aggregation: No</strong>
All fields that are selected for grouping will be collapsed to one record per distinct value. Other fields which are selected for aggregation will have the function run on them. For example, you can group nodes on title and count the number of nids in order to get a list of duplicate titles.
For more Information how aggregation work see the "Use Aggregation" Help Page
<strong>Query settings: Settings</strong>
Here can you set advanced Settings for the SQL Settings
<ul>
<li><strong>Disable SQL rewriting</strong></li>
<li><strong>Distinct: No</strong></li>
This will make the view display only distinct items. If there are multiple identical items, each will be displayed only once. You can use this to try and remove duplicates from a view, though it does not always work. Note that this can slow queries down, so use it with caution.
<li><strong>Use Slave Server</strong></li>
<li><strong>Query Comment</strong></li>
</ul>
<strong>Field Language: Current user's language</strong>
<strong>Caching: None</strong>
You can choose a "Time-based" Caching if you want. With it you get the option to choose the length of time raw query results should be cached and "The length of time rendered HTML output should be cached."
<strong>Link display: Page</strong>
<strong>CSS class: None</strong>
You can define some own CSS Classes for your View
<strong>Theme: Information</strong>
Clicking on the "Theme: Information" link provides you with a listing of all posiible theming files. The highlighted files are the ones Views is currently using. All other filenames are suggested templates.
For more Information see the <a href="/help/views/analyze-theme" title="Theme Information" >"Theme information"</a> Page
+30
View File
@@ -0,0 +1,30 @@
In Views 3 you can set Advanced Style Settings, they help you to insert markup of your own from the Views UI, so that you can fairly easily override the default markup without having to restyle via templates.
<ul>
<li> Customize field HTML</li>
With Customize field HTML you can generate html tags around the field.
<li>Customize label HTML</li>
Here you can generate html tags around the label of a field.
<li>Customize field and label wrapper HTML</li>
Here you can generate html tags around the wrapper of the label and field
</ul>
<a href="path:images/views3-semanticviews.png"><img src="path:images/views3-semanticviews.png" /></a>
Usage example
In a view with a field:
<ol>
<li>Configure the field. (Click on the field.)</li>
<li>In the modal that opens, scroll down to <strong>Style Settings</strong>.</li>
<li>Choose one or more of the three <i>Customize</i> options. This will reveal a dropdown menu where you can choose from one or more HTML tags to use on that field and allow you to add a CSS class specific to that field should you desire.</li>
<li>Decide if you want to keep the Views default classes. Unchecking this box means your markup is *it*.</li>
</ol>
In Views 2 you needed for Style Settings the <a href="http://drupal.org/project/semanticviews">Semantic Views</a> Modul, now the Semantic Views module has been mostly incorporated into Views 3.x. Semantic Views is still around for people who need it, though. For some details on how the original module is different from the Views implementation, please see <a href="http://drupal.org/node/1013876">this issue</a>.
+1
View File
@@ -0,0 +1 @@
See: <a href="topic:views/group-by">Group by</a>
+31
View File
@@ -0,0 +1,31 @@
Modifying default values of a views exposed form is tricky, because FAPI was not designed to work with GET forms. One consequence is that it often can't tell if textfields (there are others) were submitted or not.
As a consequence, it *always* thinks the value was submitted, even if it was not. To fix that, Views modifies $form_state['input'][$identifier] with the default value if $form_state['input'][$identifier] was not set. In order to modify the default value in an alter, you need to do this:
<pre>
&lt;?php
if (empty($form_state['view']->exposed_input[$identifier])) .
{ $form_state['input'][$identifier] = $default_value; }
?&gt;
</pre>
where $identifier is the particular filter for which you want to change the default value, and $default_value is the new default value you want to set.
If you use a hook_form_FORM_ID_alter or hook_form_alter, you can modify exposed filters on the fly based on information that is external to Views. For example, I modified the exposed filter of a form to set a taxonomy term ID based on the user's GeoIP.
To do this, I used the following function, where geoip_redirect_get_tid() loads the relevant term id based on the user's current ip_address():
<pre>
&lt;?php
function MODULENAME_form_views_exposed_form_alter(&$form, $form_state) {
if(strpos($form['#id'], 'volunteer-directory') !== FALSE) {
$city_tid = geoip_redirect_get_tid();
if(is_numeric($city_tid) && $city_tid != 7660) {
if (empty($form_state['view']->exposed_input['tid'])) {
$form_state['input']['tid'] = $city_tid;
}
}
}
}
?&gt;
</pre>
+24
View File
@@ -0,0 +1,24 @@
<p>Clicking on the "Theme: Information" link provides you with a listing of all posiible theming files. The highlighted files are the ones Views is currently using. All other filenames are suggested templates.</p>
<p>You may use any of the following possible theme files to modify individual parts of your view. In total, there are four parts to theming a view.</p>
<ul>
<li> The <strong>display</strong> theme is usually views-view.tpl.php and it largely controls the decorations around a view; where the header, footer, pager, more link, feed icon, etc, will be placed. </li>
<li> The <strong>style</strong> will control how all of the results of the display are put together. It may be as simple as just displaying all of the rows, or it may be a complex table generator or something in between. </li>
<li> The <strong>row</strong> style controls each individual row; not all styles utilize the row style (notably the table), but most others do.
<li> Finally, <strong>field</strong> themes allow you to override the look and even the data of each individual field, if the style uses fields. The actual template the system will use should be hilighted in <strong>bold</strong>.</li>
</ul>
<div class="help-box" style="text-align:center">
<a href="path:images/style-breakdown-large.png"><img src="path:images/style-breakdown.png" /></a>
<em>A breakdown of View output</em>
</div>
<p>The link to the left of each type will give you information about the default template used for that type. You may cut and paste this and place it in your theme with the appropriate template, or you may copy the base file from the views/theme directory (or, if provided by a module, from the module's directory). <strong>It is important that you clear the theme registry cache every time you add a new template, or the new template will not be picked up.</strong></p>
<p><strong>Important note:</strong> You place your custom template files in your theme directory, <strong>not views/theme</strong>. This is always true of theming with Drupal.
<p>In addition to this tool, the very useful <a href="http://drupal.org/project/devel">devel</a> module contains a tool called the "Theme developer" which does a good job of visually showing you which areas of your site use which themes. Be careful with it, though, as the theme developer causes the Views edit page to break.</p>
<p>Also, this feature will only work properly with Drupal 6.3 and later; prior to Drupal 6.3 <a href="http://drupal.org/node/241570">this patch</a> will be required.</p>
+103
View File
@@ -0,0 +1,103 @@
Views can be stored in the database, which is typical of smaller sites and hobby sites. However, Views may also be stored directly in the code as "default" views, (which simply means they're available by default). Modules often come with views that are specific to the module data, but it's also possible -- and <b>highly</b> recommended -- that sites which have separate "development" and "production" sites export their views into default views in a site-specific module. This makes it very easy to transfer views from dev to production without making database changes.
<h3>Creating a module</h3>
First, create a directory in <em>sites/all/modules</em> for your new module. Call it whatever you like, but for this example we will call it <em>mymodule</em>.
In this directory, create a <em>mymodule.module</em> file. It can be empty for now, but it should at least contain an opening PHP tag:
<pre>&lt;?php
</pre>
It should not contain a closing ?&gt; tag, as the closing ?&gt; tag is not required and anything AFTER the closing tag, such as a space or a linefeed, will be displayed directly to the browser and can potentially cause problems.
The .module file will contain functions and drupal hooks. Hooks are specially named functions that Drupal will call in order to get your module's response at certain times while generating pages. The only function you will need for this exercise is the 'views_api' hook that tells Views that this module supports the Views API and what version:
<pre>function mymodule_views_api() {
return array('api' => 2.0);
}
</pre>
For other uses you may well add additional functions.
Second, you need to create a <em>mymodule.info</em> file:
<pre>name = My module
description = My site specific module.
core = 6.x
</pre>
Once you have these two files set up, you should be able to activate your new module at the <em>Administer >> Modules</em> page.
<h3>Exporting your views</h3>
The easiest way to do this is to activate the 'views_export' module, and navigate to <em>Administer >> Structure >> Views >> Tools >> Bulk export</em> Place a check next to each view that you want in your module, type the module name into the text field, and click export. This will create the entire <em>hook_views_default_views()</em> function for you.
You can also export individual views. If you do this, keep in mind that this export does not include the line that adds the exported $view into the larger $views array:
<pre>$views[$view->name] = $view</pre>
To place this into your <em>hook_views_default_views()</em> you will need to place that after the view, and make sure the function returns $views at the end.
<h3>Placing your exported views into your module</h3>
Cut and paste the entire output of the bulk export tool into mymodule.views_default.inc -- and be sure to put a &lt;?php at the top of the file so that the webserver knows that it's PHP code! Then visit the Views tools page and clear the Views cache. Your views should now be listed as <b>Overridden</b> on the view list page. If you <b>revert</b> these views, they will be removed from the database, but will remain in code.
<h3>Theming your views in your module</h3>
You can theme these views in the module and not need to rely on the theme to do this at all; and in fact, the theme can continue to override these just like it ordinarily would, even if your module provides a theme. This is very useful for distributing a module where the view needs to look "just so."
To do this, you need to implement <em>hook_theme()</em> in your module:
<pre>function mymodule_theme($existing) {
return array(
'views_view__viewname__displayid' => array (
'arguments' => array('view' => NULL),
'template' => 'views-view--viewname--displayid',
'base hook' => 'views_view',
'path' => drupal_get_path('module', 'mymodule'),
),
);
}
</pre>
There are a small number of gotchas in doing this that you must be aware of.
<ol>
<li>When referring to a template filename, you always use dashes in the name. i.e, <em>views-view--viewname--displayid.tpl.php</em>. However, when referring to the hook or function names, you use underscores instead of dashes. i.e, <em>views_view</em> and <em>views_view__viewname__displayid</em></li>
<li>The 'arguments' change based upon which of the 3 types you're overriding. There's the 'display', the 'style' and the 'row' style. The above code is assuming the display, which is usually just <em>views_view</em>. Here are the possibilities:
<pre>display: array('view_array' => array(), 'view' => NULL),
style: array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL),
row: array('view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL),
field: array('view' => NULL, 'field' => NULL, 'row' => NULL),
</pre>
Be sure to use the right arguments line or the theme system will not properly translate.
</li>
<li>The 'template' line should never include the extension, so drop the .tpl.php from it.</li>
<li>You need to make sure that the Views preprocess functions get registered. The 'base hook' line in the definition does that, but it can only do it if it comes after the Views registration, which actually happens very late in theme building. 99% of the time, your module will come before Views. You have two choices to deal with this:
<ol>
<li>Set your module's weight to 11 or higher in the database. Views' weight is 10. You can make this happen automatically when the module is first installed by creating a mymodule.install file and using this code:
<pre>function mymodule_install() {
db_query("UPDATE {system} SET weight = 11 WHERE name = 'mymodule'");
}
</pre>
If you use this method, the <em>base hook</em> should be set to the name of the original template being used. i.e, if this is a variate of views-view-list.tpl.php, this should be 'views_view_list'.
</li>
<li>You can also just force it to list the preprocessors without actually having to detect them. This doesn't require modifying your module's weight, which is not always possible, you can insert this code into the array:
<pre> 'preprocess functions' => array(
'template_preprocess',
'template_preprocess_views_view',
'mymodule_preprocess_views_view__viewname_displayid',
),
</pre>
The first one is the global 'template_preprocess' function which all templates utilize. It does some basic things such as setting up $zebra and a few other items. See <a href="http://api.drupal.org/api/function/template_preprocess/6">api.drupal.org</a> for specifics.
The second one is the plugin specific preprocess. Like 'base hook' it should conform to the name used by the original template. i.e, if the original template was <em>views-view-list.tpl.php</em> then that preprocess function would be named <em>template_preprocess_views_view_list</em>.
The third one is your module's preprocess function, if it needs one. In general, you probably will not need one, and you should only attempt to use one if you are reasonably familiar with the concept of preprocess functions and Drupal's theme system in general. See Drupal's theme documentation for more information.
</li>
</ol>
</li>
<li>
If you leave the path blank the template file will be searched for in "./" which is the Drupal install base path.
</li>
</ol>
+179
View File
@@ -0,0 +1,179 @@
For the new table defined by the Node example module to be understood by the views module you need to create a node_example.views.inc file that describes the table and its relationships to the rest of the database. In order for views to know that this file is to be loaded you need to implement hook_views_api. This is done by adding the following function into your node_example.module file
<pre>
&lt;?php
/**
* Implements hook_views_api().
*
* This tells drupal that there is Views integration file named
* module-name.views.inc
*/
function node_example_views_api() {
// Note that you can include 'path' in this array so that your views.inc
// file can be stored in a different location.
return array(
'api' => 2.0
);
}
?&gt;
</pre>
Below is the contents of a simple node_example.views.inc file that allows you to create views that include the new color and quantity information.
<pre>
&lt;?php
/**
* This file is used to tell the views module about the new node_example table.
*
* Database definition:
* @code
* CREATE TABLE node_example (
* vid int(10) unsigned NOT NULL default '0',
* nid int(10) unsigned NOT NULL default '0',
* color varchar(255) NOT NULL default '',
* quantity int(10) unsigned NOT NULL default '0',
* PRIMARY KEY (vid, nid),
* KEY `node_example_nid` (nid)
* )
* @endcode
*/
function node_example_views_data() {
// Basic table information.
// ----------------------------------------------------------------
// node_example table
// New group within Views called 'Example'
// The group will appear in the UI in the dropdown tha allows you
// to narrow down which fields and filters are available.
$data = array();
$data['node_example']['table']['group'] = t('Example');
// Let Views know that our example table joins to the 'node'
// base table. This means it will be available when listing
// nodes and automatically make its fields appear.
//
// We also show up for node revisions.
$data['node_example']['table']['join'] = array(
'node_revisions' => array(
'left_field' => 'vid',
'field' => 'vid',
),
'node' => array(
'left_field' => 'vid',
'field' => 'vid',
),
);
// quantity
$data['node_example']['quantity'] = array(
'title' => t('Quantity'),
'help' => t('Quantity of items.'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Color
$data['node_example']['color'] = array(
'title' => t('Color'),
'help' => t('Color of item.'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_string',
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
return $data;
}
?&gt;
</pre>
Some notes on usage:
Within Views, click on the Add tab. You have a number of type options here. Normally you would select either 'Node' (if you only want to display information on current nodes) or 'Node revision' (if you want to display information on all revisions of the nodes)
With this configuration you always pull out of the database, data for every single node, whether or not it has color and quantity information. To display information on just those nodes that have color and quantity information you can use a filter so that only nodes which don't have a NULL color or a NULL quantity are displayed.
<h3>Type/relationship extension</h3>
When your tables have first class data, you will often need to have own View types and View relationships defined. With the current node_example table this isn't required although I try to justify it below on an efficiency basis. See [[http://groups.drupal.org/node/17236#comment-58980|this discussion]] as to why it isn't justified.
Pulling data out of the database for every node when you only want data for the new Example node type is inefficient. To reduce the initial data extraction to just that relating to the new Example nodes requires that you make the node_example table the base table. This can be done by adding the following code into the node_example.views.inc file just before the 'return $data;'
<pre>
&lt;?php
// **** Begin optional extra for type and relationships ****
// Use node_example as a new base table
// by creating a new views type called 'Node example'
// This allows it to be selected as the 'view type'
// when you initially add a new view.
$data['node_example']['table']['base'] = array(
'field' => 'vid',
'title' => t('Node example'),
'help' => t("Node example type with color and quantity information."),
'weight' => -9,
);
// When using the new 'Node example' type you need to use relationships
// to access fields in other tables.
// Relationship to the 'Node revision' table
$data['node_example']['vid'] = array(
'title' => t('Node revision'),
'help' => t('The particular node revision the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node revision'),
'base' => 'node_revisions',
'base field' => 'vid',
// This allows us to not show this relationship if the base is already
// node_revisions so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// Relationship to the 'Node' table
$data['node_example']['nid'] = array(
'title' => t('Node'),
'help' => t('The particular node the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node'),
'base' => 'node',
'base field' => 'nid',
// This allows us to not show this relationship if the base is already
// node so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// **** End optional extra for type and relationships ****
?&gt;
</pre>
The above code adds a new 'Node example' to the view types that can be selected within the Add tab window of views. Selecting this sets the node_example table to be the base table.
If you select 'Node example' as view type, when you initially go into the edit window of views you will find the only fields available are the color and quantity fields. To get fields from other tables you need to add a relationship. Relationships may be found at the top in the same column as the fields.
+88
View File
@@ -0,0 +1,88 @@
Views allows handlers to output form elements, wrapping them automatically in a form, and handling validation / submission.
The form is multistep by default, allowing other modules to add additional steps, such as confirmation screens.
<h2>Implementation</h2>
A views handler outputs a special placeholder in render(), while the real form with matching structure gets added in views_form().
When the View is being preprocessed for the theme file, all placeholders get replaced with the rendered form elements.
The views handler can also implement views_form_validate() and views_form_submit().
<pre>
function render($values) {
return '&lt;!--form-item-' . $this-&gt;options['id'] . '--' . $this-&gt;view-&gt;row_index . '--&gt;';
}
function form_element_name() {
// Make sure this value is unique for all the view fields
return $this-&gt;options['id'];
}
function form_element_row_id($row_id) {
// You could use values from $this-&gt;view-&gt;result[$row_id]
// to provide complex row ids.
return $row_id;
}
function views_form(&$form, &$form_state) {
// The view is empty, abort.
if (empty($this-&gt;view-&gt;result)) {
return;
}
$field_name = $this-&gt;form_element_name();
$form[$field_name] = array(
'#tree' => TRUE,
);
// At this point, the query has already been run, so we can access the results
foreach ($this-&gt;view-&gt;result as $row_id => $row) {
$form_element_row_id = $this-&gt;form_element_row_id($row_id);
$form[$field_name][$form_element_row_id] = array(
'#type' => 'textfield',
'#title' => t('Your name'),
'#default_value' => '',
);
}
}
// Optional validate function.
function views_form_validate($form, &$form_state) {
$field_name = $this->form_element_name();
foreach ($form_state['values'][$field_name] as $row_id => $value) {
if ($value == 'Drupal') {
form_set_error($field_name . '][' . $row_id, "You can't be named Drupal. That's my name.");
}
}
}
// Optional submit function.
function views_form_submit($form, &$form_state) {
// Do something here
}
</pre>
The form is multistep by default, with one step: 'views_form_views_form'.
A "form_example" module could add a confirmation step by setting:
<pre>
$form_state['step'] = 'form_example_confirmation';
</pre>
in form_example_views_form_submit().
Then, views_form would call form_example_confirmation($form, $form_state, $view, $output) to get that step.
<b>Important:</b> You can fetch the Views object in form_alter and validate / submit hooks from the form state:
<pre>
$view = $form_state['build_info']['args'][0];
</pre>
<h2>Relevant Views functions</h2>
<ul>
<li>template_preprocess_views_view()</li>
<li>views_form()</li>
<li>views_form_views_form()</li>
<li>views_form_views_form_validate()</li>
<li>views_form_views_form_submit()</li>
<li>theme_views_form_views_form()</li>
</ul>
<h2>Hooks</h2>
<ul>
<li>hook_views_form_substitutions()</li>
</ul>
+45
View File
@@ -0,0 +1,45 @@
In Views areas (header, footer, empty-text) are pluggable, this means you can write your own php logic to place whatever you want.
<h3>Requirements</h3>
You should have read <a href="topic:views/api">API</a> and <a href="topic:views/api-tables">Tables API</a> to get a basic knowledge
how to extend views.
<h3>Create your own area handler</h3>
The first step is to tell views: Hey i want to add a new area handler.
Therefore you have to implement hook_views_data and add a new one. For example:
<pre>
function yourmodule_views_data() {
$data['views']['collapsible_area'] = array(
'title' =&gt; t('Collabsible Text area'),
'help' =&gt; t('Provide collabsible markup text for the area.'),
'area' =&gt; array(
'handler' =&gt; 'yourmodule_handler_collapsible_area_text',
),
);
}
</pre>
The second step is to write this handler. Therefore create a file called yourmodule_handler_collapsible_area_text.inc and
add it to the .info file of your module.
Then add content to your area file like this:
<pre>
class yourmodule_handler_collapsible_area_text extends views_handler_area_text {
function render($empty = FALSE) {
// Here you just return a string of your content you want.
if ($render = parent::render($empty)) {
$element = array(
'#type' =&gt; 'fieldset',
'#title' =&gt; t('Title'),
'#value' =&gt; $render,
);
$output = theme('fieldset', $element);
return $output;
}
}
}
</pre>
As on every handler you can add options so you can configure the behavior. If the area isn't shown yet in the views interface, please clear the cache :)
+262
View File
@@ -0,0 +1,262 @@
Tables are described to Views by implementing hook_views_data(). This should be placed in MODULENAME.views.inc and it will be autoloaded (see <a href="topic:views/api">Views' API</a>). The hook implementation should return an array of table information, keyed by the name of the table. For example, if your module is describing three tables, 'foo', 'bar' and 'baz', your hook will look like this:
<pre> /**
* Implements hook_views_data().
*/
function MODULENAME_views_data() {
$data = array(
'foo' =&gt; array(
// ...info about the table named 'foo'...
),
'bar' =&gt; array(
// ...info about the table named 'bar'...
),
'baz' =&gt; array(
// ...info about the table named 'baz'...
),
);
return $data;
}
</pre>
The key should be the actual database name of the table (not including prefix), but it can be an alias as long as the join information (explained later) contains the real name of the table.
Each item in the array should be a field in the table, with the exception of a special information section called 'table'. Example:
<pre>$data['foo'] = array(
'table' =&gt; array(
// ... info about the table, described later ...
),
'bar' =&gt; array(
// ... info about the field named 'bar', i.e, foo.bar,
),
'baz' =&gt; array(
// ... info about the field named 'baz', i.e, foo.baz,
),
);
</pre>
Once you get down to an array that contains actual data, that piece of the array will often be referred to as the definition.
<h2>The 'table' section</h2>
Each table should have a 'table' section in it, which is used to set default information for the table, such as the group, as well as the very important joins and whether or not this is a base table.
First, there are several items that are actually for fields but can be placed here so that all fields within the table inherit them:
<dl>
<dt>group</dt>
<dd>The name of the group this item will be with. In the UI, this is displayed as Group: Title. For example, "Node: Node ID", "Taxonomy: Term description", etc. It is important to be consistent with groups, because the UI sorts by group, and allows filtering by group to find fields as well.</dd>
<dt>title</dt>
<dd>The actual name of the field; it should be concise and descriptive.</dd>
<dt>help</dt>
<dd>A longer description to help describe what the field is or does. It should try to be only a line or two so as not to clutter the UI.</dd>
</dl>
In general, having 'title' and 'help' at the table level doesn't make a lot of sense, but usually every item in a table is in the same group. Thus it is very common to define the 'group':
<pre>
$data['foo']['table']['group'] = t('Foo');
</pre>
The other items in the 'table' section are described in the following sections.
<h3>'base': Base table</h3>
If your table is a base table -- meaning it can be the primary, central table for a View to use, you can declare it to be a base table. This primarily provides UI information so that it can be selected.
For example:
<pre>
// Advertise this table as a possible base table
$data['node']['table']['base'] = array(
'field' =&gt; 'nid',
'title' =&gt; t('Node'),
'help' =&gt; t("Nodes are a Drupal site's primary content."),
'weight' =&gt; -10,
);
</pre>
The following items are available in the base section:
<dl>
<dt>field</dt>
<dd>The primary key field for this table. For Views to treat any table as a base table, it <b>must</b> have a primary field. For node this is the 'nid', for users this is the 'uid', etc. <strong>Without a single primary key field (i.e. not a composite key), Views will not be able to utilize the table as a base table.</strong> If your table does not have a primary key field, it is not too difficult to just add a serial field to it, usually.</dd>
<dt>title</dt>
<dd>The title of this table in the UI. It should be singular and describe the object that this table contains from the perspective of the user.</dd>
<dt>help</dt>
<dd>A short piece of text to describe what object this table contains.</dd>
<dt>database</dt>
<dd>If this table is held in a different database from your Drupal database, specify it as a string in the exact same format as the settings.php file. This is a special purpose variable that will probably be only used in site specific code, and <b>it must be the same database type as your Drupal database</b>. Also, don't try to join it to any table that isn't in the same database. That'll just create all kinds of silly errors. For example:
<pre>
// In settings.php for your site
// Your drupal (site) database needs to be called 'default'
$db_url['default'] = 'mysqli://user:pass@host/drupal_db';
$db_url['budget'] = 'mysqli://user:pass@host/other_db';
</pre>
Then when you are describing the external database in your base table you would write something like this:
<pre>
$data[$table]['table']['base'] = array(
'field' => 'Primary key',
'title' => t('Field name'),
'help' => t('Field description'),
'database' => 'budget',
'weight' => -10,
);
</pre>
</dd>
</dl>
<h3>'join': Linking your table to existing base tables</h3>
For Views to use your table, it has to either be a base table, or know how to link to an existing base table. Or sometimes both. Views uses this information to create a path to the base table; when the table is added to the query, Views will walk along this path, adding all tables required into the query.
<div class="help-box" style="text-align:center">
<a href="path:images/node-term_node-term_data-large.png"><img src="path:images/node-term_node-term_data.png" /></a>
<em>How taxonomy_term_data joins to node</em>
</div>
In the above example, to use these with 'node' as the base table, both 'taxonomy_term_data' and 'term_node' need to be defined, and they each need a join handler for node:
<pre>
$data['taxonomy_term_data']['table']['join']['node'] = array(
'left_table' =&gt; 'term_node',
'left_field' =&gt; 'tid',
'field' =&gt; 'tid',
);
</pre>
The above can be read as "In order to join to the node table, the taxonomy_term_data table must first link to the term_node table, and they join on the 'tid' field.". When adding this table to the query for a node view, Views will look at this and then look for the term_node table.
<pre>
$data['term_node']['table']['join']['node'] = array(
'left_field' =&gt; 'nid',
'field' =&gt; 'nid',
);
</pre>
Above, the fact that 'left_table' is left out lets us know that term_node links directly to the node table, using the 'nid' field on both sides of the join.
Quite a few more fields are available in this definition:
<dl>
<dt>handler</dt>
<dd>The name of the handler object to use. Defaults to 'views_join'. You may create custom join handlers that may or may not use any of the data below, as they see fit.</dd>
<dt>table</dt>
<dd>Table to join. This is optional, and should only be used if the table being referenced is an alias.</dd>
<dt>field</dt>
<dd>Field to join on. This is required.</dd>
<dt>left_table</dt>
<dd>The next step toward the final destination. If this is the final destination it may be omitted.</dd>
<dt>left_field</dt>
<dd>The field to join to on the left side. This is required.</dd>
<dt>type</dt>
<dd>Either LEFT (default) or INNER.</dd>
<dt>extra</dt>
<dd>Either a string that's directly added, or an array of items. Each item is, itself, an array:
<dl>
<dt>table</dt>
<dd>table should not be set in most cases, as it would be filled with the right table alias. Set it to NULL if you want to use a formular in "field"</dd>
<dt>field</dt>
<dd>Field or formula, therein "%alias" can be used to reference the right table.</dd>
<dt>operator</dt>
<dd>Similar to filters, this is the operator, such as &gt;, &lt;, =, etc. Defaults to = or IN.</dd>
<dt>value</dt>
<dd>Must be set. If an array, operator will be defaulted to IN.</dd>
<dt>numeric</dt>
<dd>If true, the value will not be surrounded in quotes, and %d will be used for its placeholder.</dd>
</dl>
</dd>
<dt>extra type</dt>
<dd> How all the extras will be combined. Either AND or OR. Defaults to AND.</dd>
</dl>
<h2>Describing fields on tables</h2>
Aside from the special table tag, each table can also have an unlimited number of field designations; these correspond roughly to fields on the table, though it is very common to use non-fields to display data that isn't directly in a field, such as data arrived from formulae, or special links related to the object the table is part of.
Each field is described in the view data with an array, keyed to the database name of the field. This array may contain some information fields, plus an entry in each of the five types of items Views has per field: argument, field, filter, relationship, sort. For example:
<pre>
$data['node']['nid'] = array(
'title' =&gt; t('Nid'),
'help' =&gt; t('The node ID of the node.'), // The help that appears on the UI,
// Information for displaying the nid
'field' =&gt; array(
'handler' =&gt; 'views_handler_field_node',
'click sortable' =&gt; TRUE,
),
// Information for accepting a nid as an argument
'argument' =&gt; array(
'handler' =&gt; 'views_handler_argument_node_nid',
'name field' =&gt; 'title', // the field to display in the summary.
'numeric' =&gt; TRUE,
'validate type' =&gt; 'nid',
),
// Information for accepting a nid as a filter
'filter' =&gt; array(
'handler' =&gt; 'views_handler_filter_numeric',
),
// Information for sorting on a nid.
'sort' =&gt; array(
'handler' =&gt; 'views_handler_sort',
),
);
</pre>
The above example describes the 'nid' field on the 'node' table, providing 4 of the 5 handlers. Note that while field is normally expected to be the database name of the field, it doesn't have to be; you can use an alias (which is how you get multiple handlers per field) or something completely made up for items that aren't tied to the database. For example:
<pre>
$data['node']['edit_node'] = array(
'field' =&gt; array(
'title' =&gt; t('Edit link'),
'help' =&gt; t('Provide a simple link to edit the node.'),
'handler' =&gt; 'views_handler_field_node_link_edit',
),
);
</pre>
The above handler definition an edit link to a node, but this isn't a field in and of itself. For aliased fields, here is another example:
<pre>
$data['users']['uid_current'] = array(
'real field' =&gt; 'uid',
'title' =&gt; t('Current'),
'help' =&gt; t('Filter the view to the currently logged in user.'),
'filter' =&gt; array(
'handler' =&gt; 'views_handler_filter_user_current',
),
);
</pre>
The above definition provides an alternate filter handler on the uid field for the current user.
The following items are allowed in the field definition:
<dl>
<dt>group, title, help</dt>
<dd>As above, these fields are for the UI. If placed here, any of these fields will override a setting on the base table.</dd>
<dt>real field</dt>
<dd>If this field is an alias, the "real field" may be placed here, and the handler will never know the difference.</dd>
<dt>field</dt>
<dd>A handler definition for the "Field" section, which is a field that may be displayed in a view. The definition is an array; the contents of the array are completely up to the handler, other than the 'handler' definition. If omitted, handler will default to 'views_handler_field'.</dd>
<dt>filter</dt>
<dd>A handler definition for the "Filters" section, which will be used to apply WHERE clauses to the view. The definition is an array; the contents of the array are completely up to the handler, other than the 'handler' definition. If omitted, handler will default to 'views_handler_filter'.</dd>
<dt>sort</dt>
<dd>A handler definition for the "Sort criteria" section, which will be used to add an ORDER BY clause to the view. The definition is an array; the contents of the array are completely up to the handler, other than the 'handler' definition. If omitted, handler will default to 'views_handler_sort'.</dd>
<dt>relationship</dt>
<dd>A handler definition for the "Field" section, which is a way to bring in new or alternative base tables in the view. The definition is an array; the contents of the array are completely up to the handler, other than the 'handler' definition. If omitted, handler will default to 'views_handler_relationship'. All relationships need the 'base' to be set. The basic relationship handler also requires 'base field' to be set. 'base' and 'base field' represent the "right" half of the join that will use this field as the left side.</dd>
<dt>argument</dt>
<dd>A handler definition for the "Field" section, which is method of accepting user input from the URL or some other source. The definition is an array; the contents of the array are completely up to the handler, other than the 'handler' definition. If omitted, handler will default to 'views_handler_argument'.</dd>
</dl>
For more information about what handlers need/use what data, visit <a href="http://views.doc.logrus.com">the Views API site</a> and check out the available handlers.
<h3>Support old tabledata</h3>
If you need to rename some tables/fields you can create some references in the views data to be able to continue to work.
Therefore you create the whole table structure of the current views data.
If you have to rename a single table you need to specify
<pre>
$data['oldtable']['moved to'] = 'newtable';
</pre>
If you have to rename/move a single a field to another table you specify
<pre>
$data['oldtable']['oldfield']['field']['moved to'] = array('newtable', 'newfield');
</pre>
or
<pre>
$data['oldtable']['oldfield']['moved to'] = array('newtable', 'newfield');
</pre>
+224
View File
@@ -0,0 +1,224 @@
In order to take advantage of the changes in Drupal 7, Views has gone through several API changes.
Here's what you should know.
<h3>Handler registry</h3>
Views now uses Drupal's dynamic-loading code registry.
You can safely remove your implementations of hook_views_handlers(), since they are no longer used.
Please remember to specify the handlers in your module's .info file. For example:
<pre>
name = Example module
description = "Gives an example of a module."
core = 7.x
files[] = example.module
files[] = example.install
; Views handlers
files[] = includes/views/handlers/example_handler_argument_string.inc
</pre>
<h3>Removed handlers</h3>
Note that views_handler_filter_float has been removed.
This functionality is now handled by views_handler_filter_numeric.
There's no need for having a special handler any more, thanks to the new DB layer in Drupal 7.
views_handler_sort_formula has been removed.
Everyone who used it can extend from views_handler_sort, too.
<h3>Ctools dependency</h3>
Views requires ctools now, so it can use the dependency system of ctools.
The only thing you have to do is to remove views_process_dependency.
<h3>Changed add_where api</h3>
If your field is a plain sql field:
<pre>
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . " '%s'", $this->value);
</pre>
has to be converted to
<pre>
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", $this->value, $this->operator);
</pre>
If your field is a complex where condition:
<pre>
$this->query->add_where($this->options['group'], "$upper($field) NOT LIKE $upper('%%%s')", $this->value);
</pre>
has to be converted to
<pre>
$placeholder = $this->placeholder();
$this->query->add_where_expression($this->options['group'], "$field LIKE $placeholder", array($placeholder => '%' . db_like($this->value)));
</pre>
placeholder() generates a automatic unique placeholder for you.
add_where with operator 'formula' can be converted to add_where_expression.
add_having with operator 'formula' can be converted to add_having_expression.
<h3>Changed place for display specific settings</h3>
In the new ui the new place for display settings is at the top of the second column.
Therefore use something like this code in your display plugin:
<pre>
$categories['block'] = array(
'title' => t('Block settings'),
'column' => 'second',
'build' => array(
'#weight' => -10,
),
);
</pre>
<h3>Changed filter settings and associated class variables</h3>
'optional' and 'single' are now 'required' and 'multiple', the logic is now opposite.
Also, the 'no_single' and 'no_optional' class variables (known as "object flags" in the API docs)
are now 'always_multiple' and 'always_required'.
<h3>Changed argument settings</h3>
See the init() function in views_handler_argument for an overview of everything
that changed.
1. The default actions 'summary asc', 'summary desc', 'summary asc by count', 'summary asc by count'
have been replaced by a single 'summary' action (which takes the sort order and type as options).
2. Wildcards are now called exceptions.
<pre>
$this->options['exception']['value'] = $options['wildcard'];
$this->options['exception']['title'] = $options['wildcard_substitution'];
</pre>
3. Summary plugin options are now stored in 'summary_options' instead of 'style_options'
<pre>
$this->options['summary_options'] = $options['style_options'];
</pre>
4. The selected summary plugin is no longer stored in 'style_plugin'.
<pre>
$this->options['summary']['format'] = $options['style_plugin'];
</pre>
5. The validator options have been moved.
<pre>
$options['validate']['type'] = $options['validate_type'];
$options['validate']['fail'] = $options['validate_fail'];
</pre>
6. The validator settings have been moved from $form['argument_validate'] to ['validate_options']
This means that dependent code in validate plugins needs to change.
Example change for views_plugin_argument_validate_user:
<pre>
$form['roles'] = array(
'#dependency' => array(
- 'edit-options-argument-validate-user-restrict-roles' => array(1),
+ 'edit-options-validate-options-user-restrict-roles' => array(1),
),
</pre>
<h3>The introduction of get_value() and sanitize_value()</h3>
The views_handler class got two new functions:
<pre>
/**
* Get the value that's supposed to be rendered.
*
* @param $values
* An object containing all retrieved values.
* @param $field
* Optional name of the field where the value is stored.
*/
function get_value($values, $field = NULL) {
$alias = isset($field) ? $this->aliases[$field] : $this->field_alias;
if (isset($values->{$alias})) {
return $values->{$alias};
}
}
/**
* Sanitize the value for output.
*
* @param $value
* The value being rendered.
* @param $type
* The type of sanitization needed. If not provided, check_plain() is used.
*/
function sanitize_value($value, $type = NULL) {
switch ($type) {
case 'xss':
$value = filter_xss($value);
break;
case 'url':
$value = check_url($value);
break;
default:
$value = check_plain($value);
break;
}
return $value;
}
</pre>
These functions are meant to be used in the render() functions of field handlers,
for fetching data (usually by alias) from the $values object, and for sanitizing values.
The abstraction of fetching data from rendering data is important because
different query backends have different ways of storing data in $values, and the field alias
is a SQL specific thing. So instead of overriding the whole render() function and copying
all of the logic there (as well as having to constantly keep up with upstream Views changes),
the backend can just override get_values(), which is significantly less code.
Of course, different ways of fetching and displaying data might require different
ways of sanitizing it, hence the usage of the sanitize_value() function.
Examples of converting render() field handler implementations:
<pre>
// This
$value = $values->{$this->field_alias};
// Becomes this
$value = $this->get_value($values);
// And this
$format = $values->{$this->aliases['format']};
// Becomes this
$format = $this->get_values($values, 'format');
// Instead of this:
return check_plain($value);
// We write:
return $this->sanitize_value($value);
// Since sanitize_value() supports different sanitization functions, this:
return filter_xss($value);
// Can become:
return $this->sanitize_value($value, 'xss');
</pre>
<h3>Changed views_get_page_view</h3>
In contrast to 6.x views_get_page_view now does stores the current view, not the current page display.
<h3>Removed views-view-row-node</h3>
Due to changes in comment.module there is no extra views-view-row-node template needed to display the comments. If you do some custom stuff there you should now be able to do everything in your node.tpl.php.
<h3>Entity type Key on Base tables</h3>
During the development of the drupal7 version of views the entity type associated with a table got added to $data['name']['table']['base']['entity type']. It should be moved to $data['name']['table']['entity type'].
<h3>Changed views_plugin_style::render_grouping()</h3>
The parameters as well as the structure of the methods return have changed.
The method now accepts a third optional parameter called "$group_rendered".
This parameter defines whether to use the rendered or the raw field value for grouping.
Intention for adding the parameter was that the grouping could have been acted
unexpected if the rendered field contained unique values e.g. by using drupal_html_id().
<dl>
<dt>New return structure</dt>
<dd>
{grouping value} is the value affected by the new parameter.
<pre>
array (
{grouping value} => array(
'group' => {rendered_value of the grouping field},
'rows' => array({group rows}),
),
);
</pre>
</dd>
<dt>Old return structure</dt>
<dd>
<strong>If the new parameter isn't explicitly set or its value is NULL the structure of the return will be the same as in D6!</strong>
<pre>
array (
{rendered_value of the grouping field} => array({group rows}),
);
</pre>
</dd>
</dl>
+24
View File
@@ -0,0 +1,24 @@
Views allows modules to describe their tables relationships to each other, as well as fields, filters, sort criteria and arguments via <strong>hook_views_data()</strong>. Whenever Views deems it necessary, this hook is called, the data aggregated together and cached. <strong>hook_views_data_alter()</strong> may also be used to modify existing data, changing other module's handlers or adding handlers to other module's tables.
Views also allows modules to create new display types, style types, row styles, argument default handlers and argument validators via <strong>hook_views_handlers()</strong> and <strong>hook_views_plugins()</strong>.
These hooks are kept in a file named MODULENAME.views.inc. This file is automatically included upon need, so there is no need to try and include this in hook_init or any other method of including .inc files. This file should store hook_views_data, hook_views_data_alter(), hook_views_plugins(), hook_views_handlers(), as well as any other hooks and subsidiary data that will only be used by your module when Views is active. All handlers and plugins provided by your module should be in separate .inc files, and must be referenced in your module's .info file with the files[] directive.
There are two similar files, MODULENAME.views_default.inc and MODULENAME.views_convert.inc which contain default views and views 1 to views 2 convert helpers, respectively.
<h3>hook_views_api()</h3>
<strong>In order for your files to be included, your module must first implement hook_views_api()</strong> in the main .module file. This module should return array of information. The following items may be returned:
<dl>
<dt><strong>api</strong></dt>
<dd>This must appear; it should be the oldest API version that your module can work with. If Views is currently running an older version of the API, it will ignore your module's views integration. This is a good thing, as it will prevent code crashes, at the expense of your module's functionality disappearing.
<br />
You may find the current Views API version by calling <strong>views_api_version()</strong> which is implemented at the top of views.module. This version numbering starts at 2.0. Every time changes are made to the Views handlers and plugins or other aspects of the Views API, the number will tick up (by either .001, .01 .1 or 1 depending upon how major the changes are). Note that <strong>views_api_version()</strong> was introduced in Views 2.0-rc2 and may not exist prior to that version. You may use drupal_function_exists() to test to see if this function is there.
<br />
Often these versions are basically compatible with each other and Views won't care if your module implements 2.000, 2.001, 2.002, etc. Your module can request that it won't work with any version older than a given version, however. Views will determine, itself, if a newer version will work.
</dd>
<dt><strong>path</strong></dt>
<dd>If your *.views*.inc files are not in the same directory as the .module file, then return the full path here. You should probably use something like drupal_get_path('module', 'yourmodulename') . '/includepath'.</dd>
<dt><strong>template path</strong></dt>
<dd>A path where the module has stored it's views template files. When you have specificed this key views automatically uses the template files for the views. You can use the same naming conventions like for normal views template files.</dd>
</dl>
+106
View File
@@ -0,0 +1,106 @@
Contextual Filters (known in previous versions of Views as "Arguments") are input. While they often come from the URL, they don't always. Don't be shocked when they don't. Each display type may have its own source for contextual filters. Block displays have no source of contextual filters at all; they cannot pull contextual filters from the URL, and often require use of PHP code as a default contextual filter. Various default values can be applied against contextual filters to use filters in a block view. See "Provide default value," under "When the filter value is NOT in the URL" below.
In general, contextual filters are used to filter the view, and in that sense they have a very close relationship to filters. However, this isn't necessarily true for every contextual filter. Contextual filters can be used for any purpose, really; the extent of what the contextual filter does is up to the developer of the contextual filter.
A typical use of an contextual filter might be to reduce a view to a single node, a single user, or nodes with a given taxonomy term.
<h3>When the filter value is NOT in the URL</h3>
<dl>
<dt>Display all results for the specified field</dt>
<dd>If this option is selected, the contextual filter is removed from the view as though it weren't there and all results will be displayed.</dd>
<dt>Provide default value</dt>
<dd>If no contextual filter is given, a default contextual filter can be selected. You may choose from a number of different default filter options. See "Default contextual filters" below.</dd>
<dt>Hide view</dt>
<dd>The view will remove itself entirely if the contextual filter is not present; for a block this means it simply won't appear. For page views the view will return a 404 and present a "Page not found" error. </dd>
<dt>Display a summary</dt>
<dd>The view will attempt to display a summary of contextual filters available, based upon the view, and provide links to the view with those contextual filters. Summaries have their own style handlers as well as options. The default summary style simply displays a list of contextual filters with a count of entries found per contextual filter. This special mode is a very powerful part of Views.</dd>
<dt>Display contents of "No results found"</dt>
<dd>If this option is selected, the value specified under "No results behavior" on the main view page will be displayed when there is no filter value in the URL.</dd>
</dl>
An exception value can be set under the "Exceptions" menu. If this exception value is received, any filter value specified under "When the filter value is NOT in the URL" will be ignored. This is a literal value: if you enter the word "everything" here, the exception will apply only if the value "everything" is received.
<h3>Default contextual filters</h3>
The Default contextual filter selection is available <strong>only if the action to take is "Provide default value"</strong> under "When the filter value is NOT in the URL." When that option is selected, a new fieldset will appear that will allow you to choose default contextual filters. Views provides the following default selectors by default (but other modules may add more):
<dl>
<dt>Content ID from URL</dt>
<dd>This will attempt to find a Node ID from the URL, and is primarily useful for the Node: ID contextual filter (though other contextual filters that use Node IDs, such as the CCK Node Reference contextual filter, will find it useful, too). For example, when visiting the path "node/1" or "node/1/edit" it will know that the "1" in that path is a node ID and use it.</dd>
<dt>Fixed value</dt>
<dd>You may directly enter what the contextual filter will be. This is not a variable, it will always be the same contextual filter. </dd>
<dt>PHP Code</dt>
<dd>Arbitrary php code inserted here will run whenever a contextual filter is not present in the URL.</dd>
<dt>Taxonomy term ID from URL</dt>
<dd>This default filter will attempt to return a taxonomy term from the current path. Selecting this option gives you the choice to also load default filters from terms.</dd>
<dt>User ID from URL</dt>
<dd>Like Node ID and Taxonomy term ID from URL, this will attempt to find a user ID from the path. It also includes an option to look for a node and use the node author as the user ID.</dd>
<dt>User ID from logged in user</dt>
<dd>If a user is currently logged in and accessing this view, their user ID will be returned as the contextual filter.</dd>
<dt>Current date</dt>
<dd>This option simply returns today's date.</dd>
<dt>Current node's creation time</dt>
<dd>Select this to return a node's creation time as a contextual filter.</dd>
<dt>Current node's update time</dt>
<dd>Not surprisingly, this filter returns the current node's update time</dd>
</dl>
Please bear in mind that the selection of a default contextual filter happens only if a contextual filter is not provided. If you are using a display that has no contextual filter source, such as a block, the contextual filter value selected here will always be used. However, if using a display that reads contextual filters from the URL, the options selected here will only be applied if the URL did not contain an contextual filter.
The "Skip default argument for view URL" option gives you the choice of ignoring the default argument totally. This is useful for certain applications, like the creation of feeds.
<h3>When the filter value IS in the URL or a default is provided</h3>
<dl>
<dt>Override title</dt>
<dd>Selecting this option will allow you to replace the default view title with a piece of arbitrary text. Argument substitutions in the form of %1, %2, etc. may be used here.</dd>
<dt>Override breadcrumb</dt>
<dd>This option will allow you to overwrite the view name in breadcrumbs. The same substitution values as in "Override title" may be used.</dd>
<dt>Specify validation criteria</dt>
<dd>Contextual filters can have validators, which are pluggable systems used to ensure that contextual filters fit within certain parameters. When a validator is chosen, it may provide some specific settings, including the action to take if an contextual filter is presented, but it fails to validate. These actions are generally the same as the default actions above, excluding the ability to provide another default filter. See "Contextual filter validation" below for a detailed description.</dd>
</dl>
<h3>Contextual filter validation</h3>
Note: If a view provides a menu option, such as a tab, the tab will not appear if the contextual filter does not validate.
This sytem can have other validators plugged in; by default, Views provides:
<dl>
<dt>Basic validation</dt>
<dd>This validator ensures that the contextual filter is present. A PHP NULL value (from eg. PHP default contextual filter code) will invalidate.</dd>
<dt>Content</dt>
<dd>This validator ensures that the contextual filter is a valid Node ID. You may select which node types the validator will accept.</dd>
<dt>Numeric</dt>
<dd>This validator ensures that the contextual filter is a valid number.</dd>
<dt>PHP Code</dt>
<dd>You may enter arbitrary PHP code here, similar to the "PHP code" option under "Provide default value" in "When the filter value is NOT in the URL" above, to determine if the contextual filter is valid or not.</dd>
<dt>Taxonomy term</dt>
<dd>Ensures that the contextual filter is a valid taxonomy term. This includes options to limit to specific vocabularies and can transform the contextual filter to the right type depending upon the actual contextual filter. Set the contextual filter value type option to the actual type of data that the contextual filter being used is expecting.</dd>
<dt>User</dt>
<dd>Ensures that the contextual filter refers to a valid user. If you select this validator, additional options are available to accept numeric user IDs, usernames or both, as well as to consider a user's role when filtering the view.</dd>
</dl>
If you select the "Specify validation criteria" option to select a specific validator, you will have the option to select an action to take if the filter value does not validate.
<dl>
<dt>Display all results for the specified field</dt>
<dd>Selecting this option will return all values if the filter value does not validate, similar to "Display all results for the specified field" under "When the filter value is not in the URL" above.</dd>
<dt>Hide View</dt>
<dd>Similar to "Hide view" under "When the filter value is NOT in the URL" above, if this option is selected and the selector does not validate, the view will hide itself. If the view is a block, nothing will appear. If it is a page, it will throw a 404 and present a "Page not found" error.</dd>
<dt>Return Summary</dt>
<dd>If you select this option and the filter does not validate, a summary of all values that are valid will be returned, as in the option of the same name under "When the filter value is NOT in the URL above."</dd>
<dt>Display contents of "No results found"</dt>
<dd>If this option is selected and the selector does not validate, the value specified under "No results behavior" on the main view page will be displayed.</dd>
</dl>
<h3>Adminstrative title</h3>
Located under the "More" group at the bottom of the "Contextual filters" menu, this option allows you to specify a custom name for the contextual filter. This may be particularly useful if you use the same contextual filter twice and you'd like to distinguish between the two filters.
<h3>Grouping of contextual filters</h3>
Even though contextual filters do not appear in the "<em>and/or</em>" user interface for sorting and grouping regular filters, contextual filters are always added to the <em>first</em> group of filters. Thus the order of the groups can cause the contextual filter to have entirely different effects on the results of a view that has contextual filters. Even though differences might not be apparent through the user interface.
Multiple contextual filters are therefore always in the same "and/or" group of filters, and can not be placed in different groups. There is <a href="http://drupal.org/node/357082">an effort to add this feature</a>.
+20
View File
@@ -0,0 +1,20 @@
You choose the <strong>name</strong> of the current display.
This <strong>title</strong> will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.
When you use have many items to display, you have the choice to display them in different variants.
<dl>
<dt>Display a specified number of items</dt>
<dd>Specify the number of items to display per page and an offset. The offset is the number of items to skip. For example, if this field is 3, the first 3 items will be skipped and not displayed.
<dt>Display all items</dt>
<dd>All items will display, but you can choose an offset. The number of items to skip. For example, if this field is 3, the first 3 items will be skipped and not displayed.</dd>
<dt>Paged output, full pager</dt>
<dd>A Pager can be used to display items, with the possibility to select the next page and also the first and last page. When you have many items the query is very <i>expensive</i>. To avoid this, you can choose the mini pager.
You can also choose the number of items per page. If you enter 0, then there is no limit. Pagers also will respect an offset, if present. If multiple pagers are present on one page you may need to set this number to a higher value so as not to conflict within the ?page= array. Large values will add commas to your URLs, so avoid this if possible. Unless you're experiencing problems with pagers related to this view, you should leave this at 0. Enter the total number of pages to limit the number of values. When you leave the field empty all pages will show.
The <strong>Exposed options</strong> allow users to define their values in a exposed form when view is displayed.
You can choose "Expose items per page". With this option the user can determine how many items per page show in a view. Options for which label should display and what numberic options are also available.
Furthermore, you can choose "Expose Offset". When checked, users can determine how many items should be skipped at the beginning. You can define a label.
</dd>
<dt>Paged output, mini pager</dt>
<dd>The pager optiona are the same as for the "Paged output, full pager" but you have no possibility to jump to the last or first items. </dd>
</dl>
Normally, all views are created with <strong>unrestricted access</strong>. This means any site visitor can see the views. Please consider this when you make a view with a menu link and private data as output. You have by default two options: "Permission" and "Role". If you choose permission, you get a list of all permissions. Only users with the selected permission flag will be able to access this display. If you choose role, you get all roles as checkboxes. Only the checked roles will be able to access this display. Note that users with "access all views" can see any view, regardless of role.
+5
View File
@@ -0,0 +1,5 @@
<p>Here are some links to demonstration videos. It will get you started working with Views.</p>
<p><a href="http://drupal.org/node/248766">Views 2 (Drupal 6) Demonstration Video - Drupal Handbook</a></p>
<p><a href="http://nodeone.se/blogg/taming-the-beast-learn-views-with-nodeone">NodeOne's initial screencasts on the Views 3 UI.</a>
+1
View File
@@ -0,0 +1 @@
Attachment displays are 'attached' to another display in the same view. When the display is visited, the attached display will also be rendered and may be placed before, after or both before and after the original display. Attachment displays are often useful for displaying an argument summary view along with a page display that accepts arguments. This can be used to provide a kind of glossary.
+11
View File
@@ -0,0 +1,11 @@
Block displays will show up on your blocks administration page. Once a block display is created and saved, it can be enabled and positioned in your theme by visiting <strong>administer &gt;&gt; site building &gt;&gt; blocks</strong> and selecting it from the list.
Blocks <strong>do not</strong> accept arguments from any source; the only way to get arguments to a block is to provide defaults to it, possibly via the PHP Code default setting.
<ul>
<li>Edit the argument in question; you may want to override this argument if you have multiple displays using it.</li>
<li>Change the "Action to take if argument is not present" to "Provide default argument". This will bring up a new box called "Provide default argument options".</li>
<li>The most common default argument type used for blocks is Node from URL, where it attempts to determine if the URL refers to a node, for example if visiting 'node/1' or 'node/1/edit'. User ID from URL is also very common.</li>
<li>If you change the default argument type to 'PHP Code' (note: You must have permission to use PHP code on your site) you can enter PHP to define the argument needed. Simply return the argument.</li>
<li>If you are using a <strong>more link</strong> with a block, you must have a page display for that more link to attach to. The more link will not print if there is no place (no display) for it to link to.</li>
</ul>

Some files were not shown because too many files have changed in this diff Show More