From 93a85954a6e2b78c2ebf1620a01b2760edf76aea Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Wed, 26 Jul 2017 15:06:45 +0200 Subject: [PATCH] New module 'Storm Quick TimeTracker' --- sites/all/modules/storm_quicktt/CHANGELOG.txt | 102 +++ sites/all/modules/storm_quicktt/LICENSE.txt | 339 +++++++++ sites/all/modules/storm_quicktt/README.txt | 52 ++ .../modules/storm_quicktt/jquery.timers.js | 138 ++++ .../storm_quicktt/storm_quicktt.admin.inc | 38 ++ .../modules/storm_quicktt/storm_quicktt.css | 28 + .../modules/storm_quicktt/storm_quicktt.info | 15 + .../modules/storm_quicktt/storm_quicktt.js | 128 ++++ .../storm_quicktt/storm_quicktt.module | 644 ++++++++++++++++++ 9 files changed, 1484 insertions(+) create mode 100644 sites/all/modules/storm_quicktt/CHANGELOG.txt create mode 100644 sites/all/modules/storm_quicktt/LICENSE.txt create mode 100644 sites/all/modules/storm_quicktt/README.txt create mode 100644 sites/all/modules/storm_quicktt/jquery.timers.js create mode 100644 sites/all/modules/storm_quicktt/storm_quicktt.admin.inc create mode 100644 sites/all/modules/storm_quicktt/storm_quicktt.css create mode 100644 sites/all/modules/storm_quicktt/storm_quicktt.info create mode 100644 sites/all/modules/storm_quicktt/storm_quicktt.js create mode 100644 sites/all/modules/storm_quicktt/storm_quicktt.module diff --git a/sites/all/modules/storm_quicktt/CHANGELOG.txt b/sites/all/modules/storm_quicktt/CHANGELOG.txt new file mode 100644 index 0000000..7b6fba0 --- /dev/null +++ b/sites/all/modules/storm_quicktt/CHANGELOG.txt @@ -0,0 +1,102 @@ +/* $Id*/ + +storm_quicktt 6.x-2.x +------------------------------------------------------- +- FEATURE by kfritsche: Added pause-split option, which creates seperate tt if tt is paused, instead of using billable default +- TASK by kfritsche: Added other JS behaviours will be called after a Request +- TASK by kfritsche: Fixed if default not billable, pause splits timetracking instead of setting billing_duration +- TASK by kfritsche: Moved timetracking trigger from storm dashboard +- TASK by kfritsche: Refactoring of module +- TASK #1397960 by kfritsche: Changes needed for stormattributes moved into storm + +storm_quicktt 6.x-1.x +------------------------------------------------------- +- BUG by kfritsche: Fixed sometimes currently tracking project wasn't selected in block form +- BUG #853856 by carstenmueller: Fixed drupal_get_messages() called too early +- BUG #858194 by cmueller: Fixed Bug note submitting doesn't work with enter +- TASK kfritsche: Added Order and added timetracking info, even without storm_dashboard +- FEATURE #1118902 by ericbroder: Added Alphabetically sort for Storm Projects +- BUG #651578: Turn async AJAX off +- FEATURE #682992 by JGonzalez: Include organization titles in the select box to better identify projects + +storm_quicktt 6.x-1.0, 2009-12-07 +--------------------------------- +- Intial Public Release + +storm_quicktt 6.x-1.x, 2009-11-26 (development release) +------------------------------------------------------- +- FEATURE #643452 by jalama: Allow default value for billable + +storm_quicktt 6.x-1.x, 2009-11-03 (development release) +------------------------------------------------------- +- BUG #623968 by kewlguy: fixed a problem with strpos() + +storm_quicktt 6.x-1.x, 2009-11-03 (development release) +------------------------------------------------------- +- BUG #622272 by kewlguy: RDF requires ajax data to be encapsulated in wellformed tags +- FEATURE: Updated jQuery.timers from version 1.1.3 to 1.2 +- FEATURE: Renamed the jQuery.timers file so that it's version independant +- FEATURE: Integrated a small textfield while timetracking is active to allow capturing of notes. These notes get included into the boy field of the timetracking node and can later be edited as usual. +- BUG: Make sure that timer for updating elapsed time gets properly stopped when timetracking gets stopped. +- ISSUE: Trying to avoid duplicate timer-events if the same page is open in more than one browser tab, not resolved yet. This doesn't cause a real issue with the data, but it causes too much traffic between the browser and the server. +- IMPORTANT: Please also update the module storm_dashboard!!! + +storm_quicktt 6.x-1.x, 2009-09-17 (development release) +------------------------------------------------------- +- BUG: Make sure that JS, CSS and settings only get loaded once even if QuickTT and Dashboard are installed +- BUG: The "(Paused)" info wasn't always been displayed properly + +storm_quicktt 6.x-1.x, 2009-09-16 (development release) +------------------------------------------------------- +- BUG: Edit link for created timetracking node has the wrong destination parameter for after editing the node +- API: Made storm_quicktt_add_resources() available to load necessary resources even if the QuickTT block isn't visible +- BUG 578726 by gnu4ever and GiorgosK: Provide the URL for JQuery AJAX dynamically + +storm_quicktt 6.x-1.x, 2009-09-15 (development release) +------------------------------------------------------- +- FEATURE 346131 by Jay Matwichuk, Mark_Watson27 and Magnity: implemented Pause and Resume for timetracking +- FEATURE: Replaced AJAX by JQuery, that became necessary because of implementation of the above feature +- FEATURE: Implemented a JQuery timer and the tracked time gets updated once a minute to the database +- FEATURE: If storm_dashboard is installed, the QuickTT block only displays the project selection and the details are delegated to the bottom left info box of storm_dashboard +- FEATURE: QuickTT can be used as an API without displaying the block. E.g. use storm_dashboard to start/stop/pause/resume timetracking and forget about the visible part of storm_quicktt + +storm_quicktt 6.x-1.x, 2009-09-11 (development release) +------------------------------------------------------- +- FEATURE/BUG 574266 by Magnity: Show the block only if the current user has permission to add timetracking + +storm_quicktt 6.x-1.x, 2009-09-10 (development release) +------------------------------------------------------- +- BUG: Make sure only projects are displayed that the current user has access to +- FEATURE: Use the org/project/task/ticket name into the title of the timetracking record + +storm_quicktt 6.x-1.x, 2009-09-06 (development release) +------------------------------------------------------- +- Also moved settings into /admin/setting/storm/..., following the main Storm module +- Added new function to retreive the nid for the current timetracking object, required by storm_dashboard + +storm_quicktt 6.x-1.x, 2009-09-04 (development release) +------------------------------------------------------- +- Integrated storm_quicktt with storm_dashboard and therefore allowed tracking for organizations, projects, tasks and tickets +- Set default to "in progress" for projects to be shown in the block + +storm_quicktt 6.x-1.x, 2009-08-17 (development release) +------------------------------------------------------- +- Default "Auto-Start Tracking" to TRUE at the beginning of a session. + +storm_quicktt 6.x-1.x, 2009-08-15 (development release) +------------------------------------------------------- +- Implemented checkbox "Auto-Start Tracking". If not selected, the project list can be used to go to a project page instead of startig time-tracking + +storm_quicktt 6.x-1.x, 2009-08-07 (development release) +------------------------------------------------------- +- Changed a typo with regard to the array_shift error + +storm_quicktt 6.x-1.x, 2009-08-06 (development release) +------------------------------------------------------- +- Workaround for rare bug in AHAH where $form['#parameters'] sometimes don't seem to be an array +- The settings now only display "real" statuses for projects +- Timetracking is now saved for the current user, not anonymously + +storm_quicktt 6.x-1.x, 2009-08-04 (development release) +------------------------------------------------------- +- Initial development release diff --git a/sites/all/modules/storm_quicktt/LICENSE.txt b/sites/all/modules/storm_quicktt/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/sites/all/modules/storm_quicktt/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/sites/all/modules/storm_quicktt/README.txt b/sites/all/modules/storm_quicktt/README.txt new file mode 100644 index 0000000..f08e966 --- /dev/null +++ b/sites/all/modules/storm_quicktt/README.txt @@ -0,0 +1,52 @@ +/* $Id*/ + +-- SUMMARY -- + +Storm (http://drupal.org/project/storm) is a nice and handy project management +suite for Drupal and it comes with organizations, projects, people, invoices, +tasks, notes, and timetracking. This module extends the timetracking functionality +by providing a simple block which displays a list on "in progress" projects. The +user just selects one of those projects when he/she starts working on that project +and as soon as he/she selects a different project, this module creates a records +for timetracking and allows quick access to that record to fill in further details. + +In combination with the genius module Storm Dashboard (http://drupal.org/project/storm_dashboard) +the timetracking gets even simpler as it becomes available for all relevant node types +for Storm, i.e. organizations, projects, tasks and tickets. + +-- REQUIREMENTS -- + +* Drupal 6 and Storm. + +-- INSTALLATION -- + +* Install as usual, see http://drupal.org/node/70151 for further information. + +-- CONFIGURATION -- + +* Configure Storm_QuickTT in Administer >> Storm >> Storm Quick TT + +* Make the Storm-QuickTT block visible in a region of your site in Structure >> blocks + +-- CUSTOMIZATION -- + +None + +-- TROUBLESHOOTING -- + +Not known yet + +-- FAQ -- + +Yet to come + +-- CONTACT -- + +Current maintainer: +* Jürgen Haas (jurgenhaas) - http://drupal.org/user/168924 + +This project has been sponsored by: +* PARAGON Executive Services GmbH + Providing IT services as individual as the requirements. Find out more + from http://www.paragon-es.de + diff --git a/sites/all/modules/storm_quicktt/jquery.timers.js b/sites/all/modules/storm_quicktt/jquery.timers.js new file mode 100644 index 0000000..6803250 --- /dev/null +++ b/sites/all/modules/storm_quicktt/jquery.timers.js @@ -0,0 +1,138 @@ +/** + * jQuery.timers - Timer abstractions for jQuery + * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com) + * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/). + * Date: 2009/10/16 + * + * @author Blair Mitchelmore + * @version 1.2 + * + **/ + +jQuery.fn.extend({ + everyTime: function(interval, label, fn, times) { + return this.each(function() { + jQuery.timer.add(this, interval, label, fn, times); + }); + }, + oneTime: function(interval, label, fn) { + return this.each(function() { + jQuery.timer.add(this, interval, label, fn, 1); + }); + }, + stopTime: function(label, fn) { + return this.each(function() { + jQuery.timer.remove(this, label, fn); + }); + } +}); + +jQuery.extend({ + timer: { + global: [], + guid: 1, + dataKey: "jQuery.timer", + regex: /^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/, + powers: { + // Yeah this is major overkill... + 'ms': 1, + 'cs': 10, + 'ds': 100, + 's': 1000, + 'das': 10000, + 'hs': 100000, + 'ks': 1000000 + }, + timeParse: function(value) { + if (value == undefined || value == null) + return null; + var result = this.regex.exec(jQuery.trim(value.toString())); + if (result[2]) { + var num = parseFloat(result[1]); + var mult = this.powers[result[2]] || 1; + return num * mult; + } else { + return value; + } + }, + add: function(element, interval, label, fn, times) { + var counter = 0; + + if (jQuery.isFunction(label)) { + if (!times) + times = fn; + fn = label; + label = interval; + } + + interval = jQuery.timer.timeParse(interval); + + if (typeof interval != 'number' || isNaN(interval) || interval < 0) + return; + + if (typeof times != 'number' || isNaN(times) || times < 0) + times = 0; + + times = times || 0; + + var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {}); + + if (!timers[label]) + timers[label] = {}; + + fn.timerID = fn.timerID || this.guid++; + + var handler = function() { + if ((++counter > times && times !== 0) || fn.call(element, counter) === false) + jQuery.timer.remove(element, label, fn); + }; + + handler.timerID = fn.timerID; + + if (!timers[label][fn.timerID]) + timers[label][fn.timerID] = window.setInterval(handler,interval); + + this.global.push( element ); + + }, + remove: function(element, label, fn) { + var timers = jQuery.data(element, this.dataKey), ret; + + if ( timers ) { + + if (!label) { + for ( label in timers ) + this.remove(element, label, fn); + } else if ( timers[label] ) { + if ( fn ) { + if ( fn.timerID ) { + window.clearInterval(timers[label][fn.timerID]); + delete timers[label][fn.timerID]; + } + } else { + for ( var fn in timers[label] ) { + window.clearInterval(timers[label][fn]); + delete timers[label][fn]; + } + } + + for ( ret in timers[label] ) break; + if ( !ret ) { + ret = null; + delete timers[label]; + } + } + + for ( ret in timers ) break; + if ( !ret ) + jQuery.removeData(element, this.dataKey); + } + } + } +}); + +jQuery(window).bind("unload", function() { + jQuery.each(jQuery.timer.global, function(index, item) { + jQuery.timer.remove(item); + }); +}); \ No newline at end of file diff --git a/sites/all/modules/storm_quicktt/storm_quicktt.admin.inc b/sites/all/modules/storm_quicktt/storm_quicktt.admin.inc new file mode 100644 index 0000000..4cab9e7 --- /dev/null +++ b/sites/all/modules/storm_quicktt/storm_quicktt.admin.inc @@ -0,0 +1,38 @@ + 'textfield', + '#title' => t('Minimum time to be recorded (minutes)'), + '#default_value' => variable_get('storm_quicktt_mintime2record', 5), + ); + $form['storm_quicktt_default_title'] = array( + '#type' => 'textfield', + '#title' => t('Default title'), + '#default_value' => variable_get('storm_quicktt_default_title', 'Auto-created by QuickTT'), + ); + $status_option_prj = array('none' => '-none-'); + $project_status = storm_attributes_bydomain('Project status'); + $status_option_prj = array_merge($status_option_prj, $project_status['values']); + + $form['storm_quicktt_project_stati'] = array( + '#type' => 'select', + '#title' => t('Display projects with following status'), + '#options' => $status_option_prj, + '#multiple' => TRUE, + '#size' => 5, + '#default_value' => variable_get('storm_quicktt_project_stati', array('in progress')), + '#description' => t('Select multiple values by pressing the ctrl-key while selecting with the mouse.'), + ); + + $form['storm_quicktt_pause_split'] = array( + '#type' => 'checkbox', + '#title' => t('Split timetrackings on pause.'), + '#description' => t('If selected there are multiple timetrackings for before and after each break. '. + 'If unset there is one timetracking for the whole time, but the pause time will be subtracted from the billing duration.'), + '#default_value' => variable_get('storm_quicktt_pause_split', FALSE), + ); + + return system_settings_form($form); +} \ No newline at end of file diff --git a/sites/all/modules/storm_quicktt/storm_quicktt.css b/sites/all/modules/storm_quicktt/storm_quicktt.css new file mode 100644 index 0000000..24457f0 --- /dev/null +++ b/sites/all/modules/storm_quicktt/storm_quicktt.css @@ -0,0 +1,28 @@ +#stormquicktt #edit-selects-projects, .storm-quicktt-info .note input {width:90%;} +.storm-quicktt-pause:hover, .storm-quicktt-resume:hover {cursor:pointer;} +.storm-quicktt-timer-box .storm-quicktt-resume {font-weight:bold;} +.storm-quicktt-timer-box .storm-quicktt-resume:before {content:' (';} +.storm-quicktt-timer-box .storm-quicktt-resume:after {content:'!)';} + +.sqtt-timetracking-trigger img{ + width:16px; + height:16px; + margin-bottom: -2px; +} +.sqtt-timetracking-trigger:hover{ + cursor:pointer; +} +.block .sqtt-timetracking-trigger { + font-size: 0; +} + +.timetracking-loading span { + text-decoration:blink; + font-size:25px; +} +.timetracking-loading span:before { + content:'('; +} +.timetracking-loading span:after { + content:')'; +} \ No newline at end of file diff --git a/sites/all/modules/storm_quicktt/storm_quicktt.info b/sites/all/modules/storm_quicktt/storm_quicktt.info new file mode 100644 index 0000000..d688764 --- /dev/null +++ b/sites/all/modules/storm_quicktt/storm_quicktt.info @@ -0,0 +1,15 @@ +name = Storm Quick TimeTracker +description = Get a block for permanent and quick timetracking for Storm projects +package = Storm +core = 6.x +php = 5 +dependencies[] = storm +dependencies[] = stormproject +dependencies[] = stormtimetracking + +; Information added by drupal.org packaging script on 2012-03-25 +version = "6.x-2.x-dev" +core = "6.x" +project = "storm_quicktt" +datestamp = "1332637511" + diff --git a/sites/all/modules/storm_quicktt/storm_quicktt.js b/sites/all/modules/storm_quicktt/storm_quicktt.js new file mode 100644 index 0000000..d458b08 --- /dev/null +++ b/sites/all/modules/storm_quicktt/storm_quicktt.js @@ -0,0 +1,128 @@ +(function ($) { + +Drupal.admin = Drupal.admin || {}; + +Drupal.behaviors.stormquicktt = function (context) { + Drupal.admin.stormquickttInit(); +}; + +Drupal.admin.stormquickttInit = function () { + $('#stormquicktt #edit-selects-projects:not(.storm-quicktt-processed)') + .addClass('storm-quicktt-processed') + .change(function () { + $('.storm-quicktt-timer').stopTime(); + var id = this.value; + var x = $('#stormquicktt #edit-selects-mode')[0]; + var mode = $('#stormquicktt #edit-selects-mode')[0].checked; + Drupal.admin.stormquickttInteract("startstop", id, mode); + }); + $('.storm-quicktt-pause:not(.storm-quicktt-processed)') + .addClass('storm-quicktt-processed') + .click(function () { + Drupal.admin.stormquickttInteract("pause", 0, 0); + }); + $('.storm-quicktt-resume:not(.storm-quicktt-processed)') + .addClass('storm-quicktt-processed') + .click(function () { + Drupal.admin.stormquickttInteract("resume", 0, 0); + }); + $('#edit-storm-quicktt-note:not(.storm-quicktt-processed)') + .addClass('storm-quicktt-processed') + .bind('blur keypress', function (event) { + if (event.type == 'keypress' && event.keyCode != 13) + return; + if (this.value == '') + return; + Drupal.admin.stormquickttInteract("note", this.value, 0); + this.value = ''; + $(this).focus(); + if (event.type == 'keypress' && event.keyCode == 13) + return false; + }); + $('.sqtt-timetracking-trigger:not(.storm-quicktt-processed)') + .addClass('storm-quicktt-processed') + .click(function () { + var info = this.getAttribute('id'); + $(this).addClass('timetracking-loading'); + $('.storm-quicktt-timer').stopTime(); + Drupal.admin.stormquickttInteract("triggerTimetracking", info, 0, this); + }); + + Drupal.admin.stormquickttInitTimer(); +}; + +Drupal.admin.stormquickttInitTimer = function () { + $('.storm-quicktt-timer').everyTime(60000, function(i) { + Drupal.admin.stormquickttInteract("update", 0, 0); + }); +}; + +Drupal.admin.stormquickttInteract = function (_op, _nid, _mode, _context) { + $.ajax({ + url: Drupal.settings.storm_quicktt_url, + async: false, + global: false, + type: "POST", + data: ({ + op : _op, + nid : _nid, + mode: _mode + }), + dataType: "html", + complete: function(XMLHttpRequest) { + + }, + success: function (answer) { + if (answer=='OK') { + switch (_op) { + case 'pause': + $('.storm-quicktt-pause').hide(); + $('.storm-quicktt-resume').show(); + break; + case 'resume': + $('.storm-quicktt-pause').show(); + $('.storm-quicktt-resume').hide(); + break; + } + } + else { + switch (_op) { + case 'update': + if (answer == '') { + //This shouldn't happen + } + else { + $('.storm-quicktt-timer').stopTime(); + $('.storm-quicktt-timer').html(answer); + Drupal.admin.stormquickttInitTimer(); + } + break; + case 'triggerTimetracking': + answer = Drupal.parseJson(answer); + if (answer.msg) { + $('#sdb-timetracking .content').html(answer.msg); + $('.storm-quicktt-info').html(answer.msg); + } + $("#storm-quicktt-blockform #edit-selects-projects option").removeAttr('selected'); + if (answer.pid) { + $("#storm-quicktt-blockform #edit-selects-projects option[value='"+answer.pid+"']").attr('selected',true); + } + if (answer.trigger) { + $('.'+_nid).replaceWith(answer.trigger); + } + break; + default: + $('#sdb-timetracking').html(answer); + $('.storm-quicktt-info').html(answer); + break; + } + } + Drupal.attachBehaviors($('#stormquicktt')); + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + alert(XMLHttpRequest.statusText); + } + }); +}; + +})(jQuery); diff --git a/sites/all/modules/storm_quicktt/storm_quicktt.module b/sites/all/modules/storm_quicktt/storm_quicktt.module new file mode 100644 index 0000000..778beb1 --- /dev/null +++ b/sites/all/modules/storm_quicktt/storm_quicktt.module @@ -0,0 +1,644 @@ +'. t('Here you can set up parameters for Storm-Quick-TimeTracker.') .'

'; + case 'admin/help#storm_quicktt': + return filter_filter('process', 2, NULL, file_get_contents(dirname(__FILE__) ."/README.txt")); + } +} + +/** + * Implementation of hook_menu(). + */ +function storm_quicktt_menu() { + $items = array(); + $access_config = array('administer site configuration'); + $items['admin/settings/storm/quicktt'] = array( + 'title' => 'Storm Quick TimeTracker', + 'description' => 'Configure Storm Quick TimeTracker', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('storm_quicktt_settings'), + 'access arguments' => $access_config, + 'type' => MENU_LOCAL_TASK, + 'file' => 'storm_quicktt.admin.inc', + ); + $items['stormquicktt/interact'] = array( + 'page callback' => 'storm_quicktt_interact', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +function storm_quicktt_add_resources() { + static $loaded = FALSE; + if (!$loaded) { + drupal_add_css(drupal_get_path('module', 'storm_quicktt') .'/storm_quicktt.css'); + drupal_add_js(drupal_get_path('module', 'storm_quicktt') .'/storm_quicktt.js'); + drupal_add_js(drupal_get_path('module', 'storm_quicktt') .'/jquery.timers.js'); + drupal_add_js(array('storm_quicktt_url' => url('stormquicktt/interact', array('absolute' => TRUE,))), 'setting'); + $loaded = TRUE; + } +} + +/** + * Implementation of hook_block(). + */ +function storm_quicktt_block($op = 'list', $delta = 0, $edit = array()) { + switch ($op) { + case 'list': + $blocks[0]['info'] = t('Storm Quick TimeTracker'); + $blocks[0]['cache'] = BLOCK_NO_CACHE; + return $blocks; + case 'configure': + return; + case 'save': + return; + case 'view': + if (!user_access('Storm timetracking: add')) { + return; + } + storm_quicktt_add_resources(); + $v = variable_get('storm_quicktt_project_stati', array('in progress')); + $sql = "SELECT n.*, spr.* FROM {node} AS n + INNER JOIN {stormproject} AS spr + ON n.vid=spr.vid WHERE n.status=1 AND n.type='stormproject' AND (spr.projectstatus IN ('". implode("','", $v) ."')) + ORDER BY spr.organization_title, n.title"; + $sql = stormproject_access_sql($sql); + $result = db_query($sql); + $options = array('0' => '- No billable work -'); + while ($node = db_fetch_object($result)) { + if (!isset($options[$node->organization_title])) { + $options[$node->organization_title] = array(); + } + $options[$node->organization_title][$node->nid] = $node->title; + } + $form = drupal_get_form('storm_quicktt_blockform', $options); + + $blocks['subject'] = t('Quick Timetracking'); + $blocks['content'] = << + $form + +EOT; + return $blocks; + } +} + +function storm_quicktt_blockform($form_state, $options) { + $nid = storm_quicktt_tracking_active_nid(); + $pid = isset($_SESSION['storm_quicktt_pid']) ? $_SESSION['storm_quicktt_pid'] : 0; + if (!empty($nid) && empty($pid)) { + switch(storm_quicktt_tracking_active_type()) { + case 'prj': + $pid = $nid; + break; + case 'tkt': + case 'tsk': + $node = node_load($nid); + if (!empty($node) && !empty($node->project_nid)) { + $pid = $node->project_nid; + $_SESSION['storm_quicktt_pid'] = $pid; + } + break; + } + } + + $mode = isset($_SESSION['storm_quicktt_mode']) ? $_SESSION['storm_quicktt_mode'] : TRUE; + $form = array(); + $form['#cache'] = TRUE; + $form['selects'] = array( + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + $form['selects']['mode'] = array( + '#type' => 'checkbox', + '#title' => t('Auto-Start Tracking'), + '#default_value' => $mode, + ); + $form['selects']['projects'] = array( + '#type' => 'select', + '#title' => t('Projects'), + '#options' => $options, + '#default_value' => $pid, + '#size' => 10, + ); + if (!module_exists('storm_dashboard')) { + $form['selects']['info'] = array( + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + ); + $form['selects']['info']['time'] = array( + '#type' => 'markup', + '#value' => storm_quicktt_tracking_info(), + ); + } + return $form; +} + +function storm_quicktt_interact() { + if (!isset($_POST['op'])) { + exit; + } + $_GET['q'] = $_SERVER['HTTP_REFERER']; + $op = $_POST['op']; + $nid = $_POST['nid']; + $_SESSION['storm_quicktt_mode'] = ($_POST['mode'] == 'true'); + switch ($op) { + case 'pause': + storm_quicktt_pause_time(); + print 'OK'; + break; + case 'resume': + storm_quicktt_resume_time(); + print 'OK'; + break; + case 'update': + if (!$_SESSION['storm_quicktt_update_active']) { + $_SESSION['storm_quicktt_update_active'] = TRUE; + print storm_quicktt_elapsed_time(); + $_SESSION['storm_quicktt_update_active'] = FALSE; + } + else { + print 'OK'; + } + break; + case 'note': + storm_quicktt_note_for_time($nid); + print 'OK'; + break; + case 'triggerTimetracking': + $oid = 0; + $pid = 0; + $tid = 0; + $kid = 0; + $y = explode('-', $nid); + $nid = $y[2]; + if (!storm_quicktt_tracking_active() || storm_quicktt_tracking_active_nid() != $nid) { + $node = node_load($nid); + switch ($node->type) { + case 'stormticket': + $kid = $nid; + $pid = $node->project_nid; + break; + case 'stormtask': + $tid = $nid; + $pid = $node->project_nid; + break; + case 'stormproject': + $pid = $nid; + break; + case 'stormorganization': + $oid = $nid; + break; + } + } + $msg = storm_quicktt_start($oid, $pid, $tid, $kid, FALSE); + $trigger = storm_quicktt_timetracking_trigger($nid); + $msg .= storm_quicktt_tracking_info(); + print drupal_json(array('msg' => $msg, 'pid' => $pid, 'trigger' => $trigger)); + break; + default: + $msg = ""; + if ($_SESSION['storm_quicktt_mode']) { + $msg = storm_quicktt_start(0, $nid, 0, 0, FALSE); + print $msg . storm_quicktt_tracking_info(); + } + elseif ($nid) { + $pid = isset($_SESSION['storm_quicktt_pid']) ? $_SESSION['storm_quicktt_pid'] : 0; + if ($pid == $nid) { + print $msg . storm_quicktt_tracking_info(); + } + else { + $title = t('Action'); + $link = l(t('Goto Project'), 'node/'. $nid); + $msg .= '
'. $link . '
'; + $msg .= storm_quicktt_timetracking_trigger($nid); + $active_nid = storm_quicktt_tracking_active_nid(); + if ($active_nid) { + $msg .= storm_quicktt_tracking_info(); + } + print $msg; + } + } + else { + $active_nid = storm_quicktt_tracking_active_nid(); + if ($active_nid) { + $msg .= storm_quicktt_tracking_info(); + } + print $msg; + } + + break; + } + exit; +} + +function storm_quicktt_start($oid = 0, $pid = 0, $tid = 0, $kid = 0, $show_msg = TRUE) { + $start = time(); + $msg = ""; + if ($_SESSION['storm_quicktt_oid']) { + $msg = storm_quicktt_store_time($start, $show_msg); + } + $_SESSION['storm_quicktt_note_for_time'] = ''; + if ($kid) { //Ticket-ID is set and we get the other IDs from the node + $node = node_load($kid); + $node->ticket_nid = $node->nid; + } + elseif ($tid) { //Task-ID is set and we get the other IDs from the node + $node = node_load($tid); + $node->task_nid = $node->nid; + } + elseif ($pid) { //Project-ID is set and we get the other IDs from the node + $node = node_load($pid); + $node->project_nid = $node->nid; + } + elseif ($oid) { //Organization-ID is set and we get the other IDs from the node + $node = node_load($oid); + $node->organization_nid = $node->nid; + } + else { //No ID is set and we do only store time if a timetracking was active + $node = new stdClass(); + } + $_SESSION['storm_quicktt_oid'] = (isset($node->organization_nid)) ? $node->organization_nid : 0; + $_SESSION['storm_quicktt_pid'] = (isset($node->project_nid)) ? $node->project_nid : 0; + $_SESSION['storm_quicktt_tid'] = (isset($node->task_nid)) ? $node->task_nid : 0; + $_SESSION['storm_quicktt_kid'] = (isset($node->ticket_nid)) ? $node->ticket_nid : 0; + $_SESSION['storm_quicktt_start'] = (isset($node->organization_nid)) ? $start : 0; + $_SESSION['storm_quicktt_pause_start'] = 0; + $_SESSION['storm_quicktt_pause_collect'] = 0; + return $msg; +} + +function storm_quicktt_pause_time() { + if (!storm_quicktt_tracking_active()) { + return; + } + if (!empty($_SESSION['storm_quicktt_pause_start'])) { + return; + } + if (variable_get('storm_quicktt_pause_split', FALSE)) { + $oid = $_SESSION['storm_quicktt_oid']; + $pid = $_SESSION['storm_quicktt_pid']; + $tid = $_SESSION['storm_quicktt_tid']; + $kid = $_SESSION['storm_quicktt_kid']; + $start = $_SESSION['storm_quicktt_start']; + storm_quicktt_store_time(time(), FALSE); + $_SESSION['storm_quicktt_oid'] = $oid; + $_SESSION['storm_quicktt_pid'] = $pid; + $_SESSION['storm_quicktt_tid'] = $tid; + $_SESSION['storm_quicktt_kid'] = $kid; + $_SESSION['storm_quicktt_start'] = $start; + } + $_SESSION['storm_quicktt_pause_start'] = time(); +} + +function storm_quicktt_resume_time() { + if (empty($_SESSION['storm_quicktt_pause_start'])) { + return; + } + if (variable_get('storm_quicktt_pause_split', FALSE)) { + $_SESSION['storm_quicktt_pause_collect'] += ($_SESSION['storm_quicktt_pause_start'] - $_SESSION['storm_quicktt_start']); + $_SESSION['storm_quicktt_start'] = time(); + } + else { + $_SESSION['storm_quicktt_pause_collect'] += (time() - $_SESSION['storm_quicktt_pause_start']); + } + $_SESSION['storm_quicktt_pause_start'] = 0; +} + +function storm_quicktt_elapsed_time() { + if (!empty($_SESSION['storm_quicktt_pause_start']) && variable_get('storm_quicktt_pause_split', FALSE)) { + if (!empty($_SESSION['storm_quicktt_start'])) { + return '
'. t('Elapsed time: !time min', array('!time' => floor(($_SESSION['storm_quicktt_pause_start'] - $_SESSION['storm_quicktt_start']) / 60))) .'
'; + } + return ''; + } + storm_quicktt_store_time(); + if (!empty($_SESSION['storm_quicktt_ttid'])) { + $now = time(); + if (variable_get('storm_quicktt_pause_split', FALSE) && !empty($_SESSION['storm_quicktt_pause_collect'])) { + $now += $_SESSION['storm_quicktt_pause_collect']; + } + return '
'. t('Elapsed time: !time min', array('!time' => floor(($now - $_SESSION['storm_quicktt_start']) / 60))) .'
'; + } + else { + return ''; + } +} + +function storm_quicktt_store_time($stop = 0, $show_msg = TRUE) { + if (!user_access('Storm timetracking: add')) { + return t('You have no permission to add timetracking.'); + } + global $user; + $oid = $_SESSION['storm_quicktt_oid']; + $pid = $_SESSION['storm_quicktt_pid']; + $tid = $_SESSION['storm_quicktt_tid']; + $kid = $_SESSION['storm_quicktt_kid']; + $start = $_SESSION['storm_quicktt_start']; + $pause = 0; + if ($stop) { + storm_quicktt_resume_time(); + $pause = $_SESSION['storm_quicktt_pause_collect']; + if (variable_get('storm_quicktt_pause_split', FALSE)) { + $pause = 0; + } + unset($_SESSION['storm_quicktt_oid']); + unset($_SESSION['storm_quicktt_pid']); + unset($_SESSION['storm_quicktt_tid']); + unset($_SESSION['storm_quicktt_kid']); + unset($_SESSION['storm_quicktt_start']); + } + if ($oid) { + $finish = ($stop) ? $stop : time(); + $minutes = ceil(($finish-$start) / 60); + if ($minutes >= variable_get('storm_quicktt_mintime2record', 5)) { + if ($kid) { + $type = t('Ticket'); + $node = node_load($kid); + $name = $node->title; + } + elseif ($tid) { + $type = t('Task'); + $node = node_load($tid); + $name = $node->title; + } + elseif ($pid) { + $type = t('Project'); + $node = node_load($pid); + $name = $node->title; + } + elseif ($oid) { + $type = t('Organization'); + $node = node_load($oid); + $name = $node->title; + } + if (!isset($_SESSION['storm_quicktt_ttid'])) { + $node = new stdClass(); + $node->uid = $user->uid; + $node->type = 'stormtimetracking'; + $node->title = $name .' ('. variable_get('storm_quicktt_default_title', 'Auto-created by QuickTT') .')'; + $node->organization_nid = $oid; + $node->project_nid = $pid; + $node->task_nid = $tid; + $node->ticket_nid = $kid; + $node->trackingdate = $start; + $node->timebegin = format_date($start, 'custom', 'H:i'); + $node->billable = variable_get('stormtimetracking_billable_default', FALSE); + $node->billed = FALSE; + } + else { + $node = node_load($_SESSION['storm_quicktt_ttid']); + } + $node->revision = FALSE; + $node->timeend = format_date($finish, 'custom', 'H:i'); + _stormtimetracking_beforesave($node); + $old_stormtimetracking_auto_duration = variable_get('stormtimetracking_auto_duration', TRUE); + variable_set('stormtimetracking_auto_duration', FALSE); + $node->billing_duration = $node->duration - ($pause / 3600); + if (empty($_SESSION['storm_quicktt_note_for_time']) OR empty($node->body)) { + $pos = FALSE; + } + else { + $pos = strpos($_SESSION['storm_quicktt_note_for_time'], $node->body); + } + if ($pos === FALSE) { + $node->body .= $_SESSION['storm_quicktt_note_for_time']; + } + else { + $node->body = $_SESSION['storm_quicktt_note_for_time']; + } + $_SESSION['storm_quicktt_note_for_time'] = $node->body; + node_save($node); + variable_set('stormtimetracking_auto_duration', $old_stormtimetracking_auto_duration); + if ($stop) { + unset($_SESSION['storm_quicktt_ttid']); + } + else { + $_SESSION['storm_quicktt_ttid'] = $node->nid; + return ''; + } + $destination = ($_GET['q'] == 'stormdashboard/interact') ? 'stormdashboard' : $_GET['q']; + $link = l('edit', 'node/'. $node->nid .'/edit', array('query' => array('destination' => $destination))); + $msg = t('Time spent on %type %name: %min minutes (!edit).', array('%type' => $type, '%name' => $name, '%min' => $minutes, '!edit' => $link)); + } + elseif (!$stop) { + return ''; + } + else { + $msg = t('Time spent too short and not recorded.'); + } + } + drupal_get_messages(); //Clear messages to display only the following one. + if ($show_msg) { + drupal_set_message($msg); + } + $_SESSION['storm_quicktt_pause_start'] = 0; + $_SESSION['storm_quicktt_pause_collect'] = 0; + return '
'. $msg .'
'; +} + +function storm_quicktt_note_for_time($text) { + $_SESSION['storm_quicktt_note_for_time'] .= "

