...
 
Commits (208)
DraggableViews
==============
This module provides dragging entities and saving their order.
Quick install:
1) Activate Draggableviews module at admin/modules.
2) Navigate to view edit-page, click on the first link at the Format section and then choose style "table".
3) Click Add button at the "Fields" section and choose field "Content:title", add and apply.
4) Click Add button at the "Fields" section and choose field "Draggableviews: Content", add apply.
5) Click Add button at the "Sort criteria" section and choose field "Draggableviews: Weight", add and choose sort asc, then apply.
6) Save the view and you're done.
In the case of table standard drupal tabledrag.js JavaScript is used.
We also support jQuery UI Sortable JavaScript. In order to use it please set display style HTML List.
By default HTML list is displayed like grid. If you would like it to be displayed as list override
CSS styles for example in following way:
.draggableviews-processed li.views-row { float: none; width: 100%; margin-left: 0; }
One view/display to set order another to display
================================================
You can create one view to set the order and another view to display the order. Or even
create one view with two separate displays. In a view that displays the order there
should be no draggableviews field (that makes view sortable), then in the settings of
the "draggableviews weight" sorting criteria there will be selectbox "Display sort as"
where you can choose the source view of your weights. This is applicable when you use
Native handler.
Step by Step Guide for Creating a New View with 2 Displays:
===========================================================
Requirements: Draggableviews 7.x-2.x, Views 7.x-3.x, Views UI module enabled.
1) Activate Draggableviews module at admin/modules.
2) Create a new view
- Goto '/admin/structure/views/add' on your site.
- Check off 'Create a page'.
- Check off 'Create a block'.
- Set the 'Display format' for the page to what you desire.
- Set the "'Display format' of" to fields.
- Set the 'Display format' for the block to table.
- Fill in the rest of the views information.
- Click Continue & edit button.
3) Under the "FIELDS" section, do you see "Content: Title (Title)"? If you do not:
- Click 'add' button at the "Fields" section and choose field "Content:title", add and apply.
4) Click on 'Block' under the 'Display', to change the view display to the block display.
5) Add the Draggableviews Field:
- Click Add button at the "FIELDS" section.
- At the top of the overlay, Change "For: 'All displays'" to 'This block (override)'.
- If you do not do this then the field will be add to all displays and will prevent your
page display from using the block display to sort the order.
5) Click Add button at the "SORT CRITERIA" section choose field "Draggableviews: Weight", add and choose sort asc, then apply.
6) Under the "SORT CRITERIA" section, do you see "Content: Post date (asc)"? If you do:
- Click on it. At the bottom, click the 'Remove' button.
- An alternative is to rearrange the "SORT CRITERIA" order, making sure 'Draggableviews: Weight (asc)
appears first (or on top).
7) Save the view and you're done.*
*Things to confirm after you saved your new view.
- In the Administrative Views UI, Go back to your View's 'page' display.
-> Click 'Draggableviews: Weight (asc)' under 'SORT CRITERIA'
-> You should see:
Display sort as:
<title of view> (<display title>)
This should the view and block display you just create.
FYI - This is also where you can change it to another view.
Permissions
===========
Add "Access draggable views" permission to users who should be able to reorder views. If a user does not have this
permission they can still see the view, however they will not be able to reorder it.
If you want only want the order view visible to users with "Access draggable views" then set the Access to
"Permission: Access draggable views".
When users have the "Access draggable views" and "Use contextual links" permission, they will see
a contextual link from the non-reordering view to the ordering view.
Arguments handling
==================
Every time we save the order of a view, current set of arguments are saved with order.
You can see this in draggableviews_structure table "args" column. By default when we display order we use all
currently passed arguments to a view to "match" arguments in "args" column. This means that we can create
a view with contextual filter or exposed filter criteria and save different orders for different sets of arguments.
Using the "Do not use any arguments (use empty arguments)" option will completely ignore passed arguments used
in the Arguments handling of Sort criteria Draggable views weight. Be aware that in this case empty arguments set
will be used. So you can set order for a view when no arguments passed and then whatever arguments passed,
empty set will be used.
Using the "Prepare arguments with PHP code" option will let you alter arguments before they passed to
"matching" with "args" column. For us this means that we can create, for example, several exposed filters,
but pass values of only one of values of exposed filters instead of all of them.
Please be aware that in PHP code arguments are passed as an $arguments variable and you should return an array.
An example:
return array('status' => 1, 'user' => 2); or return $arguments; // $arguments is already an array.
In the $arguments array
- Contextual filters are NUMBER keyed.
- Exposed filters are NAME keyed.
For example, say we create two exposed filters: author and node type, but wish to take into account for ordering
only node type. We would use the following:
unset($arguments['author']); return $arguments;
When using arguments, make sure your ordering view display has the same arguments as the display you want to show
the end user. If they do not match, then your ordering will not match.
Using hook_draggableviews_handler_native_arguments_alter(&$arguments, $view, &$form_values) {} You may remove or change
the arguments save to the database, just as the "Prepare arguments with PHP code" option. See draggavleviews.api.php
for more details.
Removed Arguments:
- The pager 'item_per_page' exposed filter will never be saved.
Contextual link "Order view"
============================
If there is view with sort order draggableviews weight and the order is set by another view we show "Order view"
contextual link for opening a view that sets the order.
Troubleshooting Drag n' drop Not Showing
========================================
1. Make sure JavaScript is turned on and loading property. Double check your source code. For tables (D7) its <root>/misc/tabledrag.js.
2. Make sure you have draggableviews permission for the correct role.
3. Select 'show row weights'. By default, this is located at the top right of the table. See http://drupal.org/files/draggableviews-1978526-hode-row-weights.png" for a visual image.
4. 'Show row weights' is a global variable/setting. If you turn it off for 1 table, then all tables, across all pages, across all users, will not see it. To fix this in the UI, you have to 'hide row weights' on another page/table, such as admin/structure/block (D7) or admin/build/block (D6), or go into the variables table in the database.
.draggableviews-processed {
float: left;
}
.draggableviews-processed li.views-row {
display: block;
float: left;
width: 180px;
/* height: 180px; if required for fixed height displays */
margin: 10px;
padding: 5px;
cursor:move;
}
.draggableviews-processed li.views-row.ui-sortable-helper {
border: 1px dotted blue;
}
.draggableviews-processed li.views-row {
border: 1px dotted grey;
}
.draggableviews-weight{
display:none;
}
<?php
/**
* @file
* Hooks provided by the Draggableviews module.
*/
/**
* If Native handler used, you can alter arguments set before saved to database.
*
* This can be used when you would like to exclude or add some of arguments
* to be recorded to database. Also you can add new records to be saved to
* database (for example for translated nodes, etc.)
*
* @see http://drupal.org/node/1463596#comment-5687620
*
* @param array $arguments
* Array of arguments before saving.
* @param array $form_values
* Array of submitted entity ids and weights.
* @param object $view
* Views object.
*/
function hook_draggableviews_handler_native_arguments_alter(&$arguments, $view, &$form_values) {}
name = Draggableviews
description = Makes Views draggable
package = Views
core = 7.x
files[] = views/draggableviews_handler_field_draggable.inc
files[] = views/draggableviews_handler_sort.inc
files[] = views/draggableviews_join_handler.inc
files[] = handlers/draggableviews_handler.inc
files[] = handlers/draggableviews_handler_native.inc
files[] = handlers/draggableviews_handler_fieldapi.inc
files[] = handlers/draggableviews_hierarchy_handler.inc
files[] = handlers/draggableviews_hierarchy_handler_native.inc
files[] = test/draggableviews.test
dependencies[] = ctools
dependencies[] = entity
dependencies[] = views
This diff is collapsed.
This diff is collapsed.
<?php
/**
* @file
* Rules hooks implementation.
*/
/**
* Implements hook_rules_event_info().
*/
function draggableviews_rules_event_info() {
$events = array();
$events['draggableviews_rules_event_sorted'] = array(
'label' => t('A view has been sorted'),
'group' => t('DraggableViews'),
'variables' => array(
'view_name' => array(
'type' => 'text',
'label' => t('view name'),
),
'display_name' => array(
'type' => 'text',
'label' => t('view current display name'),
),
),
);
return $events;
}
name = Draggableviews Book
description = Reorder books
package = Views
core = 7.x
files[] = draggableviews_book_views_handler_argument.inc
dependencies[] = draggableviews
dependencies[] = book
\ No newline at end of file
<?php
/**
* @file
* Draggableviews book module install/schema hooks.
*/
/**
* Implements hook_install().
*
* Set the weight more than views.
*/
function draggableviews_book_install() {
db_query("UPDATE {system} SET weight = 11 WHERE name = 'draggableviews_book'");
}
<?php
/**
* Implements hook_views_api().
*/
function draggableviews_book_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'draggableviews_book'),
);
}
/**
* Implements hook_ctools_plugin_directory().
*/
function draggableviews_book_ctools_plugin_directory($module, $plugin) {
if (($module == 'draggableviews') && ($plugin == 'handler' || $plugin == 'hierarchy_handler')) {
return 'handlers';
}
}
/**
* Implements hook_menu_alter().
*
* Set custom access callback to "Order Outline" view.
*/
function draggableviews_book_menu_alter(&$items) {
$items['node/%/book']['access callback'] = '_draggableviews_book_access';
$items['node/%/book']['access arguments'] = array(1);
}
/**
* Check whether item has children.
*/
function _draggableviews_book_access($nid) {
return db_query('SELECT has_children FROM {menu_links} WHERE module = :module AND link_path = :link_path', array(':module' => 'book', ':link_path' => 'node/' . $nid))->fetchField();
}
/**
* Implements hook_views_post_execute().
*
* We manually sort results array according to the weights of depth levels.
*/
function draggableviews_book_views_post_execute($view) {
if (!isset($view->result[0]->draggableviews_book_mlid)) {
return;
}
// First prepare array of mlid keyed items.
$keyed_result = array();
foreach ($view->result as $result_item) {
$result_item->weight = array();
$keyed_result[$result_item->draggableviews_book_mlid] = $result_item;
}
// Set the weights arrays for every item. This collects weights of all parents
// plus its own weight. Weights are saved according to depth levels.
foreach ($keyed_result as &$item) {
_draggableviews_book_result_set_weight($item, $keyed_result);
}
// Sort items with custom sort callback.
usort($keyed_result, '_draggableviews_book_uasort');
$view->result = $keyed_result;
}
/**
* Set the weight array of item.
*/
function _draggableviews_book_result_set_weight(&$item, $result) {
// If weight is already calculated we simply return it.
if (!empty($item->weight)) {
return $item->weight;
}
// Load weights array of parent (if parent item is available).
$parent_weight = array();
if (isset($result[$item->draggableviews_book_plid])) {
$parent_weight = _draggableviews_book_result_set_weight($result[$item->draggableviews_book_plid], $result);
}
// Set the weight as sum of parents weights and
// its own weight according to depth.
$item->weight = $parent_weight + array($item->draggableviews_book_depth => $item->draggableviews_book_weight);
return $item->weight;
}
/**
* Custom sort callback based on weights arrays.
*/
function _draggableviews_book_uasort($item1, $item2) {
for ($i = 0; $i < 10; $i++) {
// Item 1 is less than item 2.
if (isset($item1->weight[$i]) && !isset($item2->weight[$i])) {
return 1;
}
// Item 2 is less than item 1.
if (!isset($item1->weight[$i]) && isset($item2->weight[$i])) {
return -1;
}
if (isset($item1->weight[$i]) && isset($item2->weight[$i])) {
if ($item1->weight[$i] != $item2->weight[$i]) {
return ($item1->weight[$i] < $item2->weight[$i]) ? -1 : 1;
}
elseif (isset($item1->weight[$i+1]) || isset($item2->weight[$i+1])) {
// Loop again as there are more weights at a greater depth to compare.
continue;
}
else {
// By elimination, we know the weight and depth are the same. Sort by title.
return strcmp($item1->node_title, $item2->node_title);
}
}
}
}
<?php
/**
* @file
* Provide special views data and handlers for draggableviews_book module
*/
/**
* Implements hook_views_data().
*/
function draggableviews_book_views_data() {
// Book hierarchy and weight data are now in {menu_links}.
$data['draggableviews_book_structure']['table']['group'] = t('Book');
$data['draggableviews_book_structure']['table']['join'] = array(
'node' => array(
'table' => 'menu_links',
'left_table' => 'book',
'left_field' => 'mlid',
'field' => 'mlid',
),
);
$data['draggableviews_book_structure']['book'] = array(
'title' => t('All sub nodes of this book page.'),
'help' => t('All sub nodes of this book page.'),
'argument' => array(
'handler' => 'views_handler_argument_draggableviews_book',
),
);
return $data;
}
<?php
/**
* @file
* Draggableviews book default view.
*/
/**
* Implements hook_views_default_views().
*/
function draggableviews_book_views_default_views() {
$view = new view();
$view->name = 'book';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Book';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Reorder Book';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'none';
$handler->display->display_options['pager']['options']['offset'] = '0';
$handler->display->display_options['style_plugin'] = 'table';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Field: Draggableviews: Content */
$handler->display->display_options['fields']['draggableviews']['id'] = 'draggableviews';
$handler->display->display_options['fields']['draggableviews']['table'] = 'node';
$handler->display->display_options['fields']['draggableviews']['field'] = 'draggableviews';
$handler->display->display_options['fields']['draggableviews']['element_default_classes'] = FALSE;
$handler->display->display_options['fields']['draggableviews']['hide_alter_empty'] = FALSE;
$handler->display->display_options['fields']['draggableviews']['draggableviews']['handler'] = 'draggableviews_handler_book';
$handler->display->display_options['fields']['draggableviews']['draggableviews']['hierarchy_handler'] = 'draggableviews_hierarchy_handler_book';
$handler->display->display_options['fields']['draggableviews']['draggableviews']['ajax'] = 0;
/* Sort criterion: Draggableviews: Weight */
$handler->display->display_options['sorts']['weight']['id'] = 'weight';
$handler->display->display_options['sorts']['weight']['table'] = 'draggableviews_structure';
$handler->display->display_options['sorts']['weight']['field'] = 'weight';
$handler->display->display_options['sorts']['weight']['draggableviews_setting_view'] = 'book:page';
$handler->display->display_options['sorts']['weight']['draggableviews_setting_new_items_bottom_list'] = 1;
/* Contextual filter: Book: All sub nodes of this book page. */
$handler->display->display_options['arguments']['book']['id'] = 'book';
$handler->display->display_options['arguments']['book']['table'] = 'draggableviews_book_structure';
$handler->display->display_options['arguments']['book']['field'] = 'book';
$handler->display->display_options['arguments']['book']['default_action'] = 'empty';
$handler->display->display_options['arguments']['book']['default_argument_type'] = 'fixed';
$handler->display->display_options['arguments']['book']['summary']['number_of_records'] = '0';
$handler->display->display_options['arguments']['book']['summary']['format'] = 'default_summary';
$handler->display->display_options['arguments']['book']['summary_options']['items_per_page'] = '25';
/* Filter criterion: Content: Published */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'node';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = 1;
$handler->display->display_options['filters']['status']['group'] = 1;
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['path'] = 'node/%/book';
$handler->display->display_options['menu']['type'] = 'tab';
$handler->display->display_options['menu']['title'] = 'Order Outline';
$handler->display->display_options['menu']['weight'] = '5';
$handler->display->display_options['menu']['context'] = 0;
$translatables['book'] = array(
t('Master'),
t('Reorder Book'),
t('more'),
t('Apply'),
t('Reset'),
t('Sort by'),
t('Asc'),
t('Desc'),
t('Title'),
t('Content'),
t('All'),
t('Page'),
);
$views[$view->name] = $view;
return $views;
}
<?php
/**
* @file
* Draggableviews book views handler argument.
*/
/**
* Argument that refers to a certain book page.
*/
class views_handler_argument_draggableviews_book extends views_handler_argument {
/**
* Add condition to select only part of the tree that is under argument's id.
*/
function query($group_by = FALSE) {
$this->ensure_my_table();
$mlid = db_query("SELECT mlid FROM {book} WHERE nid = :nid", array(':nid' => $this->argument))->fetchField();
// Do not show argument menu item.
$this->query->add_where(0, $this->table . '.mlid', $mlid, '<>');
// Select all items that have argument in one of parents.
$group = $this->query->set_where_group('OR');
for ($i = 1; $i < 10; $i++) {
$this->query->add_where($group, $this->table . '.p' . $i, $mlid);
}
// We sort items in hook_views_post_execute().
$tbl = $this->table;
// Add weight, depth and parent fields.
$this->query->add_field($tbl, 'weight', 'draggableviews_book_weight');
$this->query->add_field($tbl, 'depth', 'draggableviews_book_depth');
$this->query->add_field($tbl, 'plid', 'draggableviews_book_plid');
$this->query->add_field($tbl, 'mlid', 'draggableviews_book_mlid');
}
}
<?php
/**
* @file
* Draggableviews views book handler.
*/
$plugin = array(
'label' => 'Book',
'handler' => array(
'class' => 'draggableviews_handler_book',
),
);
class draggableviews_handler_book extends draggableviews_handler {
/**
* Retrieve the weight.
*/
function get($field, $index) {
$row = $field->view->result[$index];
return isset($row->draggableviews_book_weight) ? $row->draggableviews_book_weight : 0;
}
/**
* Set both parent and weight values.
*/
function set($form_state) {
$fv = $form_state['values'];
foreach ($fv['draggableviews'] as $item) {
$node = node_load($item['id']);
$keys = array('menu_name', 'mlid', 'router_path', 'has_children', 'options', 'module',
// 'original_bid', 'parent_depth_limit',
'bid');
$book = array();
foreach ($keys as $key) {
$book[$key] = $node->book[$key];
}
$book['weight'] = $item['weight'];
$book['plid'] = db_query('SELECT mlid FROM {menu_links} WHERE link_path = :link_path AND menu_name = :menu_name', array(':link_path' => 'node/' . $item['parent'], ':menu_name' => $book['menu_name']))->fetchField();
$node->book = $book;
_book_update_outline($node);
drupal_static_reset('book_get_books');
}
}
}
<?php
/**
* @file
* Draggableviews book views hierarchy handler.
*/
$plugin = array(
'label' => 'Book',
'handler' => array(
'class' => 'draggableviews_hierarchy_handler_book',
),
);
class draggableviews_hierarchy_handler_book extends draggableviews_hierarchy_handler {
public function get($field, $index) {
$row = $field->view->result[$index];
$parent_mlid = $row->draggableviews_book_plid;
$parent_link_path = db_query('SELECT link_path FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $parent_mlid))->fetchField();
return !empty($parent_link_path) ? drupal_substr($parent_link_path, 5) : 0;
}
public function get_depth($field, $index) {
$row = $field->view->result[$index];
// Cache depth of the top parent so we do not recalculate it.
static $parent_depth;
if (is_null($parent_depth)) {
$parent_mlid = $row->draggableviews_book_plid;
$parent_depth = db_query('SELECT depth FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $parent_mlid))->fetchField() + 1;
}
return isset($row->draggableviews_book_depth) ? $row->draggableviews_book_depth - $parent_depth : 0;
}
// Don't need to set value here as it is done in "weight" handler
// draggableviews_handler in order to avoid doing multiple identical queries
// to draggableviews_structure table.
function set($form_state) {}
}
<?php
/**
* @file
* Base plugin implementation.
*/
/**
* Parent class for all sort handlers.
*/
class draggableviews_handler {
/**
* Get the weight value.
*
* @param object $field
* Draggableviews field handler. View is $field->view,
* to get a row $field->view->result[$index].
* @param int $index
* Index of the row.
*
* @return int
* Weight value.
*/
public function get($field, $index) {}
/**
* Save weight value.
*
* @param $form_state
* Array of form state of the form.
* View object $form_state['values']['view'].
*/
public function set($form_state) {}
/**
* Form with settings of the handler.
*
* @param object $field
* Draggableviews field handler.
*
* @return array
* Form array.
*/
public function options_form($field) {}
/**
* Settings form default values.
*
* @return array
* Array with default values.
*/
public function option_definition() {}
}
\ No newline at end of file
<?php
/**
* @file
* Field API handler plugin.
*/
$plugin = array(
'label' => 'FieldAPI',
'handler' => array(
'class' => 'draggableviews_handler_fieldapi',
),
);
class draggableviews_handler_fieldapi extends draggableviews_handler {
/**
* Set default value of field option.
*/
public function option_definition() {
return array('field' => '');
}
/**
* Add field options for handler.
*/
function options_form($field) {
$form = array();
$options = array('' => t('- Select -'));
// Check all the sortings added to a view.
// TODO: Research a better way to do this.
$sorts = isset($field->view->display_handler->display->display_options['sorts'])
? $field->view->display_handler->display->display_options['sorts']
: array();
// If no sorts available for current display, use sorts from default display.
if (empty($sorts)) {
$sorts = isset($field->view->display['default']->display_options['sorts'])
? $field->view->display['default']->display_options['sorts']
: array();
}
foreach ($sorts as $sort_option) {
$field_name = $sort_option['field'];
// Field should be like "field_name_value".
if (strpos($field_name, 'field_') === FALSE || strpos($field_name, '_value') === FALSE) {
continue;
}
// Remove "_value" from field name and try to load the field.
$field_name = drupal_substr($field_name, 0, drupal_strlen($field_name) - 6);
if ($field_info = field_info_field($field_name)) {
if ($field_info['type'] == 'number_integer' || $field_info['type'] == 'list_integer') {
$views_field_data = field_views_field_default_views_data($field_info);
$options[$sort_option['table'] . ':' . $sort_option['field']] = filter_xss($views_field_data[$sort_option['table']][$sort_option['field']]['title']);
}
}
}
// If options are empty, show warning message.
// Count will allows be at least 1 because we add '- Select -'.
if (count($options) == 1) {
$form['field_warning'] = array(
'#markup' => '<div class="messages warning">' . t('Add weight integer field to sorts so it can be selected.') . '</div>',
);
}
$form['field'] = array(
'#type' => 'select',
'#title' => t('Field'),
'#options' => $options,
'#default_value' => $field->options['draggableviews']['draggableviews_handler_fieldapi']['field'],
'#description' => t('Please select field that contains weight. It should be integer type and already added to sorts of the view.'),
);
return $form;
}
function get($field, $index) {
// Get the name of selected field.
$field_option = $field->options['draggableviews']['draggableviews_handler_fieldapi']['field'];
list($field_table, $field_name) = explode(':', $field_option);
// Current row.
$row = $field->view->result[$index];
$alias = $field->view->query->get_field_alias($field_table, $field_name);
return ($alias) ? $row->{$alias} : NULL;
}
function set($form_state) {
$fv = $form_state['values'];
$view = $form_state['build_info']['args'][0];
$view_name = $view->name;
$view_display = $view->current_display;
// View arguments.
$arguments = $view->args;
if (isset($view->exposed_raw_input)) {
$arguments += $view->exposed_raw_input;
ksort($arguments);
// Redirect view to the same page with exposed filters set.
$form_state['redirect'] = array(current_path(), array('query' => $view->exposed_raw_input));
}
$base_table = $view->field['draggableviews']->table;
$entity_info_all = entity_get_info();
$entity_type = '';
foreach ($entity_info_all as $entity_name_key => $entity_info) {
if ($entity_info['base table'] == $base_table) {
$entity_type = $entity_name_key;
break;
}
}
$options_field = $view->field['draggableviews']->options['draggableviews']['draggableviews_handler_fieldapi']['field'];
list($field_table, $field_column) = explode(':', $options_field);
// Remove '_value' from column name to get field name.
$field_name = drupal_substr($field_column, 0, drupal_strlen($field_column) - 6);
// Give other modules a chance to alter saved arguments.
drupal_alter('draggableviews_handler_fieldapi_arguments', $fv['draggableviews'], $view);
// Reorder the items by weight.
uasort($fv['draggableviews'], 'drupal_sort_weight');
// Save the values of the field.
foreach ($fv['draggableviews'] as $item) {
if (isset($item['id']) && isset($item['weight'])) {
$entity_wrapper = entity_metadata_wrapper($entity_type, $item['id']);
$entity_wrapper->{$field_name} = $item['weight'];
$entity_wrapper->save();
}
}
}
}
<?php
/**
* @file
* Native handler plugin.
*/
$plugin = array(
'label' => 'Native',
'handler' => array(
'class' => 'draggableviews_handler_native',
),
);
class draggableviews_handler_native extends draggableviews_handler {
public function get($field, $index) {
$row = $field->view->result[$index];
return (isset($row->draggableviews_structure_weight_coalesce)) ? $row->draggableviews_structure_weight_coalesce : 0;
}
function set($form_state) {
$fv = $form_state['values'];
$view = $form_state['build_info']['args'][0];
$view_name = $view->name;
$view_display = $view->current_display;
// View arguments.
$arguments = $view->args;
if (isset($view->exposed_raw_input)) {
$arguments += $view->exposed_raw_input;
ksort($arguments);
// Redirect view to the same page with exposed filters set.
$form_state['redirect'] = array(current_path(), array('query' => $view->exposed_raw_input));
}
// Give other modules a chance to alter saved arguments.
drupal_alter('draggableviews_handler_native_arguments', $arguments, $view, $fv['draggableviews']);
// Never save 'items_per_page' argument added by exposed pager filter.
unset($arguments['items_per_page']);
$args_string = json_encode($arguments);
// Save records to our custom table.
$weight = 0;
// Reorder the items by weight.
uasort($fv['draggableviews'], 'drupal_sort_weight');
foreach ($fv['draggableviews'] as $item) {
// Make sure id is available.
if (!isset($item['id'])) {
continue;
}
// Delete previous order record.
db_delete('draggableviews_structure')
->condition('view_name', $view_name)
->condition('view_display', $view_display)
->condition('args', $args_string)
->condition('entity_id', $item['id'])
->execute();
// Create new order record.
$record = array(
'view_name' => $view_name,
'view_display' => $view_display,
'args' => $args_string,
'entity_id' => $item['id'],
'weight' => $weight,
);
// If parent element exists, save it.
if (isset($item['parent'])) {
$record['parent'] = $item['parent'];
}
drupal_write_record('draggableviews_structure', $record);
$weight++;
}
}
}
<?php
/**
* @file
* Base plugin implementation.
*/
/**
* Parent class for all hierarchy handlers.
*/
class draggableviews_hierarchy_handler {
/**
* Get the parent value.
*
* @param object $field
* Draggableviews field handler. View is $field->view,
* to get a row $field->view->result[$index].
* @param int $index
* Index of the row.
*
* @return int
* Weight value.
*/
public function get($field, $index) {}
/**
* Save parent value.
*
* @param $form_state
* Array of form state of the form.
* View object $form_state['values']['view'].
*/
public function set($form_state) {}
/**
* Form with settings of the handler.
*
* @param object $field
* Draggableviews field handler.
*
* @return array
* Form array.
*/
public function options_form($field) {}
/**
* Settings form default values.
*
* @return array
* Array with default values.
*/
public function option_definition() {}
/**
* Get "results" array index of and item with specific base field id.
*
* @param object $view
* Views object
* @param type $id
* Base field id.
*
* @return int
*/
public function get_index($view, $id) {
foreach ($view->result as $key => $item) {
if ($item->{$view->base_field} == $id) {
return $key;
}
}
}
}
<?php
/**
* @file
* Native handler plugin.
*/
$plugin = array(
'label' => 'Native',
'handler' => array(
'class' => 'draggableviews_hierarchy_handler_native',
),
);
class draggableviews_hierarchy_handler_native extends draggableviews_hierarchy_handler {
public function get($field, $index) {
$row = $field->view->result[$index];
return (isset($row->draggableviews_structure_parent)) ? $row->draggableviews_structure_parent : 0;
}
public function get_depth($field, $index) {
// $index can be NULL for multiple reasons. Common problem
// is after parent is deleted.
if (!isset($index) || !isset($field->view->result[$index])) {
return;
}
$row = $field->view->result[$index];
// If parent is available, set parent's depth +1.
return (!empty($row->draggableviews_structure_parent)) ? $this->get_depth($field, $this->get_index($field->view, $row->draggableviews_structure_parent)) + 1 : 0;
}
// Don't need to set value here as it is done in "weight" handler
// draggableviews_handler in order to avoid doing multiple identical queries
// to draggableviews_structure table.
function set($form_state) {}
}
/**
* @file
* Adds draggable functionality to the html list display of the view.
*/
(function ($) {
Drupal.behaviors.draggableViews = {
attach: function (context, settings) {
$(Drupal.settings.draggableviews_row_class).each(function(index, row_class ) {
$('.views-form .' + row_class + ':not(.draggableviews-processed)', context)
// Add class for theming.
.addClass('draggableviews-processed')
// Add sortable effect.
.sortable({
update: function (event, ui) {
$(".draggableviews-weight").each(function (i, Val) {
$(this).val(i);
});
if (!$(this).hasClass('draggableviews-changed')) {
// If view is not ajaxified.
if (!Drupal.settings.draggableviews_ajax) {
$('<div class="draggableviews-changed-warning messages warning">' + Drupal.t('Changes made in this list will not be saved until the form is submitted.') + '</div>')
.insertBefore($(this).parents('form div.item-list')).hide().fadeIn('slow');
$(this).addClass('draggableviews-changed');
}
else {
// If view ajaxified.
$('<div class="draggableviews-changed-notice messages warning">' + Drupal.t('Order of this view has been changed.') + '</div>')
.insertBefore($(this).parents('form div.item-list')).hide().fadeIn('slow').delay(3000).fadeOut('slow');
$(this).addClass('draggableviews-changed');
}
}
// If Ajax enabled, we should submit the form.
if (Drupal.settings.draggableviews_ajax) {
$(this).closest('form').find('.js-draggableviews-save-order').trigger('mousedown');
}
},
containment: 'parent',
cursor: 'move'
});
});
}
}
})(jQuery);
/**
* @file
* Adds draggable functionality to the table display of the view.
*/
(function ($) {
Drupal.behaviors.draggableviewsAutosave = {
attach: function(){
if (typeof Drupal.tableDrag == 'undefined') {
return;
}
for (var prop in Drupal.tableDrag){
if (prop.substring(0, 14) == 'draggableviews'){
var table = Drupal.tableDrag[prop];
table.onDrop = function() {
// Hide change messages that are not relevant when saving form
// through AJAX.
$('.tabledrag-changed').hide();
$('.tabledrag-changed-warning').hide();
$table = $(this.table);
// Submit form with AJAX.
$table.closest('form').find('.js-draggableviews-save-order').triggerHandler('mousedown');
// The previously dragged row is left with class styling the row
// yellow style, indicating unsaved state. To increase UX we remove
// this class with some delay to indicate that progress was made in
// the background.
setTimeout(function() {
$('.drag-previous').removeClass('drag-previous');
}, 3000);
$('<div class="draggableviews-changed-notice messages warning">' + Drupal.t('Order of this view has been changed.') + '</div>')
.insertBefore($table).hide().fadeIn('slow').delay(3000).fadeOut('slow');
}
}
}
}
}
})(jQuery);
This diff is collapsed.
name = Draggableviews Test
description = Provides views for testing.
dependencies[] = draggableviews
package = Views
core = 7.x
hidden = TRUE
\ No newline at end of file
<?php
/**
* Implements hook_views_api().
*/
function draggableviews_test_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'draggableviews_test'),
);
}
This diff is collapsed.
<?php
/**
* @file
* Views hooks implementations.
*/
/**
* Implements hook_views_data_alter().
*/
function draggableviews_views_data_alter(&$data) {
$data['draggableviews_structure']['weight'] = array(
'title' => t('Weight'),
'group' => t('Draggableviews'),
'field' => array(
'help' => t('Display the weight value.'),
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'sort' => array(
'help' => t('Sort entities by the draggableviews weight table field.'),
'handler' => 'draggableviews_handler_sort',
),
'filter' => array(
'help' => t('Filter by the draggableviews weight value (Native handler only).'),
'handler' => 'views_handler_filter_numeric',
),
);
$data['draggableviews_structure']['parent'] = array(
'title' => t('Parent'),
'help' => t('The parent entity id.'),
'group' => t('Draggableviews'),
'field' => array(
'handler' => 'views_handler_field_numeric',
),
'filter' => array(
'help' => t('Filter by the draggableviews parent\'s entity id (Native handler only).'),
'handler' => 'views_handler_filter_numeric',
),
);
foreach (entity_get_info() as $entity_type => $info) {
if (isset($info['base table']) && isset($data[$info['base table']]['table'])) {
$data[$info['base table']]['draggableviews'] = array(
'title' => $data[$info['base table']]['table']['group'],
'group' => t('Draggableviews'),
'help' => t('Provide a draggable functionality.'),
'real field' => $info['entity keys']['id'],
'field' => array(
'handler' => 'draggableviews_handler_field_draggable',
'click sortable' => FALSE,
),
);
// Explain to every entity how to join with draggableviews structure table.
$data['draggableviews_structure']['table']['join'][$info['base table']] = array(
'handler' => 'draggableviews_join_handler',
'left_table' => $info['base table'], // Because this is a direct link it could be left out.
'left_field' => $info['entity keys']['id'],
'field' => 'entity_id',
);
}
}
}
/**
* Implements hook_views_post_execute().
*/
function draggableviews_views_post_execute(&$view) {
if (isset($view->field['draggableviews'])) {
// Move draggableviews field to last column
// otherwise tabledrag.js doesn't work.
$draggable_field = $view->field['draggableviews'];
unset($view->field['draggableviews']);
$view->field['draggableviews'] = $draggable_field;
}
}
<?php
/**
* @file
* Views field handler. Contains all relevant Draggableviews
* options and related logic.
* Implements the Views Form API.
*/
class draggableviews_handler_field_draggable extends views_handler_field {
function construct() {
parent::construct();
}
function option_definition() {
$options = parent::option_definition();
$options['draggableviews'] = array(
'contains' => array(
'handler' => array('default' => 'draggableviews_handler_native'),
'hierarchy_handler' => array('default' => ''),
'save_button_label' => array('default' => 'Save'),
'ajax' => array('default' => FALSE),
),
);
// Populate default values of form elements provided by handlers.
foreach (draggableviews_get_handlers() as $handler_id => $handler_object) {
$options['draggableviews']['contains'][$handler_id] = array('default' => $handler_object->option_definition());
}
foreach (draggableviews_get_hierarchy_handlers() as $handler_id => $handler_object) {
$options['draggableviews']['contains'][$handler_id] = array('default' => $handler_object->option_definition());
}
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
// Remove not needed settings options.
$form['alter']['#access'] = FALSE;
$form['style_settings']['#access'] = FALSE;
$form['empty_field_behavior']['#access'] = FALSE;
$form['draggableviews'] = array(
'#type' => 'fieldset',
'#title' => t('Draggable Views'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$handler_options = array();
$handler_forms = array();
foreach (draggableviews_get_handlers() as $handler_id => $handler_object) {
$handler = ctools_get_plugins('draggableviews', 'handler', $handler_id);
$handler_options[$handler_id] = filter_xss($handler['label']);
$handler_forms[$handler_id] = $handler_object->options_form($this);
}
$form['draggableviews']['handler'] = array(
'#type' => 'select',
'#title' => t('Sort handler'),
'#options' => $handler_options,
'#default_value' => $this->options['draggableviews']['handler'],
);
// Add handler's form element as fieldset that
// is active only if handler selected.
foreach ($handler_forms as $handler_id => $handler_form_element) {
// Skip empty handler's form elements.
if (empty($handler_form_element)) {
continue;
}
$form['draggableviews'][$handler_id] = array(
'#type' => 'fieldset',
'#title' => check_plain($form['draggableviews']['handler']['#options'][$handler_id]),
'#collapsible' => FALSE,
'#states' => array(
'visible' => array(
'select[name="options[draggableviews][handler]"]' => array('value' => $handler_id),
),
),
);
foreach ($handler_form_element as $key => $form_element) {
$form['draggableviews'][$handler_id][$key] = $form_element;
}
}
$hierarchy_handler_options = array('' => t('- None -'));
$hierarchy_handler_forms = array();
foreach (draggableviews_get_hierarchy_handlers() as $handler_id => $handler_object) {
$handler = ctools_get_plugins('draggableviews', 'hierarchy_handler', $handler_id);
$hierarchy_handler_options[$handler_id] = filter_xss($handler['label']);
$hierarchy_handler_forms[$handler_id] = $handler_object->options_form($this);
}
$form['draggableviews']['hierarchy_handler'] = array(
'#type' => 'select',
'#title' => t('Hierarchy handler'),
'#options' => $hierarchy_handler_options,
'#default_value' => $this->options['draggableviews']['hierarchy_handler'],
);
// Add handler's form element as fieldset that
// is active only if handler selected.
foreach ($hierarchy_handler_forms as $handler_id => $hierarchy_handler_form_element) {
// Skip empty handler's form elements.
if (empty($hierarchy_handler_form_element)) {
continue;
}
$form['draggableviews'][$handler_id] = array(
'#type' => 'fieldset',
'#title' => check_plain($form['draggableviews']['handler']['#options'][$handler_id]),
'#collapsible' => FALSE,
'#states' => array(
'visible' => array(
'select[name="options[draggableviews][hierarchy_handler]"]' => array('value' => $handler_id),
),
),
);
foreach ($hierarchy_handler_form_element as $key => $form_element) {
$form['draggableviews'][$handler_id][$key] = $form_element;
}
}
$form['draggableviews']['save_button_label'] = array(
'#type' => 'textfield',
'#title' => t('Custom Save button label'),
'#size' => 20,
'#description' => t("Allow to change Save button Label."),
'#default_value' => $this->options['draggableviews']['save_button_label'],
);
$form['draggableviews']['ajax'] = array(
'#type' => 'checkbox',
'#title' => t('Ajax'),
'#description' => t('Use ajax in draggable form.'),
'#default_value' => $this->options['draggableviews']['ajax'],
);
}
function render($values) {
return '<!--form-item-' . $this->options['id'] . '--' . $this->view->row_index . '-->';
}
/**
* The form which replaces the placeholder from render().
*/
function views_form(&$form, &$form_state) {
// The view is empty, abort.
if (empty($this->view->result)) {
return;
}
// If this view is a summary, do not add a form.
if ($this->field_alias == 'unknown') {
return;
}
$form[$this->options['id']] = array(
'#tree' => TRUE,
);
$range = count($this->view->result);
// At this point, the query has already been run, so we can access the results
// in order to get the base key value (for example, nid for nodes).
foreach ($this->view->result as $row_index => $row) {
$entity_id = $this->get_value($row);
$form[$this->options['id']][$row_index] = array(
'#tree' => TRUE,
);
$handler_object = draggableviews_get_handler_class($this->options['draggableviews']['handler']);
// Weight field selectbox.
$form[$this->options['id']][$row_index]['weight'] = array(
'#type' => 'select',
'#options' => range(-$range, $range),
'#attributes' => array('class' => array('draggableviews-weight')),
'#default_value' => $handler_object->get($this, $row_index),
);
// Item to keep id of the entity.
$form[$this->options['id']][$row_index]['id'] = array(
'#type' => 'hidden',
'#value' => $this->view->result[$row_index]->{$this->field_alias},
'#attributes' => array('class' => array('draggableviews-id')),
);
// Add parent and depth field.
if (!empty($this->options['draggableviews']['hierarchy_handler'])) {
$hierarchy_handler_object = draggableviews_get_handler_class($this->options['draggableviews']['hierarchy_handler'], 'hierarchy_handler');
$form[$this->options['id']][$row_index]['parent'] = array(
'#type' => 'hidden',
'#default_value' => $hierarchy_handler_object->get($this, $row_index),
'#attributes' => array('class' => array('draggableviews-parent')),
);
$form[$this->options['id']][$row_index]['depth'] = array(
'#type' => 'hidden',
'#default_value' => $hierarchy_handler_object->get_depth($this, $row_index),
'#attributes' => array('class' => array('draggableviews-depth')),
);
}
}
}
}