Issue #1658068 by tim.plunkett, damiankloip: Convert Views to use core TempStore instead of ctools_object_cache_*().
parent
dad1844e97
commit
53c7d23519
|
@ -220,9 +220,7 @@ function views_revert_view($view) {
|
|||
// Revert the view.
|
||||
$view->delete();
|
||||
// Clear its cache.
|
||||
// @todo Convert this: http://drupal.org/node/1658068.
|
||||
ctools_include('object-cache');
|
||||
ctools_object_cache_clear('view', $view->name);
|
||||
views_temp_store()->delete($view->name);
|
||||
// Give feedback.
|
||||
$message = dt("Reverted the view '@viewname'", array('@viewname' => $view->name));
|
||||
drush_log($message, 'success');
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\views\TempStore\UserTempStore;
|
||||
use Drupal\views\View;
|
||||
use Drupal\views\Analyzer;
|
||||
use Drupal\views\Plugin\Type\ViewsPluginManager;
|
||||
|
@ -853,7 +854,7 @@ function views_ui_break_lock_confirm($form, &$form_state, $view) {
|
|||
$cancel = $_REQUEST['cancel'];
|
||||
}
|
||||
|
||||
$account = user_load($view->locked->uid);
|
||||
$account = user_load($view->locked->ownerId);
|
||||
return confirm_form($form,
|
||||
t('Are you sure you want to break the lock on view %name?',
|
||||
array('%name' => $view->name)),
|
||||
|
@ -867,7 +868,7 @@ function views_ui_break_lock_confirm($form, &$form_state, $view) {
|
|||
* Submit handler to break_lock a view.
|
||||
*/
|
||||
function views_ui_break_lock_confirm_submit(&$form, &$form_state) {
|
||||
ctools_object_cache_clear_all('view', $form_state['view']->name);
|
||||
UserTempStore::clearAll('view', $form_state['view']->name);
|
||||
$form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit';
|
||||
drupal_set_message(t('The lock has been broken and you may now edit this view.'));
|
||||
}
|
||||
|
@ -1027,7 +1028,7 @@ function views_ui_edit_form($form, &$form_state, $view, $display_id = NULL) {
|
|||
$form['locked'] = array(
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('view-locked', 'messages', 'warning')),
|
||||
'#markup' => t('This view is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => theme('username', array('account' => user_load($view->locked->uid))), '!age' => format_interval(REQUEST_TIME - $view->locked->updated), '!break' => url('admin/structure/views/view/' . $view->name . '/break-lock'))),
|
||||
'#markup' => t('This view is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => theme('username', array('account' => user_load($view->locked->ownerId))), '!age' => format_interval(REQUEST_TIME - $view->locked->updated), '!break' => url('admin/structure/views/view/' . $view->name . '/break-lock'))),
|
||||
);
|
||||
}
|
||||
if (isset($view->vid) && $view->vid == 'new') {
|
||||
|
@ -2154,7 +2155,7 @@ function views_ui_edit_view_form_submit($form, &$form_state) {
|
|||
drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->get_human_name())));
|
||||
|
||||
// Remove this view from cache so we can edit it properly.
|
||||
ctools_object_cache_clear('view', $form_state['view']->name);
|
||||
views_temp_store()->delete($form_state['view']->name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2162,7 +2163,7 @@ function views_ui_edit_view_form_submit($form, &$form_state) {
|
|||
*/
|
||||
function views_ui_edit_view_form_cancel($form, &$form_state) {
|
||||
// Remove this view from cache so edits will be lost.
|
||||
ctools_object_cache_clear('view', $form_state['view']->name);
|
||||
views_temp_store()->delete($form_state['view']->name);
|
||||
if (empty($form['view']->vid)) {
|
||||
// I seem to have to drupal_goto here because I can't get fapi to
|
||||
// honor the redirect target. Not sure what I screwed up here.
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\views\TempStore\TempStore.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\TempStore;
|
||||
|
||||
/**
|
||||
* Handles reading and writing to a non-volatile temporary storage area.
|
||||
*
|
||||
* A TempStore is not a true cache, because it is non-volatile. While a cache
|
||||
* can be reconstructed if the data disappears (i.e, a backend goes away
|
||||
* or a cache is cleared), TempStore cannot tolerate the data disappearing.
|
||||
*
|
||||
* It is primarily used to handle in-progress edits on complicated objects
|
||||
* in order to provide state to an ordinarily stateless HTTP transaction.
|
||||
*/
|
||||
class TempStore {
|
||||
|
||||
/**
|
||||
* The subsystem or module that owns this TempStore.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $subsystem;
|
||||
|
||||
/**
|
||||
* The unique identifier for the owner of the temporary data.
|
||||
*
|
||||
* In order to ensure that users do not accidentally acquire each other's
|
||||
* changes, session IDs can be used to differentiate them. However, there
|
||||
* are cases where session IDs are not ideal. In these cases, an
|
||||
* alternative ID can be set (such as a user ID or the number 0) which
|
||||
* would indicate no special session handling is required.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $ownerID;
|
||||
|
||||
/**
|
||||
* Constructs a temporary storage object.
|
||||
*
|
||||
* @param string $subsystem
|
||||
* The module or subsystem. Possible values might include 'entity',
|
||||
* 'form', 'views', etc.
|
||||
* @param string $owner_id
|
||||
* A unique identifier for the owner of the temporary storage data.
|
||||
*/
|
||||
function __construct($subsystem, $owner_id) {
|
||||
$this->subsystem = $subsystem;
|
||||
$this->ownerID = $owner_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the data from the store.
|
||||
*
|
||||
* @param string $key
|
||||
* The key to the stored object. See TempStore::set() for details.
|
||||
*
|
||||
* @return object|null
|
||||
* The stored data object, or NULL if none exist.
|
||||
*/
|
||||
function get($key) {
|
||||
$data = db_query(
|
||||
'SELECT data FROM {temp_store} WHERE owner_id = :owner_id AND subsystem = :subsystem AND temp_key = :temp_key',
|
||||
array(
|
||||
':owner_id' => $this->ownerID,
|
||||
':subsystem' => $this->subsystem,
|
||||
':temp_key' => $key,
|
||||
)
|
||||
)
|
||||
->fetchObject();
|
||||
if ($data) {
|
||||
return unserialize($data->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the data to the store.
|
||||
*
|
||||
* @param string $key
|
||||
* The key to the object being stored. For objects that already exist in
|
||||
* the database somewhere else, this is typically the primary key of that
|
||||
* object. For objects that do not already exist, this is typically 'new'
|
||||
* or some special key that indicates that the object does not yet exist.
|
||||
* @param mixed $data
|
||||
* The data to be cached. It will be serialized.
|
||||
*
|
||||
* @todo
|
||||
* Using 'new' as a key might result in collisions if the same user tries
|
||||
* to create multiple new objects simultaneously. Document a workaround?
|
||||
*/
|
||||
function set($key, $data) {
|
||||
// Store the new data.
|
||||
db_merge('temp_store')
|
||||
->key(array('temp_key' => $key))
|
||||
->fields(array(
|
||||
'owner_id' => $this->ownerID,
|
||||
'subsystem' => $this->subsystem,
|
||||
'temp_key' => $key,
|
||||
'data' => serialize($data),
|
||||
'updated' => REQUEST_TIME,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one or more objects from this store for this owner.
|
||||
*
|
||||
* @param string|array $key
|
||||
* The key to the stored object, or an array of keys. See
|
||||
* TempStore::set() for details.
|
||||
*/
|
||||
function delete($key) {
|
||||
$this->deleteRecords($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one or more objects from this store for all owners.
|
||||
*
|
||||
* @param string|array $key
|
||||
* The key to the stored object, or an array of keys. See
|
||||
* TempStore::set() for details.
|
||||
*/
|
||||
function deleteAll($key) {
|
||||
$this->deleteRecords($key, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes database records for objects.
|
||||
*
|
||||
* @param string|array $key
|
||||
* The key to the stored object, or an array of keys. See
|
||||
* TempStore::set() for details.
|
||||
* @param bool $all
|
||||
* Whether to delete all records for this key (TRUE) or just the current
|
||||
* owner's (FALSE). Defaults to FALSE.
|
||||
*/
|
||||
protected function deleteRecords($key, $all = FALSE) {
|
||||
// The query builder will automatically use an IN condition when an array
|
||||
// is passed.
|
||||
$query = db_delete('temp_store')
|
||||
->condition('temp_key', $key)
|
||||
->condition('subsystem', $this->subsystem);
|
||||
|
||||
if (!$all) {
|
||||
$query->condition('owner_id', $this->ownerID);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the object is in use by another store for locking purposes.
|
||||
*
|
||||
* @param string $key
|
||||
* The key to the stored object. See TempStore::set() for details.
|
||||
* @param bool $exclude_owner
|
||||
* (optional) Whether or not to disregard the current user when determining
|
||||
* the lock owner. Defaults to FALSE.
|
||||
*
|
||||
* @return stdClass|null
|
||||
* An object with the user ID and updated date if found, otherwise NULL.
|
||||
*/
|
||||
public function getLockOwner($key) {
|
||||
return db_query(
|
||||
'SELECT owner_id AS ownerID, updated FROM {temp_store} WHERE subsystem = :subsystem AND temp_key = :temp_key ORDER BY updated ASC',
|
||||
array(
|
||||
':subsystem' => $this->subsystem,
|
||||
':temp_key' => $key,
|
||||
)
|
||||
)->fetchObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if another owner has locked the object.
|
||||
*
|
||||
* @param string $key
|
||||
* The key to the stored object. See TempStore::set() for details.
|
||||
*
|
||||
* @return stdClass|null
|
||||
* An object with the owner ID and updated date, or NULL if there is no
|
||||
* lock on the object belonging to a different owner.
|
||||
*/
|
||||
public function isLocked($key) {
|
||||
$lock_owner = $this->getLockOwner($key);
|
||||
if ((isset($lock_owner->ownerID) && $this->ownerID != $lock_owner->ownerID)) {
|
||||
return $lock_owner;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the last updated time for multiple objects in a given subsystem.
|
||||
*
|
||||
* @param string $subsystem
|
||||
* The module or subsystem. Possible values might include 'entity',
|
||||
* 'form', 'views', etc.
|
||||
* @param array $keys
|
||||
* An array of keys of stored objects. See TempStore::set() for details.
|
||||
*
|
||||
* @return
|
||||
* An associative array of objects and their last updated time, keyed by
|
||||
* object key.
|
||||
*/
|
||||
public static function testStoredObjects($subsystem, $keys) {
|
||||
return db_query(
|
||||
"SELECT t.temp_key, t.updated FROM {temp_store} t WHERE t.subsystem = :subsystem AND t.temp_key IN (:keys) ORDER BY t.updated ASC",
|
||||
array(':subsystem' => $subsystem, ':temp_keys' => $keys)
|
||||
)
|
||||
->fetchAllAssoc('temp_key');
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates all objects in all stores for a given key and subsystem.
|
||||
*
|
||||
* @param string $subsystem
|
||||
* The module or subsystem. Possible values might include 'entity',
|
||||
* 'form', 'views', etc.
|
||||
* @param array $key
|
||||
* The key to the stored object. See TempStore::set() for details.
|
||||
*/
|
||||
public static function clearAll($subsystem, $key) {
|
||||
$query = db_delete('temp_store')
|
||||
->condition('temp_key', $key)
|
||||
->condition('subsystem', $subsystem);
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates all objects older than a certain age, for all stores.
|
||||
*
|
||||
* @param int $age
|
||||
* The minimum age of objects to remove, in seconds. For example, 86400 is
|
||||
* one day. Defaults to 7 days.
|
||||
*/
|
||||
public static function clearOldObjects($age = NULL) {
|
||||
if (!isset($age)) {
|
||||
// 7 days.
|
||||
$age = 86400 * 7;
|
||||
}
|
||||
db_delete('temp_store')
|
||||
->condition('updated', REQUEST_TIME - $age, '<')
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\views\TempStore\UserTempStore.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\TempStore;
|
||||
|
||||
/**
|
||||
* Defines a TempStore using either the user or the session as the owner ID.
|
||||
*/
|
||||
class UserTempStore extends TempStore {
|
||||
|
||||
/**
|
||||
* Overrides TempStore::__construct().
|
||||
*
|
||||
* The $owner_id is given a default value of NULL.
|
||||
*/
|
||||
function __construct($subsystem, $owner_id = NULL) {
|
||||
if (!isset($owner_id)) {
|
||||
// If the user is anonymous, fall back to the session ID.
|
||||
$owner_id = user_is_logged_in() ? $GLOBALS['user']->uid : session_id();
|
||||
}
|
||||
|
||||
parent::__construct($subsystem, $owner_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides TempStore::set().
|
||||
*/
|
||||
function set($key, $data) {
|
||||
// Ensure that a session cookie is set for anonymous users.
|
||||
if (!user_is_logged_in()) {
|
||||
// A session is written so long as $_SESSION is not empty. Force this.
|
||||
// @todo This feels really hacky. Is there a better way?
|
||||
// @see http://drupalcode.org/project/ctools.git/blob/refs/heads/8.x-1.x:/includes/object-cache.inc#l69
|
||||
$_SESSION['temp_store_use_session'] = TRUE;
|
||||
}
|
||||
|
||||
parent::set($key, $data);
|
||||
}
|
||||
|
||||
}
|
|
@ -152,6 +152,45 @@ function views_schema() {
|
|||
$schema['cache_views_data']['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.';
|
||||
$schema['cache_views_data']['fields']['serialized']['default'] = 1;
|
||||
|
||||
$schema['temp_store'] = array(
|
||||
'description' => t('A temporary data store for objects that are being edited. Allows state to be saved in a stateless environment.'),
|
||||
'fields' => array(
|
||||
'owner_id' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The session ID this object belongs to.',
|
||||
),
|
||||
'subsystem' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => '128',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The owner (type of the object) for this data store. Allows multiple subsystems to use this data store.',
|
||||
),
|
||||
'temp_key' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => '128',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The key of the object this data store is attached to.',
|
||||
),
|
||||
'updated' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The time this data store was created or updated.',
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'description' => 'Serialized data being stored.',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('owner_id', 'subsystem', 'temp_key'),
|
||||
'indexes' => array('updated' => array('updated')),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
|
|
11
views.module
11
views.module
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Database\Query\AlterableInterface;
|
||||
use Drupal\views\TempStore\UserTempStore;
|
||||
use Drupal\views\View;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\views\Plugin\Type\ViewsPluginManager;
|
||||
|
@ -38,6 +39,16 @@ function views_init() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a TempStore for editing views.
|
||||
*
|
||||
* @return UserTempStore
|
||||
* A TempStore object for the 'view' type.
|
||||
*/
|
||||
function views_temp_store() {
|
||||
return new UserTempStore('view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ctools_exportable_info().
|
||||
*/
|
||||
|
|
|
@ -290,16 +290,15 @@ function views_ui_edit_page_title($view) {
|
|||
* someone else is already editing the view.
|
||||
*/
|
||||
function views_ui_cache_load($name) {
|
||||
// @todo Convert this: http://drupal.org/node/1658068.
|
||||
ctools_include('object-cache');
|
||||
$view = ctools_object_cache_get('view', $name);
|
||||
$views_temp_store = views_temp_store();
|
||||
$view = $views_temp_store->get($name);
|
||||
$original_view = views_get_view($name);
|
||||
|
||||
if (empty($view)) {
|
||||
$view = $original_view;
|
||||
if (!empty($view)) {
|
||||
// Check to see if someone else is already editing this view.
|
||||
$view->locked = ctools_object_cache_test('view', $view->name);
|
||||
$view->locked = $views_temp_store->isLocked($view->name);
|
||||
// Set a flag to indicate that this view is being edited.
|
||||
// This flag will be used e.g. to determine whether strings
|
||||
// should be localized.
|
||||
|
@ -331,8 +330,7 @@ function views_ui_cache_set(&$view) {
|
|||
drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
|
||||
return;
|
||||
}
|
||||
// @todo Convert this: http://drupal.org/node/1658068.
|
||||
ctools_include('object-cache');
|
||||
|
||||
$view->changed = TRUE; // let any future object know that this view has changed.
|
||||
|
||||
if (isset($view->current_display)) {
|
||||
|
@ -349,7 +347,7 @@ function views_ui_cache_set(&$view) {
|
|||
unset($view->display[$id]->handler);
|
||||
unset($view->display[$id]->default_display);
|
||||
}
|
||||
ctools_object_cache_set('view', $view->name, $view);
|
||||
views_temp_store()->set($view->name, $view);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue