FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
151
sites/all/modules/contrib/admin/workflow/CHANGELOG.txt
Normal file
151
sites/all/modules/contrib/admin/workflow/CHANGELOG.txt
Normal file
@@ -0,0 +1,151 @@
|
||||
Since 7.x-1.0
|
||||
Performance improvements.
|
||||
Major restructure of admin UI.
|
||||
Workflow form detached from comment form.
|
||||
Workflow status and form are fields that can be controlled under "Manage display."
|
||||
|
||||
Issue #1783854 by antojose: Allow setting comment in Rules.
|
||||
Issue #1891446 by interX: Allow grouped filters.
|
||||
Issue #1922262 by NancyDru, hwold: Fix join. misc. improvements.
|
||||
Issue #1540824 by NancyDru: Improve Admin UI.
|
||||
Issue #1908520 by NancyDru: Set state of pre-existing nodes.
|
||||
Issue #1922422 by NancyDru: Correct state name creation in Tokens.
|
||||
Issue #155547 by NancyDru: Tab form should call hook_workflow().
|
||||
Issue #1818424 by NancyDru: Add doc for hook_workflow_operations.
|
||||
Issue #1918424 by NancyDru: More complete api doc.
|
||||
Get rid of temp.patch.
|
||||
Issue #385038 by flyingkiki, NancyDru: Fix double history. Minor corrections.
|
||||
Issue #1802216 by NancyDru: Improve scheduling time form section.
|
||||
Issue #1418622 by NancyDru: Fix tokens during transition.
|
||||
Issue #1784092 by wamilton: stop double saving nodes.
|
||||
remove accidental file
|
||||
Issue #1900488 by NancyDru: load comment in node_load. #1904740 by NancyDru: suppress form at terminal state. Performance improvements.
|
||||
Issue #1532646 by wonder95: Add search api module.
|
||||
Issue #1493012 by kid_icarus: Better views.
|
||||
Issue #1900480 by NancyDru for Dave Reid: Move tokens stuff to separate file.
|
||||
Issue #1418622 by Dave Reid: Fix tokens.
|
||||
Issue #1427006 by NancyDru: Fix transition rule.
|
||||
Issue #1893542 by NancyDru: Transition form by perms.
|
||||
Issue #1893724 by NancyDru: transition form by perms.
|
||||
Issue #1895712 by NancyDru: Fix form when only one state.
|
||||
Issue #1896422 by NancyDru: Fix token message.
|
||||
Correct theming in form.
|
||||
Minor corrections to README
|
||||
Issue #1856180 by NancyDru: Correct update to not fail.
|
||||
Issue #1475930 by Bastlynn.
|
||||
Issue #1874400 by Nancydru.
|
||||
Issue #1884630 by NancyDru.
|
||||
Issue #1891374 by NancyDru.
|
||||
Coder finds.
|
||||
Issue #1550274 by dynamicdan, gofs: egregious typo
|
||||
Adding features include on revert so it's avail during install / enable.
|
||||
UI improvement in Rules interface when there are more than one Workflows available.
|
||||
Issue #1471014 by Bastlynn: removing node_save from workflow tab.
|
||||
Issue #558378 by hefox: Features round 2 cleanup.
|
||||
Issue #1426844 by onegenius: Correcting for missing history information errs.
|
||||
Issue #1424008 by Bastlynn: handling no workflows setup w/ Workflow Views turned on.
|
||||
Issue #1421518 by DuaelFr: Workflow access warnings on new nodes
|
||||
Issue #1468810 by firewolf, Morten Najbjerg, sbrege Strict warnings re: render and form
|
||||
Issue #1469798 by Bastlynn: errs thrown on rules change when old state = new state.
|
||||
Issue #1468810 Timezones in scheduler
|
||||
Issue #1426844 by Bastlynn: Adding catch re: propery of last_history object being empty when node is not a workflow content type.
|
||||
Issue #1424008 by Bastlynn: Accounting for trying to go to views pg with workflow_views active, but no workflows in system.
|
||||
Issue #1400352 by Bastlynn: Fixing an error appearing udner E_STRICT in admin UI re: creating a default object.
|
||||
Something got wierd betwen the push/pull for this commit so trying that again.
|
||||
Issue #1386430 by dylanhuang (patch by dozymoe): Views filter shows unknown when state selected in multi-workflow environment.
|
||||
Issue #1386430 by dylanhuang (patch by dozymoe): Views filter shows unknown when state selected in multi-workflow environment.
|
||||
Correcting for doubled ,, in install schema.
|
||||
Issue #1405688 by wonder95: Path length correction for trigger path.
|
||||
Issue #1405688 by hefox: Coder standards.
|
||||
Issue #1924004 by evaldas.uzkuras: Fix bad variable.
|
||||
Fix typo in api. Add hook_requirements to show that worflows are active.
|
||||
Issue #1924174 by NancyDru: Fix menu error on state delete.
|
||||
Issue 1925162 by fgm: Call time reference error.
|
||||
Issue #1924182 by NancyDru: Correct initial state name in edit form.
|
||||
Issue #1926746 by shenzhuxi: Fix some php warnings.
|
||||
Issue #1781308 by NancyDru: Change node form to vertical tab.
|
||||
Issue #1926556 by NancyDru: Back to transition on node creation.
|
||||
Issue #1550992 by chadhester: Add Previous Comment Author for Views.
|
||||
Issue #1926800 by NancyDru: Fix operations links.
|
||||
Issue #1865754 by NancyDru for leelooch: Add reminder to state add form.
|
||||
Issue #1540412 by NancyDru: Allow timezones for transition scheduling.
|
||||
Issue #1930638 by NancyDru, shenzhuxi: Undefined variable.
|
||||
Issue #1540824 by NancyDru: Improve Admin UI.
|
||||
Issue #1931032 by NancyDru: Add missing js file.
|
||||
Issue #1931778 by NancyDru: Add breadcrumbs to access page.
|
||||
Add admin ui css.
|
||||
Issue #1350900 by loze: list states grouped by workflow.
|
||||
Issue #1540824 by NancyDru: More Admin UI improvements.
|
||||
Issue #1931948 by NancyDru: Use menu loaders to simplify menu.
|
||||
Issue #1347116 by NancyDru: New Workflow_Cleanup module.
|
||||
Issue #1509568 by fgm: Access control export.
|
||||
Issue #1933466 by fgm: Remove Admin UI warning, repair redirection on workflow_access admin submit.
|
||||
Issue #1768350 by NancyDru: Correct test and message for type page.
|
||||
Issue #1380954 by Bastlyn, DRippstein: Add node_actions triggers.
|
||||
Issue #1909922 by NancyDru: Add warning on Access UI page about content permissions.
|
||||
Issue #1691870 by berenddeboer: Allow Rules to bypass permissions on transition.
|
||||
Issue #1540914 by NancyDru: Pass parms in node form; allow comment alter at transition.
|
||||
Issue #1744612 by NancyDru: Move VBO stuff into its own module.
|
||||
Issue #1944574 by NancyDru: Undefined index.
|
||||
|
||||
Since 7.x-1.1-rc1
|
||||
Issue #785194 by arcovia: Access to tab form.
|
||||
Issue #785194 by NancyDru: Better access to tab form.
|
||||
Issue #1691870 by NancyDru: Rules record workflow comment.
|
||||
Issue #1691870 by NancyDru: Transition check wrong choices on Force.
|
||||
Issue #1951164 by Kristen Pol: Fix left join order.
|
||||
Issue #1951718 by NancyDru: Clean up history records.
|
||||
Issue #1953642 by NancyDru: Add history row alter feature.
|
||||
Issue #1953642 by NancyDru: Add state revert feature.
|
||||
Issue #1953642 by NancyDru: Document hook_workflow_history_alter().
|
||||
Issue #1484126 by NancyDru, nathangervais: Check if node type has a workflow before adding WF info.
|
||||
Issue #1957074 by andypost: Allow Views to show Sid value.
|
||||
Address some @TODO items.
|
||||
|
||||
Since 7.x-1.1
|
||||
Issue #1961156 by mErilainen: Missing translate in Views.
|
||||
Issue #1962344 by NancyDru, manu77: Missing $node->workflow.
|
||||
Issue #1962512 by NancyDru: Don't let (creation) be changed.
|
||||
Issue #1961426 by andypost: Fix reference error.
|
||||
Issue #1893420 by JackVVo: Fix node name.
|
||||
Issue #1966292 by aBrookland: Undefined index.
|
||||
Issue #1970538 by Tim Asplin: Improve Features support.
|
||||
Issue #1589254 by DuaelFr: Improve Features support.
|
||||
Issue #1972728 by NancyDru: Deal with HTML entities and translations.
|
||||
Issue #1967794 by NancyDru, dforegger: Remove static cache.
|
||||
Issue #1966804 by andypost: Improve workflow_get_workflow_node_history_by_nid.
|
||||
Issue #1970846 by NancyDru: Allow multiple workflow_tab_forms on the same page.
|
||||
Issue #1974828 by martysteer: Fix missing brackets in query.
|
||||
Replace all SELECT * with column names. (Remove @TODO)
|
||||
Issue #1976992 by bdone, NancyDru: Make watchdog logging optional.
|
||||
Issue #1976942 by dforegger: Fix transition import.
|
||||
Issue #1984338 by NancyDru: Fix node_load looping.
|
||||
Issue #1995004 by NancyDru: Fix form issue in admin.
|
||||
Issue #1975058 by NancyDru: Revert change to node_form on comments.
|
||||
Issue #1986220 by NancyDru: Add scheduling user's id to history.
|
||||
Automatically give "Participate in Workflow" permission to new roles.
|
||||
Issue #1984338 by NancyDru: Pass $force to hook_workflow().
|
||||
Issue #1996892 by Kristen Pol: Add previous state to Rules.
|
||||
Issue #1979562 by NancyDru: workflow_tab_form not found.
|
||||
Issue #1974624 by NancyDru: Make sure all hook calls have two params.
|
||||
Issue #1971504 by NancyDru: Skip node form vertical tab if only one transition possible.
|
||||
Issue #1997242 by NancyDru: Hook to check if transition is allowed.
|
||||
Issue #1443166 by NancyDru: Clean up monster query (Oracle issue).
|
||||
Issue #2001980 by NancyDru: Revert checks if permitted.
|
||||
Issue #1856180 by JayMN: Correct typo in update.
|
||||
Issue #2009312 by NancyDru: Remove Workflow module form field.
|
||||
Issue #457348 by NancyDru: Remove view mode check.
|
||||
Fix terminal state check.
|
||||
Issue #1997242 by NancyDru: Recheck permitted transitions before execution.
|
||||
Issue #1781308 by NancyDru: Revert change to vertical tab.
|
||||
Issue #1997242 by Tim-Erwin: Honor force on transition permitted.
|
||||
Issue #2019125 by Nancydru: Add access priority setting.
|
||||
Issue #2018959 by dev team data: Fix feature revert error.
|
||||
Issue #2012516 by maximpodorov: Add state system name and sid to theme functions
|
||||
Issue #2022381 by justanothermark: Fix array key collision
|
||||
Issue #2022333 by NancyDru: Add another common permission warning.
|
||||
Issue #2023233 by NancyDru: Fix double encoding.
|
||||
Issue #1993408 by NancyDru: Default new states to active.
|
||||
Issue #1542188 by justanothermark: Fix workflow display.
|
||||
Issue #2027507 by NancyDru: Fix sorting issue on history page.
|
||||
Issue #1993408 by NancyDru: Sysid not always set correctly.
|
339
sites/all/modules/contrib/admin/workflow/LICENSE.txt
Normal file
339
sites/all/modules/contrib/admin/workflow/LICENSE.txt
Normal file
@@ -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.
|
113
sites/all/modules/contrib/admin/workflow/README.txt
Normal file
113
sites/all/modules/contrib/admin/workflow/README.txt
Normal file
@@ -0,0 +1,113 @@
|
||||
********************************************************************
|
||||
D R U P A L M O D U L E
|
||||
********************************************************************
|
||||
Name: Workflow Module
|
||||
Author: John VanDyk
|
||||
Maintainers: Mark Fredrickson <mark.m.fredrickson at gmail dot com>
|
||||
John VanDyk drupal.org/user/2375
|
||||
Bastlynn http://drupal.org/user/275249
|
||||
Nancy Wichmann (NancyDru) http://drupal.org/user/101412
|
||||
Drupal: 7
|
||||
********************************************************************
|
||||
DESCRIPTION:
|
||||
|
||||
The workflow module enables you to create arbitrary workflows in
|
||||
Drupal and associate them with node types.
|
||||
|
||||
Workflows are made up of workflow states.
|
||||
|
||||
Moving from one state to another is called a transition.
|
||||
|
||||
Actions are associated with transitions (actions.module was used
|
||||
for this in Drupal 5; core actions support is in Drupal 6).
|
||||
|
||||
Alex Reisner introduced role-based permissions for workflow states
|
||||
and generally enhanced this module.
|
||||
|
||||
********************************************************************
|
||||
INSTALLATION:
|
||||
|
||||
1. Place the entire workflow directory into your Drupal
|
||||
sites/all/modules directory (or appropriate alternative).
|
||||
|
||||
2. Enable the workflow module by navigating to:
|
||||
|
||||
Administer > Site building > Modules
|
||||
|
||||
Enabling the workflow module will create the necessary database
|
||||
tables for you.
|
||||
|
||||
3. If you wish to use the administrative UI, then enable the
|
||||
Workflow UI module. There are several other optional modules
|
||||
that you may also enable, if needed.
|
||||
|
||||
4. If you want anyone besides the administrative user to be able
|
||||
to configure workflows (usually a bad idea), they must be given
|
||||
the "administer workflow" access permission:
|
||||
|
||||
Administer > User management > Permissions
|
||||
|
||||
When the module is enabled and the user has the "administer
|
||||
workflow" permission, a "Workflow" menu should appear in the
|
||||
menu system under Administer -> Site building.
|
||||
|
||||
You may also allow only some users to schedule transitions. Select
|
||||
the "schedule workflow transitions" permission to allow transitions.
|
||||
|
||||
********************************************************************
|
||||
GETTING STARTED:
|
||||
|
||||
Let's create a new workflow. Click on Administer -> Configuration ->
|
||||
Workflow -> Workflow and click on the "Add workflow" tab.
|
||||
|
||||
We'll start simple. Call our workflow "Draft-Done" and click Add Workflow.
|
||||
|
||||
Now lets add some workflow states to our workflow. Click "add state" and
|
||||
enter "draft" and click the Add State button. Do the same for "done".
|
||||
|
||||
So we've got a workflow with two states, "draft" and "done". Now we
|
||||
have to tell each state which other states it can move to. With only
|
||||
two states, this is easy. Click on the "edit" link to edit the workflow
|
||||
and see its states.
|
||||
|
||||
The "From / To -->" column lists all states. To the right are columns
|
||||
for each state. Within each cell is a list of roles with checkboxes.
|
||||
|
||||
This is confusing. It's easiest to understand if you read rows
|
||||
across from the left. For example, we start with the creation
|
||||
state. Who may move a node from its creation state to the "draft"
|
||||
state? Well, the author of the node, for one. So check the "author"
|
||||
checkbox.
|
||||
|
||||
Who may move the node from the "draft" state to the "done" state?
|
||||
This is up to you. If you want authors to be able to do this,
|
||||
check the "author" checkbox under the "done" state. If you had
|
||||
another role, say "editor", that you wanted to give the ability
|
||||
to decree a node as "done", you'd check the checkbox next to
|
||||
the "editor" role and not the author role. In this scenario authors
|
||||
would turn in drafts and editors would say when they are "done".
|
||||
|
||||
Be sure to click the Save button to save your settings.
|
||||
|
||||
Now let's tell Drupal which node types should use this workflow. Click
|
||||
on Administer -> Configuration -> Workflow -> Workflow. Let's assign
|
||||
the Draft-Done workflow to the article node type and click Save Workflow
|
||||
Mapping.
|
||||
|
||||
Now we could add an action (previously configured using the trigger
|
||||
module). Click on the Actions link above
|
||||
your workflow. Add the action to the transition.
|
||||
|
||||
Now create a new article by going to Create content -> article. If there
|
||||
is no sign of a workflow interface here, don't panic. The interface
|
||||
is only displayed if there is more than one state to which the user
|
||||
can move the node (why bother the user with a form with only one
|
||||
selection?) Click Submit to create the article.
|
||||
|
||||
You can see the state the node is in and the history of state changes
|
||||
by clicking on the Workflow tab while viewing a node.
|
||||
|
||||
Changing the state to "done" and clicking Submit will fire the action
|
||||
you set up earlier.
|
||||
|
||||
********************************************************************
|
39
sites/all/modules/contrib/admin/workflow/UPDATE.txt
Normal file
39
sites/all/modules/contrib/admin/workflow/UPDATE.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Update from Drupal 6 to Drupal 7 Instructions
|
||||
|
||||
* Update Rules that use Workflow
|
||||
|
||||
UPDATE FROM DRUPAL 6 TO DRUPAL 7 INSTRUCTIONS
|
||||
------------
|
||||
|
||||
Upgrading from 6 to 7 for Workflow using this module is fairly simple. The tables and formats of Drupal 6 configurations have not been changed, nor have the names of tokens or of Views filters and plugins.
|
||||
|
||||
The only major change in the Drupal 7 update was to the Rules integration of Workflow. The following steps should lead you through the process:
|
||||
|
||||
1) Follow the standard process of updating core from Drupal 6 to Drupal 7. This includes making sure you are on the latest version core for Drupal 6 and latest versions of all Drupal 6 modules. Turn off all contributed modules. Make backups of all files and database tables and run update.php. (See the directions at drupal.org for a more in-depth approach to this step.)
|
||||
|
||||
2) Remove the previous version of the Workflow module from your modules directory.
|
||||
|
||||
3) Unzip the updated version of Workflow and replace the old module.
|
||||
|
||||
4) Turn your modules back on and run update.php.
|
||||
|
||||
At this point your Workflows should be in place on your site. Assuming you are making use of the additional modules, your views will be using the updated View integrations for Workflow and Workflow Tokens will be in place.
|
||||
|
||||
UPDATE RULES THAT USE WORKFLOW
|
||||
------------
|
||||
|
||||
Rules for Drupal 7 underwent major changes. Thankfully there is a built in update script for Rules to allow configurations created in previous versions of Rules to be updated and saved as new rules, but it is not perfect. To update your pre-existing Rules configurations that used Workflows:
|
||||
|
||||
1) Go to the Rules upgrade page at admin/config/workflow/rules/upgrade
|
||||
|
||||
2) Select the rule(s) you want to convert to the latest version of Rules
|
||||
|
||||
3) Under "Method" select "Convert configuration and save it". IMPORTANT: With the changes to Workflow's rules integration the configurations will not function properly on export / import. Save the configuration to the database to get around this.
|
||||
|
||||
4) IMPORTANT: Because the conversion is not perfect, check through each of your rules configurations for conditions and actions. You will need to change configurations for workflow states from "data selection" to "direct input" mode and re-configure the proper values to compare the given node against.
|
||||
|
||||
Updated to Drupal 7 by Bastlynn http://drupal.org/user/275249
|
63
sites/all/modules/contrib/admin/workflow/workflow.api.php
Normal file
63
sites/all/modules/contrib/admin/workflow/workflow.api.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the workflow module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_workflow().
|
||||
*
|
||||
* @param $op
|
||||
* The current workflow operation: 'transition permitted', 'transition pre' or 'transition post'.
|
||||
* @param $old_state
|
||||
* The state ID of the current state.
|
||||
* @param $new_state
|
||||
* The state ID of the new state.
|
||||
* @param $node
|
||||
* The node whose workflow state is changing.
|
||||
* @param $force
|
||||
* The caller indicated that the transition should be forced. (bool).
|
||||
* This is only available on the "pre" and "post" calls.
|
||||
*/
|
||||
function hook_workflow($op, $old_state, $new_state, $node, $force = FALSE) {
|
||||
switch ($op) {
|
||||
case 'transition permitted':
|
||||
// The workflow module does nothing during this operation.
|
||||
// This operation occurs when the list of available transitions
|
||||
// is built. Your module's implementation could return FALSE
|
||||
// here and disallow the presentation of the choice.
|
||||
break;
|
||||
|
||||
case 'transition pre':
|
||||
// The workflow module does nothing during this operation.
|
||||
// But your module's implementation of the workflow hook could
|
||||
// return FALSE here and veto the transition.
|
||||
break;
|
||||
|
||||
case 'transition post':
|
||||
break;
|
||||
|
||||
case 'transition delete':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_history_alter().
|
||||
* Add an 'undo' operation for the most recent history change.
|
||||
*
|
||||
* @param $variables
|
||||
* The current workflow history information as an array.
|
||||
* 'old_sid' - The state ID of the previous state.
|
||||
* 'old_state_name' - The state name of the previous state.
|
||||
* 'sid' - The state ID of the current state.
|
||||
* 'state_name' - The state name of the current state.
|
||||
* 'history' - The row from the workflow_node_history table.
|
||||
*
|
||||
* If you want to add additional data, such as an operation link,
|
||||
* place it in the 'extra' value.
|
||||
*/
|
||||
function hook_workflow_history_alter(&$variables) {
|
||||
// The Worflow module does nothing with this hook.
|
||||
// For an example implementation, see the Workflow Revert add-on.
|
||||
}
|
270
sites/all/modules/contrib/admin/workflow/workflow.features.inc
Normal file
270
sites/all/modules/contrib/admin/workflow/workflow.features.inc
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Intergrates workflow with features.
|
||||
*/
|
||||
|
||||
define('WORKFLOW_FEATURES_AUTHOR_NAME', 'workflow_features_author_name');
|
||||
|
||||
/**
|
||||
* Workflows are a **faux-exportable** component.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function workflow_features_export($data, &$export, $module_name = '') {
|
||||
// fontyourface_default_fonts integration is provided by Features.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$export['dependencies']['workflow'] = 'workflow';
|
||||
foreach (workflow_get_workflows() as $workflow) {
|
||||
if (in_array($workflow->name, $data)) {
|
||||
$export['features']['workflow'][$workflow->name] = $workflow->name;
|
||||
}
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function workflow_features_export_render($module, $data) {
|
||||
$translatables = $code = array();
|
||||
$code[] = ' $workflows = array();';
|
||||
$code[] = '';
|
||||
|
||||
$workflows = workflow_get_workflows();
|
||||
foreach ($data as $name) {
|
||||
if ($workflow = workflow_get_workflows_full_object($name)) {
|
||||
unset($workflow->wid);
|
||||
$workflow_export = features_var_export($workflow, ' ');
|
||||
$workflow_identifier = features_var_export($workflow->name);
|
||||
$code[] = " // Exported workflow: $name";
|
||||
$code[] = " \$workflows[{$workflow_identifier}] = {$workflow_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
}
|
||||
|
||||
$code[] = ' return $workflows;';
|
||||
$code = implode("\n", $code);
|
||||
return array('workflow_default_workflows' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function workflow_features_export_options() {
|
||||
$workflows = array();
|
||||
foreach (workflow_get_workflows() as $workflow) {
|
||||
$workflows[$workflow->name] = $workflow->name;
|
||||
}
|
||||
return $workflows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function workflow_features_revert($module) {
|
||||
// Including the features inc to make sure this func is available during install of a Features module.
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
foreach (features_get_default('workflow', $module) as $key => $workflow) {
|
||||
workflow_update_workflows_full_object($workflow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_rebuild().
|
||||
*/
|
||||
function workflow_features_export_rebuild($module) {
|
||||
workflow_features_revert($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD style functions below.
|
||||
*/
|
||||
|
||||
/**
|
||||
* For use by CRUD only, save everything from the CRUD formed object.
|
||||
*
|
||||
* @see workflow_get_workflows_full_object
|
||||
*
|
||||
* @param $workflow
|
||||
* A fully loaded workflow object to save the states of.
|
||||
*
|
||||
* @return
|
||||
* Returns whether the workflow was saved fully.
|
||||
*/
|
||||
function workflow_update_workflows_full_object($workflow) {
|
||||
$workflow = (object) $workflow;
|
||||
// Given a workflow in the format returned from export.
|
||||
// First we grab the states, transitions and node_maps out.
|
||||
$states = isset($workflow->states) ? $workflow->states : array();
|
||||
$transitions = isset($workflow->transitions) ? $workflow->transitions : array();
|
||||
$node_types = isset($workflow->node_types) ? $workflow->node_types : array();
|
||||
unset($workflow->states, $workflow->transitions, $workflow->node_types);
|
||||
|
||||
// Then make a workflow so we can track by wid.
|
||||
if ($orig_workflow = workflow_get_workflows_by_name($workflow->name)) {
|
||||
$workflow->wid = $orig_workflow->wid;
|
||||
}
|
||||
|
||||
workflow_update_workflows($workflow, FALSE);
|
||||
|
||||
// Cancel out if workflow failed to save.
|
||||
if (!isset($workflow->wid) || empty($workflow->wid)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Workflow is now a fully vetted workflow object. We have NOT created a creation state with this.
|
||||
// Then make states, marking state name to state sid.
|
||||
$active_states = array();
|
||||
foreach ($states as $state) {
|
||||
$state = (object) $state;
|
||||
$state->wid = $workflow->wid;
|
||||
if ($orig_state = reset(workflow_get_workflow_states_by_wid_state($state->wid, $state->state))) {
|
||||
$state->sid = $orig_state->sid;
|
||||
}
|
||||
workflow_update_workflow_states($state);
|
||||
$active_states[$state->state] = $state->sid;
|
||||
}
|
||||
|
||||
// Delete any states *not* in our original construction.
|
||||
foreach (workflow_get_workflow_states_by_wid($workflow->wid) as $state) {
|
||||
if (!in_array($state->sid, $active_states)) {
|
||||
workflow_delete_workflow_states_by_sid($state->sid);
|
||||
}
|
||||
}
|
||||
|
||||
// Then make transitions with the state mapping.
|
||||
$active_transitions = array();
|
||||
foreach ($transitions as $transition) {
|
||||
$transition = (object) $transition;
|
||||
$transition->sid = $active_states[$transition->state];
|
||||
$transition->target_sid = $active_states[$transition->target_state];
|
||||
// Roles are exported by rolename, so need to translate to RID.
|
||||
$transition->roles = !empty($transition->roles) ? _workflow_roles_to_rids($transition->roles) : '';
|
||||
workflow_update_workflow_transitions($transition);
|
||||
$active_transitions[] = $transition->tid;
|
||||
}
|
||||
|
||||
// Delete any transitions in our workflow that are *not* in our original construction.
|
||||
foreach (workflow_get_workflow_transitions_by_wid($workflow->wid) as $transition) {
|
||||
if (!in_array($transition->tid, $active_transitions)) {
|
||||
workflow_delete_workflow_transitions_by_tid($transition->tid);
|
||||
}
|
||||
}
|
||||
// Then add the node_type mapping.
|
||||
foreach ($node_types as $node_type) {
|
||||
$node_type = (object) array(
|
||||
'type' => $node_type,
|
||||
'wid' => $workflow->wid
|
||||
);
|
||||
// Insert, nodes only have one workflow. Insert will delete any prior workflow assoc.
|
||||
workflow_insert_workflow_type_map($node_type);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use by CRUD only, gather everything into the CRUD formed object.
|
||||
*
|
||||
* @param $name
|
||||
* A string corresponding to a workflow object.
|
||||
*
|
||||
* @return
|
||||
* A fully loaded workflow object with type and statue mappings.
|
||||
*/
|
||||
function workflow_get_workflows_full_object($name) {
|
||||
if ($workflow = workflow_get_workflows_by_name($name)) {
|
||||
// Now we need to add data to the object for each state, an array of sub-objects.
|
||||
$options = array('status' => 1); // We only want active states for this export.
|
||||
$active_states = array();
|
||||
foreach (workflow_get_workflow_states_by_wid($workflow->wid, $options) as $index => $state) {
|
||||
$active_states[$state->sid] = $state->state;
|
||||
// Remove what we don't need to export.
|
||||
unset($state->sid);
|
||||
unset($state->wid);
|
||||
$workflow->states[] = $state;
|
||||
}
|
||||
|
||||
// Now we need to add data to the export for each transition, an array of sub-objects.
|
||||
// Same goes for transitions, see above re: states.
|
||||
foreach ($active_states as $sid => $state) {
|
||||
// We're going to look everythign up by the start state, not state involved, to avoid dupes.
|
||||
foreach (workflow_get_workflow_transitions_by_sid($sid, $options) as $transition) {
|
||||
// And to get the target state (by name) we need to look it up too.
|
||||
$target_state = workflow_get_workflow_states_by_sid($transition->target_sid);
|
||||
$transition->state = $state;
|
||||
$transition->target_state = $target_state->state;
|
||||
unset($transition->sid, $transition->target_sid);
|
||||
// Translate to role names so works cross install.
|
||||
$transition->roles = !empty($transition->roles) ? _workflow_rids_to_roles($transition->roles) : '';
|
||||
// Remove what we don't need to export.
|
||||
unset($transition->tid);
|
||||
$workflow->transitions[] = $transition;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to add data to the export for each type map, an array of sub-objects.
|
||||
// Same goes for node mappings, see above re: states.
|
||||
foreach (workflow_get_workflow_type_map_by_wid($workflow->wid) as $index => $type_map) {
|
||||
$workflow->node_types[] = $type_map->type;
|
||||
}
|
||||
}
|
||||
return $workflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally cache the user roles as core doesn't.
|
||||
*/
|
||||
function _workflow_user_roles($reset = FALSE) {
|
||||
$roles = &drupal_static(__FUNCTION__);
|
||||
if ($reset || !isset($roles)) {
|
||||
$roles = user_roles();
|
||||
}
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a role string to RIDs for importing.
|
||||
*
|
||||
* @param $role_string
|
||||
* A string of roles or fake 'author' role.
|
||||
*
|
||||
* @return
|
||||
* A string of RIDs seperated by commas.
|
||||
*/
|
||||
function _workflow_roles_to_rids($role_string) {
|
||||
$roles = _workflow_user_roles();
|
||||
$rid_array = array();
|
||||
foreach (explode(',', $role_string) as $role_name) {
|
||||
if ($role_name === WORKFLOW_FEATURES_AUTHOR_NAME) {
|
||||
$rid_array[] = 'author';
|
||||
}
|
||||
elseif ($role_name && in_array($role_name, $roles)) {
|
||||
$rid_array[] = array_search($role_name, $roles);
|
||||
}
|
||||
}
|
||||
return implode(',', $rid_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string of rids to role names for exporting.
|
||||
*
|
||||
* @param $rid_string
|
||||
* A string of rids or fake 'author' role.
|
||||
*
|
||||
* @return
|
||||
* A string of role names seperated by commas.
|
||||
*/
|
||||
function _workflow_rids_to_roles($rid_string) {
|
||||
$roles = _workflow_user_roles();
|
||||
$rid_array = explode(',', $rid_string);
|
||||
// There may be a role named 'author', so make 'author' distinct.
|
||||
$return = in_array('author', $rid_array) ? WORKFLOW_FEATURES_AUTHOR_NAME . ',' : '';
|
||||
// Translate RIDs to rolenames.
|
||||
$return .= implode(',', array_intersect_key($roles, array_flip($rid_array)));
|
||||
return trim($return, ',');
|
||||
}
|
12
sites/all/modules/contrib/admin/workflow/workflow.info
Normal file
12
sites/all/modules/contrib/admin/workflow/workflow.info
Normal file
@@ -0,0 +1,12 @@
|
||||
name = Workflow
|
||||
description = "Allows the creation and assignment of arbitrary workflows to node types."
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
files[] = workflow.pages.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
379
sites/all/modules/contrib/admin/workflow/workflow.install
Normal file
379
sites/all/modules/contrib/admin/workflow/workflow.install
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the workflow module.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function workflow_install() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function workflow_uninstall() {
|
||||
variable_del('workflow_states_per_page');
|
||||
// Delete type-workflow mapping variables.
|
||||
foreach (node_type_get_types() as $type => $name) {
|
||||
variable_del('workflow_' . $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function workflow_schema() {
|
||||
$schema['workflows'] = array(
|
||||
'description' => 'Workflows',
|
||||
'fields' => array(
|
||||
'wid' => array(
|
||||
'description' => 'The primary identifier for a node.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE
|
||||
),
|
||||
'name' => array(
|
||||
'description' => 'The name of the workflow (used as machine name for features intergration).',
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'not null' => TRUE,
|
||||
'default' => ''
|
||||
),
|
||||
'tab_roles' => array(
|
||||
'description' => 'The role IDs that can access the workflow tabs on node pages.',
|
||||
'type' => 'varchar',
|
||||
'length' => '60',
|
||||
'not null' => TRUE,
|
||||
'default' => ''
|
||||
),
|
||||
'options' => array(
|
||||
'description' => 'Additional settings for the workflow.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE
|
||||
),
|
||||
),
|
||||
'primary key' => array('wid'),
|
||||
'unique keys' => array('name' => array('name')),
|
||||
);
|
||||
$schema['workflow_type_map'] = array(
|
||||
'fields' => array(
|
||||
'type' => array(
|
||||
'description' => 'The {node_type}.type the workflow is used on.',
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'not null' => TRUE,
|
||||
'default' => ''
|
||||
),
|
||||
'wid' => array(
|
||||
'description' => 'The {workflows}.wid this record affects.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10'
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'type' => array('type', 'wid'),
|
||||
),
|
||||
);
|
||||
$schema['workflow_transitions'] = array(
|
||||
'fields' => array(
|
||||
'tid' => array(
|
||||
'description' => 'The primary identifier for a workflow transition.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE
|
||||
),
|
||||
'sid' => array(
|
||||
'description' => 'The {workflow_states}.sid start state.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10'
|
||||
),
|
||||
'target_sid' => array(
|
||||
'description' => 'The {workflow_states}.sid target state.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10'
|
||||
),
|
||||
'roles' => array(
|
||||
'description' => 'The {role}.sid that a user must have to perform transition.',
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tid'),
|
||||
'indexes' => array(
|
||||
'sid' => array('sid'),
|
||||
'target_sid' => array('target_sid'),
|
||||
),
|
||||
);
|
||||
$schema['workflow_states'] = array(
|
||||
'fields' => array(
|
||||
'sid' => array(
|
||||
'description' => 'The primary identifier for a workflow state.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'wid' => array(
|
||||
'description' => 'The {workflows}.wid this state is part of.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10'
|
||||
),
|
||||
'state' => array(
|
||||
'description' => 'The name of the state.',
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'weight' => array(
|
||||
'description' => 'The weight (order) of the state.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '4',
|
||||
),
|
||||
'sysid' => array(
|
||||
'description' => 'The type of state, usually either WORKFLOW_CREATION or empty.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '4',
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'Whether the current state is active still.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
'disp-width' => '4',
|
||||
),
|
||||
),
|
||||
'primary key' => array('sid'),
|
||||
'indexes' => array(
|
||||
'sysid' => array('sysid'),
|
||||
'wid' => array('wid')
|
||||
),
|
||||
);
|
||||
$schema['workflow_scheduled_transition'] = array(
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this transition is scheduled for.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'old_sid' => array(
|
||||
'description' => 'The {workflow_states}.sid this state starts at.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'sid' => array(
|
||||
'description' => 'The {workflow_states}.sid this state transitions to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The user who scheduled this state transition.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'scheduled' => array(
|
||||
'description' => 'The date this transition is scheduled for.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'comment' => array(
|
||||
'description' => 'The comment explaining this transition.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid')
|
||||
),
|
||||
);
|
||||
$schema['workflow_node_history'] = array(
|
||||
'fields' => array(
|
||||
'hid' => array(
|
||||
'description' => 'The unique ID for this record.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE
|
||||
),
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record is for.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'old_sid' => array(
|
||||
'description' => 'The {workflow_states}.sid this transition started as.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE, '
|
||||
default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'sid' => array(
|
||||
'description' => 'The {workflow_states}.sid this transition transitioned to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The {users}.uid who made this transition.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'stamp' => array(
|
||||
'description' => 'The unique stamp for this transition.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'comment' => array(
|
||||
'description' => 'The comment explaining this transition.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('hid'),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid', 'sid'),
|
||||
),
|
||||
);
|
||||
$schema['workflow_node'] = array(
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record is for.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'sid' => array(
|
||||
'description' => 'The {workflow_states}.sid that this node is currently in.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The {users}.uid who triggered this state.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
),
|
||||
'stamp' => array(
|
||||
'description' => 'The unique stamp for the transition.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '11',
|
||||
),
|
||||
),
|
||||
'primary key' => array('nid'),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid', 'sid'),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require highest 6.x release.
|
||||
*/
|
||||
function workflow_update_last_removed() {
|
||||
return 6101;
|
||||
}
|
||||
|
||||
/**
|
||||
* Table update from 6 to 7. Adding a unique key for fields (already held unique in code).
|
||||
*/
|
||||
function workflow_update_7000() {
|
||||
if (!db_index_exists('workflows', 'name')) {
|
||||
db_add_unique_key('workflows', 'name', array('name'));
|
||||
}
|
||||
if (!db_index_exists('workflow_states', 'wid_state')) {
|
||||
db_add_unique_key('workflow_states', 'wid_state', array('wid', 'state'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all workflows to show watchdog messages.
|
||||
*/
|
||||
function workflow_update_7001() {
|
||||
// Get all workflows.
|
||||
$workflows = workflow_get_workflows();
|
||||
|
||||
foreach ($workflows as $workflow) {
|
||||
// Add the option.
|
||||
$workflow->options['watchdog_log'] = 1;
|
||||
|
||||
// Serialize the options array.
|
||||
$workflow->options = serialize($workflow->options);
|
||||
|
||||
// Update the workflow without creating a creation state.
|
||||
workflow_update_workflows($workflow, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add userid to scheduled transition table.
|
||||
*/
|
||||
function workflow_update_7002() {
|
||||
db_add_field('workflow_scheduled_transition', 'uid', array(
|
||||
'description' => 'The user who scheduled this state transition.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'disp-width' => '10',
|
||||
'initial' => 0,
|
||||
));
|
||||
}
|
15
sites/all/modules/contrib/admin/workflow/workflow.js
Normal file
15
sites/all/modules/contrib/admin/workflow/workflow.js
Normal file
@@ -0,0 +1,15 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Custom summary for the module vertical tab.
|
||||
*/
|
||||
Drupal.behaviors.workflow_node_formFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
// Use the fieldset id to identify the vertical tab element
|
||||
$('fieldset#edit-workflow', context).drupalSetSummary(function (context) {
|
||||
return Drupal.checkPlain($('.form-item-workflow-scheduled input:checked', context).next('label').text());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
1819
sites/all/modules/contrib/admin/workflow/workflow.module
Normal file
1819
sites/all/modules/contrib/admin/workflow/workflow.module
Normal file
File diff suppressed because it is too large
Load Diff
259
sites/all/modules/contrib/admin/workflow/workflow.pages.inc
Normal file
259
sites/all/modules/contrib/admin/workflow/workflow.pages.inc
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide user interface for changing workflow state.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback. Display workflow summary of a node.
|
||||
*/
|
||||
function workflow_tab_page($node = NULL) {
|
||||
drupal_set_title($node->title);
|
||||
$workflow = workflow_get_workflow_type_map_by_type($node->type);
|
||||
$states = array();
|
||||
$state_system_names = array();
|
||||
foreach (workflow_get_workflow_states() as $data) {
|
||||
$states[$data->sid] = check_plain(t($data->state));
|
||||
$state_system_names[$data->sid] = check_plain($data->state);
|
||||
}
|
||||
$deleted_states = array();
|
||||
$deleted_state_system_names = array();
|
||||
$options = array('status' => 0);
|
||||
foreach (workflow_get_workflow_states($options) as $data) {
|
||||
$deleted_states[$data->sid] = check_plain(t($data->state));
|
||||
$deleted_state_system_names[$data->sid] = check_plain($data->state);
|
||||
}
|
||||
$current = workflow_node_current_state($node);
|
||||
|
||||
// theme_workflow_history_current_state() must run state through check_plain().
|
||||
$current_state = theme('workflow_history_current_state', array('state_name' => $states[$current], 'state_system_name' => $state_system_names[$current], 'sid' => $current));
|
||||
|
||||
$output = theme('workflow_current_state', array('state' => $states[$current], 'state_system_name' => $state_system_names[$current], 'sid' => $current));
|
||||
|
||||
// Show the form to allow state changing.
|
||||
// But only if we are not at the terminal state.
|
||||
$choices = workflow_field_choices($node);
|
||||
if (count($choices) != 0 || count($choices) != 1 || $current != key($choices)) {
|
||||
$form = drupal_get_form('workflow_tab_form', $node, $workflow->wid, $states, $current);
|
||||
$output .= drupal_render($form);
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach (workflow_get_workflow_node_history_by_nid($node->nid) as $history) {
|
||||
if ($history->sid == $current && !isset($deleted_states[$history->sid]) && !isset($current_themed)) {
|
||||
// Theme the current state differently so it stands out.
|
||||
$state_name = theme('workflow_history_current_state', array('state_name' => $states[$history->sid], 'state_system_name' => $state_system_names[$history->sid], 'sid' => $history->sid));
|
||||
// Make a note that we have themed the current state; other times in the history
|
||||
// of this node where the node was in this state do not need to be specially themed.
|
||||
$current_themed = TRUE;
|
||||
}
|
||||
elseif (isset($deleted_states[$history->sid])) {
|
||||
// The state has been deleted, but we include it in the history.
|
||||
$state_name = theme('workflow_deleted_state', array('state_name' => $deleted_states[$history->sid], 'state_system_name' => $deleted_state_system_names[$history->sid], 'sid' => $history->sid));
|
||||
$footer_needed = TRUE;
|
||||
}
|
||||
else {
|
||||
// Regular state.
|
||||
$state_name = check_plain(t($states[$history->sid]));
|
||||
}
|
||||
if (isset($deleted_states[$history->old_sid])) {
|
||||
$old_state_name = theme('workflow_deleted_state', array('state_name' => $deleted_states[$history->old_sid], 'state_system_name' => $deleted_state_system_names[$history->old_sid], 'sid' => $history->old_sid));
|
||||
$footer_needed = TRUE;
|
||||
}
|
||||
elseif (isset($states[$history->old_sid])) {
|
||||
$old_state_name = check_plain(t($states[$history->old_sid]));
|
||||
}
|
||||
else {
|
||||
$old_state_name = '*';
|
||||
}
|
||||
|
||||
$variables = array(
|
||||
'history' => $history,
|
||||
'old_sid' => $history->old_sid,
|
||||
'old_state_name' => $old_state_name,
|
||||
'sid' => $history->sid,
|
||||
'uid' => $history->uid,
|
||||
'state_name' => $state_name,
|
||||
);
|
||||
|
||||
// Allow other modules to modify the row.
|
||||
drupal_alter('workflow_history', $variables);
|
||||
|
||||
$rows[] = theme('workflow_history_table_row', $variables);
|
||||
}
|
||||
|
||||
// Mark the first and last rows.
|
||||
$rows[0]['class'][] = 'first';
|
||||
$last = count($rows) - 1;
|
||||
$rows[$last]['class'][] = 'last';
|
||||
|
||||
// Only display the table if there's anything in it.
|
||||
if ($rows) {
|
||||
$output .= theme('workflow_history_table', array('rows' => $rows, 'footer' => !empty($footer_needed)));
|
||||
$output .= theme('pager', array('tags' => variable_get('workflow_states_per_page', 20)));
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme one workflow history table row.
|
||||
*
|
||||
* $old_state_name and $state_name must be run through check_plain(t()) prior
|
||||
* to calling this theme function.
|
||||
*/
|
||||
function theme_workflow_history_table_row($variables) {
|
||||
$row = array();
|
||||
$old_state_name = $variables['old_state_name'];
|
||||
$state_name = $variables['state_name'];
|
||||
$history = $variables['history'];
|
||||
$account = user_load($variables['uid']);
|
||||
$row = array(
|
||||
'data' => array(
|
||||
array('data' => format_date($history->stamp), 'class' => array('timestamp')),
|
||||
array('data' => $old_state_name, 'class' => array('previous-state-name')),
|
||||
array('data' => $state_name, 'class' => array('state-name')),
|
||||
array('data' => theme('username', array('account' => $account)), 'class' => array('user-name')),
|
||||
array('data' => filter_xss($history->comment), 'class' => array('log-comment')),
|
||||
),
|
||||
'class' => array('workflow_history_row'),
|
||||
);
|
||||
|
||||
if (!empty($variables['extra'])) {
|
||||
$row['data'][] = $variables['extra'];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme entire workflow history table.
|
||||
*/
|
||||
function theme_workflow_history_table($variables) {
|
||||
$rows = $variables['rows'];
|
||||
$footer = $variables['footer'];
|
||||
$headers = array(t('Date'), t('Old State'), t('New State'), t('By'), t('Comment'));
|
||||
$output = theme('table', array('header' => $headers, 'rows' => $rows, 'caption' => t('Workflow History')));
|
||||
if ($footer) {
|
||||
$output .= t('*State is no longer available.');
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme the current state in the workflow history table.
|
||||
*/
|
||||
function theme_workflow_history_current_state($variables) {
|
||||
return check_plain(t($variables['state_name']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme a deleted state in the workflow history table.
|
||||
*/
|
||||
function theme_workflow_deleted_state($variables) {
|
||||
return check_plain(t($variables['state_name'])) . '*';
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder. Allow workflow state change and scheduling from workflow tab.
|
||||
*
|
||||
* @param $node
|
||||
* Node for which workflow information will be displayed.
|
||||
* @param $wid
|
||||
* ID of workflow to display.
|
||||
* @param $states
|
||||
* Array of states for the workflow.
|
||||
* @param $current
|
||||
* Current workflow state of this node.
|
||||
* @return
|
||||
* Form definition array.
|
||||
*/
|
||||
function workflow_tab_form($form, $form_state, $node, $wid, $states, $current) {
|
||||
// Tell FAPI where this form is.
|
||||
form_load_include($form_state, 'inc', 'workflow', 'workflow.pages');
|
||||
|
||||
// Let's make sure we should be here.
|
||||
if (workflow_node_tab_access($node) === FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form['#tab'] = TRUE;
|
||||
$choices = workflow_field_choices($node);
|
||||
$min = ($states[$current] == t('(creation)') ? 1 : 2);
|
||||
|
||||
// Only build form if user has possible target state(s).
|
||||
if (count($choices) >= $min) {
|
||||
$workflow = workflow_get_workflow_type_map_by_type($node->type);
|
||||
$workflow = workflow_get_workflows_by_wid($workflow->wid);
|
||||
|
||||
$form['#wf'] = $workflow;
|
||||
$form['#choices'] = $choices;
|
||||
|
||||
$name = t($workflow->name);
|
||||
$timestamp = '';
|
||||
$comment = '';
|
||||
|
||||
// See if scheduling information is present.
|
||||
if (!empty($node->workflow_scheduled_timestamp) && !empty($node->workflow_scheduled_sid)) {
|
||||
global $user;
|
||||
if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) {
|
||||
$timezone = $user->timezone;
|
||||
}
|
||||
else {
|
||||
$timezone = variable_get('date_default_timezone', 0);
|
||||
}
|
||||
// The default value should be the upcoming sid.
|
||||
$current = $node->workflow_scheduled_sid;
|
||||
$timestamp = $node->workflow_scheduled_timestamp;
|
||||
$comment = $node->workflow_scheduled_comment;
|
||||
}
|
||||
|
||||
// Include the same form elements here that are included on a
|
||||
// regular node editing page. $form is modified by reference.
|
||||
workflow_node_form($form, $form_state, t('Change !name state', array('!name' => $name)), $name, $current, $choices, $timestamp, $comment);
|
||||
|
||||
$form['node'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $node,
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update workflow'),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the form on the workflow tab.
|
||||
*
|
||||
* @see workflow_tab_form()
|
||||
*/
|
||||
function workflow_tab_form_submit($form, &$form_state) {
|
||||
// The entire node object was stashed in the form.
|
||||
$node = $form_state['values']['node'];
|
||||
|
||||
if (isset($form_state['values']['workflow'])) {
|
||||
$node->workflow = $form_state['values']['workflow'];
|
||||
$node->workflow_comment = isset($form_state['values']['workflow_comment']) ?
|
||||
$form_state['values']['workflow_comment'] : '';
|
||||
|
||||
if (!empty($form_state['values']['workflow_scheduled'])) {
|
||||
$node->workflow_scheduled = $form_state['values']['workflow_scheduled'];
|
||||
}
|
||||
if (!empty($form_state['values']['workflow_scheduled_date'])) {
|
||||
$node->workflow_scheduled_date = $form_state['values']['workflow_scheduled_date'];
|
||||
}
|
||||
if (!empty($form_state['values']['workflow_scheduled_hour'])) {
|
||||
$node->workflow_scheduled_hour = $form_state['values']['workflow_scheduled_hour'];
|
||||
}
|
||||
if (!empty($form_state['values']['workflow_scheduled_timezone'])) {
|
||||
$node->workflow_scheduled_timezone = $form_state['values']['workflow_scheduled_timezone'];
|
||||
}
|
||||
}
|
||||
// ALERT: Rules that use node_save to check the node transition are going to be missed if
|
||||
// the tab form is used to check for the change. It is *always* better practice to use
|
||||
// the transition change itself as your value to check for changes with Rules and other
|
||||
// behaviors. Do NOT rely on node_save() to drive transition changes.
|
||||
workflow_transition($node, $node->workflow);
|
||||
}
|
229
sites/all/modules/contrib/admin/workflow/workflow.tokens.inc
Normal file
229
sites/all/modules/contrib/admin/workflow/workflow.tokens.inc
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Tokens hooks for Workflow module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function workflow_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
$langcode = !empty($options['language']->language) ? $options['language']->language : LANGUAGE_NONE;
|
||||
$date = REQUEST_TIME;
|
||||
|
||||
if (($type == 'node' || $type == 'workflow') && !empty($data['node']) && !empty($data['node']->nid)) {
|
||||
$node = $data['node'];
|
||||
|
||||
// Get a list of all possible states for returning state names.
|
||||
$states = workflow_get_workflow_states_all();
|
||||
|
||||
if ($workflow = workflow_get_workflow_type_map_by_type($node->type)) {
|
||||
// @TODO: If we ever move Workflow to allow M:M content types to workflows, this will need updating.
|
||||
if ($workflow = workflow_get_workflows_by_wid($workflow->wid)) {
|
||||
$wid = $workflow->wid;
|
||||
$last_history = workflow_get_recent_node_history($node->nid);
|
||||
|
||||
if (isset($node->workflow) && !isset($node->workflow_stamp)) {
|
||||
// The node is being submitted but the form data has not been saved to the database yet,
|
||||
// so we set the token values from the workflow form fields.
|
||||
$sid = $node->workflow;
|
||||
$old_sid = isset($last_history->old_sid) ? $last_history->old_sid : workflow_get_creation_state_by_wid($workflow->wid);
|
||||
$user_name = $node->uid ? (isset($node->name) ? $node->name
|
||||
: variable_get('anonymous', 'Anonymous'))
|
||||
: variable_get('anonymous', 'Anonymous');
|
||||
$uid = $node->uid;
|
||||
$account = user_load($node->uid);
|
||||
$mail = ($node->uid && isset($node->user_mail)) ? $node->user_mail : '';
|
||||
$comment = isset($node->workflow_comment) ? $node->workflow_comment : '';
|
||||
}
|
||||
else {
|
||||
if (empty($last_history)) {
|
||||
// If the node has no workflow history,
|
||||
// the node is being inserted and will soon be transitioned to the first valid state.
|
||||
// We find this state using the same logic as workflow_nodeapi().
|
||||
if ($choices = workflow_field_choices($node)) {
|
||||
// @TODO: Some users, like anonymous, may not be allowed
|
||||
// to cause transitions, so there will be no choices.
|
||||
$keys = array_keys($choices);
|
||||
$sid = array_shift($keys);
|
||||
$old_sid = workflow_get_creation_state_by_wid($workflow->wid);
|
||||
}
|
||||
else {
|
||||
// What to choose?
|
||||
$sid = $node->workflow;
|
||||
$old_sid = $workflow->creation_state;
|
||||
}
|
||||
$sid = workflow_get_workflow_states_by_sid($sid)->sid;
|
||||
$old_sid = workflow_get_workflow_states_by_sid($old_sid)->sid;
|
||||
$user_name = $node->uid ? $node->name : variable_get('anonymous', 'Anonymous');
|
||||
$uid = $node->uid;
|
||||
$account = user_load($node->uid);
|
||||
$mail = ($node->uid && isset($node->user_mail)) ? $node->user_mail : '';
|
||||
$comment = isset($node->workflow_comment) ? $node->workflow_comment : '';
|
||||
}
|
||||
else {
|
||||
// Sometimes there is no workflow set (edit?).
|
||||
if (!isset($node->workflow)) {
|
||||
$node->workflow = $last_history->sid;
|
||||
}
|
||||
// Is a transition in progress?
|
||||
if ($node->workflow != $last_history->sid) {
|
||||
$sid = $node->workflow;
|
||||
$old_sid = $last_history->sid;
|
||||
$date = $node->workflow_stamp;
|
||||
$uid = $node->uid;
|
||||
$account = user_load($node->uid);
|
||||
}
|
||||
else {
|
||||
// Not a transition.
|
||||
$sid = $last_history->sid;
|
||||
$old_sid = $last_history->old_sid;
|
||||
$date = $last_history->stamp;
|
||||
$uid = $last_history->uid;
|
||||
$account = user_load($last_history->uid);
|
||||
}
|
||||
|
||||
// Default to the most recent transition data in the workflow history table.
|
||||
$user_name = $account->uid ? $account->name : variable_get('anonymous', 'Anonymous');
|
||||
$mail = $account->uid ? $account->mail : '';
|
||||
$comment = $last_history->comment;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'workflow-name':
|
||||
$replacements[$original] = $sanitize ? check_plain($workflow->name) : $workflow->name;
|
||||
break;
|
||||
case 'workflow-current-state-name':
|
||||
$replacements[$original] = $sanitize ? check_plain($states[$sid]) : $states[$sid];
|
||||
break;
|
||||
case 'workflow-old-state-name':
|
||||
$replacements[$original] = $sanitize ? check_plain($states[$old_sid]) : $states[$old_sid];
|
||||
break;
|
||||
case 'workflow-current-state-updating-user-name':
|
||||
$name = format_username($account);
|
||||
$replacements[$original] = $sanitize ? check_plain($name) : $name;
|
||||
break;
|
||||
case 'workflow-current-state-updating-user-link':
|
||||
$replacements[$original] = theme('username', array('account' => $account));
|
||||
break;
|
||||
case 'workflow-current-state-updating-user-uid':
|
||||
// User IDs are integers only and do not need sanitization.
|
||||
$replacements[$original] = $uid;
|
||||
break;
|
||||
case 'workflow-current-state-updating-user-mail':
|
||||
$replacements[$original] = $sanitize ? check_plain($account->mail) : $account->mail;
|
||||
break;
|
||||
case 'workflow-current-state-updating-user-mailto':
|
||||
$replacements[$original] = '<a href="mailto:' . check_plain($account->mail) . '">' . check_plain($user_name) . '</a>';
|
||||
break;
|
||||
case 'workflow-current-state-log-entry':
|
||||
$replacements[$original] = $sanitize ? check_markup($comment, filter_default_format(), $langcode) : $comment;
|
||||
break;
|
||||
case 'workflow-current-state-date-iso':
|
||||
$replacements[$original] = format_date($date, 'custom', 'c', NULL, $langcode);
|
||||
break;
|
||||
case 'workflow-current-state-date-tstamp':
|
||||
$replacements[$original] = $sanitize ? check_plain($date) : $date;
|
||||
break;
|
||||
case 'workflow-current-state-date-formatted':
|
||||
$replacements[$original] = format_date($date, 'medium', '', NULL, $langcode);
|
||||
break;
|
||||
default:
|
||||
// Add support for custom date formats. (see token.tokens.inc)
|
||||
// @todo Remove when http://drupal.org/node/1173706 is fixed.
|
||||
$date_format_types = system_get_date_types();
|
||||
foreach ($date_format_types as $date_type => $date_info) {
|
||||
if ($name == 'workflow-current-state-date-' . $date_type) {
|
||||
$replacements[$original] = format_date($date, $date_type, '', NULL, $langcode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function workflow_token_info() {
|
||||
$type = array(
|
||||
'name' => t('Workflow'),
|
||||
'description' => t('Tokens related to workflows.'),
|
||||
'needs-data' => 'node',
|
||||
);
|
||||
|
||||
$tokens = array();
|
||||
$tokens['workflow-name'] = array(
|
||||
'name' => t('Workflow name'),
|
||||
'description' => t('Name of workflow appied to this node'),
|
||||
);
|
||||
$tokens['workflow-current-state-name'] = array(
|
||||
'name' => t('Current state name'),
|
||||
'description' => t('Current state of content'),
|
||||
);
|
||||
$tokens['workflow-old-state-name'] = array(
|
||||
'name' => t('Old state name'),
|
||||
'description' => t('Old state of content'),
|
||||
);
|
||||
$tokens['workflow-current-state-updating-user-name'] = array(
|
||||
'name' => t('Username of last state changer'),
|
||||
'description' => t('Username of last state changer'),
|
||||
);
|
||||
$tokens['workflow-current-state-updating-user-link'] = array(
|
||||
'name' => t('Username link of last state changer'),
|
||||
'description' => t('Themed Username of last state changer'),
|
||||
);
|
||||
$tokens['workflow-current-state-updating-user-uid'] = array(
|
||||
'name' => t('Uid of last state changer'),
|
||||
'description' => t('uid of last state changer'),
|
||||
);
|
||||
$tokens['workflow-current-state-updating-user-mail'] = array(
|
||||
'name' => t('Email of last state changer'),
|
||||
'description' => t('email of last state changer'),
|
||||
);
|
||||
$tokens['workflow-current-state-updating-user-mail'] = array(
|
||||
'name' => t('Email link of last state changer'),
|
||||
'description' => t('email link of last state changer'),
|
||||
);
|
||||
$tokens['workflow-current-state-log-entry'] =array(
|
||||
'name' => t('Last workflow comment log'),
|
||||
'description' => t('Last workflow comment log'),
|
||||
);
|
||||
$tokens['workflow-current-state-date-iso'] = array(
|
||||
'name' => t('Current state date (ISO)'),
|
||||
'description' => t('Date of last state change (ISO)'),
|
||||
);
|
||||
$tokens['workflow-current-state-date-tstamp'] = array(
|
||||
'name' => t('Current state date (timestamp)'),
|
||||
'description' => t('Date of last state change (timestamp)'),
|
||||
);
|
||||
$tokens['workflow-current-state-date-formatted'] = array(
|
||||
'name' => t('Current state date (formatted by site default)'),
|
||||
'description' => t('Date of last state change (formatted by site default)'),
|
||||
);
|
||||
|
||||
// Add support for custom date formats. (see token.tokens.inc)
|
||||
// @todo Remove when http://drupal.org/node/1173706 is fixed.
|
||||
$date_format_types = system_get_date_types();
|
||||
foreach ($date_format_types as $date_type => $date_info) {
|
||||
$tokens['workflow-current-state-date-' . $date_type] = array(
|
||||
'name' => t('Current state date (using @format format)', array('@format' => $date_info['title'])),
|
||||
'description' => t("A date in '@type' format. (%date)", array('@type' => $date_type, '%date' => format_date(REQUEST_TIME, $date_type))),
|
||||
'module' => 'workflow',
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'types' => array('workflow' => $type),
|
||||
'tokens' => array('workflow' => $tokens, 'node' => $tokens),
|
||||
);
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Workflow_access records are a **faux-exportable** component.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to find the roles needs by an export.
|
||||
*
|
||||
* @return array
|
||||
* A workflow_name-indexed hash of non-default roles in workflow_access for
|
||||
* that workflow.
|
||||
*/
|
||||
function _workflow_access_get_affected_roles() {
|
||||
$sql = <<<SQL
|
||||
SELECT DISTINCT
|
||||
w.name AS wname,
|
||||
r.name AS rname
|
||||
FROM {workflow_access} wa
|
||||
INNER JOIN {workflow_states} ws ON wa.sid = ws.sid
|
||||
INNER JOIN {workflows} w ON ws.wid = w.wid
|
||||
INNER JOIN {role} r ON wa.rid = r.rid
|
||||
SQL;
|
||||
$roles = array();
|
||||
foreach (db_query($sql) as $row) {
|
||||
$roles[$row->wname][$row->rname] = $row->rname;
|
||||
}
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*
|
||||
* Provide a list of all workflows as selectable Feature components.
|
||||
*
|
||||
* We'll make all workflow access records related to the selected workflows
|
||||
* available for export.
|
||||
*/
|
||||
function workflow_access_features_export_options() {
|
||||
$workflows = array();
|
||||
foreach (workflow_get_workflows() as $workflow) {
|
||||
$workflows[$workflow->name] = t('workflow access configuration for: ') . $workflow->name;
|
||||
}
|
||||
|
||||
return $workflows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*
|
||||
* Inform Features about dependencies.
|
||||
*/
|
||||
function workflow_access_features_export($data, &$export, $module_name = '') {
|
||||
$roles = _workflow_access_get_affected_roles();
|
||||
$export['dependencies']['workflow_access'] = 'workflow_access';
|
||||
$export['dependencies']['workflow'] = 'workflow';
|
||||
$pipe = array();
|
||||
foreach ($data as $component) {
|
||||
$export['features']['workflow_access'][$component] = $component;
|
||||
|
||||
// Access configuration for a workflow needs that worfklow to exist.
|
||||
$pipe['workflow'][$component] = $component;
|
||||
|
||||
// And it needs the roles to which rights are granted.
|
||||
foreach ($roles[$component] as $rname) {
|
||||
$pipe['user_role'][$rname] = $rname;
|
||||
}
|
||||
}
|
||||
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*
|
||||
* Instruct Features which php code to generate, including the code-ified
|
||||
* workflow access records we want to export from db into code.
|
||||
*/
|
||||
function workflow_access_features_export_render($module_name, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = ' $workflows = array();';
|
||||
|
||||
// For each selected workflow, fetch all related workflow access records
|
||||
// we want to put into code.
|
||||
foreach ($data as $workflow_name) {
|
||||
$workflow = workflow_get_workflows_by_name($workflow_name);
|
||||
if (!empty($workflow)) {
|
||||
$states = workflow_get_workflow_states_by_wid($workflow->wid);
|
||||
if (!empty($states)) {
|
||||
$code[] = "\n \$workflows['$workflow_name'] = array();";
|
||||
foreach ($states as $state) {
|
||||
$access_records = workflow_access_get_features_workflow_access_by_sid($state->sid);
|
||||
if (!empty($access_records)) {
|
||||
$state_name = $state->state;
|
||||
$code[] = " \$workflows['$workflow_name']['$state_name'] = array();";
|
||||
foreach ($access_records as $record) {
|
||||
$rname = $record->rname;
|
||||
unset($record->rname);
|
||||
$code[] = " \$workflows['$workflow_name']['$state_name']['$rname'] = " . features_var_export($record, ' ') . ';';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$code[] = "\n return \$workflows;";
|
||||
$code = implode("\n", $code);
|
||||
return array('workflow_access_features_default_settings' => $code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*
|
||||
* Instruct Features to insert our records (that were exported into code)
|
||||
* into the workflow_access table.
|
||||
*/
|
||||
function workflow_access_features_rebuild($module) {
|
||||
$workflow_access_records = module_invoke($module, 'workflow_access_features_default_settings');
|
||||
// retrieve the workflow ids
|
||||
$wids = array();
|
||||
foreach ($workflow_access_records as $workflow_name => $state) {
|
||||
$workflow = workflow_get_workflows_by_name($workflow_name);
|
||||
$wids[$workflow_name] = $workflow->wid;
|
||||
}
|
||||
|
||||
foreach ($wids as $workflow_name => $wid) {
|
||||
$states = workflow_get_workflow_states_by_wid($wid);
|
||||
foreach ($states as $state) {
|
||||
// Remove all workflow access records for states belonging to this
|
||||
// workflow. We don't want lingering entries - we only want the ones we're
|
||||
// about to insert.
|
||||
workflow_access_delete_workflow_access_by_sid($state->sid);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert our workflow access records. They look like
|
||||
// workflow_name[state_name][role_name] => array(grant_name => 0|1, ...)
|
||||
foreach ($workflow_access_records as $workflow_name => $states) {
|
||||
foreach ($states as $state_name => $records) {
|
||||
$state = workflow_get_workflow_states_by_wid_state($wids[$workflow_name], $state_name);
|
||||
if (is_array($state)) {
|
||||
$state = array_pop($state);
|
||||
}
|
||||
foreach ($records as $rname => $record) {
|
||||
$record['sid'] = (is_array($state)) ? $state[0]->sid : $state->sid;
|
||||
if ($rname == WORKFLOW_FEATURES_AUTHOR_NAME) {
|
||||
$record['rid'] = -1;
|
||||
}
|
||||
else {
|
||||
$role = user_role_load_by_name($rname);
|
||||
$record['rid'] = $role->rid;
|
||||
}
|
||||
workflow_access_insert_workflow_access_by_sid($record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function workflow_access_features_revert($module) {
|
||||
workflow_access_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get workflow_access object like below by state id.
|
||||
*
|
||||
* array(
|
||||
* 'rname' => 'authenticated user',
|
||||
* 'grant_view' => 1,
|
||||
* 'grant_update' => 0,
|
||||
* 'grant_delete' => 0,
|
||||
* );
|
||||
*
|
||||
* State id and workflow id are not returned because they are implicit for a
|
||||
* given sid.
|
||||
*/
|
||||
function workflow_access_get_features_workflow_access_by_sid($sid) {
|
||||
// Get all workflow access rules for a sid, where wa.rid is either a valid role or -1,
|
||||
// stands for the author
|
||||
$sql = <<<SQL
|
||||
SELECT
|
||||
r.name as rname,
|
||||
wa.grant_view, wa.grant_update, wa.grant_delete
|
||||
FROM {workflow_access} wa
|
||||
LEFT JOIN {role} r ON wa.rid = r.rid
|
||||
WHERE
|
||||
wa.sid = :sid AND (wa.rid = r.rid OR wa.rid = -1)
|
||||
SQL;
|
||||
$results = db_query($sql, array(':sid' => $sid));
|
||||
$records = $results->fetchAll();
|
||||
foreach ($records as $record) {
|
||||
if (empty($record->rname)) {
|
||||
$record->rname = WORKFLOW_FEATURES_AUTHOR_NAME;
|
||||
}
|
||||
}
|
||||
return $records;
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
name = Workflow access
|
||||
description = Content access control based on workflows and roles.
|
||||
dependencies[] = workflow
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Workflow access installation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function workflow_access_install() { }
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function workflow_access_uninstall() { }
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function workflow_access_schema() {
|
||||
$schema['workflow_access'] = array(
|
||||
'description' => 'Workflow access tables',
|
||||
'fields' => array(
|
||||
'sid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'rid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'grant_view' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'grant_update' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'grant_delete' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'rid' => array('rid'),
|
||||
'sid' => array('sid'),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
}
|
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provides node access permissions based on workflow states.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function workflow_access_menu() {
|
||||
$items = array();
|
||||
|
||||
$items["admin/config/workflow/access/%workflow"] = array(
|
||||
'title' => 'Access',
|
||||
'access arguments' => array('administer workflow'),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('workflow_access_form', 4),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_grants().
|
||||
*
|
||||
* Supply the workflow access grants. We are simply using
|
||||
* roles as access lists, so rids translate directly to gids.
|
||||
*/
|
||||
function workflow_access_node_grants($account, $op) {
|
||||
return array(
|
||||
'workflow_access' => array_keys($account->roles),
|
||||
'workflow_access_owner' => array($account->uid),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_access_records().
|
||||
*
|
||||
* Returns a list of grant records for the passed in node object.
|
||||
*/
|
||||
function workflow_access_node_access_records($node) {
|
||||
$grants = array();
|
||||
if ($state = workflow_get_workflow_node_by_nid($node->nid)) {
|
||||
$state = workflow_get_workflow_states_by_sid($state->sid);
|
||||
foreach (workflow_access_get_workflow_access_by_sid($state->sid) as $grant) {
|
||||
$grants[] = array(
|
||||
'realm' => ($grant->rid == -1) ? 'workflow_access_owner' : 'workflow_access',
|
||||
'gid' => ($grant->rid == -1) ? $node->uid : $grant->rid,
|
||||
'grant_view' => $grant->grant_view,
|
||||
'grant_update' => $grant->grant_update,
|
||||
'grant_delete' => $grant->grant_delete,
|
||||
'priority' => variable_get('workflow_access_priority', 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_access_explain().
|
||||
* This is a Devel Node Access hook.
|
||||
*/
|
||||
function workflow_access_node_access_explain($row) {
|
||||
static $interpretations = array();
|
||||
switch ($row->realm) {
|
||||
case 'workflow_access_owner':
|
||||
$interpretations[$row->gid] = t('Workflow access: author of the content may access');
|
||||
break;
|
||||
case 'workflow_access':
|
||||
$roles = user_roles();
|
||||
$interpretations[$row->gid] = t('Workflow access: %role may access', array('%role' => $roles[$row->gid]));
|
||||
break;
|
||||
}
|
||||
return (!empty($interpretations[$row->gid]) ? $interpretations[$row->gid] : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_operations().
|
||||
* Create action link for access form.
|
||||
*/
|
||||
function workflow_access_workflow_operations($op, $workflow = NULL, $state = NULL) {
|
||||
switch ($op) {
|
||||
case 'workflow':
|
||||
$alt = t('Control content access for @wf', array('@wf' => $workflow->name));
|
||||
$actions = array(
|
||||
'workflow_access_form' => array(
|
||||
'title' => t('Access'),
|
||||
'href' => "admin/config/workflow/access/$workflow->wid",
|
||||
'attributes' => array('alt' => $alt, 'title' => $alt),
|
||||
),
|
||||
);
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form().
|
||||
*
|
||||
* Add a "three dimensional" (state, role, permission type) configuration
|
||||
* interface to the workflow edit form.
|
||||
*/
|
||||
function workflow_access_form($form, $form_state, $workflow) {
|
||||
if ($workflow) {
|
||||
drupal_set_title(t('@name Access', array('@name' => $workflow->name)));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Unknown workflow'));
|
||||
drupal_goto('admin/config/workflow/workflow');
|
||||
}
|
||||
|
||||
$bc = array(l(t('Home'), '<front>'));
|
||||
$bc[] = l(t('Configuration'), 'admin/config');
|
||||
$bc[] = l(t('Workflow'), 'admin/config/workflow');
|
||||
$bc[] = l(t('Workflow'), 'admin/config/workflow/workflow');
|
||||
$bc[] = l(t($workflow->name), "admin/config/workflow/workflow/$workflow->wid");
|
||||
// $bc[] = l(t('Access'), "admin/config/workflow/access/$workflow->wid");
|
||||
drupal_set_breadcrumb($bc);
|
||||
|
||||
$form = array('#tree' => TRUE);
|
||||
|
||||
$form['#wid'] = $workflow->wid;
|
||||
|
||||
// A list of roles available on the site and our
|
||||
// special -1 role used to represent the node author.
|
||||
$rids = user_roles(FALSE, 'participate in workflow');
|
||||
|
||||
$rids['-1'] = t('author');
|
||||
|
||||
// Add a table for every workflow state.
|
||||
$options = array('status' => 1);
|
||||
foreach (workflow_get_workflow_states_by_wid($workflow->wid, $options) as $state) {
|
||||
if ($state->state == t('(creation)')) {
|
||||
// No need to set perms on creation.
|
||||
continue;
|
||||
}
|
||||
$view = $update = $delete = array();
|
||||
$count = 0;
|
||||
foreach (workflow_access_get_workflow_access_by_sid($state->sid) as $access) {
|
||||
$count++;
|
||||
if ($access->grant_view) {
|
||||
$view[] = $access->rid;
|
||||
}
|
||||
if ($access->grant_update) {
|
||||
$update[] = $access->rid;
|
||||
}
|
||||
if ($access->grant_delete) {
|
||||
$delete[] = $access->rid;
|
||||
}
|
||||
}
|
||||
// Allow view grants by default for anonymous and authenticated users,
|
||||
// if no grants were set up earlier.
|
||||
if (!$count) {
|
||||
$view = array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID);
|
||||
}
|
||||
// TODO better tables using a #theme function instead of direct #prefixing
|
||||
$form[$state->sid] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('@state', array('@state' => $state->state)),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$form[$state->sid]['view'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $rids,
|
||||
'#default_value' => $view,
|
||||
'#title' => t('Roles who can view posts in this state'),
|
||||
'#prefix' => '<table width="100%" style="border: 0;"><tbody style="border: 0;"><tr><td>',
|
||||
);
|
||||
|
||||
$form[$state->sid]['update'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $rids,
|
||||
'#default_value' => $update,
|
||||
'#title' => t('Roles who can edit posts in this state'),
|
||||
'#prefix' => "</td><td>",
|
||||
);
|
||||
|
||||
$form[$state->sid]['delete'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $rids,
|
||||
'#default_value' => $delete,
|
||||
'#title' => t('Roles who can delete posts in this state'),
|
||||
'#prefix' => "</td><td>",
|
||||
'#suffix' => "</td></tr></tbody></table>",
|
||||
);
|
||||
}
|
||||
|
||||
$form['warning'] = array(
|
||||
'#type' => 'markup',
|
||||
'#markup' => '<p><strong>'
|
||||
. t('WARNING:')
|
||||
. '</strong> '
|
||||
. t('Use of the "Edit any," "Edit own," and even "View published content" permissions
|
||||
for the content type may override these access settings.')
|
||||
. '</p>',
|
||||
);
|
||||
|
||||
$form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
|
||||
|
||||
return $form;
|
||||
// Place our block comfortably down the page.
|
||||
$form['submit']['#weight'] = 10;
|
||||
$form['#submit'][] = 'workflow_access_form_submit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Store permission settings for workflow states.
|
||||
*/
|
||||
function workflow_access_form_submit($form, &$form_state) {
|
||||
foreach ($form_state['values'] as $sid => $access) {
|
||||
// Ignore irrelevant keys.
|
||||
if (!is_numeric($sid)) {
|
||||
continue;
|
||||
}
|
||||
$grants = array();
|
||||
foreach ($access['view'] as $rid => $checked) {
|
||||
$data = array(
|
||||
'sid' => $sid,
|
||||
'rid' => $rid,
|
||||
'grant_view' => (!empty($checked)) ? (bool) $checked : 0,
|
||||
'grant_update' => (!empty($access['update'][$rid])) ? (bool) $access['update'][$rid] : 0,
|
||||
'grant_delete' => (!empty($access['delete'][$rid])) ? (bool) $access['delete'][$rid] : 0,
|
||||
);
|
||||
$id = workflow_access_insert_workflow_access_by_sid($data);
|
||||
}
|
||||
|
||||
// Update all nodes having same workflow state to reflect new settings.
|
||||
foreach (workflow_get_workflow_node_by_sid($sid) as $data) {
|
||||
// Instead of trying to construct what the grants should be per node as we save.
|
||||
// Let's fall back on existing node grant systems that will find it for us.
|
||||
$node = node_load($data->nid);
|
||||
node_access_acquire_grants($node);
|
||||
}
|
||||
}
|
||||
|
||||
drupal_set_message(t('Workflow access permissions updated.'));
|
||||
$form_state['redirect'] = 'admin/config/workflow/workflow/' . $form['#wid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow().
|
||||
*
|
||||
* Update grants when a node changes workflow state.
|
||||
* This is already called when node_save is called.
|
||||
*/
|
||||
function workflow_access_workflow($op, $old_sid, $sid, $node) {
|
||||
// ALERT:
|
||||
// This is a tricky spot when called on node_insert as part of the transition from create to state1.
|
||||
// node_save invokes this function as a hook before calling node_access_acquire_grants.
|
||||
// But when it calls node_access_acquire_grants later, it does so without deleting the access
|
||||
// set when calling workflow_node_insert because it is an insert and no prior grants are expected.
|
||||
// This leads to a SQL error of duplicate grants which causes a rollback of all changes.
|
||||
// Unfortunately, setting access rights isn't the only thing we're doing on node_insert so we
|
||||
// can't skip the whole thing. So we need to fix it further downstream in order to get this to work.
|
||||
// Here we don't want to run this in the case of (and ONLY in the case of) a brand new node.
|
||||
// Node access grants will be run as part of node_save's own granting after this.
|
||||
//
|
||||
// NOTE: Any module that sets node access rights on insert will hit this situation.
|
||||
//
|
||||
if ($old_state = workflow_get_workflow_states_by_sid($old_sid)) {
|
||||
if ($op == 'transition post' && $old_sid != $sid && (empty($node->is_new) || !$node->is_new)) {
|
||||
node_access_acquire_grants($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DB functions - all DB interactions are isolated here to make for easy updating should our schema change.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given a sid, retrieve the access information and return the row(s).
|
||||
*/
|
||||
function workflow_access_get_workflow_access_by_sid($sid) {
|
||||
$results = db_query('SELECT * from {workflow_access} where sid = :sid', array(':sid' => $sid));
|
||||
return $results->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a sid and a rid (the unique key), delete all access data for this state.
|
||||
*/
|
||||
function workflow_access_delete_workflow_access_by_sid_rid($sid, $rid) {
|
||||
db_delete('workflow_access')->condition('sid', $sid)->condition('rid', $rid)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a sid, delete all access data for this state.
|
||||
*/
|
||||
function workflow_access_delete_workflow_access_by_sid($sid) {
|
||||
db_delete('workflow_access')->condition('sid', $sid)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given data, insert into workflow access - we never update.
|
||||
*/
|
||||
function workflow_access_insert_workflow_access_by_sid(&$data) {
|
||||
$data = (object) $data;
|
||||
workflow_access_delete_workflow_access_by_sid_rid($data->sid, $data->rid);
|
||||
drupal_write_record('workflow_access', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*
|
||||
* Tell the Features module that we intend to provide one exportable component.
|
||||
*/
|
||||
function workflow_access_form_alter(&$form, &$form_state, $form_id) {
|
||||
switch ($form_id) {
|
||||
case 'workflow_admin_ui_types_form':
|
||||
$form['workflow_access'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Workflow Access'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form['workflow_access']['workflow_access_priority'] = array(
|
||||
'#type' => 'weight',
|
||||
'#delta' => 10,
|
||||
'#title' => t('Workflow Access Priority'),
|
||||
'#default_value' => variable_get('workflow_access_priority', 0),
|
||||
'#description' => t('This sets the node access priority. This can be dangerous. If there is any doubt,
|
||||
leave it at 0.')
|
||||
. ' '
|
||||
. l(t('Read the manual.'), 'https://api.drupal.org/api/drupal/modules!node!node.api.php/function/hook_node_access_records/7'),
|
||||
);
|
||||
|
||||
$form['#submit'][] = 'workflow_access_priority_submit';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler.
|
||||
*/
|
||||
function workflow_access_priority_submit($form, &$form_state) {
|
||||
variable_set('workflow_access_priority', $form_state['values']['workflow_access']['workflow_access_priority']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*
|
||||
* Tell the Features module that we intend to provide one exportable component.
|
||||
*/
|
||||
function workflow_access_features_api() {
|
||||
return array(
|
||||
'workflow_access' => array(
|
||||
'name' => t('Workflow access'),
|
||||
'file' => drupal_get_path('module', 'workflow_access') . '/workflow_access.features.inc',
|
||||
'default_hook' => 'workflow_access_features_default_settings',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
There is an important issue to keep in mind as you use action hooks and workflow!!
|
||||
|
||||
If the machine readable name of the content type on which you want to define actions
|
||||
in the workflow exceeds 20 characters then the actions you define will not be visible
|
||||
in the screen where you define the triggers nor will they execute.
|
||||
|
||||
The reason is that the length of the field "op" in the "trigger-assignments" table
|
||||
is 32 characters. The name of this "op"-field is a concatenation of the string
|
||||
"workflow-" with the machine readable name of the content type, another "-" and the
|
||||
transition-id on which the action has to be performed. If the latter has a length of
|
||||
1 then this leaves 32 - 9 - 1 - 1 = 21 characters for the machine readable name of
|
||||
the content type.
|
||||
|
||||
So: KEEP YOUR CONTENT TYPE NAMES SHORT.
|
||||
|
||||
Unfortunately the code that handles this is in core, so not readily changable. If
|
||||
you have trouble seeing your actions check your name lengths.
|
||||
|
||||
See further discussion at:
|
||||
http://drupal.org/node/585726
|
||||
|
||||
See request put to core to make the change at:
|
||||
|
||||
http://drupal.org/node/1062068
|
||||
|
||||
Closed and told to have workflow make the table change ourselves. Given that changing
|
||||
name lengths haphazardly would spread the bugs around even more this approach was
|
||||
not followed.
|
@@ -0,0 +1,12 @@
|
||||
name = Workflow actions and triggers
|
||||
description = Provides actions and triggers for workflows.
|
||||
dependencies[] = workflow
|
||||
dependencies[] = trigger
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide actions and triggers for workflows.
|
||||
* Why it's own module? Some sites prefer rules, some prefer actions,
|
||||
* all prefer a lower code footprint and better performance.
|
||||
* Additional creadit to gcassie ( http://drupal.org/user/80260 ) for
|
||||
* the initial push to split actions out of core workflow.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
* Expose each transition as a hook.
|
||||
*/
|
||||
function workflow_actions_trigger_info() {
|
||||
$states = array();
|
||||
foreach (workflow_get_workflow_states() as $data) {
|
||||
$states[$data->sid] = check_plain($data->state);
|
||||
}
|
||||
if (empty($states)) {
|
||||
return array();
|
||||
}
|
||||
$trigger_page = drupal_substr($_GET['q'], 0, 32) == 'admin/structure/trigger/workflow';
|
||||
|
||||
// TODO these should be pulled into their own DB function calls.
|
||||
if ($trigger_page && $wid = arg(4)) {
|
||||
$result = db_query('SELECT tm.type, w.wid, w.name, ws.state, wt.tid, wt.sid, wt.target_sid ' .
|
||||
'FROM {workflow_type_map} tm ' .
|
||||
'LEFT JOIN {workflows} w ON tm.wid = w.wid ' .
|
||||
'LEFT JOIN {workflow_states} ws ON w.wid = ws.wid ' .
|
||||
'LEFT JOIN {workflow_transitions} wt ON ws.sid = wt.sid ' .
|
||||
'WHERE w.wid = :wid AND ws.status = 1 AND wt.target_sid IS NOT NULL ' .
|
||||
'ORDER BY tm.type, ws.weight', array(':wid' => $wid));
|
||||
}
|
||||
else {
|
||||
$result = db_query('SELECT tm.type, w.wid, w.name, ws.state, wt.tid, wt.sid, wt.target_sid ' .
|
||||
'FROM {workflow_type_map} tm ' .
|
||||
'LEFT JOIN {workflows} w ON tm.wid = w.wid ' .
|
||||
'LEFT JOIN {workflow_states} ws ON w.wid = ws.wid ' .
|
||||
'LEFT JOIN {workflow_transitions} wt ON ws.sid = wt.sid ' .
|
||||
'WHERE ws.status = 1 AND wt.target_sid IS NOT NULL ' .
|
||||
'ORDER BY tm.type, ws.weight');
|
||||
}
|
||||
|
||||
$creation_state = t('(creation)');
|
||||
foreach ($result as $data) {
|
||||
$creation_flag = FALSE;
|
||||
if ($states[$data->sid] == $creation_state) {
|
||||
$creation_flag = TRUE;
|
||||
}
|
||||
$pseudohooks['workflow-' . $data->type . '-' . $data->tid] =
|
||||
array('label' => t('When %type moves from %state to %target_state',
|
||||
array('%type' => $data->type, '%state' => $states[$data->sid], '%target_state' => $states[$data->target_sid])),
|
||||
'workflow_creation_state' => $creation_flag,
|
||||
);
|
||||
}
|
||||
|
||||
// $pseudohooks will not be set if no workflows have been assigned
|
||||
// to node types.
|
||||
if (isset($pseudohooks)) {
|
||||
return array(
|
||||
'workflow' => $pseudohooks,
|
||||
);
|
||||
}
|
||||
if ($trigger_page) {
|
||||
drupal_set_message(t('Either no transitions have been set up or this workflow has not yet been ' .
|
||||
'assigned to a content type. To enable the assignment of actions, edit the workflow to assign ' .
|
||||
'permissions for roles to do transitions. After that is completed, transitions will appear here ' .
|
||||
'and you will be able to assign actions to them.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow().
|
||||
*
|
||||
* @param $hook
|
||||
* The current workflow operation: 'transition pre' or 'transition post'.
|
||||
* @param $old_state
|
||||
* The state ID of the current state.
|
||||
* @param $new_state
|
||||
* The state ID of the new state.
|
||||
* @param $node
|
||||
* The node whose workflow state is changing.
|
||||
*/
|
||||
function workflow_actions_workflow($op, $old_state, $new_state, $node) {
|
||||
switch ($op) {
|
||||
case 'transition post':
|
||||
// A transition has occurred; fire off actions associated with this transition.
|
||||
if ($transition = workflow_get_workflow_transitions_by_sid_target_sid($old_state, $new_state) ) {
|
||||
$hook = 'workflow-' . $node->type . '-' . $transition->tid;
|
||||
$aids = trigger_get_assigned_actions($hook);
|
||||
if ($aids) {
|
||||
$context = array(
|
||||
'hook' => $hook,
|
||||
);
|
||||
// We need to get the expected object if the action's type is not 'node'.
|
||||
// We keep the object in $objects so we can reuse it if we have multiple actions
|
||||
// that make changes to an object.
|
||||
foreach ($aids as $aid => $action_info) {
|
||||
if ($action_info['type'] != 'node') {
|
||||
if (!isset($objects[$action_info['type']])) {
|
||||
$objects[$action_info['type']] = _trigger_normalize_node_context($action_info['type'], $node);
|
||||
}
|
||||
// Since we know about the node, we pass that info along to the action.
|
||||
$context['node'] = $node;
|
||||
$result = actions_do($aid, $objects[$action_info['type']], $context);
|
||||
}
|
||||
else {
|
||||
actions_do($aid, $node, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'transition delete':
|
||||
if ($transition = workflow_get_workflow_transitions_by_sid_target_sid($old_state, $new_state) ) {
|
||||
$actions = workflow_actions_get_actions_by_tid($transition->tid);
|
||||
foreach ($actions as $aid) {
|
||||
workflow_actions_remove($transition->tid, $aid);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_operations().
|
||||
* Called in workflow.admin.inc to add actions for states.
|
||||
*/
|
||||
function workflow_actions_workflow_operations($level, $workflow = NULL, $state = NULL) {
|
||||
if ($workflow) {
|
||||
return array('workflow_overview_actions' => array(
|
||||
'title' => t('Actions'),
|
||||
'href' => 'admin/structure/trigger/workflow/' . $workflow->wid),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an action assignment programmatically.
|
||||
*
|
||||
* Helpful when deleting a workflow.
|
||||
*
|
||||
* @param $tid
|
||||
* Transition ID.
|
||||
* @param $aid
|
||||
* Action ID.
|
||||
*/
|
||||
function workflow_actions_remove($tid, $aid) {
|
||||
$ops = array();
|
||||
foreach (workflow_actions_get_trigger_assignments_by_aid($aid) as $data) {
|
||||
// Transition ID is the last part, e.g., foo-bar-1.
|
||||
$transition = array_pop(explode('-', $data->hook));
|
||||
if ($tid == $transition) {
|
||||
$hooks[] = $data->hook;
|
||||
}
|
||||
}
|
||||
foreach ($hooks as $hook) {
|
||||
workflow_actions_delete_trigger_assignments_by_aid_op($aid, $hook);
|
||||
foreach (workflow_actions_get_actions_by_aid($aid) as $action) {
|
||||
watchdog('workflow', 'Action %action has been unassigned.',
|
||||
array('%action' => $action->description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DB functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get all trigger assingments for workflow.
|
||||
*/
|
||||
function workflow_actions_get_trigger_assignments() {
|
||||
$results = db_query('SELECT hook FROM {trigger_assignments} WHERE hook = "workflow"');
|
||||
return $results->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all trigger assignements for workflow and a given action.
|
||||
*/
|
||||
function workflow_actions_get_trigger_assignments_by_aid($aid) {
|
||||
$results = db_query('SELECT hook FROM {trigger_assignments} WHERE hook = "workflow" AND aid = ":aid"', array(':aid' => $aid));
|
||||
return $results->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete assignments, by action and operation.
|
||||
*/
|
||||
function workflow_actions_delete_trigger_assignments_by_aid_op($aid, $op) {
|
||||
return db_delete('trigger_assignments')->condition('hook', 'workflow')->condition('hook', $op)->condition('aid', $aid)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific action.
|
||||
*/
|
||||
function workflow_actions_get_actions_by_aid($aid) {
|
||||
$results = db_query('SELECT * FROM {actions} WHERE aid = ":aid"', array(':aid' => $aid));
|
||||
return $results->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions associated with a given transition.
|
||||
* Array of action ids in the same format as _trigger_get_hook_aids().
|
||||
*/
|
||||
function workflow_actions_get_actions_by_tid($tid) {
|
||||
$aids = array();
|
||||
foreach (workflow_actions_get_trigger_assignments() as $data) {
|
||||
// Transition ID is the last part, e.g., foo-bar-1.
|
||||
$transition = array_pop(explode('-', $data->hook));
|
||||
if ($tid == $transition) {
|
||||
// Specialized, TODO seprate this SQL out later
|
||||
$results = db_query('SELECT aa.aid, a.type FROM {trigger_assignments} aa ' .
|
||||
'LEFT JOIN {actions} a ON aa.aid = a.aid ' .
|
||||
'WHERE aa.hook = ":hook" ' .
|
||||
'ORDER BY weight', array(':hook' => $data->hook));
|
||||
foreach ($results as $action) {
|
||||
$aids[$action->aid]['type'] = $action->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_drupal_alter().
|
||||
*/
|
||||
function workflow_actions_action_info_alter(&$info) {
|
||||
$transitions = workflow_actions_trigger_info();
|
||||
if (empty($transitions)) {
|
||||
return;
|
||||
}
|
||||
foreach ((array)$transitions['workflow'] as $transition => $data) {
|
||||
// Loop through all available node actions and add them as triggers.
|
||||
// But not if this has been flagged as a creation state.
|
||||
if ($data['workflow_creation_state'] != TRUE) {
|
||||
foreach (node_action_info() as $action => $data) {
|
||||
$info[$action]['triggers'][] = $transition;
|
||||
}
|
||||
}
|
||||
// Either way, unset the creation flag so we don't confuse anyone later.
|
||||
unset($transitions['workflow'][$transition]['workflow_creation_state']);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the workflow_admin_ui module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_operations().
|
||||
*
|
||||
* @param $op
|
||||
* 'top_actions': Allow modules to insert their own front page action links.
|
||||
* 'operations': Allow modules to insert their own workflow operations.
|
||||
* 'state': Allow modules to insert state operations.
|
||||
* @param $workflow
|
||||
* The current workflow object.
|
||||
* @param $state
|
||||
* The current state object.
|
||||
*/
|
||||
function hook_workflow($op, object $workflow, object $state) {
|
||||
switch ($op) {
|
||||
case 'top_actions':
|
||||
$actions = array();
|
||||
// The workflow_admin_ui module creates links to add a new state,
|
||||
// and reach each workflow.
|
||||
// Your module may add to these actions.
|
||||
return $actions;
|
||||
|
||||
case 'operations':
|
||||
$actions = array();
|
||||
// The workflow_admin_ui module creates links to add a new state,
|
||||
// edit the workflow, and delete the workflow.
|
||||
// Your module may add to these actions.
|
||||
return $actions;
|
||||
|
||||
case 'state':
|
||||
$ops = array();
|
||||
// The workflow_admin_ui module does not use this.
|
||||
// Your module may add operations.
|
||||
return $ops;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file
|
||||
* Style Sheets for the Workflow_Admin_UI module.
|
||||
*/
|
||||
|
||||
#workflow_admin_ui_overview .state-name input {
|
||||
margin-right: 2em; /* Make room for dragging handle. */
|
||||
}
|
||||
|
||||
#workflow_admin_ui_overview .state-status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#workflow_admin_ui_overview .state-count {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#workflow_admin_ui_overview th.state-ops {
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
name = Workflow UI
|
||||
description = "Provides administrative UI for workflow."
|
||||
package = Workflow
|
||||
dependencies[] = workflow
|
||||
core = 7.x
|
||||
configure = admin/config/workflow/workflow
|
||||
stylesheets[all][] = workflow_admin_ui.css
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the workflow_admin_ui module.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_enable().
|
||||
*/
|
||||
function workflow_admin_ui_enable() {
|
||||
drupal_set_message(_workflow_admin_ui_participate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Used both by update and enable.
|
||||
*/
|
||||
function _workflow_admin_ui_participate() {
|
||||
$perms = array('participate in workflow' => 1);
|
||||
foreach (user_roles() as $rid => $name) {
|
||||
user_role_change_permissions($rid, $perms);
|
||||
}
|
||||
return t('Please review which roles may participate in workflows. <a href="!url">Permissions</a>',
|
||||
array('!url' => url('admin/people/permissions', array('fragment' => 'module-workflow_admin_ui'))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch for #1540824 requires that the menu be rebuilt.
|
||||
*/
|
||||
function workflow_admin_ui_update_7000(&$sandbox) {
|
||||
menu_rebuild();
|
||||
return t('Workflow_admin_ui requested a menu rebuild.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Give all user roles the ability to participate in workflows.
|
||||
*/
|
||||
function workflow_admin_ui_update_7001(&$sandbox) {
|
||||
return _workflow_admin_ui_participate();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
name = Workflow Clean Up
|
||||
description = "Cleans up Workflow cruft"
|
||||
dependencies[] = workflow
|
||||
core = 7.x
|
||||
package = Workflow
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Cleans up Workflow cruft that may build up over time.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function workflow_cleanup_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/config/workflow/workflow/cleanup'] = array(
|
||||
'title' => 'Workflow Clean Up',
|
||||
'access arguments' => array('administer workflow'),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('workflow_cleanup_form'),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function workflow_cleanup_theme() {
|
||||
return array(
|
||||
'workflow_cleanup_form' => array('render element' => 'form'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_operations().
|
||||
* Might as well eat our own cooking.
|
||||
*/
|
||||
function workflow_cleanup_workflow_operations($op, $workflow = NULL, $state = NULL) {
|
||||
switch ($op) {
|
||||
case 'top_actions':
|
||||
$workflows = workflow_get_workflows();
|
||||
$alt = t('Clean up workflow cruft');
|
||||
$actions = array(
|
||||
'workflow-cleanup' => array(
|
||||
'title' => t('Clean up'),
|
||||
'href' => 'admin/config/workflow/workflow/cleanup',
|
||||
'attributes' => array('alt' => $alt, 'title' => $alt),
|
||||
),
|
||||
);
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main cleanup page.
|
||||
*/
|
||||
function workflow_cleanup_form($form, $form_state) {
|
||||
$bc = array(l(t('Home'), '<front>'));
|
||||
$bc[] = l(t('Configuration'), 'admin/config');
|
||||
$bc[] = l(t('Workflow'), 'admin/config/workflow');
|
||||
$bc[] = l(t('Workflow'), 'admin/config/workflow/workflow');
|
||||
drupal_set_breadcrumb($bc);
|
||||
|
||||
$form = array();
|
||||
|
||||
// Get all of the states, indexed by sid.
|
||||
$states = $orphans = $inactive = array();
|
||||
foreach (workflow_get_workflow_states() as $state) {
|
||||
$states[$state->sid] = $state;
|
||||
// Is it associated with a workflow?
|
||||
if (empty($state->name)) {
|
||||
$orphans[$state->sid] = $state->state;
|
||||
}
|
||||
else {
|
||||
// Is it associated with a workflow?
|
||||
if (!$state->status) {
|
||||
$inactive[$state->sid] = $state->state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with no orphan states.
|
||||
if (!$orphans) {
|
||||
$orphans[0] = t('None');
|
||||
}
|
||||
|
||||
// Deal with no inactive states.
|
||||
if (!$inactive) {
|
||||
$inactive[0] = $states[0] = t('None');
|
||||
}
|
||||
|
||||
$form['#workflow_states'] = $states;
|
||||
|
||||
$form['no_workflow'] = array(
|
||||
'#type' => 'container',
|
||||
'#title' => t('Orphaned States'),
|
||||
'#description' => t('These states no longer belong to an existing workflow.'),
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
foreach ($orphans as $sid => $state) {
|
||||
$form['no_workflow'][$sid]['check'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#return_value' => $sid,
|
||||
);
|
||||
|
||||
$form['no_workflow'][$sid]['name'] = array(
|
||||
'#type' => 'markup',
|
||||
'#markup' => check_plain($state),
|
||||
);
|
||||
}
|
||||
|
||||
$form['inactive'] = array(
|
||||
'#type' => 'container',
|
||||
'#title' => t('Inactive (Deleted) States'),
|
||||
'#description' => t('These states belong to a workflow, but have been marked inactive (deleted).'),
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
foreach ($inactive as $sid => $state) {
|
||||
$form['inactive'][$sid]['check'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#return_value' => $sid,
|
||||
);
|
||||
|
||||
$form['inactive'][$sid]['name'] = array(
|
||||
'#type' => 'markup',
|
||||
'#markup' => check_plain($state),
|
||||
);
|
||||
|
||||
$form['inactive'][$sid]['wf'] = array(
|
||||
'#type' => 'markup',
|
||||
'#markup' => check_plain($states[$sid]->name),
|
||||
);
|
||||
}
|
||||
|
||||
$form['submit'] = array('#type' => 'submit', '#value' => t('Delete selected states'));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme the main form.
|
||||
*/
|
||||
function theme_workflow_cleanup_form($variables) {
|
||||
$form = $variables['form'];
|
||||
$output = '';
|
||||
$header = array(t('select'), t('State'));
|
||||
|
||||
$rows = array();
|
||||
foreach (element_children($form['no_workflow']) as $sid) {
|
||||
$rows[] = array(
|
||||
drupal_render($form['no_workflow'][$sid]['check']),
|
||||
drupal_render($form['no_workflow'][$sid]['name']),
|
||||
);
|
||||
}
|
||||
|
||||
$output .= '<h3>' . $form['no_workflow']['#title'] . '</h3>';
|
||||
$output .= '<div class="description">' . $form['no_workflow']['#description'] . '</div>';
|
||||
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('style' => 'width: auto;')));
|
||||
|
||||
$header[] = t('Workflow');
|
||||
$rows = array();
|
||||
foreach (element_children($form['inactive']) as $sid) {
|
||||
$rows[] = array(
|
||||
drupal_render($form['inactive'][$sid]['check']),
|
||||
drupal_render($form['inactive'][$sid]['name']),
|
||||
drupal_render($form['inactive'][$sid]['wf']),
|
||||
);
|
||||
}
|
||||
|
||||
$output .= '<h3>' . $form['inactive']['#title'] . '</h3>';
|
||||
$output .= '<div class="description">' . $form['inactive']['#description'] . '</div>';
|
||||
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('style' => 'width: auto;')));
|
||||
|
||||
$output .= drupal_render_children($form);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submission handler for main cleanup form.
|
||||
*/
|
||||
function workflow_cleanup_form_submit($form, $form_state) {
|
||||
$states = $form['#workflow_states'];
|
||||
foreach (array('no_workflow', 'inactive') as $section) {
|
||||
foreach ($form_state['values'][$section] as $sid => $data) {
|
||||
// FAPI returns either a 0 or the sid.
|
||||
if ($data['check']) {
|
||||
// Delete any transitions this state is involved in.
|
||||
$trans_del = db_delete('workflow_transitions')->condition('target_sid', $sid)->execute();
|
||||
$trans_del += db_delete('workflow_transitions')->condition('sid', $sid)->execute();
|
||||
if ($trans_del) {
|
||||
drupal_set_message(t('@count transitions for the "@state" state have been deleted.',
|
||||
array('@state' => $states[$sid]->state, '@count' => $trans_del)));
|
||||
}
|
||||
|
||||
// Remove history records too.
|
||||
$hist_del = db_delete('workflow_node_history')->condition('sid', $sid)->execute();
|
||||
if ($hist_del) {
|
||||
drupal_set_message(t('@count history records for the "@state" state have been deleted.',
|
||||
array('@state' => $states[$sid]->state, '@count' => $hist_del)));
|
||||
}
|
||||
|
||||
// Go ahead and delete the state.
|
||||
db_delete('workflow_states')->condition('sid', $sid)->execute();
|
||||
drupal_set_message(t('The "@state" state has been deleted.', array('@state' => $states[$sid]->state)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = Workflow Revert
|
||||
description = "Adds an 'Revert' link to the first workflow history row."
|
||||
dependencies[] = workflow
|
||||
core = 7.x
|
||||
package = Workflow
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Adds an 'Revert' link to the first workflow history row.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function workflow_revert_permission() {
|
||||
return array(
|
||||
'revert workflow' => array(
|
||||
'title' => t('Revert workflow'),
|
||||
'description' => t('Allow user to revert workflow state.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function workflow_revert_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['workflow_revert'] = array(
|
||||
'title' => 'Workflow Undo',
|
||||
'access arguments' => array('revert workflow'),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('workflow_revert_form'),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback to do the revert function.
|
||||
*/
|
||||
function workflow_revert_form($form, $form_state, $nid = NULL, $sid = NULL) {
|
||||
$args = array('#nid' => $nid, '#sid' => $sid);
|
||||
|
||||
if (drupal_valid_token($_GET['token'], 'workflow_revert ' . $sid)) {
|
||||
$states = workflow_get_workflow_states_all();
|
||||
$node = node_load($nid);
|
||||
$args['#node'] = $node;
|
||||
$question = t('Are you sure you want to revert %title to the "@state" state?', array(
|
||||
'@state' => $states[$sid],
|
||||
'%title' => $node->title,
|
||||
));
|
||||
return confirm_form($args,
|
||||
$question,
|
||||
"node/$nid",
|
||||
t('The worflow state will be changed.')
|
||||
);
|
||||
}
|
||||
else {
|
||||
watchdog('workflow_revert', 'Invalid token', array(), WATCHDOG_ERROR);
|
||||
drupal_set_message(t('Invalid token. Your information has been recorded.'), 'error');
|
||||
drupal_goto("node/$nid");
|
||||
}
|
||||
}
|
||||
|
||||
function workflow_revert_form_submit($form, $form_state) {
|
||||
$node = $form['#node'];
|
||||
$comment = t('State reverted');
|
||||
$sid = $form['#sid'];
|
||||
|
||||
// Force the transition because it's probably not valid.
|
||||
workflow_execute_transition($node, $sid, $comment, TRUE);
|
||||
drupal_set_message(t('Workflow reverted.', array()));
|
||||
|
||||
drupal_goto('node/' . $form['#nid'] . '/workflow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_workflow_history_alter().
|
||||
* Add an 'undo' operation for the most recent history change.
|
||||
*
|
||||
* @param $variables
|
||||
* The current workflow history information as an array.
|
||||
* 'old_sid' - The state ID of the previous state.
|
||||
* 'old_state_name' - The state name of the previous state.
|
||||
* 'sid' - The state ID of the current state.
|
||||
* 'state_name' - The state name of the current state.
|
||||
* 'history' - The row from the workflow_node_history table.
|
||||
*
|
||||
* If you want to add additional data, place it in the 'extra' value.
|
||||
*/
|
||||
function workflow_revert_workflow_history_alter(&$variables) {
|
||||
static $first = TRUE;
|
||||
// Only mark the first row.
|
||||
if ($first) {
|
||||
// Let's ask other modules if the reversion is allowed.
|
||||
$node = node_load($variables['history']->nid);
|
||||
$result = module_invoke_all('workflow', 'transition permitted', $variables['sid'], $variables['old_sid'], $node);
|
||||
// Did anybody veto this choice?
|
||||
if (!in_array(FALSE, $result)) {
|
||||
// If not vetoed, mark it.
|
||||
$options = array('query' => array('token' => drupal_get_token('workflow_revert ' . $variables['old_sid'])));
|
||||
$path = 'workflow_revert/' . $variables['history']->nid . '/' . $variables['old_sid'];
|
||||
$variables['extra'] = l('Revert state change', $path, $options);
|
||||
}
|
||||
// That was your one chance...
|
||||
$first = FALSE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = Workflow rules
|
||||
description = Provides rules integration for workflows.
|
||||
dependencies[] = workflow
|
||||
dependencies[] = rules
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide rules for workflows.
|
||||
* Why it's own module? Some sites prefer rules, some prefer actions,
|
||||
* all prefer a lower code footprint and better performance.
|
||||
* Additional creadit to gcassie ( http://drupal.org/user/80260 ) for
|
||||
* the initial push to split rules out of core workflow.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_workflow().
|
||||
*
|
||||
* @param $op
|
||||
* The current workflow operation: 'transition pre' or 'transition post'.
|
||||
* @param $old_state
|
||||
* The state ID of the current state.
|
||||
* @param $new_state
|
||||
* The state ID of the new state.
|
||||
* @param $node
|
||||
* The node whose workflow state is changing.
|
||||
*/
|
||||
function workflow_rules_workflow($op, $old_state, $new_state, $node) {
|
||||
switch ($op) {
|
||||
case 'transition post':
|
||||
// Rules are updated on after the transition.
|
||||
if ($old_state == $new_state) {
|
||||
rules_invoke_event('workflow_comment_added', $node, $old_state, $new_state);
|
||||
return;
|
||||
}
|
||||
rules_invoke_event('workflow_state_changed', $node);
|
||||
break;
|
||||
}
|
||||
}
|
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Rules integration for the Workflow module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_event_info().
|
||||
*/
|
||||
function workflow_rules_rules_event_info() {
|
||||
$events = array(
|
||||
'workflow_state_changed' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Workflow state has changed'),
|
||||
'variables' => rules_events_node_variables(t('updated content'), TRUE),
|
||||
),
|
||||
'workflow_comment_added' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Workflow comment was added, but state did not change'),
|
||||
'variables' => rules_events_node_variables(t('updated content'), TRUE),
|
||||
),
|
||||
);
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rules_condition_info().
|
||||
*/
|
||||
function workflow_rules_rules_condition_info() {
|
||||
return array(
|
||||
'workflow_check_transition' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Check workflow transition'),
|
||||
'parameter' => array(
|
||||
'node' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Node'),
|
||||
'description' => t('The node whose workflow state is being checked.'),
|
||||
),
|
||||
'old_state' => array(
|
||||
'type' => 'list<integer>',
|
||||
'label' => t('Old workflow state'),
|
||||
'options list' => '_workflow_rules_condition_select',
|
||||
'description' => t('The workflow state moved from.'),
|
||||
),
|
||||
'new_state' => array(
|
||||
'type' => 'list<integer>',
|
||||
'label' => t('New workflow state'),
|
||||
'options list' => '_workflow_rules_condition_select',
|
||||
'description' => t('The workflow state moved to.'),
|
||||
),
|
||||
),
|
||||
),
|
||||
'workflow_check_state' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Content has a workflow state'),
|
||||
'parameter' => array(
|
||||
'node' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Node'),
|
||||
'description' => t('The node to compare the current workflow state of.'),
|
||||
),
|
||||
'workflow_state' => array(
|
||||
'type' => 'list<integer>',
|
||||
'label' => t('Compare workflow state'),
|
||||
'options list' => '_workflow_rules_condition_select',
|
||||
'description' => t('The possible workflow states to compare against.'),
|
||||
),
|
||||
),
|
||||
),
|
||||
'workflow_check_previous_state' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Content has a previous workflow state'),
|
||||
'parameter' => array(
|
||||
'node' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Node'),
|
||||
'description' => t('The node to compare the previous workflow state of.'),
|
||||
),
|
||||
'workflow_state' => array(
|
||||
'type' => 'list<integer>',
|
||||
'label' => t('Compare workflow state'),
|
||||
'options list' => '_workflow_rules_condition_select',
|
||||
'description' => t('The possible workflow states to compare against.'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rules_action_info().
|
||||
*/
|
||||
function workflow_rules_rules_action_info() {
|
||||
return array(
|
||||
'workflow_rules_set_state' => array(
|
||||
'group' => t('Workflow'),
|
||||
'label' => t('Set workflow state for content'),
|
||||
'parameter' => array(
|
||||
'node' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Node'),
|
||||
'description' => t('The node to set the current workflow state of.'),
|
||||
// 'save' => TRUE,
|
||||
),
|
||||
'workflow_state' => array(
|
||||
'type' => 'list<integer>',
|
||||
'label' => t('New workflow state'),
|
||||
'options list' => '_workflow_rules_action_select',
|
||||
'description' => t('The workflow state to set (select only one).'),
|
||||
),
|
||||
'workflow_comment' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Workflow Comment'),
|
||||
'description' => t('The workflow comment to set.'),
|
||||
'optional' => TRUE,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition callback: gather all workflow states.
|
||||
*/
|
||||
function _workflow_rules_condition_select() {
|
||||
$options['ANY'] = 'ANY state';
|
||||
foreach (workflow_get_workflows() as $workflow) {
|
||||
foreach (workflow_get_workflow_states_by_wid($workflow->wid) as $state) {
|
||||
$states[$state->sid] = check_plain($workflow->name) . ': ' . check_plain($state->state);
|
||||
}
|
||||
}
|
||||
$options = $options + $states;
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition callback: gather all workflow states.
|
||||
*/
|
||||
function _workflow_rules_action_select() {
|
||||
foreach (workflow_get_workflows() as $workflow) {
|
||||
foreach (workflow_get_workflow_states_by_wid($workflow->wid) as $state) {
|
||||
$states[$state->sid] = check_plain($workflow->name) . ': ' . check_plain($state->state);
|
||||
}
|
||||
}
|
||||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition implementation: check state transition.
|
||||
*/
|
||||
function workflow_check_transition($node, $old_states, $new_states) {
|
||||
$node_current_state = workflow_node_current_state($node);
|
||||
$node_old_state = workflow_node_previous_state($node);
|
||||
|
||||
if (in_array('ANY', $old_states)) {
|
||||
if (in_array('ANY', $new_states)) {
|
||||
return TRUE;
|
||||
}
|
||||
return in_array($node_current_state, $new_states);
|
||||
}
|
||||
|
||||
if (in_array('ANY', $new_states)) {
|
||||
return in_array($node_old_state, $old_states);
|
||||
}
|
||||
return in_array($node_old_state, $old_states) && in_array($node_current_state, $new_states);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition implementation: check current state.
|
||||
*/
|
||||
function workflow_check_state($node, $states) {
|
||||
$node_state = workflow_node_current_state($node);
|
||||
return workflow_check_given_state($node, $states, $node_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition implementation: check previous state.
|
||||
*/
|
||||
function workflow_check_previous_state($node, $states) {
|
||||
$node_state = workflow_node_previous_state($node);
|
||||
return workflow_check_given_state($node, $states, $node_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition implementation helper function: check given state.
|
||||
*/
|
||||
function workflow_check_given_state($node, $states, $node_state) {
|
||||
if (in_array('ANY', $states)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (in_array($node_state, $states)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action implementation: set current state, ignoring current user permissione.
|
||||
*/
|
||||
function workflow_rules_set_state($node, $states, $comment = NULL) {
|
||||
// Select the last state on the list.
|
||||
$sid = array_pop($states);
|
||||
if (!empty($comment)) {
|
||||
$node->workflow_comment = $comment;
|
||||
}
|
||||
workflow_transition($node, $sid, TRUE);
|
||||
unset($node->workflow_comment);
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
name = Workflow Search API
|
||||
description = "Adds workflow state information to Search API index"
|
||||
dependencies[] = workflow
|
||||
dependencies[] = entity
|
||||
core = 7.x
|
||||
package = Workflow
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Adds workflow state information to Search API index.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter().
|
||||
*/
|
||||
function workflow_search_api_entity_property_info_alter(&$info) {
|
||||
$info['node']['properties']['workflow_state_name'] = array(
|
||||
'type' => 'text',
|
||||
'label' => t('Workflow state name'),
|
||||
'sanitized' => TRUE,
|
||||
'getter callback' => 'workflow_search_api_property_workflow_state_getter_callback',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter callback for workflow state defined in workflow_search_api_entity_property_info_alter.
|
||||
*/
|
||||
function workflow_search_api_property_workflow_state_getter_callback($item) {
|
||||
if ($item->workflow) {
|
||||
// Get text value of workflow state.
|
||||
$state = workflow_get_workflow_states_by_sid($item->workflow);
|
||||
$state_name = $state->state;
|
||||
}
|
||||
else {
|
||||
$state_name = '';
|
||||
}
|
||||
|
||||
return $state_name;
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
There is an important issue to keep in mind as you use action hooks and workflow!!
|
||||
|
||||
If the machine readable name of the content type on which you want to define actions
|
||||
in the workflow exceeds 20 characters then the actions you define will not be visible
|
||||
in the screen where you define the triggers nor will they execute.
|
||||
|
||||
The reason is that the length of the field "op" in the "trigger-assignments" table
|
||||
is 32 characters. The name of this "op"-field is a concatenation of the string
|
||||
"workflow-" with the machine readable name of the content type, another "-" and the
|
||||
transition-id on which the action has to be performed. If the latter has a length of
|
||||
1 then this leaves 32 - 9 - 1 - 1 = 21 characters for the machine readable name of
|
||||
the content type.
|
||||
|
||||
So: KEEP YOUR CONTENT TYPE NAMES SHORT.
|
||||
|
||||
Unfortunately the code that handles this is in core, so not readily changable. If
|
||||
you have trouble seeing your actions check your name lengths.
|
||||
|
||||
See further discussion at:
|
||||
http://drupal.org/node/585726
|
||||
|
||||
See request put to core to make the change at:
|
||||
|
||||
http://drupal.org/node/1062068
|
||||
|
||||
Closed and told to have workflow make the table change ourselves. Given that changing
|
||||
name lengths haphazardly would spread the bugs around even more this approach was
|
||||
not followed.
|
@@ -0,0 +1,12 @@
|
||||
name = Workflow VBO
|
||||
description = Provides workflow actions for VBO.
|
||||
dependencies[] = workflow
|
||||
dependencies[] = views_bulk_operations
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide workflow actions for VBO.
|
||||
* Split out from workflow_actions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function workflow_vbo_action_info() {
|
||||
return array(
|
||||
'workflow_vbo_next_state_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Change workflow state of post to next state'),
|
||||
'configurable' => FALSE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
|
||||
'workflow_vbo_given_state_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Change workflow state of post to new state'),
|
||||
'configurable' => TRUE,
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a Drupal action. Move a node to the next state in the workfow.
|
||||
*/
|
||||
function workflow_vbo_next_state_action($node, $context) {
|
||||
// If this action is being fired because it's attached to a workflow transition
|
||||
// then the node's new state (now its current state) should be in $node->workflow
|
||||
// because that is where the value from the workflow form field is stored;
|
||||
// otherwise the current state is placed in $node->workflow by our nodeapi load.
|
||||
if (!isset($node->nid)) {
|
||||
watchdog('workflow_vbo', 'Unable to get current node id state of node - node is not yet saved.');
|
||||
return;
|
||||
}
|
||||
if (!isset($node->workflow)) {
|
||||
watchdog('workflow_vbo', 'Unable to get current workflow state of node %nid.',
|
||||
array('%nid' => $node->nid));
|
||||
return;
|
||||
}
|
||||
|
||||
$current_state = $node->workflow;
|
||||
$new_state = $current_state;
|
||||
|
||||
// Get the node's new state.
|
||||
$choices = workflow_field_choices($node);
|
||||
foreach ($choices as $sid => $name) {
|
||||
if (isset($flag)) {
|
||||
$new_state = $sid;
|
||||
$new_state_name = $name;
|
||||
break;
|
||||
}
|
||||
if ($sid == $current_state) {
|
||||
$flag = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the transition.
|
||||
workflow_execute_transition($node, $new_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a Drupal action. Move a node to a specified state.
|
||||
*/
|
||||
function workflow_vbo_given_state_action($node, $context) {
|
||||
global $user;
|
||||
if (!isset($node->nid)) {
|
||||
watchdog('workflow_vbo', 'Unable to get current node id state of node - node is not yet saved.');
|
||||
return;
|
||||
}
|
||||
|
||||
$comment = t($context['workflow_comment'], array(
|
||||
'%title' => check_plain($node->title),
|
||||
'%state' => check_plain($context['state_name']),
|
||||
'%user' => theme('username', array('account' => $user)),
|
||||
));
|
||||
|
||||
workflow_execute_transition($node, $context['target_sid'], $comment, $context['force']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration form for "Change workflow state of post to new state" action.
|
||||
*
|
||||
* @see workflow_vbo_given_state_action()
|
||||
*/
|
||||
function workflow_vbo_given_state_action_form($context) {
|
||||
$previous_workflow = '';
|
||||
$options = array();
|
||||
|
||||
// Get all states, only where active.
|
||||
foreach (workflow_get_workflow_states(array('status' => 1)) as $data) {
|
||||
$options[$data->name][$data->sid] = check_plain($data->state);
|
||||
}
|
||||
|
||||
$form['target_sid'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target state'),
|
||||
'#description' => t('Please select that state that should be assigned when this action runs.'),
|
||||
'#default_value' => isset($context['target_sid']) ? $context['target_sid'] : '',
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
$form['force'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Force transition'),
|
||||
'#description' => t('If this box is checked, the new state will be assigned even if workflow ' .
|
||||
'permissions disallow it.'),
|
||||
'#default_value' => isset($context['force']) ? $context['force'] : '',
|
||||
);
|
||||
|
||||
$form['workflow_comment'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Message'),
|
||||
'#description' => t('This message will be written into the workflow history log when the action ' .
|
||||
'runs. You may include the following variables: %state, %title, %user'),
|
||||
'#default_value' => isset($context['workflow_history']) ? $context['workflow_history'] : t('Action set %title to %state by %user.'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for "Change workflow state of post to new state" action
|
||||
* configuration form.
|
||||
*
|
||||
* @see workflow_vbo_given_state_action_form()
|
||||
*/
|
||||
function workflow_vbo_given_state_action_submit($form_id, $form_state) {
|
||||
if ($state = workflow_get_workflow_states_by_sid($form_state['values']['target_sid'])) {
|
||||
return array(
|
||||
'target_sid' => $form_state['values']['target_sid'],
|
||||
'state_name' => check_plain($state->state),
|
||||
'force' => $form_state['values']['force'],
|
||||
'workflow_comment' => $form_state['values']['workflow_comment'],
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data and handler information for workflow.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup views_workflow_module workflow.module handlers
|
||||
*
|
||||
* Includes the 'workflow_node' and 'workflow_state' tables, but generates a
|
||||
* pseudo-table for each separate workflow that's been set up.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function workflow_views_views_data() {
|
||||
// Workflow states.
|
||||
$data['workflow_states']['table']['group'] = t('Workflow');
|
||||
$data['workflow_states']['table']['join'] = array(
|
||||
'node' => array(
|
||||
'field' => 'sid',
|
||||
'left_table' => 'workflow_node',
|
||||
'left_field' => 'sid',
|
||||
),
|
||||
);
|
||||
$data['workflow_states']['weight'] = array(
|
||||
'title' => t('State weight'),
|
||||
'help' => t('The weight of the current workflow state that the node is in.'),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
$data['workflow_states']['state'] = array(
|
||||
'title' => t('Current state name'),
|
||||
'help' => t('The readable name of the workflow state that the node is in. (Less efficient, use only when click-sorting by state name.)'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
// Workflow node.
|
||||
$data['workflow_node']['table']['group'] = t('Workflow');
|
||||
$data['workflow_node']['table']['join'] = array(
|
||||
'node' => array(
|
||||
'field' => 'nid',
|
||||
'left_table' => 'node',
|
||||
'left_field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['workflow_node']['sid'] = array(
|
||||
'title' => t('Current state'),
|
||||
'help' => t('The current workflow state that the node is in.'),
|
||||
'field' => array(
|
||||
'handler' => 'workflow_views_handler_field_sid',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'click sortable' => TRUE,
|
||||
'numeric' => TRUE,
|
||||
'name table' => 'workflow_states',
|
||||
'name field' => 'state',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'workflow_views_handler_filter_sid',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
$data['workflow_node']['stamp'] = array(
|
||||
'title' => t('Current time'),
|
||||
'help' => t('The time at which the node moved to the current state.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
);
|
||||
$data['workflow_node']['uid'] = array(
|
||||
'title' => t('Last changed user'),
|
||||
'help' => t('The user who performed the last state change.'),
|
||||
'relationship' => array(
|
||||
'base' => 'users',
|
||||
'base field' => 'uid',
|
||||
'handler' => 'views_handler_relationship',
|
||||
'label' => t('User'),
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_user_uid',
|
||||
'click sortable' => TRUE,
|
||||
'name table' => 'workflow_node',
|
||||
'name field' => 'uid',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_user_name',
|
||||
'numeric' => TRUE,
|
||||
'name table' => 'workflow_node',
|
||||
'name field' => 'uid',
|
||||
),
|
||||
);
|
||||
|
||||
$data['workflow_node_current']['table']['group'] = t('Workflow');
|
||||
// Explain how this table joins to others.
|
||||
$data['workflow_node_current']['table']['join'] = array(
|
||||
'node' => array(
|
||||
'table' => 'workflow_node_history',
|
||||
'field' => 'nid',
|
||||
'left_table' => 'workflow_node',
|
||||
'left_field' => 'nid',
|
||||
'extra' => 'workflow_node.stamp = workflow_node_current.stamp AND workflow_node.nid = workflow_node_current.nid',
|
||||
),
|
||||
);
|
||||
// Workflow node current comment.
|
||||
$data['workflow_node_current']['comment'] = array(
|
||||
'title' => t('Current comment'),
|
||||
'help' => t('The comment describing why the node was moved from the last state to the current state.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_xss',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
// Workflow scheduled transition.
|
||||
$data['workflow_scheduled_transition']['table']['group'] = t('Workflow');
|
||||
$data['workflow_scheduled_transition']['table']['join'] = array(
|
||||
'node' => array(
|
||||
'field' => 'nid',
|
||||
'left_table' => 'node',
|
||||
'left_field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['workflow_scheduled_transition']['sid'] = array(
|
||||
'title' => t('Scheduled state'),
|
||||
'help' => t('The current workflow state that the node is in.'),
|
||||
'field' => array(
|
||||
'handler' => 'workflow_views_handler_field_sid',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'workflow_views_handler_filter_sid',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
$data['workflow_scheduled_transition']['scheduled'] = array(
|
||||
'title' => t('Scheduled time'),
|
||||
'help' => t('The time at which the node will change workflow states.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
);
|
||||
$data['workflow_scheduled_transition']['comment'] = array(
|
||||
'title' => t('Scheduled comment'),
|
||||
'help' => t('A comment describing why the node was scheduled for state transition.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_xss',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
// Workflow node history.
|
||||
$data['workflow_node_history']['table']['group'] = t('Workflow');
|
||||
$data['workflow_node_history']['table']['join'] = array(
|
||||
'node' => array(
|
||||
'field' => 'nid',
|
||||
'left_table' => 'node',
|
||||
'left_field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['workflow_node_history']['sid'] = array(
|
||||
'title' => t('Previous state'),
|
||||
'help' => t('A workflow state that the node was in previously.'),
|
||||
'field' => array(
|
||||
'handler' => 'workflow_views_handler_field_sid',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'workflow_views_handler_filter_sid',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
$data['workflow_node_history']['stamp'] = array(
|
||||
'title' => t('Previous time'),
|
||||
'help' => t('The time at which the node moved from one state to another.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
);
|
||||
$data['workflow_node_history']['comment'] = array(
|
||||
'title' => t('Previous comment'),
|
||||
'help' => t('A comment describing why the node was moved from one state to another in the past.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_xss',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
'numeric' => TRUE,
|
||||
),
|
||||
);
|
||||
$data['workflow_node_history']['uid'] = array(
|
||||
'title' => t('Previous comment author'),
|
||||
'help' => t('The author of a comment describing why the node was moved from one state to another in the past.'),
|
||||
'field' => array(
|
||||
'handler' => 'workflow_views_handler_field_username',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'relationship' => array(
|
||||
'title' => t('Author'),
|
||||
'help' => t("The User ID of the comment's author."),
|
||||
'base' => 'users',
|
||||
'base field' => 'uid',
|
||||
'handler' => 'views_handler_relationship',
|
||||
'label' => t('author'),
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_user_name',
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* View for listing nodes by workflow state.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_default_views().
|
||||
*/
|
||||
function workflow_views_views_default_views() {
|
||||
$view = new view;
|
||||
$view->name = 'workflow_summary';
|
||||
$view->description = 'See which posts are in which workflow state.';
|
||||
$view->tag = 'workflow';
|
||||
$view->view_php = '';
|
||||
$view->base_table = 'node';
|
||||
$view->is_cacheable = FALSE;
|
||||
$view->api_version = 2;
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
$handler = $view->new_display('default', 'Defaults', 'default');
|
||||
$handler->override_option('fields', array(
|
||||
'sid' => array(
|
||||
'label' => 'Current state',
|
||||
'exclude' => 0,
|
||||
'id' => 'sid',
|
||||
'table' => 'workflow_node',
|
||||
'field' => 'sid',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'title' => array(
|
||||
'label' => 'Title',
|
||||
'link_to_node' => 1,
|
||||
'exclude' => 0,
|
||||
'id' => 'title',
|
||||
'table' => 'node',
|
||||
'field' => 'title',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'type' => array(
|
||||
'label' => 'Type',
|
||||
'link_to_node' => 0,
|
||||
'exclude' => 0,
|
||||
'id' => 'type',
|
||||
'table' => 'node',
|
||||
'field' => 'type',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
$handler->override_option('filters', array(
|
||||
'sid' => array(
|
||||
'operator' => 'in',
|
||||
'value' => array(),
|
||||
'group' => '0',
|
||||
'exposed' => TRUE,
|
||||
'expose' => array(
|
||||
'use_operator' => 0,
|
||||
'operator' => 'sid_op',
|
||||
'identifier' => 'sid',
|
||||
'label' => 'Current State',
|
||||
'optional' => 1,
|
||||
'single' => 1,
|
||||
'remember' => 0,
|
||||
'reduce' => 0,
|
||||
),
|
||||
'id' => 'sid',
|
||||
'table' => 'workflow_node',
|
||||
'field' => 'sid',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'type' => array(
|
||||
'operator' => 'in',
|
||||
'value' => array(),
|
||||
'group' => '0',
|
||||
'exposed' => TRUE,
|
||||
'expose' => array(
|
||||
'use_operator' => 0,
|
||||
'operator' => 'type_op',
|
||||
'identifier' => 'type',
|
||||
'label' => 'Content Type',
|
||||
'optional' => 1,
|
||||
'single' => 1,
|
||||
'remember' => 0,
|
||||
'reduce' => 0,
|
||||
),
|
||||
'id' => 'type',
|
||||
'table' => 'node',
|
||||
'field' => 'type',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
$handler->override_option('access', array(
|
||||
'type' => 'perm',
|
||||
'perm' => 'access workflow summary views',
|
||||
));
|
||||
$handler->override_option('title', 'Workflow summary');
|
||||
$handler->override_option('items_per_page', 25);
|
||||
$handler->override_option('use_pager', '1');
|
||||
$handler->override_option('style_plugin', 'table');
|
||||
$handler->override_option('style_options', array(
|
||||
'grouping' => '',
|
||||
'override' => 1,
|
||||
'sticky' => 0,
|
||||
'order' => 'asc',
|
||||
'columns' => array(
|
||||
'state' => 'state',
|
||||
'title' => 'title',
|
||||
),
|
||||
'info' => array(
|
||||
'state' => array(
|
||||
'sortable' => 1,
|
||||
'separator' => '',
|
||||
),
|
||||
'title' => array(
|
||||
'sortable' => 0,
|
||||
'separator' => '',
|
||||
),
|
||||
),
|
||||
'default' => 'state',
|
||||
));
|
||||
$handler = $view->new_display('page', 'Page', 'page_1');
|
||||
$handler->override_option('path', 'workflow/summary');
|
||||
$handler->override_option('menu', array(
|
||||
'type' => 'default tab',
|
||||
'title' => 'Summary',
|
||||
'weight' => '-1',
|
||||
));
|
||||
$handler->override_option('tab_options', array(
|
||||
'type' => 'normal',
|
||||
'title' => 'Workflow summary',
|
||||
'weight' => '0',
|
||||
));
|
||||
|
||||
$views[$view->name] = $view;
|
||||
|
||||
$view = new view;
|
||||
$view->name = 'workflow_pending';
|
||||
$view->description = 'Shows upcoming state changes.';
|
||||
$view->tag = 'workflow';
|
||||
$view->view_php = '';
|
||||
$view->base_table = 'node';
|
||||
$view->is_cacheable = FALSE;
|
||||
$view->api_version = 2;
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
$handler = $view->new_display('default', 'Defaults', 'default');
|
||||
$handler->override_option('fields', array(
|
||||
'title' => array(
|
||||
'label' => 'Title',
|
||||
'link_to_node' => 1,
|
||||
'exclude' => 0,
|
||||
'id' => 'title',
|
||||
'table' => 'node',
|
||||
'field' => 'title',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'sid' => array(
|
||||
'label' => 'Current state',
|
||||
'exclude' => 0,
|
||||
'id' => 'sid',
|
||||
'table' => 'workflow_node',
|
||||
'field' => 'sid',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'sid_1' => array(
|
||||
'label' => 'Scheduled state',
|
||||
'exclude' => 0,
|
||||
'id' => 'sid_1',
|
||||
'table' => 'workflow_scheduled_transition',
|
||||
'field' => 'sid',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'scheduled' => array(
|
||||
'label' => 'Scheduled time',
|
||||
'date_format' => 'small',
|
||||
'custom_date_format' => '',
|
||||
'exclude' => 0,
|
||||
'id' => 'scheduled',
|
||||
'table' => 'workflow_scheduled_transition',
|
||||
'field' => 'scheduled',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'comment' => array(
|
||||
'label' => 'Scheduled comment',
|
||||
'exclude' => 0,
|
||||
'id' => 'comment',
|
||||
'table' => 'workflow_scheduled_transition',
|
||||
'field' => 'comment',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
$handler->override_option('sorts', array(
|
||||
'scheduled' => array(
|
||||
'order' => 'ASC',
|
||||
'granularity' => 'second',
|
||||
'id' => 'scheduled',
|
||||
'table' => 'workflow_scheduled_transition',
|
||||
'field' => 'scheduled',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
$handler->override_option('filters', array(
|
||||
'scheduled' => array(
|
||||
'operator' => '>=',
|
||||
'value' => array(
|
||||
'type' => '',
|
||||
'value' => '-1 hour',
|
||||
'min' => '-1 hour',
|
||||
'max' => '',
|
||||
),
|
||||
'group' => '0',
|
||||
'exposed' => FALSE,
|
||||
'expose' => array(
|
||||
'operator' => FALSE,
|
||||
'label' => '',
|
||||
),
|
||||
'id' => 'scheduled',
|
||||
'table' => 'workflow_scheduled_transition',
|
||||
'field' => 'scheduled',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
$handler->override_option('access', array(
|
||||
'type' => 'perm',
|
||||
'perm' => 'access workflow summary views',
|
||||
));
|
||||
$handler->override_option('title', 'Workflow Pending Changes');
|
||||
$handler->override_option('items_per_page', 25);
|
||||
$handler->override_option('use_pager', '1');
|
||||
$handler->override_option('style_plugin', 'table');
|
||||
$handler = $view->new_display('page', 'Page', 'page_1');
|
||||
$handler->override_option('path', 'workflow/pending');
|
||||
$handler->override_option('menu', array(
|
||||
'type' => 'tab',
|
||||
'title' => 'Pending',
|
||||
'weight' => '0',
|
||||
));
|
||||
$handler->override_option('tab_options', array(
|
||||
'type' => 'none',
|
||||
'title' => '',
|
||||
'weight' => 0,
|
||||
));
|
||||
|
||||
$views[$view->name] = $view;
|
||||
return $views;
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views argument handler for workflow.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Argument handler to accept a node type.
|
||||
*/
|
||||
class views_handler_argument_workflow_state extends views_handler_argument {
|
||||
function construct() {
|
||||
parent::construct('type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of summary_name(). Get the user-friendly version
|
||||
* of the workflow state.
|
||||
*/
|
||||
function summary_name($data) {
|
||||
return $this->workflow_states($data->{$this->name_alias});
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the behavior of title(). Get the user-friendly version of the
|
||||
* workflow state.
|
||||
*/
|
||||
function title() {
|
||||
return $this->workflow_states($this->argument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function, to gather the workflow name from a given arguement.
|
||||
*/
|
||||
function workflow_states($sid) {
|
||||
if (empty($sid)) {
|
||||
return t('No state');
|
||||
}
|
||||
static $states;
|
||||
if (!isset($states)) {
|
||||
// Get all, even the non-active states, filter by sid.
|
||||
foreach (workflow_get_workflow_states() as $state) {
|
||||
$states[$state->sid] = check_plain($state->state);
|
||||
}
|
||||
}
|
||||
$output = $states[$sid];
|
||||
if (empty($output)) {
|
||||
$output = t('No state');
|
||||
}
|
||||
return check_plain($output);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide views field handler for workflow.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler to provide simple status name or renderer.
|
||||
*/
|
||||
class workflow_views_handler_field_sid extends views_handler_field {
|
||||
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['value'] = array('default' => FALSE, 'bool' => TRUE);
|
||||
return $options;
|
||||
}
|
||||
|
||||
function options_form(&$form, &$form_state) {
|
||||
$form['value'] = array(
|
||||
'#title' => t('Display value'),
|
||||
'#description' => t('If checked, row field value will be displayed.'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->options['value'],
|
||||
);
|
||||
parent::options_form($form, $form_state);
|
||||
}
|
||||
|
||||
function render($values) {
|
||||
if ($this->options['value']) {
|
||||
if (empty($values->{$this->field_alias})) {
|
||||
return NULL;
|
||||
}
|
||||
return $values->{$this->field_alias};
|
||||
}
|
||||
else {
|
||||
if (empty($values->{$this->field_alias})) {
|
||||
return t('No state');
|
||||
}
|
||||
static $states;
|
||||
if (!isset($states)) {
|
||||
$states = array();
|
||||
foreach (workflow_get_workflow_states() as $state) {
|
||||
$states[$state->sid] = $state->state;
|
||||
}
|
||||
}
|
||||
$output = t($states[$values->{$this->field_alias}]);
|
||||
if (empty($output)) {
|
||||
$output = t('Unknown state');
|
||||
}
|
||||
return check_plain($output);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// $Id$
|
||||
/**
|
||||
* @file
|
||||
* Provide views field handler for workflow.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler to allow linking to a user account
|
||||
*/
|
||||
class workflow_views_handler_field_username extends views_handler_field {
|
||||
/**
|
||||
* Override init function to add uid field.
|
||||
*/
|
||||
function init(&$view, &$data) {
|
||||
parent::init($view, $data);
|
||||
$this->additional_fields['uid'] = 'uid';
|
||||
}
|
||||
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['link_to_user'] = array('default' => TRUE);
|
||||
return $options;
|
||||
}
|
||||
|
||||
function options_form(&$form, &$form_state) {
|
||||
$form['link_to_user'] = array(
|
||||
'#title' => t("Link this field to its user"),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->options['link_to_user'],
|
||||
);
|
||||
parent::options_form($form, $form_state);
|
||||
}
|
||||
|
||||
function render_link($data, $values) {
|
||||
if (!empty($this->options['link_to_user']) && $this->get_value($values, 'uid') > 0) {
|
||||
$account = user_load($this->get_value($values, 'uid'));
|
||||
|
||||
return theme('username', array(
|
||||
'account' => $account
|
||||
));
|
||||
}
|
||||
else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
function render($values) {
|
||||
$value = $this->get_value($values);
|
||||
return $this->render_link($this->sanitize_value($value), $values);
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provide views filter handler for workflow.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter by state.
|
||||
*/
|
||||
class workflow_views_handler_filter_sid extends views_handler_filter_in_operator {
|
||||
var $value_form_type = 'select';
|
||||
|
||||
function get_value_options() {
|
||||
if (!isset($this->value_options)) {
|
||||
$this->value_title = t('Workflow state');
|
||||
$workflows = array();
|
||||
foreach (workflow_get_workflows() as $data) {
|
||||
$workflows[$data->wid] = check_plain(t($data->name));
|
||||
}
|
||||
if (!isset($workflows) || empty($workflows)) {
|
||||
$this->value_options = array();
|
||||
return;
|
||||
}
|
||||
$options = array('status' => 1);
|
||||
if (count($workflows) > 1) {
|
||||
$states = array('' => t('No state'));
|
||||
foreach ($workflows as $wid => $name) {
|
||||
foreach (workflow_get_workflow_states_by_wid($wid, $options) as $state) {
|
||||
$states[$state->sid] = check_plain($name . ': ' . $state->state);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (workflow_get_workflow_states($options) as $state) {
|
||||
$states[$state->sid] = check_plain($state->state);
|
||||
}
|
||||
}
|
||||
$this->value_options = $states;
|
||||
}
|
||||
}
|
||||
|
||||
function query() {
|
||||
$value = $this->is_a_group() && !$this->options['expose']['multiple'] ? drupal_array_merge_deep_array($this->value) : $this->value;
|
||||
if (empty($value)) {
|
||||
return;
|
||||
}
|
||||
$this->ensure_my_table();
|
||||
$placeholder = !empty($this->definition['numeric']) ? '%d' : "'%s'";
|
||||
if (count($value) == 1) {
|
||||
$this->operator = ($this->operator == 'in') ? '= ' : '!= ';
|
||||
$in = !empty($this->definition['numeric']) ? '%d' : "'%s'";
|
||||
}
|
||||
|
||||
if ($this->operator == 'empty' || $this->operator == 'not empty') {
|
||||
$value = NULL;
|
||||
if ($this->operator == 'empty') {
|
||||
$this->operator = "IS NULL";
|
||||
}
|
||||
else {
|
||||
$this->operator = "IS NOT NULL";
|
||||
}
|
||||
}
|
||||
|
||||
$this->query->add_where($this->options['group'], $this->table_alias . '.' . $this->real_field, $value, $this->operator);
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
name = Workflow views
|
||||
description = Provides views integration for workflows.
|
||||
package = Workflow
|
||||
core = 7.x
|
||||
dependencies[] = workflow
|
||||
dependencies[] = views
|
||||
files[] = includes/workflow_views.views.inc
|
||||
files[] = includes/workflow_views.views_default.inc
|
||||
files[] = includes/workflow_views_handler_filter_sid.inc
|
||||
files[] = includes/workflow_views_handler_field_sid.inc
|
||||
files[] = includes/workflow_views_handler_field_username.inc
|
||||
files[] = includes/workflow_views_handler_argument_state.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-04
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "workflow"
|
||||
datestamp = "1372980654"
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views integration for workflows.
|
||||
* Why it's own module? Some sites have views some don't,
|
||||
* all prefer a lower code footprint and better performance.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function workflow_views_permission() {
|
||||
return array(
|
||||
'access workflow summary views' => array(
|
||||
'title' => t('Access workflow summary views'),
|
||||
'description' => t('Access workflow summary views.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function workflow_views_views_api() {
|
||||
return array(
|
||||
'api' => 2,
|
||||
'path' => drupal_get_path('module', 'workflow_views') . '/includes',
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user