flag.install in Flag 6.2
Same filename and directory in other branches
Flag module install/schema/update hooks.
File
flag.installView source
<?php
/**
* @file
* Flag module install/schema/update hooks.
*/
/**
* Implementation of hook_install().
*/
function flag_install() {
// Load the flag API in case we want to use it when installing.
include_once drupal_get_path('module', 'flag') . '/flag.module';
// If Views Bookmark is available, skip the install and do an upgrade instead.
if (strpos($GLOBALS['db_type'], 'mysql') === 0) {
include_once drupal_get_path('module', 'flag') . '/includes/flag.views_bookmark.inc';
if (flag_views_bookmark_update_needed()) {
flag_views_bookmark_update();
return;
}
}
drupal_install_schema('flag');
}
/**
* Implements hook_uninstall().
*/
function flag_uninstall() {
drupal_uninstall_schema('flag');
$result = db_query("SELECT name FROM {variable} WHERE name LIKE 'flag_%'");
while ($row = db_fetch_object($result)) {
variable_del($row->name);
}
drupal_set_message(t('Flag has been uninstalled.'));
}
/**
* Implements hook_enable().
*
* We create the demonstration flag on enable, so hook implementations in flag
* module will fire correctly, as the APIs are not available on install.
*/
function flag_enable() {
// Load the flag API in case we want to use it when enabling.
include_once drupal_get_path('module', 'flag') . '/flag.module';
if (!flag_get_flags()) {
// Install a demonstration flag only if no flag exists. This is to prevent
// a case where a disables and enables the module, and the demonstration
// flag is overwritten or re-created.
$flag = flag_flag::factory_by_content_type('node');
$configuration = array(
'name' => 'bookmarks',
'global' => 0,
'show_on_page' => 1,
'show_on_teaser' => 1,
'show_on_form' => 1,
// The following UI labels aren't wrapped in t() because they are written
// to the DB in English. They are passed to t() later, thus allowing for
// multilingual sites.
'title' => 'Bookmarks',
'flag_short' => 'Bookmark this',
'flag_long' => 'Add this post to your bookmarks',
'flag_message' => 'This post has been added to your bookmarks',
'unflag_short' => 'Unbookmark this',
'unflag_long' => 'Remove this post from your bookmarks',
'unflag_message' => 'This post has been removed from your bookmarks',
'types' => _flag_install_get_suggested_node_types(),
);
$flag
->form_input($configuration);
$flag
->save();
}
}
/**
* Returns some node types to which the demonstration 'bookmarks' flag will apply.
*/
function _flag_install_get_suggested_node_types() {
$preferred = array(
'story',
'forum',
'blog',
);
$existing = array_intersect($preferred, array_keys(node_get_types()));
if (!$existing) {
// As a last resort, take the first preference.
return array(
$preferred[0],
);
}
return $existing;
}
/**
* Implements hook_requirements().
*
* Prevent installing this module if the "Flag content" module is installed as well.
*/
function flag_requirements($phase) {
$requirements = array();
$t = get_t();
if ($phase == 'install') {
if (!defined('MAINTENANCE_MODE') && _flag_flag_content_installed()) {
$requirements['flag_content_clash'] = array(
'title' => $t('Flag'),
'severity' => REQUIREMENT_ERROR,
'description' => _flag_flag_content_message(),
);
}
}
if ($phase == 'runtime') {
if (module_exists('translation') && !module_exists('translation_helpers')) {
$requirements['flag_translation'] = array(
'title' => $t('Flag'),
'severity' => REQUIREMENT_ERROR,
'description' => $t('To have the flag module work with translations, you need to install and enable the <a href="http://drupal.org/project/translation_helpers">Translation helpers</a> module.'),
'value' => $t('Translation helpers module not found.'),
);
}
if (module_exists('session_api')) {
if (file_exists('./robots.txt')) {
$flag_path = url('flag') . '/';
$robots_string = 'Disallow: ' . $flag_path;
$contents = file_get_contents('./robots.txt');
if (strpos($contents, $robots_string) === FALSE) {
$requirements['flag_robots'] = array(
'title' => $t('Flag robots.txt problem'),
'severity' => REQUIREMENT_WARNING,
'description' => $t('Flag module may currently be used with anonymous users, however the robots.txt file does not exclude the "@flag-path" path, which may cause search engines to randomly flag and unflag content when they index the site. It is highly recommended to add "@robots-string" to your robots.txt file (located in the root of your Drupal installation).', array(
'@flag-path' => $flag_path,
'@robots-string' => $robots_string,
)),
'value' => $t('Search engines flagging content'),
);
}
}
}
}
return $requirements;
}
/**
* Returns TRUE if the "Flag content" module, which we aren't compatible with,
* is installed.
*/
function _flag_flag_content_installed() {
$version = @drupal_get_installed_schema_version('flag_content', TRUE);
return isset($version) && $version != SCHEMA_UNINSTALLED;
}
function _flag_flag_content_message() {
$t = get_t();
return $t("You are trying to install the <em>Flag</em> module. However, you have the <em>\"Flag content\"</em> module installed, and these two modules aren't compatible (because they happen to use a database table by the same name). To install the <em>Flag</em> module, you'll first have to disable and then <a href='@uninstall-url'>uninstall</a> the <em>\"Flag content\"</em> module.", array(
'@uninstall-url' => url('admin/build/modules/uninstall'),
));
}
/**
* Implements hook_schema().
*/
function flag_schema() {
$schema = array();
$schema['flags'] = array(
'description' => 'All available flags in the system.',
'fields' => array(
'fid' => array(
'description' => 'The unique ID for this particular flag.',
'type' => 'serial',
'size' => 'small',
'unsigned' => TRUE,
'not null' => TRUE,
),
'content_type' => array(
'description' => 'The flag type, such as one of "node", "comment", or "user".',
'type' => 'varchar',
'length' => '32',
'not null' => TRUE,
'default' => '',
),
'name' => array(
'description' => 'The machine-name for this flag.',
'type' => 'varchar',
'length' => '32',
'not null' => FALSE,
'default' => '',
),
'title' => array(
'description' => 'The human-readable title for this flag.',
'type' => 'varchar',
'length' => '255',
'not null' => FALSE,
'default' => '',
),
'global' => array(
'description' => 'Whether this flag state should act as a single toggle to all users across the site.',
'type' => 'int',
'size' => 'tiny',
'not null' => FALSE,
'default' => 0,
),
'options' => array(
'description' => 'The options and configuration of this flag.',
'type' => 'text',
'not null' => FALSE,
),
),
'primary key' => array(
'fid',
),
'unique keys' => array(
'name' => array(
'name',
),
),
);
$schema['flag_content'] = array(
'description' => 'Content that has been flagged.',
'fields' => array(
'fcid' => array(
'description' => 'The unique ID for this particular tag.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'fid' => array(
'description' => 'The unqiue flag ID this content has been flagged with, from {flags}.',
'type' => 'int',
'size' => 'small',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'content_type' => array(
'description' => 'The flag type, one of "node", "comment", "user".',
'type' => 'varchar',
'length' => '32',
'not null' => TRUE,
'default' => '',
),
'content_id' => array(
'description' => 'The unique ID of the content, such as either the {cid}, {uid}, or {nid}.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'uid' => array(
'description' => 'The user ID by whom this content was flagged.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'sid' => array(
'description' => "The user's session id as stored in the session table.",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'timestamp' => array(
'description' => 'The UNIX time stamp representing when the flag was set.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'disp-size' => 11,
),
),
'primary key' => array(
'fcid',
),
'unique keys' => array(
'fid_content_id_uid_sid' => array(
'fid',
'content_id',
'uid',
'sid',
),
),
'indexes' => array(
'content_type_uid_sid' => array(
'content_type',
'uid',
'sid',
),
'content_type_content_id_uid_sid' => array(
'content_type',
'content_id',
'uid',
'sid',
),
'content_id_fid' => array(
'content_id',
'fid',
),
),
);
$schema['flag_types'] = array(
'description' => 'The types (usually as defined in {node_type}) that are affected by a flag.',
'fields' => array(
'fid' => array(
'description' => 'The unqiue flag ID as defined for the flag in {flags}.',
'type' => 'int',
'size' => 'small',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'type' => array(
'description' => 'The types (usually from {node_type}) that can be flagged by this fid.',
'type' => 'varchar',
'length' => '32',
'not null' => TRUE,
'default' => '',
),
),
'indexes' => array(
'fid' => array(
'fid',
),
),
);
$schema['flag_counts'] = array(
'description' => 'The number of times an item has been flagged.',
'fields' => array(
'fid' => array(
'type' => 'int',
'size' => 'small',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'content_type' => array(
'description' => 'The flag type, usually one of "node", "comment", "user".',
'type' => 'varchar',
'length' => '32',
'not null' => TRUE,
'default' => '',
),
'content_id' => array(
'description' => 'The unique ID of the content, usually either the {cid}, {uid}, or {nid}.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'disp-width' => '10',
),
'count' => array(
'description' => 'The number of times this content has been flagged for this flag.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'disp-width' => '10',
),
'last_updated' => array(
'description' => 'The UNIX time stamp representing when the flag was last updated.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'disp-size' => 11,
),
),
'primary key' => array(
'fid',
'content_id',
),
'indexes' => array(
'fid_content_type' => array(
'fid',
'content_type',
),
'content_type_content_id' => array(
'content_type',
'content_id',
),
'fid_count' => array(
'fid',
'count',
),
'fid_last_updated' => array(
'fid',
'last_updated',
),
),
);
return $schema;
}
/**
* Add auto-increment to the flags.fid column for users upgrading from Drupal 5.
*
* Delete obsolete actions provided by beta3.
*
* Note: To ease maintenance, this code is compatible with both D5 and D6.
*
* Check out #305391 for a discussion about this code.
*/
function flag_update_6000() {
$ret = array();
// Convert the flags.fid column to auto-increment.
db_drop_primary_key($ret, 'flags');
db_change_field($ret, 'flags', 'fid', 'fid', array(
'type' => 'serial',
'size' => 'small',
'unsigned' => TRUE,
'not null' => TRUE,
), array(
'primary key' => array(
'fid',
),
));
// This conditional ensures we have Actions 2.x, not Actions 1.x.
//
// At the beginning we had only db_table_exists() checks here. However, it
// turned out that db_table_exists() fails when the $db_prefix string contains
// a dot. So module_exists() and function_exists() are added, as backup.
if (module_exists('trigger') || function_exists('_actions_get_hook_aids') || db_table_exists('actions_assignments') || db_table_exists('trigger_assignments')) {
// Step 1: Find all actions we need to delete.
$aids = array();
// We can't just search for "actions of type flag" because this isn't future-compatible.
$res = db_query("SELECT aid FROM {actions} WHERE callback IN ('flag_action_email', 'flag_action_delete', 'flag_action_unpublish', 'flag_action_moderate')");
while ($row = db_fetch_object($res)) {
$aids[] = $row->aid;
}
if (!$aids) {
$ret[] = array(
'success' => TRUE,
'query' => t('No old actions to remove.'),
);
}
else {
$ret[] = array(
'success' => TRUE,
'query' => t('Deleting the following actions: @aids', array(
'@aids' => implode(', ', $aids),
)),
);
}
// Step 2: Delete them through API.
// We can't do `if (module_exists('actions'))`: this code should work for D6 as
// well, and it doesn't have an Actions module.
if (function_exists('actions_delete')) {
foreach ($aids as $aid) {
actions_delete($aid);
$ret[] = array(
'success' => TRUE,
'query' => t('actions_delete("@aid") called.', array(
'@aid' => $aid,
)),
);
}
}
// Step 3: Delete them through SQL, in case Actions/Trigger aren't enabled.
foreach ($aids as $aid) {
foreach (array(
'actions',
'actions_assignments',
'trigger_assignments',
) as $table) {
if (db_table_exists($table)) {
$ret[] = _flag_update_sql("DELETE FROM {{$table}} WHERE aid = '%s'", $aid);
}
}
}
// Note: No need to delete from the {actions_aid} table; Actions doesn't do that.
// Step 4: Remove a bogus record possibly created because of a bug (see
// http://drupal.org/node/271460).
foreach (array(
'actions_assignments',
'trigger_assignments',
) as $table) {
if (db_table_exists($table)) {
$ret[] = _flag_update_sql("DELETE FROM {{$table}} WHERE hook = 'flag' AND aid = ''");
}
}
}
else {
$ret[] = array(
'success' => TRUE,
'query' => t('Cleanup of Actions tables is unneeded.'),
);
}
return $ret;
}
/**
* Move flag messages and link titles into the options array.
*/
function flag_update_6001() {
$ret = array();
if (db_column_exists('flags', 'flag_short')) {
$result = db_query("SELECT * FROM {flags}");
while ($flag = db_fetch_object($result)) {
$options = unserialize($flag->options);
$options['flag_short'] = $flag->flag_short;
$options['flag_long'] = $flag->flag_long;
$options['flag_message'] = $flag->flag_message;
$options['unflag_short'] = $flag->unflag_short;
$options['unflag_long'] = $flag->unflag_long;
$options['unflag_message'] = $flag->unflag_message;
db_query("UPDATE {flags} SET options = '%s' WHERE fid = %d", serialize($options), $flag->fid);
}
db_drop_field($ret, 'flags', 'flag_short');
db_drop_field($ret, 'flags', 'flag_long');
db_drop_field($ret, 'flags', 'flag_message');
db_drop_field($ret, 'flags', 'unflag_short');
db_drop_field($ret, 'flags', 'unflag_long');
db_drop_field($ret, 'flags', 'unflag_message');
}
return $ret;
}
/**
* Add a 'serial' primary key, fcid, to the flag_content table.
*/
function flag_update_6002() {
$ret = array();
if (db_column_exists('flag_content', 'fcid')) {
db_drop_primary_key($ret, 'flag_content');
db_add_field($ret, 'flag_content', 'fcid', array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
), array(
'primary key' => array(
'fcid',
),
));
db_add_unique_key($ret, 'flag_content', 'fid_content_type_content_id_uid', array(
'fid',
'content_type',
'content_id',
'uid',
));
}
return $ret;
}
/**
* Remove the previous default views that are no longer bundled with Flag.
*
* These views are saved to the database so that they are preserved.
*/
function flag_update_6003() {
$ret = array();
// Bail out if Views doesn't exist.
if (!function_exists('views_get_view')) {
return $ret;
}
drupal_load('module', 'flag');
$flags = flag_get_flags();
foreach ($flags as $name => $flag) {
if ($view = views_get_view('flags_' . $name)) {
if (!$view->disabled && $view->type == t('Default')) {
$view
->save();
$ret[] = array(
'success' => TRUE,
'query' => t('The view %name as been saved to the database. Flag no longer provides this view by default.', array(
'%name' => $view->name,
)),
);
}
}
}
return $ret;
}
/**
* Remove count = 0 rows from the count tables.
*/
function flag_update_6004() {
// Same update as flag_update_6203()
return array();
}
/**
* Convert role access to have separate "flag" and "unflag" permissions.
*/
function flag_update_6200() {
$ret = array();
if (db_column_exists('flags', 'roles')) {
$result = db_query('SELECT * FROM {flags}');
while ($flag = db_fetch_object($result)) {
$roles = array_filter(explode(',', $flag->roles));
$options = unserialize($flag->options);
$options['roles'] = array(
'flag' => $roles,
'unflag' => $roles,
);
db_query("UPDATE {flags} SET options = '%s' WHERE fid = %d", serialize($options), $flag->fid);
}
db_drop_field($ret, 'flags', 'roles');
}
return $ret;
}
/**
* Refine the indexes.
*
* The content type inclusion actually slowed down on unique key. And a count
* index would be helpful for sorting by counts.
*/
function flag_update_6201() {
$ret = array();
// Remove "content type" from one key, see http://drupal.org/node/612602.
db_drop_unique_key($ret, 'flag_content', 'fid_content_type_content_id_uid');
db_add_unique_key($ret, 'flag_content', 'fid_content_id_uid', array(
'fid',
'content_id',
'uid',
));
// Add a count index, see http://drupal.org/node/489610.
db_add_index($ret, 'flag_counts', 'count', array(
'count',
));
return $ret;
}
/**
* Add the sid column and unique index on the flag_content table.
*/
function flag_update_6202() {
$ret = array();
// Drop the keys affected by the addition of the SID column.
db_drop_unique_key($ret, 'flag_content', 'fid_content_id_uid');
db_drop_index($ret, 'flag_content', 'content_type_uid');
// Add the column.
db_add_field($ret, 'flag_content', 'sid', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
));
// Re-add the removed keys.
db_add_unique_key($ret, 'flag_content', 'fid_content_id_uid_sid', array(
'fid',
'content_id',
'uid',
'sid',
));
db_add_index($ret, 'flag_content', 'content_type_uid_sid', array(
'content_type',
'uid',
'sid',
));
return $ret;
}
/**
* Remove count = 0 rows from the count tables.
*/
function flag_update_6203() {
$ret = array();
$ret[] = update_sql("DELETE FROM {flag_counts} WHERE count = 0");
return $ret;
}
/**
* Remove "content type" from the flag_counts primary key.
*/
function flag_update_6204() {
$ret = array();
db_drop_primary_key($ret, 'flag_counts');
db_add_primary_key($ret, 'flag_counts', array(
'fid',
'content_id',
));
return $ret;
}
/**
* Provide a better index on the flag_content table to include 'uid' and 'sid'.
*/
function flag_update_6205() {
// This update has been removed and corrected in flag_update_6206.
// See http://drupal.org/node/1105490.
return array();
}
/**
* Correction to flag_update_6205(). Convert unique key to an index.
*/
function flag_update_6206() {
$ret = array();
// These two indexes may or may not exist depending on if flag_update_6205
// was run or not. Surpress errors if the indexes don't exist.
@db_drop_index($ret, 'flag_content', 'content_type_content_id');
@db_drop_unique_key($ret, 'flag_content', 'content_type_content_id_uid_sid');
// Empty out the errors.
$ret = array();
db_add_index($ret, 'flag_content', 'content_type_content_id_uid_sid', array(
'content_type',
'content_id',
'uid',
'sid',
));
return $ret;
}
/**
* Adds column last_updated to flag_counts table.
*/
function flag_update_6207() {
$ret = array();
db_add_field($ret, 'flag_counts', 'last_updated', array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'disp-size' => 11,
), array(
'indexes' => array(
'last_updated' => array(
'last_updated',
),
),
));
return $ret;
}
/**
* Convert flag_count indexes to include FID for more efficient indexing.
*/
function flag_update_6208() {
$ret = array();
db_drop_index($ret, 'flag_counts', 'count');
db_drop_index($ret, 'flag_counts', 'last_updated');
db_add_index($ret, 'flag_counts', 'fid_count', array(
'fid',
'count',
));
db_add_index($ret, 'flag_counts', 'fid_last_updated', array(
'fid',
'last_updated',
));
return $ret;
}
/**
* Clean up orphaned flags due to node and comment deletion.
*/
function flag_update_6209() {
$ret = array();
// Clean-up for nodes.
db_query("DELETE FROM {flag_content} WHERE content_type = 'node' AND NOT EXISTS (SELECT 1 FROM {node} n WHERE content_id = n.nid)");
db_query("DELETE FROM {flag_counts} WHERE content_type = 'node' AND NOT EXISTS (SELECT 1 FROM {node} n WHERE content_id = n.nid)");
// Clean-up for comments. Do not use module_exists() because comment module
// could be disabled.
if (db_table_exists('comment')) {
db_query("DELETE FROM {flag_content} WHERE content_type = 'comment' AND NOT EXISTS (SELECT 1 FROM {comment} c WHERE content_id = c.cid)");
db_query("DELETE FROM {flag_counts} WHERE content_type = 'comment' AND NOT EXISTS (SELECT 1 FROM {comment} c WHERE content_id = c.cid)");
}
return $ret;
}
/**
* Add an index to help with view's flag_handler_relationship when not required.
*/
function flag_update_6210() {
$ret = array();
db_add_index($ret, 'flag_content', 'content_id_fid', array(
'content_id',
'fid',
));
return $ret;
}
// This is a replacement for update_sql(). The latter doesn't support placeholders.
function _flag_update_sql($sql) {
$args = func_get_args();
array_shift($args);
$result = db_query($sql, $args);
// Users are going to see '%s' and '%d' in the report and they're going to
// think there's a bug. So lets replace the placeholders with something less
// suspicious.
$sql = strtr($sql, array(
'%s' => '***',
'%d' => '***',
));
return array(
'success' => $result !== FALSE,
'query' => check_plain($sql),
);
}
Functions
Name | Description |
---|---|
flag_enable | Implements hook_enable(). |
flag_install | Implementation of hook_install(). |
flag_requirements | Implements hook_requirements(). |
flag_schema | Implements hook_schema(). |
flag_uninstall | Implements hook_uninstall(). |
flag_update_6000 | Add auto-increment to the flags.fid column for users upgrading from Drupal 5. |
flag_update_6001 | Move flag messages and link titles into the options array. |
flag_update_6002 | Add a 'serial' primary key, fcid, to the flag_content table. |
flag_update_6003 | Remove the previous default views that are no longer bundled with Flag. |
flag_update_6004 | Remove count = 0 rows from the count tables. |
flag_update_6200 | Convert role access to have separate "flag" and "unflag" permissions. |
flag_update_6201 | Refine the indexes. |
flag_update_6202 | Add the sid column and unique index on the flag_content table. |
flag_update_6203 | Remove count = 0 rows from the count tables. |
flag_update_6204 | Remove "content type" from the flag_counts primary key. |
flag_update_6205 | Provide a better index on the flag_content table to include 'uid' and 'sid'. |
flag_update_6206 | Correction to flag_update_6205(). Convert unique key to an index. |
flag_update_6207 | Adds column last_updated to flag_counts table. |
flag_update_6208 | Convert flag_count indexes to include FID for more efficient indexing. |
flag_update_6209 | Clean up orphaned flags due to node and comment deletion. |
flag_update_6210 | Add an index to help with view's flag_handler_relationship when not required. |
_flag_flag_content_installed | Returns TRUE if the "Flag content" module, which we aren't compatible with, is installed. |
_flag_flag_content_message | |
_flag_install_get_suggested_node_types | Returns some node types to which the demonstration 'bookmarks' flag will apply. |
_flag_update_sql |