". $text ."

\n"; +} + +function storm_quicktt_tracking_active_nid() { + if (!storm_quicktt_tracking_active()) { + return 0; + } + $oid = $_SESSION['storm_quicktt_oid']; + $pid = $_SESSION['storm_quicktt_pid']; + $tid = $_SESSION['storm_quicktt_tid']; + $kid = $_SESSION['storm_quicktt_kid']; + if ($kid) { //Ticket-ID is set and we get the other IDs from the node + return $kid; + } + elseif ($tid) { //Task-ID is set and we get the other IDs from the node + return $tid; + } + elseif ($pid) { //Project-ID is set and we get the other IDs from the node + return $pid; + } + elseif ($oid) { //Organization-ID is set and we get the other IDs from the node + return $oid; + } + else { //No ID is set which shouldn't be possible + return 0; + } +} + +function storm_quicktt_tracking_active_type() { + if (!storm_quicktt_tracking_active()) { + return NULL; + } + $oid = $_SESSION['storm_quicktt_oid']; + $pid = $_SESSION['storm_quicktt_pid']; + $tid = $_SESSION['storm_quicktt_tid']; + $kid = $_SESSION['storm_quicktt_kid']; + if ($kid) { //Ticket-ID is set and we get the other IDs from the node + return 'tkt'; + } + elseif ($tid) { //Task-ID is set and we get the other IDs from the node + return 'tsk'; + } + elseif ($pid) { //Project-ID is set and we get the other IDs from the node + return 'prj'; + } + elseif ($oid) { //Organization-ID is set and we get the other IDs from the node + return 'org'; + } + else { //No ID is set which shouldn't be possible + return NULL; + } +} + +function storm_quicktt_tracking_active() { + return !empty($_SESSION['storm_quicktt_oid']); +} + +function storm_quicktt_tracking_start() { + return storm_quicktt_tracking_active() ? $_SESSION['storm_quicktt_start'] : 0; +} + +function storm_quicktt_tracking_info() { + $output = ''; + if (storm_quicktt_tracking_active()) { + $oid = $_SESSION['storm_quicktt_oid']; + $pid = $_SESSION['storm_quicktt_pid']; + $tid = $_SESSION['storm_quicktt_tid']; + $kid = $_SESSION['storm_quicktt_kid']; + $start = $_SESSION['storm_quicktt_start']; + if ($kid) { //Ticket-ID is set and we get the other IDs from the node + $node = node_load($kid); + $type = t('Ticket'); + } + elseif ($tid) { //Task-ID is set and we get the other IDs from the node + $node = node_load($tid); + $type = t('Task'); + } + elseif ($pid) { //Project-ID is set and we get the other IDs from the node + $node = node_load($pid); + $type = t('Project'); + } + elseif ($oid) { //Organization-ID is set and we get the other IDs from the node + $node = node_load($oid); + $type = t('Organization'); + } + else { //No ID is set which shouldn't be possible + return '
'. t('An error occured.') .'
'; + } + $output .= '
'. t('Started !time', array('!time' => storm_quicktt_start_link())) .'
'; + $output .= '
'. t('Timetracking is active for %type !title.', array('%type' => $type, '!title' => l($node->title, 'node/'.$node->nid))) .'
'; + $output .= '
'. t('Take a note: !noteform', array('!noteform' => storm_quicktt_noteform())) .'
'; + } + else { + $output .= '
'. t('Currently not tracking any time.') .'
'; + } + $output .= ''; + return $output; +} + +function storm_quicktt_start_link() { + if (!empty($_SESSION['storm_quicktt_kid'])) { + $nid = $_SESSION['storm_quicktt_kid']; + } + elseif (!empty($_SESSION['storm_quicktt_tid'])) { + $nid = $_SESSION['storm_quicktt_tid']; + } + elseif (!empty($_SESSION['storm_quicktt_pid'])) { + $nid = $_SESSION['storm_quicktt_pid']; + } + elseif (!empty($_SESSION['storm_quicktt_oid'])) { + $nid = $_SESSION['storm_quicktt_oid']; + } + if (!empty($nid)) { + if (!empty($_SESSION['storm_quicktt_pause_start'])) { + $style_pause = ' style="display:none"'; + $style_resume = ''; + } + else { + $style_pause = ''; + $style_resume = ' style="display:none"'; + } + $text_paused = ''. t('Paused') .''; + $path = drupal_get_path('module', 'storm'); + $title_pause = t('Pause'); + $title_resume = t('Resume'); + + $text = '
'. format_date($_SESSION['storm_quicktt_start']) .'
'; + $text .= '
'. storm_quicktt_elapsed_time() .''. $text_paused .'
'; + $text .= ''. $title_pause .''; + $text .= ''. $title_resume .''; + $text .= storm_quicktt_timetracking_trigger($nid, FALSE); + return $text; + } + return format_date(time()); +} + +function storm_quicktt_noteform() { + return '
'; +} + +/** + * Returns the sanitized clickable icon to start/stop time tracking + */ +function storm_quicktt_timetracking_trigger($nid = 0, $info = TRUE) { + if (!user_access('Storm timetracking: add')) { + return ''; + } + storm_quicktt_add_resources(); + $active = FALSE; + if (!empty($nid) && storm_quicktt_tracking_active() && storm_quicktt_tracking_active_nid() == $nid) { + $active = TRUE; + } + + if (!$active) { + $title = t('Start time tracking.'); + $img_src = url(drupal_get_path('module', 'storm') .'/images/timetrackings.png', array('absolute' => TRUE)); + $class = ""; + $info_txt = ' '.t('Click to start timetracking'); + } + else { + $title = t('Stop time tracking.'); + $img_src = url(variable_get('storm_icons_path', drupal_get_path('module', 'storm') .'/icons') .'/dialog_close.png', array('absolute' => TRUE)); + $class = "timetracking-active"; + $info_txt = ' '.t('Click to stop timetracking'); + } + if (!$info) { + $info_txt = ''; + } + return ''. + ''. $title .''. + '' . $info_txt . ''; +} + +function storm_quicktt_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { + + if (!in_array($node->type, array('stormproject', 'stormorganization', 'stormtask', 'stormticket')) || !user_access('Storm timetracking: add')) { + return; + } + + if ($op == 'view') { + $node->content['links']['storm_quicktt_timetracking_trigger'] = array( + '#value' => storm_quicktt_timetracking_trigger($node->nid), + '#weight' => 10, + ); + } + +} \ No newline at end of file