From db64734193e468077aedf6e5089ac56f95cc6e31 Mon Sep 17 00:00:00 2001 From: Eric Rasmussen <ericrasmussen1@gmail.com> Date: Tue, 2 Oct 2012 14:53:33 -0500 Subject: [PATCH] [gh-462] Add Draggable Views module to project --- sites/all/modules/draggableviews/LICENSE.txt | 339 ++++++++++++ sites/all/modules/draggableviews/README.txt | 60 +++ .../css/draggableviews_list.css | 24 + .../draggableviews/draggableviews.api.php | 24 + .../draggableviews/draggableviews.info | 26 + .../draggableviews/draggableviews.install | 111 ++++ .../draggableviews/draggableviews.module | 509 ++++++++++++++++++ .../draggableviews/draggableviews.rules.inc | 30 ++ .../draggableviews_book.info | 13 + .../draggableviews_book.install | 10 + .../draggableviews_book.module | 110 ++++ .../draggableviews_book.views.inc | 32 ++ .../draggableviews_book.views_default.inc | 84 +++ ...gableviews_book_views_handler_argument.inc | 31 ++ .../handlers/draggableviews_handler_book.inc | 47 ++ .../draggableviews_hierarchy_handler_book.inc | 38 ++ .../handlers/draggableviews_handler.inc | 54 ++ .../draggableviews_handler_fieldapi.inc | 126 +++++ .../draggableviews_handler_native.inc | 71 +++ .../draggableviews_hierarchy_handler.inc | 72 +++ ...raggableviews_hierarchy_handler_native.inc | 31 ++ .../draggableviews/js/draggableviews_list.js | 45 ++ .../draggableviews/js/draggableviews_table.js | 37 ++ .../draggableviews/test/draggableviews.test | 425 +++++++++++++++ .../draggableviews_test.info | 12 + .../draggableviews_test.module | 11 + .../draggableviews_test.views_default.inc | 248 +++++++++ .../views/draggableviews.views.inc | 56 ++ ...draggableviews_handler_field_draggable.inc | 199 +++++++ .../views/draggableviews_handler_sort.inc | 112 ++++ .../views/draggableviews_join_handler.inc | 83 +++ 31 files changed, 3070 insertions(+) create mode 100644 sites/all/modules/draggableviews/LICENSE.txt create mode 100644 sites/all/modules/draggableviews/README.txt create mode 100644 sites/all/modules/draggableviews/css/draggableviews_list.css create mode 100644 sites/all/modules/draggableviews/draggableviews.api.php create mode 100644 sites/all/modules/draggableviews/draggableviews.info create mode 100644 sites/all/modules/draggableviews/draggableviews.install create mode 100644 sites/all/modules/draggableviews/draggableviews.module create mode 100644 sites/all/modules/draggableviews/draggableviews.rules.inc create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.info create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.install create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.module create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views.inc create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views_default.inc create mode 100644 sites/all/modules/draggableviews/draggableviews_book/draggableviews_book_views_handler_argument.inc create mode 100644 sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_handler_book.inc create mode 100644 sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_hierarchy_handler_book.inc create mode 100644 sites/all/modules/draggableviews/handlers/draggableviews_handler.inc create mode 100644 sites/all/modules/draggableviews/handlers/draggableviews_handler_fieldapi.inc create mode 100644 sites/all/modules/draggableviews/handlers/draggableviews_handler_native.inc create mode 100644 sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler.inc create mode 100644 sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler_native.inc create mode 100644 sites/all/modules/draggableviews/js/draggableviews_list.js create mode 100644 sites/all/modules/draggableviews/js/draggableviews_table.js create mode 100644 sites/all/modules/draggableviews/test/draggableviews.test create mode 100644 sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.info create mode 100644 sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.module create mode 100644 sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.views_default.inc create mode 100644 sites/all/modules/draggableviews/views/draggableviews.views.inc create mode 100644 sites/all/modules/draggableviews/views/draggableviews_handler_field_draggable.inc create mode 100644 sites/all/modules/draggableviews/views/draggableviews_handler_sort.inc create mode 100644 sites/all/modules/draggableviews/views/draggableviews_join_handler.inc diff --git a/sites/all/modules/draggableviews/LICENSE.txt b/sites/all/modules/draggableviews/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/draggableviews/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/sites/all/modules/draggableviews/README.txt b/sites/all/modules/draggableviews/README.txt new file mode 100644 index 000000000..c75fa1ed8 --- /dev/null +++ b/sites/all/modules/draggableviews/README.txt @@ -0,0 +1,60 @@ +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. + +Permissions +=========== + +Please add "Access draggable views" permission to users who should be able to reorder views. + +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. + +We can also completely ignore passed arguments using "Do not use any arguments (use empty arguments)" option +in 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. + +Prepare arguments with PHP code is an option when you would like to 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 (like we create two exposed +filters: author and node type, but take into account for ordering only node type). +Please be aware that in PHP code arguments are passed as $arguments variable and you should return array. +Contextual filters are number keyed and exposed filters are name keyed. + +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. diff --git a/sites/all/modules/draggableviews/css/draggableviews_list.css b/sites/all/modules/draggableviews/css/draggableviews_list.css new file mode 100644 index 000000000..9ecf3ecfc --- /dev/null +++ b/sites/all/modules/draggableviews/css/draggableviews_list.css @@ -0,0 +1,24 @@ +.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; +} diff --git a/sites/all/modules/draggableviews/draggableviews.api.php b/sites/all/modules/draggableviews/draggableviews.api.php new file mode 100644 index 000000000..25b52d444 --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews.api.php @@ -0,0 +1,24 @@ +<?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) {} diff --git a/sites/all/modules/draggableviews/draggableviews.info b/sites/all/modules/draggableviews/draggableviews.info new file mode 100644 index 000000000..7ca651faa --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews.info @@ -0,0 +1,26 @@ +name = Draggableviews +description = Makes Views draggable +dependencies[] = views +package = Views +core = 7.x + +files[] = handlers/draggableviews_handler.inc +files[] = handlers/draggableviews_hierarchy_handler.inc +files[] = views/draggableviews_handler_field_draggable.inc +files[] = views/draggableviews_handler_sort.inc +files[] = views/draggableviews_join_handler.inc + +files[] = test/draggableviews.test + +files[] = handlers/draggableviews_handler_native.inc +files[] = handlers/draggableviews_handler_fieldapi.inc +files[] = handlers/draggableviews_hierarchy_handler_native.inc + +dependencies[] = entity + +; Information added by drupal.org packaging script on 2012-10-01 +version = "7.x-2.0+26-dev" +core = "7.x" +project = "draggableviews" +datestamp = "1349093683" + diff --git a/sites/all/modules/draggableviews/draggableviews.install b/sites/all/modules/draggableviews/draggableviews.install new file mode 100644 index 000000000..b1b3e9bc5 --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews.install @@ -0,0 +1,111 @@ +<?php + +/** + * @file + * Draggableviews defines a new database schema + * for saving the order. + */ + +/** + * Implements hook_schema(). + */ +function draggableviews_schema() { + $schema['draggableviews_structure'] = array( + 'description' => 'The table saves the order settings of an draggableview.', + 'fields' => array( + 'dvid' => array( + 'description' => 'The primary identifier.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'view_name' => array( + 'description' => 'Makes the order unique for each view.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'view_display' => array( + 'description' => 'Makes the order unique for each view display.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'args' => array( + 'description' => 'Makes the order unique for a given set of arguments', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'default' => '', + ), + 'entity_id' => array( + 'description' => 'Id of the entity that we are sorting (node, user, etc.).', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'weight' => array( + 'description' => 'The order weight.', + 'type' => 'int', + 'unsigned' => FALSE, + 'not null' => TRUE, + 'default' => 0, + ), + 'parent' => array( + 'description' => 'The order parent.', + 'type' => 'int', + 'unsigned' => FALSE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'unique keys' => array( + 'dvid' => array('dvid'), + ), + 'primary key' => array('dvid'), + ); + return $schema; +} + +/** + * Increase sizes of view_name and view_display fields of + * draggableviews_strucutre table. + */ +function draggableviews_update_7201() { + $new_field = array( + 'description' => 'Makes the order unique for each view.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ); + db_change_field('draggableviews_structure', 'view_name', 'view_name', $new_field); + + $new_field = array( + 'description' => 'Makes the order unique for each view display.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ); + db_change_field('draggableviews_structure', 'view_display', 'view_display', $new_field); +} + +/** + * Add "parent" field to draggableviews_strucutre table. + */ +function draggableviews_update_7202() { + // Commit 0fd7c9f create this as 7002(). This got rename to 7202(). Check if field exist. + if (!count(db_query("SHOW COLUMNS FROM `draggableviews_structure` WHERE Field = 'parent'")->fetchALL())) { + $spec = array( + 'description' => 'The order parent.', + 'type' => 'int', + 'unsigned' => FALSE, + 'not null' => TRUE, + 'default' => 0, + ); + db_add_field('draggableviews_structure', 'parent', $spec); + } +} diff --git a/sites/all/modules/draggableviews/draggableviews.module b/sites/all/modules/draggableviews/draggableviews.module new file mode 100644 index 000000000..9194cdc31 --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews.module @@ -0,0 +1,509 @@ +<?php + +/** + * Implements hook_views_api(). + */ +function draggableviews_views_api() { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'draggableviews') . '/views', + ); +} + +/** + * Implements hook_form_alter(). + * + * Alter views form to change button label. + */ +function draggableviews_form_alter(&$form, &$form_state, $form_id) { + if (isset($form['draggableviews']) && !empty($form['draggableviews'])) { + $view = $form_state['build_info']['args'][0]; + // Check permissions and number of results. + if (!user_access('access draggableviews') || count($view->result) < 2) { + $form['actions']['submit']['#access'] = FALSE; + return; + } + $options = $view->field['draggableviews']->options['draggableviews']; + $form['actions']['submit']['#value'] = t($options['save_button_label']); + $form['actions']['submit']['#submit'] = array('draggableviews_views_submit'); + + if ($options['ajax']) { + $form['actions']['submit']['#ajax'] = array( + 'callback' => 'draggableviews_view_draggabletable_form_ajax' + ); + } + // Set action as current path. + $form['#action'] = url(current_path()); + // Keep destination and other GET params. + if (count($_GET) > 1) { + $get = $_GET; + unset($get['q']); + if (!isset($_GET['destination'])) { + $get['destination'] = current_path() . '?' . drupal_http_build_query($get); + } + $form['#action'] .= '?' . drupal_http_build_query($get); + } + } +} + +/** + * Save weight records after form submit. + */ +function draggableviews_views_submit($form, &$form_state) { + $view = $form_state['build_info']['args'][0]; + + // Use 'input' instead of mapped 'values' here. This is done because if in + // table display we sort by header then set weights and save, we got + // totally wrong results ($form_state['values']['draggableviews'] mapped + // wrong from $form_state['input']['draggableviews']) + $form_state['values']['draggableviews'] = $form_state['input']['draggableviews']; + + // Set the weight. + $handler_object = draggableviews_get_handler_class($view->field['draggableviews']->options['draggableviews']['handler']); + $handler_object->set($form_state); + + // Trigger the event "A view has been sorted" + if (module_exists('rules')) { + rules_invoke_event('draggableviews_rules_event_sorted', $view->name, $view->current_display); + } +} + +/** + * Implementes hook_preprocess_views_view_table(). + */ +function draggableviews_preprocess_views_view_table(&$vars) { + if ($order_view = _draggableviews_load_order_view($vars['view'])) { + // Add identation if hierarchy is available. + if (!empty($order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler'])) { + $hierarchy_handler_object = draggableviews_get_handler_class($order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler'], 'hierarchy_handler'); + foreach ($vars['rows'] as $key => $row) { + $first_column = current(array_keys($row)); + $field = (object) array('view' => $vars['view']); + $depth = $hierarchy_handler_object->get_depth($field, $key); + $vars['rows'][$key][$first_column] = theme('indentation', array('size' => $depth)) . $vars['rows'][$key][$first_column]; + } + } + } + + // Check whether this table view has draggableview field. + if (!isset($vars['view']->field['draggableviews'])) { + return; + } + + // Check permissions. + if (!user_access('access draggableviews')) { + // Remove column "draggableviews" from results and header. + foreach ($vars['rows'] as &$row) { + unset($row['draggableviews']); + } + unset($vars['header']['draggableviews']); + return; + } + + // Add table class. + $vars['classes_array'][] = 'draggable'; + + // Add row class. + foreach ($vars['row_classes'] as &$row_classes) { + $row_classes[] = 'draggable'; + } + + $vars['attributes_array']['id'] = drupal_html_id('draggableviews-table-' . $vars['view']->name . '-' . $vars['view']->current_display); + // Add javascript. + drupal_add_tabledrag($vars['attributes_array']['id'], 'order', 'sibling', 'draggableviews-weight'); + + // Add javascript for autosave functionality. + if ($vars['view']->field['draggableviews']->options['draggableviews']['ajax']) { + drupal_add_js(drupal_get_path('module', 'draggableviews') . '/js/draggableviews_table.js'); + } + // Parent javascripts. + if (!empty($vars['view']->field['draggableviews']->options['draggableviews']['hierarchy_handler'])) { + drupal_add_tabledrag($vars['attributes_array']['id'], 'match', 'parent', 'draggableviews-parent', 'draggableviews-parent', 'draggableviews-id', FALSE); + drupal_add_tabledrag($vars['attributes_array']['id'], 'depth', 'group', 'draggableviews-depth', NULL, NULL, FALSE); + } +} + +/** + * Implementes hook_preprocess_views_view_list(). + */ +function draggableviews_preprocess_views_view_list(&$vars) { + // Check whether this table view has draggableview field. + if (!isset($vars['view']->field['draggableviews'])) { + return; + } + + // Check permissions. + if (!user_access('access draggableviews')) { + return; + } + + // Add class to ul item of the view. + $class = 'draggableviews-grid-' . $vars['view']->name . '-' . $vars['view']->current_display; + $vars['list_type_prefix'] = str_replace('>', ' class="' . $class . '">', $vars['list_type_prefix']); + // Add javascript. + drupal_add_library('system', 'ui.sortable'); + // Add setting of the row class. + $js_setting = array('draggableviews_row_class' => $class); + // Add setting whether ajax enabled or not. + $js_setting['draggableviews_ajax'] = $vars['view']->field['draggableviews']->options['draggableviews']['ajax']; + drupal_add_js($js_setting, 'setting'); + // Add custom js and css. + drupal_add_js(drupal_get_path('module', 'draggableviews') . '/js/draggableviews_list.js'); + drupal_add_css(drupal_get_path('module', 'draggableviews') . '/css/draggableviews_list.css'); +} + +/** + * Implements hook_permission(). + */ +function draggableviews_permission() { + return array( + 'access draggableviews' => array( + 'title' => t('Access draggable views'), + 'description' => t('Give users the right to sort their views'), + ), + ); +} + +/** + * Implements hook_ctools_plugin_type(). + */ +function draggableviews_ctools_plugin_type() { + return array( + 'handler' => array( + 'use hooks' => FALSE, + ), + 'hierarchy_handler' => array( + 'use hooks' => FALSE, + ), + ); +} + +/** + * Implements hook_ctools_plugin_directory(). + */ +function draggableviews_ctools_plugin_directory($module, $plugin) { + if (($module == 'draggableviews') && ($plugin == 'handler' || $plugin == 'hierarchy_handler')) { + return 'handlers'; + } +} + +/** + * Implements hook_contextual_links_view_alter(). + * + * Adds "Order view" contextual link. + */ +function draggableviews_contextual_links_view_alter(&$element, $items) { + // Check permission to use draggable. + if (!user_access('access draggableviews')) { + return; + } + // Do not add contextual link on view preview. + if (module_exists('views_ui') && views_ui_contextual_links_suppress()) { + return; + } + + // Add Draggableviews contextual link "Order view". + $views_ui_element = array(); + if (isset($element['#element']['#views_contextual_links_info']['views_ui'])) { + $views_ui_element = $element['#element']['#views_contextual_links_info']['views_ui']; + } + // In case of block #views_contextual_links_info element is inside of + // 'content' and not '#element' directly. + // @see http://drupal.org/node/1413596#comment-5912688 + if (empty($views_ui_element) && isset($element['#element']['content']['#views_contextual_links_info']['views_ui'])) { + $views_ui_element = $element['#element']['content']['#views_contextual_links_info']['views_ui']; + } + + if ( !empty($views_ui_element['view_display_id']) && isset($views_ui_element['view'])) { + $display_id = $views_ui_element['view_display_id']; + $view = $views_ui_element['view']; + $view->build($display_id); + + // Get the order view's path. Don't include itself. + if ($path = _draggableviews_get_order_path($view, FALSE)) { + $element['#links']['draggableviews-order'] = array( + 'title' => t('Order view'), + 'href' => $path, + 'query' => array('destination' => current_path()), + ); + } + } +} + +/** + * 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; + } +} + +/** + * Implements hook_post_render(). + */ +function draggableviews_views_post_render(&$view, &$output, &$cache) { + // Hide "Save" button when there are no results available. We cannot do this + // in form_alter hook as view is not yet executed there. + if (empty($view->result)) { + $output = str_replace('<div class="form-actions form-wrapper" id="edit-actions">', '<div class="form-actions form-wrapper" id="edit-actions" style="display:none">', $output); + } +} + +/** + * Get class of handler. + * + * @param object + * Handler's class object. + */ +function draggableviews_get_handler_class($handler_name, $handler_type = 'handler') { + $objects = &drupal_static(__FUNCTION__); + if (!isset($objects[$handler_name])) { + ctools_include('plugins'); + if ($class = ctools_plugin_load_class('draggableviews', $handler_type, $handler_name, 'handler')) { + $objects[$handler_name] = new $class(); + } + } + + return $objects[$handler_name]; +} + +/** + * Retrieve all sort plugins. + * + * Check whether handler class inherits draggablevies_handler. + * + * @return array + * Array of proper draggableviews handlers. + */ +function draggableviews_get_handlers() { + ctools_include('plugins'); + $handlers = ctools_get_plugins('draggableviews', 'handler'); + $return = array(); + foreach ($handlers as $handler_id => $handler) { + $handler_object = draggableviews_get_handler_class($handler_id); + if (in_array('draggableviews_handler', class_parents(get_class($handler_object)))) { + $return[$handler_id] = $handler_object; + } + } + return $return; +} + +/** + * Retrieve all hierarchy plugins. + * + * Check whether handler class inherits draggablevies_hierarcy_handler. + * + * @return array + * Array of proper draggableviews handlers. + */ +function draggableviews_get_hierarchy_handlers() { + ctools_include('plugins'); + $handlers = ctools_get_plugins('draggableviews', 'hierarchy_handler'); + $return = array(); + foreach ($handlers as $handler_id => $handler) { + $handler_object = draggableviews_get_handler_class($handler_id); + if (in_array('draggableviews_hierarchy_handler', class_parents(get_class($handler_object)))) { + $return[$handler_id] = $handler_object; + } + } + return $return; +} + +/** + * Ajax draggabletable submit handler. + */ +function draggableviews_view_draggabletable_form_ajax($form, $form_state) { + // Find the form element + $form_element = "form:has(input[name=form_build_id][value='{$form['form_build_id']['#value']}'])"; + + // Remove warning and asterisk. + return array('#type' => 'ajax', '#commands' => array( + ajax_command_remove("$form_element div.tabledrag-changed-warning"), + ajax_command_remove("$form_element span.tabledrag-changed"), + ajax_command_remove("$form_element div.draggableviews-changed-warning"), + ajax_command_invoke("$form_element ul.draggableviews-changed", 'removeClass', array('draggableviews-changed')), + )); +} + +/** + * Get the draggable views weight sort of a view if there is one and return its + * ID. If there are multiple of these sorts the first is returned. + * + * @param $view + * The view object. + * + * @return + * The ID of the sort or FALSE if there isn't one. + */ +function _draggableviews_get_draggable_sort($view) { + foreach ($view->sort as $id => $sort) { + if ($sort->definition['handler'] == 'draggableviews_handler_sort') { + return $id; + } + } + return FALSE; +} + +/** + * Evalutes the given PHP code, with the given variables defined. + * + * @param $code + * The PHP code to run, without <?php ?> + * @param $arguments + * Views arguments including values of exposed filters. + * @param $view + * The view being sorted. + * + * @return + * The return value of the evaled code. + */ +function _draggableviews_eval_return($code, $arguments, $view) { + return eval($code); +} + +/** + * Load built order view. + */ +function _draggableviews_load_order_view($view, $include_self = TRUE) { + if ($order_view_name_display = _draggableviews_get_order_view_display($view, $include_self)) { + if ($order_view_name_display == 'self' && $include_self) { + $order_view = $view; + } + else { + list($order_view_name, $order_view_display) = explode(':', $order_view_name_display); + if ($order_view_name == $view->name) { + $order_view = $view; + } + else { + // Need to get the order view, as order is not part of this one. + $order_view = views_get_view($order_view_name); + $order_view->build($order_view_display); + } + } + + return $order_view; + } +} + +/** + * Get the view display identifier. + * + * @param $view + * The view object + * + * @return + * A string with the view name and display id separated by ':'. + */ +function _draggableviews_get_order_view_display($view, $include_self = TRUE) { + // Proceed only if weight sort criteria is available. + if (!$sort_key = _draggableviews_get_draggable_sort($view)) { + return FALSE; + } + $order_view_display = $view->sort[$sort_key]->options['draggableviews_setting_view']; + if (empty($order_view_display)) { + return FALSE; + } + if (!$include_self) { + if ($order_view_display == 'self' || $order_view_display == $view->name . ':' . $view->current_display) { + return FALSE; + } + } + return $order_view_display; +} + +/** + * Get the path to the order view. + * + * @param $view + * The view object. + * + * @return + * The path of the page or FALSE if there isn't one. + */ +function _draggableviews_get_order_path($view, $include_self = TRUE) { + $path = FALSE; + if ($order_view = _draggableviews_load_order_view($view, $include_self)) { + if (isset($order_view->display[$order_view->current_display]->display_options['path'])) { + $path = $order_view->display[$order_view->current_display]->display_options['path']; + } + } + elseif ($include_self) { + if (isset($view->display[$view->current_display]->display_options['path'])) { + $path = $view->display[$view->current_display]->display_options['path']; + } + } + else { + return FALSE; + } + + // If page expects arguments, we provide arguments set to current view. + $args = $view->args; + if (strpos($path, '%') !== FALSE && !empty($args)) { + $new_path_array = array(); + foreach (explode('/', $path) as $path_part) { + if (strpos($path_part, '%') !== FALSE) { + $new_path_array[] = (!empty($args)) ? array_shift($args) : ''; + } + else { + $new_path_array[] = $path_part; + } + } + $path = implode('/', $new_path_array); + } + // If page path doesn't have % in the path or we still have some argument + // remain, simply append them to the end of the path. + if (!empty($args)) { + $path .= '/' . implode('/', $args); + } + return $path; +} + +/** + * Helper function that returns an option list of all draggable views or let + * you inspect a specific view to see if it's a draggable view itself and + * returns the appropriate option for that. + * + * @param $view + * The view object to incpect. Optional. + * + * @return + * An option array of draggable views. + */ +function _draggableviews_get_views_options($view = NULL) { + if (!empty($view)) { + $view_clone = clone $view; + $view_clone->set_display($view_clone->current_display); + $view_clone->init_handlers(); + if (isset($view_clone->field['draggableviews'])) { + return 'self'; + } + } + // Check whether field exists for all enabled views. We only want the + // 'setting' views. + $views = views_get_enabled_views(); + $options = array(); + + // Convert list of objects to options for the form. + foreach ($views as $view_name => $view) { + foreach ($view->display as $display_name => $display) { + if ($display_name == 'default') { + continue; + } + // Clone view and build it so we can see all the fields. + $view_clone = clone $view; + $view_clone->set_display($display_name); + $view_clone->init_handlers(); + + // If draggableviews field attached, show this view in options. + if (isset($view_clone->field['draggableviews'])) { + $options[$view_name . ':' . $display_name] = $view->human_name . ' (' . $display->display_title . ')'; + } + } + } + return $options; +} diff --git a/sites/all/modules/draggableviews/draggableviews.rules.inc b/sites/all/modules/draggableviews/draggableviews.rules.inc new file mode 100644 index 000000000..2719f7438 --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews.rules.inc @@ -0,0 +1,30 @@ +<?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; +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.info b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.info new file mode 100644 index 000000000..131f0eccb --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.info @@ -0,0 +1,13 @@ +name = Draggableviews Book +description = Reorder books +dependencies[] = draggableviews +package = Views +core = 7.x + +files[] = draggableviews_book_views_handler_argument.inc +; Information added by drupal.org packaging script on 2012-10-01 +version = "7.x-2.0+26-dev" +core = "7.x" +project = "draggableviews" +datestamp = "1349093683" + diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.install b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.install new file mode 100644 index 000000000..60fdf880c --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.install @@ -0,0 +1,10 @@ +<?php + +/** + * Implements hook_install(). + * + * Set the weight more than views. + */ +function draggableviews_book_install() { + db_query("UPDATE {system} SET weight = 11 WHERE name = 'draggableviews_book'"); +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.module b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.module new file mode 100644 index 000000000..1895a11be --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.module @@ -0,0 +1,110 @@ +<?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 weght. 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 weights on the same depth level are not the same compare them. + if (isset($item1->weight[$i]) && isset($item2->weight[$i]) && $item1->weight[$i] != $item2->weight[$i]) { + return ($item1->weight[$i] < $item2->weight[$i]) ? -1 : 1; + } + } +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views.inc b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views.inc new file mode 100644 index 000000000..2fed2c55f --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views.inc @@ -0,0 +1,32 @@ +<?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; +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views_default.inc b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views_default.inc new file mode 100644 index 000000000..6c394d3be --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book.views_default.inc @@ -0,0 +1,84 @@ +<?php + +/** + * 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['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; + /* 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; +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book_views_handler_argument.inc b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book_views_handler_argument.inc new file mode 100644 index 000000000..737abf0bc --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/draggableviews_book_views_handler_argument.inc @@ -0,0 +1,31 @@ +<?php + +/** + * 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() { + $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'); + } +} diff --git a/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_handler_book.inc b/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_handler_book.inc new file mode 100644 index 000000000..9b4fd40d9 --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_handler_book.inc @@ -0,0 +1,47 @@ +<?php + +$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'); + } + } +} + diff --git a/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_hierarchy_handler_book.inc b/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_hierarchy_handler_book.inc new file mode 100644 index 000000000..00e53abee --- /dev/null +++ b/sites/all/modules/draggableviews/draggableviews_book/handlers/draggableviews_hierarchy_handler_book.inc @@ -0,0 +1,38 @@ +<?php + +$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) ? 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) {} +} + diff --git a/sites/all/modules/draggableviews/handlers/draggableviews_handler.inc b/sites/all/modules/draggableviews/handlers/draggableviews_handler.inc new file mode 100644 index 000000000..1fbef1636 --- /dev/null +++ b/sites/all/modules/draggableviews/handlers/draggableviews_handler.inc @@ -0,0 +1,54 @@ +<?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 + * Attay 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 diff --git a/sites/all/modules/draggableviews/handlers/draggableviews_handler_fieldapi.inc b/sites/all/modules/draggableviews/handlers/draggableviews_handler_fieldapi.inc new file mode 100644 index 000000000..814d50947 --- /dev/null +++ b/sites/all/modules/draggableviews/handlers/draggableviews_handler_fieldapi.inc @@ -0,0 +1,126 @@ +<?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. Hope there is + // better way to determine them. Need to research. + $sorts = $field->view->display_handler->display->display_options['sorts']; + // If no sorts avaialble for current display, use sorts from default display. + if (empty($sorts)) { + $sorts = $field->view->display['default']->display_options['sorts']; + } + 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') { + $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 emtpy, show warning message. + 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]; + // Check whether key has table name and field name in it. + foreach ($row as $key => $value) { + if (strpos($key, $field_table) !== FALSE && strpos($key, $field_name) !== FALSE) { + return $value; + } + } + } + + 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->base_table; + $entity_info_all = entity_get_info(); + $entity_type = ''; + foreach ($entity_info_all as $entity_name_key => $entity_info) { + if ($entity_info['base table'] == $view->base_table) { + $entity_type = $entity_name_key; + break; + } + } + + $options_field = $view->field['draggableviews']->options['draggableviews']['draggableviews_handler_fieldapi']['field']; + list($field_tabe, $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); + + // Save the values of the field. + foreach ($fv['draggableviews'] as $item) { + if (isset($item['id']) && isset($item['weight'])) { + $entity = reset(entity_load($entity_type, array($item['id']))); + $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $item['weight']; + entity_save($entity_type, $entity); + } + } + } +} diff --git a/sites/all/modules/draggableviews/handlers/draggableviews_handler_native.inc b/sites/all/modules/draggableviews/handlers/draggableviews_handler_native.inc new file mode 100644 index 000000000..b39853091 --- /dev/null +++ b/sites/all/modules/draggableviews/handlers/draggableviews_handler_native.inc @@ -0,0 +1,71 @@ +<?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)) ? $row->draggableviews_structure_weight : 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']); + + $args_string = json_encode($arguments); + + // Save records to our custom table. + $weight = 0; + 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++; + } + } +} diff --git a/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler.inc b/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler.inc new file mode 100644 index 000000000..915bdb583 --- /dev/null +++ b/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler.inc @@ -0,0 +1,72 @@ +<?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 + * Attay 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; + } + } + } +} diff --git a/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler_native.inc b/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler_native.inc new file mode 100644 index 000000000..c3a4eccba --- /dev/null +++ b/sites/all/modules/draggableviews/handlers/draggableviews_hierarchy_handler_native.inc @@ -0,0 +1,31 @@ +<?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) { + $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) {} +} diff --git a/sites/all/modules/draggableviews/js/draggableviews_list.js b/sites/all/modules/draggableviews/js/draggableviews_list.js new file mode 100644 index 000000000..822994860 --- /dev/null +++ b/sites/all/modules/draggableviews/js/draggableviews_list.js @@ -0,0 +1,45 @@ +/** + * @file + * Adds draggable functionality to the html list display of the view. + */ + +(function ($) { + Drupal.behaviors.draggableViews = { + attach: function (context, settings) { + $('.views-form .' + Drupal.settings.draggableviews_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).parent().parent().find('#edit-submit').trigger('mousedown'); + } + }, + containment: 'parent', + cursor: 'move' + }); + if (Drupal.settings.draggableviews_ajax) { + $('.views-form .' + Drupal.settings.draggableviews_row_class).parent().parent().find('#edit-submit').hide(); + } + } + } +})(jQuery); diff --git a/sites/all/modules/draggableviews/js/draggableviews_table.js b/sites/all/modules/draggableviews/js/draggableviews_table.js new file mode 100644 index 000000000..2ad0de6ea --- /dev/null +++ b/sites/all/modules/draggableviews/js/draggableviews_table.js @@ -0,0 +1,37 @@ +/** + * @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.parent().find('#edit-actions input').triggerHandler('mousedown'); + // The previously dragged row is left with class styling the row + // yellow style, indicating unsaved state. To increate UX we remove + // this class with some delay to indicate that progress was made in + // the background. + $('.drag-previous').removeClass('drag-previous'); + $('<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'); + } + // Hide Save button. + $('#' + prop).parent().find('#edit-actions input').hide(); + } + } + } + } +})(jQuery); diff --git a/sites/all/modules/draggableviews/test/draggableviews.test b/sites/all/modules/draggableviews/test/draggableviews.test new file mode 100644 index 000000000..1ca36ea7f --- /dev/null +++ b/sites/all/modules/draggableviews/test/draggableviews.test @@ -0,0 +1,425 @@ +<?php + +/** + * @file + * Test cases file. + */ + +/** + * Class for testing Draggableviews module. + */ +class DraggableviewsTestCase extends DrupalWebTestCase { + + function setUp() { + parent::setUp(array('ctools', 'views', 'views_ui', 'entity', 'draggableviews', 'draggableviews_test')); + } + + /** + * Fork from drupalPost(). + * + * When action of the form determined we don't care about exposed filter + * arguments passed to the view. In this fork we use + * $this->getUrl() unconditionally. + */ + protected function drupalDraggableviewsPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { + $submit_matches = FALSE; + $ajax = is_array($submit); + if (isset($path)) { + $this->drupalGet($path, $options); + } + if ($this->parse()) { + $edit_save = $edit; + // Let's iterate over all the forms. + $xpath = "//form"; + if (!empty($form_html_id)) { + $xpath .= "[@id='" . $form_html_id . "']"; + } + $forms = $this->xpath($xpath); + foreach ($forms as $form) { + // We try to set the fields of this form as specified in $edit. + $edit = $edit_save; + $post = array(); + $upload = array(); + $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form); +// $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); + $action = $this->getUrl(); + if ($ajax) { + $action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); + // Ajax callbacks verify the triggering element if necessary, so while + // we may eventually want extra code that verifies it in the + // handleForm() function, it's not currently a requirement. + $submit_matches = TRUE; + } + + // We post only if we managed to handle every field in edit and the + // submit button matches. + if (!$edit && ($submit_matches || !isset($submit))) { + $post_array = $post; + if ($upload) { + // TODO: cURL handles file uploads for us, but the implementation + // is broken. This is a less than elegant workaround. Alternatives + // are being explored at #253506. + foreach ($upload as $key => $file) { + $file = drupal_realpath($file); + if ($file && is_file($file)) { + $post[$key] = '@' . $file; + } + } + } + else { + foreach ($post as $key => $value) { + // Encode according to application/x-www-form-urlencoded + // Both names and values needs to be urlencoded, according to + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + $post[$key] = urlencode($key) . '=' . urlencode($value); + } + $post = implode('&', $post) . $extra_post; + } + $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers)); + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + + // Replace original page output with new output from redirected page(s). + if ($new = $this->checkForMetaRefresh()) { + $out = $new; + } + $this->verbose('POST request to: ' . $path . + '<hr />Ending URL: ' . $this->getUrl() . + '<hr />Fields: ' . highlight_string('<?php ' . var_export($post_array, TRUE), TRUE) . + '<hr />' . $out); + return $out; + } + } + // We have not found a form which contained all fields of $edit. + foreach ($edit as $name => $value) { + $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value))); + } + if (!$ajax && isset($submit)) { + $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit))); + } + $this->fail(t('Found the requested form fields at @path', array('@path' => $path))); + } + } +} + +/** + * Testing Native Handler. + */ +class DraggableviewsNativeHandlerTestCase extends DraggableviewsTestCase { + + public static function getInfo() { + return array( + 'name' => 'Native handler', + 'description' => 'Test the native handler.', + 'group' => 'Draggableviews', + ); + } + + function testSort() { + $permissions = array('access content'); + $rid = $this->drupalCreateRole($permissions); + + // Create five test users. + $accounts = array(); + for ($i = 0; $i < 5; $i++) { + $edit = array(); + $edit['name'] = $this->randomName(); + // First three users should be prefixed 'test_'. + if ($i < 3) { + $edit['name'] = 'test_' . $edit['name']; + } + $edit['mail'] = $edit['name'] . '@example.com'; + $edit['roles'] = array($rid => $rid); + $edit['pass'] = user_password(); + $edit['status'] = 1; + + $account = user_save(drupal_anonymous_user(), $edit); + $account->pass_raw = $edit['pass']; + + $accounts[$account->uid] = $account; + } + + $account = $this->drupalCreateUser(array('access content', 'access draggableviews', 'access user profiles', 'access contextual links')); + $this->drupalLogin($account); + + // Now lets sort and save a view. + $edit = array( + 'draggableviews[0][weight]' => 0, + 'draggableviews[0][id]' => 1, + 'draggableviews[1][weight]' => 1, + 'draggableviews[1][id]' => 2, + 'draggableviews[2][weight]' => 2, + 'draggableviews[2][id]' => 3, + 'draggableviews[3][weight]' => 3, + 'draggableviews[3][id]' => 4, + 'draggableviews[4][weight]' => 4, + 'draggableviews[4][id]' => 5, + 'draggableviews[5][weight]' => 5, + 'draggableviews[5][id]' => 6, + 'draggableviews[6][weight]' => 6, + 'draggableviews[6][id]' => 7, + ); + $this->drupalPost('users-set', $edit, t('Save')); + + // Assert that first user is on first place, and second is on second. + $first_row = $this->xpath('//tr[@class="odd views-row-first draggable"]/td/a[@class="username"]'); + $second_row = $this->xpath('//tr[@class="even draggable"]/td/a[@class="username"]'); + $this->assertEqual((string) $first_row[0], 'placeholder-for...', t('First row user uid 1.')); + $this->assertEqual((string) $second_row[0], $accounts[2]->name, t('Second row user uid 2.')); + + // Now save a different sort (first and second rows changed places). + $edit = array( + 'draggableviews[0][weight]' => 0, + 'draggableviews[0][id]' => 2, + 'draggableviews[1][weight]' => 1, + 'draggableviews[1][id]' => 1, + 'draggableviews[2][weight]' => 2, + 'draggableviews[2][id]' => 3, + 'draggableviews[3][weight]' => 3, + 'draggableviews[3][id]' => 4, + 'draggableviews[4][weight]' => 4, + 'draggableviews[4][id]' => 5, + 'draggableviews[5][weight]' => 5, + 'draggableviews[5][id]' => 6, + 'draggableviews[6][weight]' => 6, + 'draggableviews[6][id]' => 7, + ); + $this->drupalPost('users-set', $edit, t('Save')); + // Assert that first user is on second place, and second user is on first. + $first_row = $this->xpath('//tr[@class="odd views-row-first draggable"]/td/a[@class="username"]'); + $second_row = $this->xpath('//tr[@class="even draggable"]/td/a[@class="username"]'); + $this->assertEqual((string) $first_row[0], $accounts[2]->name, t('First row user uid 2.')); + $this->assertEqual((string) $second_row[0], 'placeholder-for...', t('Second row user uid 1.')); + + // Apply exposed filter and set weights. + $edit = array( + 'draggableviews[0][weight]' => 0, + 'draggableviews[0][id]' => 4, + 'draggableviews[1][weight]' => 1, + 'draggableviews[1][id]' => 3, + 'draggableviews[2][weight]' => 2, + 'draggableviews[2][id]' => 2, + ); + $this->drupalDraggableviewsPost('users-set', $edit, t('Save'), array('query' => array('mail' => 'test'))); + + // Now lets check display view page. + $this->drupalGet('users-display'); + $first_row = $this->xpath('//tr[@class="odd views-row-first"]/td/a[@class="username"]'); + $second_row = $this->xpath('//tr[@class="even"]/td/a[@class="username"]'); + $this->assertEqual((string) $first_row[0], $accounts[2]->name, t('Display view. First row user uid 2.')); + $this->assertEqual((string) $second_row[0], 'placeholder-for...', t('Display view. Second row user uid 1.')); + + // Check display view with applied exposed filter. + $this->drupalGet('users-display', array('query' => array('mail' => 'test'))); + $first_row = $this->xpath('//tr[@class="odd views-row-first"]/td/a[@class="username"]'); + $second_row = $this->xpath('//tr[@class="even"]/td/a[@class="username"]'); + $this->assertEqual((string) $first_row[0], $accounts[4]->name, t('Display view. Exposed filter applied. First row user uid 4.')); + $this->assertEqual((string) $second_row[0], $accounts[3]->name, t('Display view. Exposed filter applied. Second row user uid 3.')); + + // Check contextual link existense. + $contextual_links = $this->xpath('//ul[@class="contextual-links views-contextual-links-page"]/li/a'); + $href = (string) $contextual_links[0]['href']; + $this->assertTrue(strpos($href, 'users-set?destination=users-display') !== FALSE, t('Contextual link exists.')); + } +} + +/** + * Testing Fielad API Handler. + */ +class DraggableviewsFieldAPIHandlerTestCase extends DraggableviewsTestCase { + + public static function getInfo() { + return array( + 'name' => 'Field API handler', + 'description' => 'Test the Field API handler.', + 'group' => 'Draggableviews', + ); + } + + public function testSort() { + $this->createField(); + + $account = $this->drupalCreateUser(array('access content', 'access draggableviews', 'access user profiles', 'access contextual links')); + $this->drupalLogin($account); + + // Create five nodes. + $nodes = array(); + for ($i = 0; $i < 5; $i++) { + $node = $this->drupalCreateNode(array('type' => 'article',)); + $nodes[$node->nid] = $node; + } + + // Now lets sort and save a view. + $edit = array( + 'draggableviews[0][weight]' => 0, + 'draggableviews[0][id]' => 1, + 'draggableviews[1][weight]' => 1, + 'draggableviews[1][id]' => 2, + 'draggableviews[2][weight]' => 2, + 'draggableviews[2][id]' => 3, + 'draggableviews[3][weight]' => 3, + 'draggableviews[3][id]' => 4, + 'draggableviews[4][weight]' => 4, + 'draggableviews[4][id]' => 5, + ); + $this->drupalPost('nodes-set', $edit, t('Save')); + // Assert that first node is on first place, and second is on second. + $first_row = $this->xpath('//tr[@class="odd views-row-first draggable"]/td/a'); + $second_row = $this->xpath('//tr[@class="even draggable"]/td/a'); + $this->assertEqual((string) $first_row[0], $nodes[1]->title, t('First row node nid 1.')); + $this->assertEqual((string) $second_row[0], $nodes[2]->title, t('Second row node nid 2.')); + + // Now save a different sort (first and second rows changed places). + $edit = array( + 'draggableviews[0][weight]' => 0, + 'draggableviews[0][id]' => 2, + 'draggableviews[1][weight]' => 1, + 'draggableviews[1][id]' => 1, + 'draggableviews[2][weight]' => 2, + 'draggableviews[2][id]' => 3, + 'draggableviews[3][weight]' => 3, + 'draggableviews[3][id]' => 4, + 'draggableviews[4][weight]' => 4, + 'draggableviews[4][id]' => 5, + ); + $this->drupalPost('nodes-set', $edit, t('Save')); + // Assert that first node is on second place, and second is on first. + $first_row = $this->xpath('//tr[@class="odd views-row-first draggable"]/td/a'); + $second_row = $this->xpath('//tr[@class="even draggable"]/td/a'); + $this->assertEqual((string) $first_row[0], $nodes[2]->title, t('First row node nid 2.')); + $this->assertEqual((string) $second_row[0], $nodes[1]->title, t('Second row node nid 1.')); + + // Check display view order. + $this->drupalGet('nodes-display'); + $first_row = $this->xpath('//tr[@class="odd views-row-first"]/td/a'); + $second_row = $this->xpath('//tr[@class="even"]/td/a'); + $this->assertEqual((string) $first_row[0], $nodes[2]->title, t('First row node nid 2.')); + $this->assertEqual((string) $second_row[0], $nodes[1]->title, t('Second row node nid 1.')); + + // Check values of nodes. + $node1 = node_load(1); + $node2 = node_load(2); + $this->assertTrue($node1->field_weight[LANGUAGE_NONE][0]['value'] > $node2->field_weight[LANGUAGE_NONE][0]['value'], t('Weight of node 1 is more than weight of node 2.')); + } + + // Create a integer field for Article nodes. + function createField() { + $field = array ( + 'translatable' => '0', + 'entity_types' => + array ( + ), + 'settings' => + array ( + ), + 'storage' => + array ( + 'type' => 'field_sql_storage', + 'settings' => + array ( + ), + 'module' => 'field_sql_storage', + 'active' => '1', + 'details' => + array ( + 'sql' => + array ( + 'FIELD_LOAD_CURRENT' => + array ( + 'field_data_field_weight' => + array ( + 'value' => 'field_weight_value', + ), + ), + 'FIELD_LOAD_REVISION' => + array ( + 'field_revision_field_weight' => + array ( + 'value' => 'field_weight_value', + ), + ), + ), + ), + ), + 'foreign keys' => + array ( + ), + 'indexes' => + array ( + ), + 'id' => '5', + 'field_name' => 'field_weight', + 'type' => 'number_integer', + 'module' => 'number', + 'active' => '1', + 'locked' => '0', + 'cardinality' => '1', + 'deleted' => '0', + 'columns' => + array ( + 'value' => + array ( + 'type' => 'int', + 'not null' => false, + ), + ), + 'bundles' => + array ( + 'node' => + array ( + 0 => 'article', + ), + ), + ); + field_create_field($field); + + $instance = array ( + 'label' => 'Weight', + 'widget' => + array ( + 'weight' => 0, + 'type' => 'number', + 'module' => 'number', + 'active' => 0, + 'settings' => + array ( + ), + ), + 'settings' => + array ( + 'min' => '', + 'max' => '', + 'prefix' => '', + 'suffix' => '', + 'user_register_form' => false, + ), + 'display' => + array ( + 'default' => + array ( + 'label' => 'above', + 'type' => 'number_integer', + 'settings' => + array ( + 'thousand_separator' => ' ', + 'decimal_separator' => '.', + 'scale' => 0, + 'prefix_suffix' => true, + ), + 'module' => 'number', + 'weight' => 11, + ), + ), + 'required' => 0, + 'description' => '', + 'default_value' => NULL, + 'id' => '7', + 'field_id' => '5', + 'field_name' => 'field_weight', + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + ); + field_create_instance($instance); + } +} diff --git a/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.info b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.info new file mode 100644 index 000000000..f38e1b0e6 --- /dev/null +++ b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.info @@ -0,0 +1,12 @@ +name = Draggableviews Test +description = Provides views for testing. +dependencies[] = draggableviews +package = Views +core = 7.x +hidden = TRUE +; Information added by drupal.org packaging script on 2012-10-01 +version = "7.x-2.0+26-dev" +core = "7.x" +project = "draggableviews" +datestamp = "1349093683" + diff --git a/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.module b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.module new file mode 100644 index 000000000..58229c311 --- /dev/null +++ b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.module @@ -0,0 +1,11 @@ +<?php + +/** + * Implements hook_views_api(). + */ +function draggableviews_test_views_api() { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'draggableviews_test'), + ); +} diff --git a/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.views_default.inc b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.views_default.inc new file mode 100644 index 000000000..b6e7ad87f --- /dev/null +++ b/sites/all/modules/draggableviews/test/draggableviews_test/draggableviews_test.views_default.inc @@ -0,0 +1,248 @@ +<?php + +/** + * @file + * Views to import for testing. + */ + +/** + * Implements hook_views_default_views(). + */ +function draggableviews_test_views_default_views() { + $view = new view; + $view->name = 'users'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'users'; + $view->human_name = 'Users'; + $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'] = 'Users'; + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['access']['perm'] = 'access user profiles'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['query']['options']['query_comment'] = FALSE; + $handler->display->display_options['query']['options']['query_tags'] = FALSE; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['pager']['options']['items_per_page'] = '10'; + $handler->display->display_options['style_plugin'] = 'table'; + /* Field: User: Name */ + $handler->display->display_options['fields']['name']['id'] = 'name'; + $handler->display->display_options['fields']['name']['table'] = 'users'; + $handler->display->display_options['fields']['name']['field'] = 'name'; + $handler->display->display_options['fields']['name']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['name']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['name']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['name']['alter']['word_boundary'] = 0; + $handler->display->display_options['fields']['name']['alter']['ellipsis'] = 0; + $handler->display->display_options['fields']['name']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['name']['alter']['trim'] = 0; + $handler->display->display_options['fields']['name']['alter']['html'] = 0; + $handler->display->display_options['fields']['name']['hide_empty'] = 0; + $handler->display->display_options['fields']['name']['empty_zero'] = 0; + $handler->display->display_options['fields']['name']['link_to_user'] = 1; + $handler->display->display_options['fields']['name']['overwrite_anonymous'] = 0; + /* Field: Draggableviews: User */ + $handler->display->display_options['fields']['draggableviews']['id'] = 'draggableviews'; + $handler->display->display_options['fields']['draggableviews']['table'] = 'users'; + $handler->display->display_options['fields']['draggableviews']['field'] = 'draggableviews'; + $handler->display->display_options['fields']['draggableviews']['element_label_colon'] = 1; + $handler->display->display_options['fields']['draggableviews']['element_default_classes'] = 0; + $handler->display->display_options['fields']['draggableviews']['hide_empty'] = 0; + $handler->display->display_options['fields']['draggableviews']['empty_zero'] = 0; + $handler->display->display_options['fields']['draggableviews']['hide_alter_empty'] = 0; + $handler->display->display_options['fields']['draggableviews']['draggableviews']['ajax'] = 0; + /* Field: User: E-mail */ + $handler->display->display_options['fields']['mail']['id'] = 'mail'; + $handler->display->display_options['fields']['mail']['table'] = 'users'; + $handler->display->display_options['fields']['mail']['field'] = 'mail'; + $handler->display->display_options['fields']['mail']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['mail']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['mail']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['mail']['alter']['external'] = 0; + $handler->display->display_options['fields']['mail']['alter']['replace_spaces'] = 0; + $handler->display->display_options['fields']['mail']['alter']['trim_whitespace'] = 0; + $handler->display->display_options['fields']['mail']['alter']['nl2br'] = 0; + $handler->display->display_options['fields']['mail']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['mail']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['mail']['alter']['more_link'] = 0; + $handler->display->display_options['fields']['mail']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['mail']['alter']['trim'] = 0; + $handler->display->display_options['fields']['mail']['alter']['html'] = 0; + $handler->display->display_options['fields']['mail']['element_label_colon'] = 1; + $handler->display->display_options['fields']['mail']['element_default_classes'] = 1; + $handler->display->display_options['fields']['mail']['hide_empty'] = 0; + $handler->display->display_options['fields']['mail']['empty_zero'] = 0; + $handler->display->display_options['fields']['mail']['hide_alter_empty'] = 1; + /* 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'] = 'users:page'; + $handler->display->display_options['sorts']['weight']['draggableviews_setting_new_items_bottom_list'] = 1; + /* Filter criterion: User: Active */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'users'; + $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; + /* Filter criterion: User: E-mail */ + $handler->display->display_options['filters']['mail']['id'] = 'mail'; + $handler->display->display_options['filters']['mail']['table'] = 'users'; + $handler->display->display_options['filters']['mail']['field'] = 'mail'; + $handler->display->display_options['filters']['mail']['operator'] = 'starts'; + $handler->display->display_options['filters']['mail']['exposed'] = TRUE; + $handler->display->display_options['filters']['mail']['expose']['operator_id'] = 'mail_op'; + $handler->display->display_options['filters']['mail']['expose']['label'] = 'E-mail'; + $handler->display->display_options['filters']['mail']['expose']['operator'] = 'mail_op'; + $handler->display->display_options['filters']['mail']['expose']['identifier'] = 'mail'; + $handler->display->display_options['filters']['mail']['expose']['required'] = 0; + $handler->display->display_options['filters']['mail']['expose']['multiple'] = FALSE; + + /* Display: Page */ + $handler = $view->new_display('page', 'Page', 'page_1'); + $handler->display->display_options['defaults']['fields'] = FALSE; + /* Field: User: Name */ + $handler->display->display_options['fields']['name']['id'] = 'name'; + $handler->display->display_options['fields']['name']['table'] = 'users'; + $handler->display->display_options['fields']['name']['field'] = 'name'; + $handler->display->display_options['fields']['name']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['name']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['name']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['name']['alter']['word_boundary'] = 0; + $handler->display->display_options['fields']['name']['alter']['ellipsis'] = 0; + $handler->display->display_options['fields']['name']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['name']['alter']['trim'] = 0; + $handler->display->display_options['fields']['name']['alter']['html'] = 0; + $handler->display->display_options['fields']['name']['hide_empty'] = 0; + $handler->display->display_options['fields']['name']['empty_zero'] = 0; + $handler->display->display_options['fields']['name']['link_to_user'] = 1; + $handler->display->display_options['fields']['name']['overwrite_anonymous'] = 0; + /* Field: User: E-mail */ + $handler->display->display_options['fields']['mail']['id'] = 'mail'; + $handler->display->display_options['fields']['mail']['table'] = 'users'; + $handler->display->display_options['fields']['mail']['field'] = 'mail'; + $handler->display->display_options['fields']['mail']['alter']['alter_text'] = 0; + $handler->display->display_options['fields']['mail']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['mail']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['mail']['alter']['external'] = 0; + $handler->display->display_options['fields']['mail']['alter']['replace_spaces'] = 0; + $handler->display->display_options['fields']['mail']['alter']['trim_whitespace'] = 0; + $handler->display->display_options['fields']['mail']['alter']['nl2br'] = 0; + $handler->display->display_options['fields']['mail']['alter']['word_boundary'] = 1; + $handler->display->display_options['fields']['mail']['alter']['ellipsis'] = 1; + $handler->display->display_options['fields']['mail']['alter']['more_link'] = 0; + $handler->display->display_options['fields']['mail']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['mail']['alter']['trim'] = 0; + $handler->display->display_options['fields']['mail']['alter']['html'] = 0; + $handler->display->display_options['fields']['mail']['element_label_colon'] = 1; + $handler->display->display_options['fields']['mail']['element_default_classes'] = 1; + $handler->display->display_options['fields']['mail']['hide_empty'] = 0; + $handler->display->display_options['fields']['mail']['empty_zero'] = 0; + $handler->display->display_options['fields']['mail']['hide_alter_empty'] = 1; + $handler->display->display_options['path'] = 'users-display'; + + /* Display: Set Page */ + $handler = $view->new_display('page', 'Set Page', 'page'); + $handler->display->display_options['path'] = 'users-set'; + + $views[$view->name] = $view; + + $view = new view; + $view->name = 'nodes'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'node'; + $view->human_name = 'nodes'; + $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'] = 'nodes'; + $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['query']['options']['query_comment'] = FALSE; + $handler->display->display_options['query']['options']['query_tags'] = FALSE; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['pager']['options']['items_per_page'] = '10'; + $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']['alter_text'] = 0; + $handler->display->display_options['fields']['title']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['title']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = 0; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = 0; + $handler->display->display_options['fields']['title']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['title']['alter']['trim'] = 0; + $handler->display->display_options['fields']['title']['alter']['html'] = 0; + $handler->display->display_options['fields']['title']['hide_empty'] = 0; + $handler->display->display_options['fields']['title']['empty_zero'] = 0; + $handler->display->display_options['fields']['title']['link_to_node'] = 1; + /* 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_label_colon'] = 1; + $handler->display->display_options['fields']['draggableviews']['element_default_classes'] = 0; + $handler->display->display_options['fields']['draggableviews']['hide_empty'] = 0; + $handler->display->display_options['fields']['draggableviews']['empty_zero'] = 0; + $handler->display->display_options['fields']['draggableviews']['hide_alter_empty'] = 0; + $handler->display->display_options['fields']['draggableviews']['draggableviews']['handler'] = 'draggableviews_handler_fieldapi'; + $handler->display->display_options['fields']['draggableviews']['draggableviews']['ajax'] = 0; + $handler->display->display_options['fields']['draggableviews']['draggableviews']['draggableviews_handler_fieldapi'] = array( + 'field' => 'field_data_field_weight:field_weight_value', + ); + /* Sort criterion: Content: Weight (field_weight) */ + $handler->display->display_options['sorts']['field_weight_value']['id'] = 'field_weight_value'; + $handler->display->display_options['sorts']['field_weight_value']['table'] = 'field_data_field_weight'; + $handler->display->display_options['sorts']['field_weight_value']['field'] = 'field_weight_value'; + /* 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: Set Page */ + $handler = $view->new_display('page', 'Set Page', 'page'); + $handler->display->display_options['path'] = 'nodes-set'; + + /* Display: Display Page */ + $handler = $view->new_display('page', 'Display Page', 'page_1'); + $handler->display->display_options['defaults']['fields'] = FALSE; + /* 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']['alter_text'] = 0; + $handler->display->display_options['fields']['title']['alter']['make_link'] = 0; + $handler->display->display_options['fields']['title']['alter']['absolute'] = 0; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = 0; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = 0; + $handler->display->display_options['fields']['title']['alter']['strip_tags'] = 0; + $handler->display->display_options['fields']['title']['alter']['trim'] = 0; + $handler->display->display_options['fields']['title']['alter']['html'] = 0; + $handler->display->display_options['fields']['title']['hide_empty'] = 0; + $handler->display->display_options['fields']['title']['empty_zero'] = 0; + $handler->display->display_options['fields']['title']['link_to_node'] = 1; + $handler->display->display_options['path'] = 'nodes-display'; + + $views[$view->name] = $view; + + return $views; +} diff --git a/sites/all/modules/draggableviews/views/draggableviews.views.inc b/sites/all/modules/draggableviews/views/draggableviews.views.inc new file mode 100644 index 000000000..4a347278f --- /dev/null +++ b/sites/all/modules/draggableviews/views/draggableviews.views.inc @@ -0,0 +1,56 @@ +<?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', + ), + ); + $data['draggableviews_structure']['parent'] = array( + 'title' => t('Parent'), + 'help' => t('The parent entity id.'), + 'group' => t('Draggableviews'), + 'field' => array( + 'handler' => 'views_handler_field_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', + ); + } + } +} diff --git a/sites/all/modules/draggableviews/views/draggableviews_handler_field_draggable.inc b/sites/all/modules/draggableviews/views/draggableviews_handler_field_draggable.inc new file mode 100644 index 000000000..9f4b130f1 --- /dev/null +++ b/sites/all/modules/draggableviews/views/draggableviews_handler_field_draggable.inc @@ -0,0 +1,199 @@ +<?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()); + } + 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 fielset 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 fielset 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) { + if (user_access('access draggableviews')) { + 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; + } + + $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' => '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' => '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' => 'draggableviews-depth'), + ); + } + } + } +} \ No newline at end of file diff --git a/sites/all/modules/draggableviews/views/draggableviews_handler_sort.inc b/sites/all/modules/draggableviews/views/draggableviews_handler_sort.inc new file mode 100644 index 000000000..1931473a3 --- /dev/null +++ b/sites/all/modules/draggableviews/views/draggableviews_handler_sort.inc @@ -0,0 +1,112 @@ +<?php + +/** + * @file + * Native handler sort. + */ + +/** + * Sort handler for ordering by weight. + */ +class draggableviews_handler_sort extends views_handler_sort { + function query() { + $this->ensure_my_table(); + // If new items should be placed in the bottom. + if ($this->options['draggableviews_setting_new_items_bottom_list']) { + // New items will get big default instead of NULL + $alias = $this->table_alias . '_' . $this->field . '_coalesce'; + $this->query->add_field(NULL, "COALESCE($this->table_alias.$this->field, 10000)", $alias); + $this->query->orderby[] = array( + 'field' => $alias, + 'direction' => drupal_strtoupper($this->options['order']) + ); + } + else { + // New items will be placed at the top as have NULL value. + $this->query->add_orderby($this->table_alias, $this->real_field); + } + } + + function option_definition() { + $options = parent::option_definition(); + + // This handler invokes few times for one view, + // in the first time the $this->view->name is empty, + // so we need this check. + if (is_object($this->view)) { + $options['draggableviews_setting_view'] = array('default' => $this->view->name); + } + else { + $options['draggableviews_setting_view'] = array('default' => 'self'); + } + + $options['draggableviews_setting_arguments'] = array('default' => 'all'); + $options['draggableviews_setting_arguments_php'] = array('default' => ''); + $options['draggableviews_setting_new_items_bottom_list'] = array('default' => TRUE); + + return $options; + } + + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + $form['expose_button']['#access'] = FALSE; + $form['order']['#description'] = t('Please remember to override settings of the sort criterion if you have display that sets weights and you choose descendling order.'); + + // Check whether current views display doesn't have draggableviews field. + // If it has, it means that this is setting view so we should set + // option draggableviews_setting_view to 'self' + $options = _draggableviews_get_views_options($this->view); + + // If it is setting view. + if (!is_array($options)) { + $form['order']['#access'] = FALSE; + $options = isset($this->options['draggableviews_setting_view']) ? $this->options['draggableviews_setting_view'] : 'self'; + $form['draggableviews_setting_view'] = array( + '#type' => 'value', + '#value' => $options, + ); + } + else { + $form['draggableviews_setting_view'] = array( + '#type' => 'select', + '#title' => t('Display sort as'), + '#default_value' => $this->options['draggableviews_setting_view'], + '#options' => $options, + '#description' => t('Please choose the view and display that sets the order.') + ); + // If there is no setting views available, show error message. + if (empty($options)) { + drupal_set_message(t('First you should create a view that sets sorting order.'), 'error'); + } + } + + $form['draggableviews_setting_arguments'] = array( + '#title' => t('Arguments handling'), + '#type' => 'radios', + '#options' => array( + 'all' => t('Use all arguments'), + 'none' => t('Do not use any arguments (use empty arguments)'), + 'php' => t('Prepare arguments with PHP code'), + ), + '#default_value' => $this->options['draggableviews_setting_arguments'], + '#description' => t('When sorting order is saved all arguments passed are saved with order. In display view we can choose how to use these arguments.') + ); + $form['draggableviews_setting_arguments_php'] = array( + '#title' => t('PHP code to prepare arguments'), + '#type' => 'textarea', + '#default_value' => $this->options['draggableviews_setting_arguments_php'], + '#description' => t('Enter the php code to prepare the arguments. Do not enter <?php ?>. The following variables are available - $view (the view), $arguments (existing arguments - manipulate these to alter the arguments used to sort).'), + '#states' => array( + 'visible' => array( + 'input[name="options[draggableviews_setting_arguments]"]' => array('value' => 'php'), + ), + ), + ); + $form['draggableviews_setting_new_items_bottom_list'] = array( + '#type' => 'checkbox', + '#title' => t('New items appear bottom of the list'), + '#description' => t('New items means elements (for example nodes) that do not have saved weight (newly created).'), + '#default_value' => $this->options['draggableviews_setting_new_items_bottom_list'], + ); + } +} diff --git a/sites/all/modules/draggableviews/views/draggableviews_join_handler.inc b/sites/all/modules/draggableviews/views/draggableviews_join_handler.inc new file mode 100644 index 000000000..9a5451539 --- /dev/null +++ b/sites/all/modules/draggableviews/views/draggableviews_join_handler.inc @@ -0,0 +1,83 @@ +<?php + +/** + * @file + * Native handler join handler. + */ + +/** + * Join handler for extra join conditions. + */ +class draggableviews_join_handler extends views_join { + /** + * Build the SQL for the join this object represents. + */ + function build_join($select_query, $table, $view_query) { + $view = $view_query->view; + + if (empty($this->definition['table formula'])) { + $right_table = $this->table; + } + else { + $right_table = $this->definition['table formula']; + } + + if ($this->left_table) { + $left = $view_query->get_table_info($this->left_table); + $left_field = "$left[alias].$this->left_field"; + } + else { + // This can be used if left_field is a formula or something. It should be used only *very* rarely. + $left_field = $this->left_field; + } + + $condition = "$left_field = $table[alias].$this->field"; + + // Check whether setting view is set. + $arguments = array(); + $weight_key = _draggableviews_get_draggable_sort($view); + if (!empty($view->sort[$weight_key]->options['draggableviews_setting_view'])) { + $condition .= " AND $table[alias].view_name = :view_name"; + $condition .= " AND $table[alias].view_display = :view_display"; + + // If it is setting view, set current view name and display name. + if ($view->sort[$weight_key]->options['draggableviews_setting_view'] == 'self') { + $arguments[':view_name'] = $view->name; + $arguments[':view_display'] = $view->current_display; + } + else { + list($setting_view_name, $setting_view_display) = explode(':', $view->sort[$weight_key]->options['draggableviews_setting_view']); + $arguments[':view_name'] = $setting_view_name; + $arguments[':view_display'] = $setting_view_display; + } + + // Arguments passed to view (including exposed filters). + $view_arguments = $view->args; + if (isset($view->exposed_raw_input)) { + $view_arguments += $view->exposed_raw_input; + ksort($view_arguments); + } + // Alter arguments according to sort criteria settings. + if ($view->sort[$weight_key]->options['draggableviews_setting_arguments'] == 'none') { + $view_arguments = array(); + } + // If PHP arguments processing is set. + if ($view->sort[$weight_key]->options['draggableviews_setting_arguments'] == 'php') { + $clone_view = clone $view; + $view_arguments = _draggableviews_eval_return($view->sort[$weight_key]->options['draggableviews_setting_arguments_php'], $view_arguments, $clone_view); + } + $condition .= " AND $table[alias].args = :view_arguments"; + $arguments[':view_arguments'] = json_encode($view_arguments); + } + + $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments); + + // Add also parent field. + if ($order_view = _draggableviews_load_order_view($view)) { + if (isset($order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler']) + && $order_view->field['draggableviews']->options['draggableviews']['hierarchy_handler'] == 'draggableviews_hierarchy_handler_native') { + $select_query->addField($table['alias'], 'parent', 'draggableviews_structure_parent'); + } + } + } +} \ No newline at end of file -- GitLab