FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,243 @@
You can extend cron functionality in you modules by using elysia_cron api.
With it you can:
- have more than one cron job per module
- have a different schedule rule for each cron job defined
- set a description for each cron job
To do this you should add in you module a new hook. This is the syntax:
function hook_cronapi($op, $job = NULL) {
$items['key'] = array(
'description' => 'string',
'rule' => 'string',
'weight' => 1234,
'callback' => 'function_name',
'arguments' => array(...),
'file' => 'string', // External file, like hook_menu
'file path' => 'string'
);
$items['key2'] = ...
...
return $items;
}
- 'key' is the identifier for the task you are defining.
You can define a timing for the standard cron hook of the module by using
the "MODULENAME_cron" key. (See examples).
- description: a textual description of the job, used in elysia cron's status
page. Use the untranslated string, without the "t()" wrapper (elysia_cron
will apply it)
- rule: the crontab rule. For example: "*/30 * * * *" to execute the task every
30 minutes.
- weight (optional): a numerical value to define order of execution. (Default:0)
- callback (optional): you can define here a name of a PHP function that should
by called to execute the task. This is not mandatory: if you don't specify
it Elysia cron will search for a function called like the task KEY.
If this function is not found, Elysia cron will call the "hook_cronapi"
function with $op = 'execute' and $job = 'KEY' (the key of the task).
(See examples)
- arguments (optional): an array of arguments passed to callback (only if
callback is defined)
- file/file path: the PHP file that contains the callback (hook_menu's syntax)
-----------------------------------------------------------------------------
EXAMPLES
-----------------------------------------------------------------------------
Some examples...
Example 1: Basic
-----------------
example.module:
function example_cronapi($op, $job = NULL) {
$items['example_sendmail_cron'] = array(
'description' => 'Send mail with news',
'rule' => '0 */2 * * *', // Every 2 hours
// Note: i don't need to define a callback, i'll use "example_sendmail_cron"
// function
);
$items['example_news_cron'] = array(
'description' => 'Send mail with news',
'rule' => '*/5 * * * *', // Every 5 minutes
// i must call: example_news_fetch('all')
'callback' => 'example_news_fetch',
'arguments' => array('all'),
);
return $items;
}
function example_sendmail_cron() {
// ...
}
function example_news_fetch($what) {
// ...
}
Example 2: Embedded code
-------------------------
function example_cronapi($op, $job = NULL) {
if ($op == 'list') {
$items['job1'] = array(
'description' => 'Send mail with news',
'rule' => '0 */2 * * *', // Every 2 hours
);
$items['job2'] = array(
'description' => 'Send mail with news',
'rule' => '*/5 * * * *', // Every 5 minutes
);
}
elseif ($op == 'execute') {
switch ($job) {
case 'job1':
// ... job1 code
break;
case 'job2':
// ... job2 code
break;
}
}
return $items;
}
-----------------------------------------------------------------------------
ALTERING HOOK CRON DEFINITION
-----------------------------------------------------------------------------
You can use the "hook_cron_alter" function to edit cronapi data of other
modules.
Example:
function module_cron_alter($data) {
$data['key']['rule'] = '0 */6 * * *';
}
-----------------------------------------------------------------------------
HANDLING DEFAULT MODULE_CRON FUNCTION
-----------------------------------------------------------------------------
To support standard drupal cron, all cron hooks (*_cron function) are
automatically added to supported jobs, even if you don't declare them
on cronapi hook (or if you don't implement the hook at all).
However you can define job description and job rule in the same way as
above (considering the job as an external function).
Example:
function module_cronapi($op, $job = NULL) {
$items['module_cron'] = array(
'description' => 'Standard cron process',
'rule' => '*/15 * * * *',
)
return $items;
}
function module_cron() {
...
// this is the standard cron hook, but with cronapi above
// it has a default rule (execution every 15 minutes) and
// a description
...
}
-----------------------------------------------------------------------------
THEMING & JOB DESCRIPTION
-----------------------------------------------------------------------------
If you want to have a nicer cron administration page with all modules
description, and assuming only a few modules supports cronapi hooks,
you can add your own description by script (see above) or by
'elysia_cron_description' theme function.
For example, in your phptemplate theme, you can declare:
function phptemplate_elysia_cron_description($job) {
switch($job) {
case 'job 1': return 'First job';
case 'job 2': return 'Second job';
default: return theme_elysia_cron_description($job);
}
}
Note: module default theme_elysia_cron_description($job) already contains
some common tasks descriptions.
-----------------------------------------------------------------------------
OLD 1.x MODULE API (OBSOLETE)
-----------------------------------------------------------------------------
Elysia Cron 2.0 defines the totally new module API you see above. However the
compatibility with old API is mantained.
This is the old format for reference.
function module_cronapi($op, $job = NULL) {
...
}
$op can have 3 values:
- 'list': you should return the list of available jobs, in the form
array(
array( 'job' => 'description' ),
array( 'job' => 'description' ),
...
)
'job' could be the name of a real function or an identifier used with
$op = 'execute' (see below).
Warn: 'job' should be a unique identified, even if it's not a function
name.
- 'rule' : when called with this method, $job variable will contain the
job name you should return the crun rule of.
The rule you return is the default/module preferred schedule rule.
An administrator can always override it to fit his needs.
- 'execute' : when the system needs to call the job task, if no function
with the same of the job exists, it will call the cronapi with this
value and with $job filled with the name of the task to execute.
Example:
Assume your module needs 2 cron tasks: one executed every hour (process_queue)
and one executed once a day (send_summary_mail).
You can do this with this cronapi:
function module_cronapi($op, $job = NULL) {
switch ($op) {
case 'list':
return array(
'module_process_queue' => 'Process queue of new data',
'module_send_summary_mail' => 'Send summary of data processed'
);
case 'rule':
if ($job == 'module_process_queue') return '0 * * * *';
else return '0 1 * * *';
case 'execute':
if ($job == 'module_process_queue') {
... do the job ...
}
// Just for example, module_send_summary_mail is on a separate
// function (see below)
}
}
function module_send_summary_mail() {
... do the job ...
}

View File

@@ -0,0 +1,83 @@
UPGRADE A NEW VERSION
----------------------
If you are upgrading from an old version of elysia_cron you should simply
replace module files. You don't even need to execute the "update.php" drupal
process: Elysia cron will detect the new version on its first run and updates
itself automatically.
INSTALLATION
-------------
For the basic install you only need to enable the module and Elysia Cron will be
up and running.
You can stop here if you don't need a great precision over task execution and
you don't have to execute a task more often than once an hour.
For example, if you need only the "Once a day", "Once a week" or "Once a month"
schedule rules the basic install is fine. (For D6 users that want to stop here:
you should have installed Drupal crontab as described in Drupal INSTALL guide).
Instead, if you need:
- to run some tasks more often than once an hour (eg: you have a function that
should be executed every 5 minutes)
- to execute a task at an exact time (eg:you must run a function at exactly
"17:23")
... you must follow Step B
STEP B: CHANGE SYSTEM CRONTAB (OPTIONAL)
-----------------------------------------
To get the full potential out of elysia cron and have the full control over you
tasks a further step is needed: you need to configure the system crontab to
execute drupal cron every minute.
To do this refer to the section "Configuring cron jobs" of drupal documentation:
http://drupal.org/cron
The only difference is that you should use the "* * * * *" rule part instead of
"0 * * * *" or "45 * * * *" as described in the guide.
While you're editing the system crontab, it's also recommended to replace the
"/cron.php" part with "/sites/modules/elysia_cron/cron.php" (if you have
installed elysia_cron in "sites/modules" directory).
This is an optional step (you can leave "/cron.php" if you want), doing it will
result in a better performance in bigger sites (elysia_cron's cron.php handles
cache in a better way).
For example:
* * * * * wget -O - -q -t 1 http://www.example.com/sites/all/modules/elysia_cron/cron.php
IF YOU WANT TO ENABLE CRON KEY SECURITY...
... you should also add the choosen cron key (from elysia_cron setup) to the
call.
Example:
* * * * * wget -O - -q -t 1 http://www.example.com/sites/all/modules/elysia_cron/cron.php?cron_key=12345678
That's all, now you can go the the cron administration page to configure your
tasks.
(NOTE: In D7, having a system crontab defined, you can set the "Run cron on
visitor's requests, every" setting to "Never")
By default elysia_cron will run all standard cron jobs sequentially (in a
single channel) once an hour. You can change this behavious with elysia_cron
setup.
To access elysia_cron setup, and change the scheduling of your jobs or view
execution stats, go to cron page from administration menu.
Note: to better see warnings on administration page you should consider adding
a rule for "warn" class to theme CSS stylesheet, like this one: .warn { color:
red; font-weight: bold; }
PERMISSIONS
------------
You can also give 'administer elysia_cron' permission to all user roles that
needs to administer cron jobs. You can do this with standard drupal users
administration.

View 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.

View File

@@ -0,0 +1,247 @@
ELYSIA_CRON
by Eric Berdondini (gotheric)
<eric@void.it>
For installation instructions read INSTALL.TXT
For module developers API documetation read API.TXT
-----------------------------------------------------------------------------
FEATURES
-----------------------------------------------------------------------------
Elysia Cron extends Drupal standard cron, allowing a fine grain control over
each task and several ways to add custom cron jobs to your site.
- Set the timings and frequencies of each cron task (you can run some jobs every
day at a specified hour, other only monthly and so on...).
For each task you can simply choose between some frequently used options
("once a day", "once a month" ...), or use a powerful "linux crontab"-like
syntax to set the accurate timings. You can even define your frequently used
options to speed up site configuration.
- Parallel execution of cron task: you can group jobs in channels and execute
then simultaneously: so a task that takes a lot of time to execute won't block
other tasks that need to be executed every 5 minutes...
- You can disable all tasks, an entire channel or a single task.
- Change the priority/order of task execution.
- Manual force the execution of a cron tasks.
- Detailed overview of cron status with time statistics for single tasks and
channels.
- Powerful API for module developers: you can define extra cron tasks for your
modules, each one with own default timings (site administrators can override
them by configuration, other modules via hook_alter). Elysia Cron 2.0 gives a
brand new API support (compatible with 1.0 version) with a lot of features.
- Administrators can define custom jobs (call to functions with parameters), via
the "script" option.
- Several optimization for frequent cron calls and error handling.
- Protection from external cron calling by cron_key or allowed host list.
Elysia has no dependencies with contributed modules, and doesn't need to patch
the core: it can be used in minimal Drupal installation with only core modules.
It also can be used in a Drupal install profile.
3rd party integration:
- Ping feature, for external tracking services like host-tracker to tell whether
cron is functioning properly on your site.
- Drush support: you can call "drush elysia-cron" to manually run extended cron.
- CTools support for exports/backup of task settings.
- Features support.
-----------------------------------------------------------------------------
USAGE EXAMPLES
-----------------------------------------------------------------------------
Elysia cron is usually used in large sites that needs performance optimization.
- Avoid drupal peak loads by distributing heavy load tasks during quiet periods
of the day: for example you may want to rebuild the XML Sitemap of your site
at 2:00AM in the morning, where usually only a few people are visiting your
site. You can even move some tasks to be executed only once a month (log
rotation, old records expiry...).
- If you have tasks that should be executed very often, but don't want to
execute ALL drupal cron tasks that often! For example, you may want to check
for emails that needs to be sent to your users every 2 minutes. Standard cron
is managed in a "monolithic" way, so even if you find out how to execute it
every 2 minutes, you will end in having all cron tasks executed so often, with
a lot of performance problems.
- Fine tune cron cache management : drupal cron will invalidate variable cache
every cron run, and this is a great performance problem if you have a
frequently called task. Elysia cron optimize cache management, and doesn't
need to invalidate cache.
- Setup tasks that should be run at a precise time: for example if you want to
send a SimpleNews newsletter every monday at 9:00AM, you can do it.
- Parallel execution: if you have a task that takes a lot of time to execute,
you can setup a different channel for it so it won't block other tasks that
need to be executed every 5 minutes.
- Turn off (disable) a cron task/feature you don't need.
- Debug system cron problems. If your cron does not terminate correctly you can
use extended logging, see at each cron timings and disable task to track down
the problem.
-----------------------------------------------------------------------------
CHANNELS
-----------------------------------------------------------------------------
Channels are groups of tasks. Each channel is a "parallel line" of execution
(= multiple channels can be executed simultaneously).
Tasks inside a channel will be executed sequentially (if they should).
WARNING: It's not recommended to create more than 2 or 3 channels.
Every channel will increase the delay between each cron check (of the same
channel), because each cron call will cycle between all channels.
So, for example:
If you have 1 channel it will be checked once a minute.
If you have 2 channel each one will be checked every 2 minutes (almost usually,
when the other one is running it will be checked once a minute).
It you have 10 channels there will be a check every 10 minutes... if you have
a job that should be executed every 5 minutes it won't do so!
-----------------------------------------------------------------------------
EXPORT VIA CTOOLS/FEATURES MODULE
-----------------------------------------------------------------------------
With 2.0 version of Elysia Cron you can use "Bulk Export" functionality of
"Chaos Tools Suite" to export cron settings.
To use it simply enable all modules, go to Structure > Bulk Exporter and
select the tasks you want to export settings. You can also select all
"elysia_cron" prefixed variables to export global options.
Than generate the module.
The generated code will set the new defaults of elysia cron settings. This way
you can simply enable it to use them, but you are free to override them in
the future using the normal settings page.
Note that if you want to delete all overrides, and return to exported settings,
you should do a "reset to defaults" from elysia cron settings page.
You can also use "Features" module to create a Feature module will the settings
you need.
Note that if you want to delete the overridden settings it's preferable to use
the "reset to defaults" elysia cron's button.
You can use the "Revert components" Features's button, but this will reset also
all cron statistics (if you are not interested in them you can freely use that
button).
-----------------------------------------------------------------------------
DRUSH SUPPORT
-----------------------------------------------------------------------------
Elysia Cron 2.0 adds a simple support for Drush module.
You can execute the "elysia-cron" command to run cron.
-----------------------------------------------------------------------------
RULES AND SCRIPT SYNTAX
-----------------------------------------------------------------------------
1. FIELDS ORDER
---------------------------------
+---------------- minute (0 - 59)
| +------------- hour (0 - 23)
| | +---------- day of month (1 - 31)
| | | +------- month (1 - 12)
| | | | +---- day of week (0 - 6) (Sunday=0)
| | | | |
* * * * *
Each of the patterns from the first five fields may be either * (an asterisk),
which matches all legal values, or a list of elements separated by commas
(see below).
For "day of the week" (field 5), 0 is considered Sunday, 6 is Saturday (7 is
an illegal value)
A job is executed when the time/date specification fields all match the current
time and date. There is one exception: if both "day of month" and "day of week"
are restricted (not "*"), then either the "day of month" field (3) or the "day
of week" field (5) must match the current day (even though the other of the two
fields need not match the current day).
2. FIELDS OPERATOR
---------------------------------
There are several ways of specifying multiple date/time values in a field:
* The comma (',') operator specifies a list of values, for example: "1,3,4,7,8"
* The dash ('-') operator specifies a range of values, for example: "1-6", which
is equivalent to "1,2,3,4,5,6"
* The asterisk ('*') operator specifies all possible values for a field. For
example, an asterisk in the hour time field would be equivalent to 'every hour'
(subject to matching other specified fields).
* The slash ('/') operator (called "step") can be used to skip a given number of
values. For example, "*/3" in the hour time field is equivalent to
"0,3,6,9,12,15,18,21".
3. EXAMPLES
---------------------------------
*/15 * * * : Execute job every 15 minutes
0 2,14 * * *: Execute job every day at 2:00 and 14:00
0 2 * * 1-5: Execute job at 2:00 of every working day
0 12 1 */2 1: Execute job every 2 month, at 12:00 of first day of the month OR
at every monday.
4. SCRIPTS
---------------------------------
You can use the script section to easily create new jobs (by calling a php
function) or to change the scheduling of an existing job.
Every line of the script can be a comment (if it starts with #) or a job
definition.
The syntax of a job definition is:
<-> [rule] <ctx:CONTEXT> [job]
(Tokens betweens [] are mandatory)
* <->: a line starting with "-" means that the job is DISABLED.
* [rule]: a crontab schedule rule. See above.
* <ctx:CHANNEL>: set the channel of the task.
* [job]: could be the name of a supported job (for example: 'search_cron') or a
function call, ending with ; (for example: 'process_queue();').
A comment on the line just preceding a job definition is considered the job
description.
Remember that script OVERRIDES all settings on single jobs sections or channel
sections of the configuration
5. EXAMPLE OF SCRIPT
---------------------------------
# Search indexing every 2 hours (i'm setting this as the job description)
0 */2 * * * search_cron
# I'll check for module status only on sunday nights
# (and this is will not be the job description, see the empty line below)
0 2 * * 0 update_status_cron
# Trackback ping process every 15min and on a channel called "net"
*/15 * * * * ctx:net trackback_cron
# Disable node_cron (i must set the cron rule even if disabled)
- */15 * * * * node_cron
# Launch function send_summary_mail('test@test.com', false); every night
# And set its description to "Send daily summary"
# Send daily summary
0 1 * * * send_summary_mail('test@test.com', false);
-----------------------------------------------------------------------------
CREDITS
-----------------------------------------------------------------------------
Elysia cron is a part of the Elysia project (but could be used stand alone
with no limitation).
Developing is sponsored by :
Void Labs s.n.c
http://www.void.it

View File

@@ -0,0 +1,44 @@
<?php
/**
* @file
* Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
*/
if (!file_exists('includes/bootstrap.inc')) {
if (!empty($_SERVER['DOCUMENT_ROOT']) && file_exists($_SERVER['DOCUMENT_ROOT'] . '/includes/bootstrap.inc')) {
chdir($_SERVER['DOCUMENT_ROOT']);
} elseif (preg_match('@^(.*)[\\\\/]sites[\\\\/][^\\\\/]+[\\\\/]modules[\\\\/]([^\\\\/]+[\\\\/])?elysia(_cron)?$@', getcwd(), $r) && file_exists($r[1] . '/includes/bootstrap.inc')) {
chdir($r[1]);
} else {
die("Cron Fatal Error: Can't locate bootstrap.inc. Check cron.php position.");
}
}
/**
* Root directory of Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_override_server_variables(array(
'SCRIPT_NAME' => '/cron.php',
));
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
drupal_access_denied();
}
elseif (variable_get('maintenance_mode', 0)) {
watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
drupal_access_denied();
}
else {
if (function_exists('elysia_cron_run')) {
elysia_cron_run();
}
else {
drupal_cron_run();
}
}

View File

@@ -0,0 +1,785 @@
<?php
/*******************************************************************************
* INTERFACE
*
* WARN: Below this point the word "context" should be avoided (use channel)
* Disabled should always be referenced as "disabled" (in db is "disable" for
* compatibility with Ctools )
******************************************************************************/
function elysia_cron_admin_page() {
$aoutput = array();
$aoutput[] = drupal_get_form('elysia_cron_run_form');
$output = '';
elysia_cron_initialize();
global $elysia_cron_settings, $elysia_cron_settings_by_channel, $elysia_cron_current_channel, $cron_completed, $cron_completed_time;
$v = variable_get('elysia_cron_disabled', false);
$output .= '<p>' . t('Global disable') . ': <i>' . ($v ? '<span class="warn">' . t('YES') . '</span>' : 'no') . '</i></p>';
$output .= '<p>' . t('Last channel executed') . ': <i>' . (($c = elysia_cron_last_channel()) ? $c : t('n/a')) . '</i></p>';
if (EC_DRUPAL_VERSION < 7) {
if (_ec_variable_get('elysia_cron_semaphore', 0)) {
$output .= '<p><span class="warn">' . t('Global semaphore active since !date', array('!date' => elysia_cron_date(_ec_variable_get('elysia_cron_semaphore', 0)))) . '</span></p>';
}
}
$running = '';
foreach ($elysia_cron_settings_by_channel as $channel => $data) {
if (elysia_cron_is_channel_running($channel)) {
$running .= $channel . ' ';
}
}
if ($running) {
$output .= '<p>' . t('Running channels') . ': <span class="warn">' . $running . '</span></p>';
}
$output .= '<p>' . t('Last run') . ': ' . elysia_cron_date(_ec_variable_get('elysia_cron_last_run', 0)) . '</p>';
$rows = array();
$ipath = url(drupal_get_path('module', 'elysia_cron') . '/images/icon_');
foreach ($elysia_cron_settings_by_channel as $channel => $data) {
$running = elysia_cron_is_channel_running($channel);
$rows[] = array(
array('data' => '<h3>' . t('Channel') . ': ' . $channel . ($data['#data']['disabled'] ? ' <span class="warn">(' . t('DISABLED') . ')</span>' : '') . '</h3>', 'colspan' => 2, 'header' => 'true'),
array('data' => elysia_cron_date($data['#data']['last_run']), 'header' => 'true'),
array('data' => $data['#data']['last_execution_time'] . 's ' . t('(Shutdown: !shutdown)', array('!shutdown' => $data['#data']['last_shutdown_time'] . 's')), 'header' => 'true'),
array('data' => $data['#data']['execution_count'], 'header' => 'true'),
array('data' => $data['#data']['avg_execution_time'] . 's / ' . $data['#data']['max_execution_time'] . 's', 'header' => 'true'),
);
$messages = '';
if ($running) {
$messages .= t('Running since !date', array('!date' => elysia_cron_date($running))) . '<br />';
}
if ($data['#data']['last_aborted'] || $data['#data']['abort_count']) {
$msg = array();
if ($data['#data']['last_aborted']) {
$msg[] = t('Last aborted') . (!empty($data['#data']['last_abort_function']) ? ': <span class="warn">' . t('On function !function', array('!function' => $data['#data']['last_abort_function'])) . '</span>' : '');
}
if ($data['#data']['abort_count']) {
$msg[] = t('Abort count') . ': <span class="warn">' . $data['#data']['abort_count'] . '</span>';
}
$messages .= implode(', ', $msg) . '<br />';
}
if ($messages) {
$rows[] = array( '', '', array('data' => $messages, 'colspan' => 4, 'header' => true) );
$rows[] = array( array('data' => '', 'colspan' => 6) );
}
foreach ($data as $job => $conf) {
$icon = 'idle';
$caption = '<b>' . $job . '</b>';
$tip = t('Idle');
if ($conf['disabled']) {
$icon = 'disabled';
$caption = '<strike><b>' . $job . '</b></strike>';
$tip = t('Disabled');
}
elseif (!empty($conf['running'])) {
$icon = 'running';
$caption = '<b><u>' . $job . '</u></b>';
$tip = t('Running');
}
elseif (elysia_cron_should_run($conf)) {
$icon = 'waiting';
$tip = t('Waiting for execution');
}
if ($job != '#data') {
$rows[] = array(
array( 'data' => '<img src="' . $ipath . $icon . '.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" />', 'align' => 'right' ),
array( 'data' => $caption . ': <i>' . elysia_cron_description($job) . '</i> ', 'colspan' => 4 ),
array( 'data' => _dco_l(t('Force run'), _dcf_internal_path('admin/config/system/cron/execute/') . $job, array('attributes' => array('onclick' => 'return confirm("' . t('Force execution of !job?', array('!job' => $job)) . '");'))), 'align' => 'right'),
);
$rows[] = array(
'',
$conf['rule'] . (!empty($conf['weight']) ? ' <small>(' . t('Weight') . ': ' . $conf['weight'] . ')</small>' : ''),
elysia_cron_date($conf['last_run']),
$conf['last_execution_time'] . 's',
$conf['execution_count'],
$conf['avg_execution_time'] . 's / ' . $conf['max_execution_time'] . 's',
);
}
}
$rows[] = array('&nbsp;','','','','','');
}
$output .= _dco_theme('table', array('header' => array('', t('Job / Rule'), t('Last run'), t('Last exec time'), t('Exec count'), t('Avg/Max Exec time')), 'rows' => $rows));
$output .= '<br />';
$output .= _dco_theme('table', array(
'header' => array(t('Legend')),
'rows' => array(
array('<img src="' . $ipath . 'idle.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job shouldn\'t do anything right now')),
array('<img src="' . $ipath . 'waiting.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is ready to be executed, and is waiting for system cron call')),
array('<img src="' . $ipath . 'running.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is running right now')),
array('<img src="' . $ipath . 'disabled.png" width="16" height="16" align="top" alt="' . $tip . '" title="' . $tip . '" /> ' . t('Job is disabled by settings, and won\'t run until enabled again')),
array(t('Notes: job times don\'t include shutdown times (only shown on channel times).')),
array(t('If an abort occours usually the job is not properly terminated, and so job timings can be inaccurate or wrong.')),
),
));
$aoutput[] = array(
'#type' => 'markup',
'#markup' => $output,
);
return _dcr_render_array($aoutput);
}
function elysia_cron_settings_form() {
global $elysia_cron_settings, $elysia_cron_settings_by_channel;
elysia_cron_initialize();
$form = array();
$form['prefix_1'] = array(
'#type' => 'fieldset',
'#title' => t('Click for help and cron rules and script syntax'),
'#collapsible' => true,
'#collapsed' => true,
'#description' => t(<<<EOT
<h3>Fields order</h3>
<pre>
+---------------- minute (0 - 59)
| +------------- hour (0 - 23)
| | +---------- day of month (1 - 31)
| | | +------- month (1 - 12)
| | | | +---- day of week (0 - 6) (Sunday=0)
| | | | |
* * * * *
</pre>
<p>Each of the patterns from the first five fields may be either * (an asterisk),
which matches all legal values, or a list of elements separated by commas (see below).</p>
<p>For "day of the week" (field 5), 0 is considered Sunday, 6 is Saturday
(7 is an illegal value)</p>
<p>A job is executed when the time/date specification fields all match the current
time and date. There is one exception: if both "day of month" and "day of week"
are restricted (not "*"), then either the "day of month" field (3) or the "day of week"
field (5) must match the current day (even though the other of the two fields
need not match the current day).</p>
<h3>Fields operators</h3>
<p>There are several ways of specifying multiple date/time values in a field:</p>
<ul>
<li>The comma (',') operator specifies a list of values, for example: "1,3,4,7,8"</li>
<li>The dash ('-') operator specifies a range of values, for example: "1-6", which is equivalent to "1,2,3,4,5,6"</li>
<li>The asterisk ('*') operator specifies all possible values for a field. For example, an asterisk in the hour time field would be equivalent to 'every hour' (subject to matching other specified fields).</li>
<li>The slash ('/') operator (called "step") can be used to skip a given number of values. For example, "*/3" in the hour time field is equivalent to "0,3,6,9,12,15,18,21".</li>
</ul>
<h3>Examples</h3>
<pre>
*/15 * * * * : Execute job every 15 minutes
0 2,14 * * *: Execute job every day at 2:00 and 14:00
0 2 * * 1-5: Execute job at 2:00 of every working day
0 12 1 */2 1: Execute job every 2 month, at 12:00 of first day of the month OR at every monday.
</pre>
<h3>Script</h3>
<p>You can use the script section to easily create new jobs (by calling a php function)
or to change the scheduling of an existing job.</p>
<p>Every line of the script can be a comment (if it starts with #) or a job definition.</p>
<p>The syntax of a job definition is:</p>
<code>
&lt;-&gt; [rule] &lt;ch:CHANNEL&gt; [job]
</code>
<p>(Tokens betweens [] are mandatory)</p>
<ul>
<li>&lt;-&gt;: a line starting with "-" means that the job is DISABLED.</li>
<li>[rule]: a crontab schedule rule. See above.</li>
<li>&lt;ch:CHANNEL&gt;: set the channel of the job.</li>
<li>[job]: could be the name of a supported job (for example: 'search_cron') or a function call, ending with ; (for example: 'process_queue();').</li>
</ul>
<p>A comment on the line just preceding a job definition is considered the job description.</p>
<p>Remember that script OVERRIDES all settings on single jobs sections or channel sections of the configuration</p>
<h3>Examples of script</h3>
<pre>
# Search indexing every 2 hours (i'm setting this as the job description)
0 */2 * * * search_cron
# I'll check for module status only on sunday nights
# (and this is will not be the job description, see the empty line below)
0 2 * * 0 update_status_cron
# Trackback ping process every 15min and on a channel called "net"
*/15 * * * * ch:net trackback_cron
# Disable node_cron (i must set the cron rule even if disabled)
- */15 * * * * node_cron
# Launch function send_summary_mail('test@test.com', false); every night
# And set its description to "Send daily summary"
# Send daily summary
0 1 * * * send_summary_mail('test@test.com', false);
</pre>
EOT
),
);
$form['prefix_2'] = array(
'#markup' => '<hr>',
);
$form['main'] = array(
'#title' => t('Main'),
'#type' => 'fieldset',
'#collapsible' => false,
'#collapsed' => false,
);
$form['main']['elysia_cron_disabled'] = array(
'#title' => t('Global disable'),
'#type' => 'checkbox',
'#default_value' => variable_get('elysia_cron_disabled', false),
);
$form['installation'] = array(
'#title' => t('Installation settings'),
'#type' => 'fieldset',
'#collapsible' => true,
'#collapsed' => true,
);
if (EC_DRUPAL_VERSION >= 7) {
$form['installation']['cron_safe_threshold'] = array(
'#type' => 'select',
'#title' => t('Run cron on visitor\'s requests, every'),
'#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
'#description' => t('Setting a time here will enable the "poormanscron" method, which runs the Drupal cron operation using normal browser/page requests instead of having to set up a crontab to request the cron.php script. This approach requires that your site gets regular traffic/visitors in order to trigger the cron request.') . '<br />' .
t('This way is fine if you don\'t need a great control over job starting times and execution frequency.') . '<br />' .
t('If you need a fine grained control over cron timings use the crontab metod, as <a href="!cron_url">described in Drupal installation guide</a>.', array('!cron_url' => url('http://drupal.org/cron'))) . '<br />' .
t('If you have a very large site, or you need to execute some jobs very often (more than once an hour) refer to Elysia cron\'s INSTALL.TXT to improve main cron setup.'),
'#options' => array(0 => t('Never / Use external crontab')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
);
}
$form['installation']['cron_key'] = array(
'#title' => t('Cron key'),
'#type' => 'textfield',
'#default_value' => variable_get('cron_key', ''),
'#description' => t('This is used to avoid external cron calling. If you set this cron will by accessible only by calling http://site/cron.php?cron_key=XXX, so you\'ll need to modify system crontab to support this (Logged user with [administer elysia_cron] permission avoid this check).'),
);
$form['installation']['elysia_cron_allowed_hosts'] = array(
'#title' => t('Allowed hosts'),
'#type' => 'textfield',
'#default_value' => variable_get('elysia_cron_allowed_hosts', ''),
'#description' => t('Insert a list of ip addresses separated by , that can run cron.php (Logged user with [administer elysia_cron] permission avoid this check).'),
);
$form['installation']['elysia_cron_default_rule'] = array(
'#title' => t('Default schedule rule'),
'#type' => 'textfield',
'#default_value' => variable_get('elysia_cron_default_rule', false),
'#description' => t('If you don\'t specify a rule for a process, and if it has not a module specified one, this rule will apply'),
);
if (!ini_get('safe_mode')) {
$form['installation']['elysia_cron_time_limit'] = array(
'#title' => t('Time limit'),
'#type' => 'textfield',
'#default_value' => variable_get('elysia_cron_time_limit', 240),
'#description' => t('Set the number of seconds a channel is allowed to run. If you have some jobs that needs more time to execute increase it or set to 0 to disable the limit (WARN: that way a stuck job will block the channel forever!).'),
);
}
$form['installation']['elysia_cron_stuck_time'] = array(
'#title' => t('Stuck time'),
'#type' => 'textfield',
'#default_value' => variable_get('elysia_cron_stuck_time', 3600),
'#description' => t('How many seconds the process should wait to consider the job as stuck (so the channel can run again)'),
);
$form['installation']['elysia_cron_debug_messages'] = array(
'#title' => t('Debug'),
'#type' => 'select',
'#default_value' => variable_get('elysia_cron_debug_messages', 0),
'#options' => array(
0 => t('Disabled'),
1 => t('Enabled'),
),
'#description' => t('Enable extended logging (in watchdog)'),
);
$default_ruless = '';
$default_rules = variable_get('elysia_cron_default_rules', $GLOBALS['elysia_cron_default_rules']);
foreach ($default_rules as $dk => $dr) {
$default_ruless .= $dr . ' = ' . $dk . "\n";
}
$form['installation']['elysia_cron_default_rules'] = array(
'#title' => t('Predefined rules'),
'#type' => 'textarea',
'#rows' => 5,
'#default_value' => $default_ruless,
'#description' => t('You can put here standard rules used in your system, each one with its own caption. Put each rule in a separate line, in the form "caption = rule". For example: <i>"every 15 minutes = */15 * * * *"</i>.'),
);
$form['installation']['elysia_cron_alert_fieldset'] = array(
'#title' => t('External cron tracking'),
'#type' => 'fieldset',
'#collapsible' => true,
'#collapsed' => true,
'#description' => t('This lets you use an external tracking system like <a href="http://www.host-tracker.com/">Host Tracker</a> to be used to monitor the health of cron on your site. Point the tracking service to <a href="!cron-ping-url">!cron-ping-url</a>. If Elysia cron has been called within the time interval specified below, the ping page will return HTTP 200. If not, the ping page will throw a 404 (page not found).', array('!cron-ping-url' => url('admin/build/cron/ping'))),
);
$form['installation']['elysia_cron_alert_fieldset']['elysia_cron_alert_interval'] = array(
'#title' => t('Lapse interval (minutes)'),
'#type' => 'textfield',
'#size' => 20,
'#default_value' => variable_get('elysia_cron_alert_interval', 60),
'#description' => t('Specify the number of minutes to allow to lapse before the cron ping page returns a 404 (page not found).'),
);
$form['elysia_cron_script_fieldset'] = array(
'#title' => t('Script'),
'#type' => 'fieldset',
'#collapsible' => true,
'#collapsed' => !variable_get('elysia_cron_script', ''),
);
$form['elysia_cron_script_fieldset']['elysia_cron_script'] = array(
'#type' => 'textarea',
'#rows' => 20,
'#default_value' => variable_get('elysia_cron_script', ''),
'#description' => t('You can specify new cron jobs or modify existing schedules by adding lines to the script.<br>' .
'<b>Warning</b> All rules specified in the script will OVERRIDE single job settings and channel settings (sections below).'),
);
$form['single_job'] = array(
'#title' => t('Single job settings'),
'#description' =>
'<b>'.t('Disabled').'</b>: '.t('Flag this to disable job execution').'<br />'.
'<b>'.t('Schedule rule').'</b>: '.t('Timing rule for the job. Leave empty to use default rule (shown after the field in parenthesis)').'<br />'.
'<b>'.t('Weight').'</b>: '.t('Use this to specify execution order: low weights are executed before high weights. Default value shown in parenthesis').'<br />'.
'<b>'.t('Channel').'</b>: '.t('Specify a channel for the job (create the channel if not exists)').'<br /><br />',
'#type' => 'fieldset',
'#collapsible' => true,
//'#collapsed' => true,
);
$jobchannels = array(
'#title' => t('Job channel associations'),
'#description' => t('Leave empty for default channel'),
'#type' => 'fieldset',
'#collapsible' => true,
'#collapsed' => true,
);
foreach ($elysia_cron_settings_by_channel as $channel => $cconf) {
foreach ($cconf as $job => $conf) {
if ($job != '#data' && empty($conf['expression'])) {
$form['single_job']['elysia_cron_' . $job] = array(
'#title' => $job, // t('Job !job', array('!job' => $job)),
'#description' => elysia_cron_description($job),
'#type' => 'fieldset',
'#collapsible' => true,
'#collapsed' => !elysia_cron_get_job_rule($job) && !elysia_cron_get_job_weight($job) && !elysia_cron_is_job_disabled($job) && !elysia_cron_get_job_channel($job),
);
//if (!$form['single_job']['elysia_cron_'.$job]['#collapsed'])
// $form['single_job']['#collapsed'] = false;
$rule = elysia_cron_get_job_rule($job);
$options = array_merge(array('default' => t('Default') . ' (' . (!empty($default_rules[$conf['default_rule']]) ? $default_rules[$conf['default_rule']] : $conf['default_rule']) . ')'), $default_rules);
if ($rule && !isset($options[$rule])) {
$options[$rule] = $rule;
}
$options['custom'] = t('Custom') . ' ...';
$form['single_job']['elysia_cron_' . $job]['_elysia_cron_seljob_rule_' . $job] = array(
'#title' => t('Schedule rule'),
'#type' => 'select',
'#options' => $options,
'#default_value' => $rule ? $rule : 'default',
);
$form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_rule_' . $job] = array(
'#title' => t('Schedule rule'),
'#type' => 'textfield',
'#size' => 20,
'#default_value' => $rule ? $rule : $conf['default_rule'],
);
$form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_weight_' . $job] = array(
'#title' => t('Weight'),
'#type' => 'textfield',
'#size' => 4,
'#default_value' => elysia_cron_get_job_weight($job),
'#description' => '(' . $conf['default_weight'] . ')',
);
//$form['single_job']['elysia_cron_'.$job]['elysia_cron_'.$job.'_disabled'] = array(
$form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_disabled_' . $job] = array(
'#title' => t('Disabled'),
'#type' => 'checkbox',
'#default_value' => elysia_cron_is_job_disabled($job, false),
);
//$jobchannels['elysia_cron_'.$job.'_channel'] = array(
$form['single_job']['elysia_cron_' . $job]['_elysia_cron_job_channel_' . $job] = array(
'#title' => t('Channel'), // t('Channel for !job', array('!job' => $job)),
'#type' => 'textfield',
'#size' => 20,
'#default_value' => elysia_cron_get_job_channel($job),
);
//if (elysia_cron_get_job_channel($job))
// $jobchannels['#collapsed'] = false;
}
}
}
$form['channels'] = array(
'#title' => t('Channels settings'),
'#type' => 'fieldset',
'#collapsible' => true,
//'#collapsed' => $jobchannels['#collapsed'],
);
foreach ($elysia_cron_settings_by_channel as $channel => $conf) {
$form['channels']['elysia_cron_ch_' . $channel] = array(
'#title' => $channel, // t('Channel !channel', array('!channel' => $channel)),
'#type' => 'fieldset',
);
$form['channels']['elysia_cron_ch_' . $channel]['_elysia_cron_ch_disabled_' . $channel] = array(
'#title' => t('Disabled'),
'#type' => 'checkbox',
'#default_value' => elysia_cron_is_channel_disabled($channel, ''),
);
$form['channels']['elysia_cron_ch_' . $channel]['_elysia_cron_ch_rule_' . $channel] = array(
'#title' => t('Default schedule rule'),
'#type' => 'textfield',
'#size' => 20,
'#default_value' => elysia_cron_get_channel_rule($channel),
);
//if (elysia_cron_is_channel_disabled($channel))
// $form['channels']['#collapsed'] = false;
}
//$form['channels']['jobchannels'] = $jobchannels;
$form['buttons'] = array('#type' => 'actions');
$form['buttons']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
$form['buttons']['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset to defaults'),
);
if (!empty($_POST) && form_get_errors()) {
elysia_cron_error('The settings have not been saved because of the errors.');
}
return _dcr_form($form);
}
function theme_elysia_cron_settings_form($_dco_variables) {
extract(_dcf_theme_form($_dco_variables));
$form = &$variables['form'];
$output = '<script type="text/javascript"><!--' . "\n" .
/*'function _ec_select(editid, select) { if (select.value == \'custom\') {'.
'$ = jQuery; $(select).hide();$("#"+editid).show();$("#"+editid).focus();'.
'}}'.*/
'function _ec_select(key, select) { if (select.value == \'custom\') {' .
'$ = jQuery; $("#_ec_select_"+key).hide();$("#_ec_custom_"+key).show();$("#_ec_custom_"+key).focus();' .
'}}' .
"\n" . '--></script>';
$coutput = '<table>';
$i = 0;
foreach (element_children($form['single_job']) as $c) {
$key = substr($c, 12);
//print_r($form['single_job'][$c]);
if ($i++ == 0) {
$coutput .= '<tr>' .
'<th>' . $form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]['#title'] . '</th>' .
'<th>' . $form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#title'] . '</th>' .
'<th colspan="2">' . $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] . '</th>' .
'<th>' . $form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#title'] . '</th>' .
'</tr>';
}
//$def_rule = $form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#description'];
$def_weight = $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#description'];
$posted_key = $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#name'];
$posted_val = !empty($_REQUEST[$posted_key]) ? $_REQUEST[$posted_key] : false;
$form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#prefix'] = '<span id="_ec_custom_' . $key . '" style="' . ($posted_val != 'custom' ? 'display: none;' : '') . '">';
$form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#suffix'] = '</span>';
$form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_rule_' . $key]['#description'] = NULL;
//$form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#attributes']['style'] = ($posted_val != 'custom' ? 'display: none;' : '').'width: 20em; margin: 0';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#prefix'] = '<span id="_ec_select_' . $key . '" style="' . ($posted_val == 'custom' ? 'display: none;' : '') . '">';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#suffix'] = '</span>';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#description'] = NULL;
//$form['single_job'][$c]['_elysia_cron_seljob_rule_'.$key]['#attributes']['style'] = ($posted_val == 'custom' ? 'display: none;' : '').'width: 20em; margin: 0';
//$form['single_job'][$c]['_elysia_cron_seljob_rule_'.$key]['#attributes']['onchange'] = '_ec_select(\''.$form['single_job'][$c]['_elysia_cron_job_rule_'.$key]['#id'].'\', this)';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#attributes']['onchange'] = '_ec_select(\'' . $key . '\', this)';
$form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#description'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#attributes']['style'] = 'margin: 0';
$form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]['#attributes']['style'] = 'margin: 0';
$form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_channel_' . $key]['#attributes']['style'] = 'margin: 0';
$coutput .= '<tr><td colspan="6"><b>' . $form['single_job'][$c]['#title'] . '</b>' . (($d = $form['single_job'][$c]['#description']) && $d != '-' ? ' <i>(' . $d . ')</i>' : '' ) . '</td></tr>';
$coutput .= '<tr>' .
'<td align="center">' . drupal_render($form['single_job'][$c]['_elysia_cron_job_disabled_' . $key]) . '</td>' .
'<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]) . drupal_render($form['single_job'][$c]['_elysia_cron_job_rule_' . $key]) . '</td>' .//'<td><small>'.$def_rule.'</small></td>'.
'<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_weight_' . $key]) . '</td><td><small>' . $def_weight . '</small></td>' .
'<td>' . drupal_render($form['single_job'][$c]['_elysia_cron_job_channel_' . $key]) . '</td>' .
'</tr>';
drupal_render($form['single_job'][$c]);
}
$coutput .= '</table>';
$form['single_job']['#children'] = $coutput;
//$form['single_job'][] = array('#type' => 'markup', '#markup' => $output);
$coutput = '<table>';
$i = 0;
foreach (element_children($form['channels']) as $c) {
$key = substr($c, 15);
if ($i++ == 0) {
$coutput .= '<tr>' .
'<th>' . t('Name') . '</th>' .
'<th>' . $form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#title'] . '</th>' .
'<th>' . $form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#title'] . '</th>' .
'</tr>';
}
$form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#title'] = NULL;
$form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]['#attributes']['style'] = 'margin: 0';
$form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#title'] = NULL;
$form['channels'][$c]['_elysia_cron_ch_rule_' . $key]['#attributes']['style'] = 'margin: 0';
$coutput .= '<tr>' .
'<td><b>' . $form['channels'][$c]['#title'] . '</b></td>' .
'<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_disabled_' . $key]) . '</td>' .
'<td>' . drupal_render($form['channels'][$c]['_elysia_cron_ch_rule_' . $key]) . '</td>' .
'</tr>';
drupal_render($form['channels'][$c]);
}
$coutput .= '</table>';
$form['channels']['#children'] = $coutput;
return $output . drupal_render_children($form);
//$form['channels'][] = array('#type' => 'markup', '#markup' => $output);
//return drupal_render(_dcr_form($form));
}
function elysia_cron_settings_form_validate($_dco_form, &$_dco_form_state) {
extract(_dcf_form_validate($_dco_form, $_dco_form_state));
global $elysia_cron_settings;
$script = $form_state['values']['elysia_cron_script'];
if ($script) {
$errors = elysia_cron_decode_script($script, false);
if ($errors) {
form_set_error('elysia_cron_script', t('Invalid lines:') . implode('<br>', $errors));
}
}
foreach ($form_state['values'] as $key => $value) {
if ($value && preg_match('/^_elysia_cron_([^_]+_[^_]+)_(.*)$/', $key, $r) && ($r[1] == 'job_rule' || $r[1] == 'ch_rule')) {
if (!preg_match('/^\\s*([0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+[ ]+[0-9*,\/-]+)\\s*$/', $value)) {
form_set_error($key, t('Invalid rule: !rule', array('!rule' => $value)));
}
}
}
}
function elysia_cron_settings_form_submit($_dco_form, &$_dco_form_state) {
extract(_dcf_form_validate($_dco_form, $_dco_form_state));
$form_values = $form_state['values'];
$op = isset($form_values['op']) ? $form_values['op'] : '';
// Exclude unnecessary elements.
unset($form_values['submit'], $form_values['reset'], $form_values['form_id'], $form_values['op'], $form_values['form_token']);
$elysia_cron_default_rules = array();
$rules = explode("\n", $form_values['elysia_cron_default_rules']);
foreach ($rules as $r) {
if (trim($r)) {
$rr = explode("=", $r);
$elysia_cron_default_rules[trim($rr[1])] = trim($rr[0]);
}
}
variable_set('elysia_cron_default_rules', $elysia_cron_default_rules);
foreach ($form_values as $key => $value) {
$value = trim($value);
if (!preg_match('/^_elysia_cron_([^_]+_[^_]+)_(.*)$/', $key, $r)) {
if ($op == t('Reset to defaults') || ($key != 'cron_safe_threshold' && !$value)) {
variable_del($key);
}
elseif ($key != 'elysia_cron_default_rules') {
if (is_array($value) && isset($form_values['array_filter'])) {
$value = array_keys(array_filter($value));
}
variable_set($key, $value);
}
}
else {
$nullvalue = $r[1] != 'job_weight' ? !$value : !$value && $value !== '0';
//dprint($r[1].' '.$r[1].' '.$r[2]);
if ($op == t('Reset to defaults') || $nullvalue) {
switch ($r[1]) {
case 'job_channel':
elysia_cron_reset_job_channel($r[2]);
break;
case 'job_rule':
elysia_cron_reset_job_rule($r[2]);
break;
case 'job_weight':
elysia_cron_reset_job_weight($r[2]);
break;
case 'job_disabled':
elysia_cron_reset_job_disabled($r[2]);
break;
case 'ch_disabled':
elysia_cron_reset_channel_disabled($r[2]);
break;
case 'ch_rule':
elysia_cron_reset_channel_rule($r[2]);
break;
}
}
else {
switch ($r[1]) {
case 'job_channel':
elysia_cron_set_job_channel($r[2], $value);
break;
case 'job_rule':
if ($form_values['_elysia_cron_seljob_rule_' . $r[2]] == 'custom') {
elysia_cron_set_job_rule($r[2], $value);
}
break;
case 'seljob_rule':
if ($value != 'custom') {
if ($value == 'default') {
elysia_cron_reset_job_rule($r[2]);
}
else {
elysia_cron_set_job_rule($r[2], $value);
}
}
break;
case 'job_weight':
elysia_cron_set_job_weight($r[2], $value);
break;
case 'job_disabled':
elysia_cron_set_job_disabled($r[2], $value);
break;
case 'ch_disabled':
elysia_cron_set_channel_disabled($r[2], $value);
break;
case 'ch_rule':
elysia_cron_set_channel_rule($r[2], $value);
break;
}
}
}
}
if ($op == t('Reset to defaults')) {
elysia_cron_message('The configuration options have been reset to their default values.');
}
else {
elysia_cron_message('The configuration options have been saved.');
}
}
function elysia_cron_date($timestamp) {
return $timestamp > 0 ? format_date($timestamp, EC_DRUPAL_VERSION >= 7 ? 'short' : 'small') : t('n/a');
//return date(variable_get('date_format_short', 'm/d/Y - H:i'), $timestamp);
}
function elysia_cron_run_form() {
$form = array();
$form['runf'] = array(
'#type' => 'fieldset',
);
$form['runf']['run'] = array(
'#type' => 'submit',
'#value' => t('Run cron'),
);
return $form;
}
function elysia_cron_run_form_submit($_dco_form, &$_dco_form_state) {
// Run cron manually from Cron form.
if (elysia_cron_run()) {
elysia_cron_message('Cron run successfully.');
}
else {
elysia_cron_error('Cron run failed.');
}
drupal_goto(_dcf_internal_path('admin/config/system/cron'));
}
function elysia_cron_execute_page($job = false) {
if (!$job) {
elysia_cron_error('No job specified');
drupal_goto(_dcf_internal_path('admin/config/system/cron'));
}
elysia_cron_run_job($job, true, true, false); // Run also if disabled or not ready (but not if it's already running)
drupal_goto(_dcf_internal_path('admin/config/system/cron'));
}
function elysia_cron_maintenance_page() {
$output = array();
$output[] = drupal_get_form('elysia_cron_reset_statistics_form');
return _dcr_render_array($output);
}
function elysia_cron_reset_statistics_form() {
$form = array();
$form['fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Reset statistics'),
'#description' => t('Deletes all cron execution statistics (Last run, last exec time, exec count, avg/max exec time...). Do not touch cron settings.<br /><b>This operation could not be reverted</b><br />'),
);
$form['fieldset']['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset'),
'#attributes' => array(
'onclick' => 'return confirm(\'' . htmlentities(t('Are you sure you want to reset statistics?')) . '\')',
),
);
return $form;
}
function elysia_cron_reset_statistics_form_submit($_dco_form, &$_dco_form_state) {
elysia_cron_reset_stats();
elysia_cron_message('Reset done.');
drupal_goto(_dcf_internal_path('admin/config/system/cron/maintenance'));
}
function elysia_cron_reset_page() {
elysia_cron_reset_statistics_form_submit(false, $res = array());
}

View File

@@ -0,0 +1,203 @@
<?php
/*******************************************************************************
* EXPORTABLES
******************************************************************************/
// WARN Features button "Revert components" will reset also statistics
function elysia_cron_get_ctools_defaults() {
if (module_exists('ctools') && function_exists('ctools_include')) {
ctools_include('export');
if (function_exists('ctools_export_get_schema') && function_exists('_ctools_export_get_defaults') && ($schema = ctools_export_get_schema('elysia_cron'))) {
$export = $schema['export'];
return _ctools_export_get_defaults('elysia_cron', $export);
}
}
return array();
}
/**
* Ctools load callback
* Ctools does not support override of PARTIAL record, this is an elysia cron specific replacement to support it
*/
function elysia_cron_ctools_export_load($name) {
$schema = ctools_export_get_schema('elysia_cron');
if (!empty($schema)) {
$export = $schema['export'];
if (EC_DRUPAL_VERSION >= 7) {
$object = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron} where name = :name", array(':name' => $name))->fetch();
}
else {
$object = db_fetch_object(db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron} where name = '%s'", $name));
}
$default_objects = _ctools_export_get_defaults('elysia_cron', $export);
if ($object) {
if (isset($default_objects[$name])) {
return _elysia_cron_ctools_export_load_object_db_and_code($object, $default_objects[$name], $export);
}
else {
return _elysia_cron_ctools_export_load_object_db($object, $export);
}
}
elseif (isset($default_objects[$name])) {
return _elysia_cron_ctools_export_load_object_code($default_objects[$name], $export);
}
}
}
/**
* Ctools load all callback
* Ctools does not support override of PARTIAL record, this is an elysia cron specific replacement to support it
*/
function elysia_cron_ctools_export_load_all() {
$schema = ctools_export_get_schema('elysia_cron');
if (empty($schema)) {
return array();
}
$export = $schema['export'];
if (EC_DRUPAL_VERSION >= 7) {
$objects = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron}")->fetchAll();
}
else {
$objects = array();
$rs = db_query("select " . implode(", ", $GLOBALS['_ec_columns']) . " from {elysia_cron}");
while ($o = db_fetch_object($rs)) {
$objects[] = $o;
}
}
$default_objects = _ctools_export_get_defaults('elysia_cron', $export);
$result = array();
foreach ($objects as $object) {
$key = $object->{$export['key']};
if (isset($default_objects[$key])) {
$result[$key] = _elysia_cron_ctools_export_load_object_db_and_code($object, $default_objects[$key], $export);
unset($default_objects[$key]);
} else {
$result[$key] = _elysia_cron_ctools_export_load_object_db($object, $export);
}
}
foreach ($default_objects as $key => $object) {
$result[$key] = _elysia_cron_ctools_export_load_object_code($object, $export);
}
return $result;
}
function _elysia_cron_ctools_export_load_object_db_and_code($object, $code_object, $export) {
$overridden = false;
foreach ($code_object as $keyd => $value) {
if (!isset($object->$keyd) || is_null($object->$keyd)) {
$object->$keyd = $value;
}
else if ($object->$keyd !== $value) {
$overridden = true;
}
}
$object->table = 'elysia_cron';
$object->export_type = EXPORT_IN_DATABASE | EXPORT_IN_CODE;
if (!empty($export['export type string'])) {
$object->{$export['export type string']} = $overridden ? t('Overridden') : t('Normal');
}
return $object;
}
function _elysia_cron_ctools_export_load_object_db($object, $export) {
$object->table = 'elysia_cron';
$object->export_type = EXPORT_IN_DATABASE;
if (!empty($export['export type string'])) {
$object->{$export['export type string']} = t('Normal');
}
return $object;
}
function _elysia_cron_ctools_export_load_object_code($object, $export) {
$object->table = 'elysia_cron';
$object->export_type = EXPORT_IN_CODE;
if (!empty($export['export type string'])) {
$object->{$export['export type string']} = t('Default');
}
$object->in_code_only = TRUE;
return $object;
}
/**
* Ctools export object factory
* Original ctools export factory (_ctools_export_unpack_object) does not handle NULL values correctly.
* This function does not support $schema['join'].
*/
function elysia_cron_ctools_export_object_factory($schema, $data) {
$object = new stdClass;
foreach ($schema['fields'] as $field => $info) {
$object->$field = isset($data->$field) && !is_null($data->$field) ? (empty($info['serialize']) ? $data->$field : unserialize($data->$field)) : NULL;
}
return $object;
}
/**
* Ctools export callback
* Handles NULL value (it's not possible to do this with "field" export callback, because null values are rewritten before its call)
*/
function elysia_cron_ctools_export_callback($object, $indent) {
$table = 'elysia_cron';
$schema = ctools_export_get_schema($table);
$identifier = $schema['export']['identifier'];
$output = $indent . '$' . $identifier . ' = new ' . get_class($object) . ";\n";
if ($schema['export']['can disable']) {
$output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . "\n";
}
if (!empty($schema['export']['api']['current_version'])) {
$output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ";\n";
}
$fields = $schema['fields'];
foreach ($fields as $field => $info) {
if (!empty($info['no export'])) {
continue;
}
$value = isset($object->$field) ? $object->$field : (isset($info['default']) ? $info['default'] : NULL);
if (!is_null($value) && $info['type'] == 'int') {
$value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value;
}
$output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n";
}
return $output;
}
/**
* Ctools export to hook code callback
* Original "to hook code" callback doesn't support the replacement of "load/load all" callback (it simply ignores them, even if defined and supported elsewhere)
* This code is equal to the original ctools one, but uses specific load callback
*/
function elysia_cron_ctools_to_hook_code($names, $name) {
$table = 'elysia_cron';
$schema = ctools_export_get_schema($table);
$export = $schema['export'];
$output = '';
$objects = elysia_cron_ctools_export_load_all();
$objects = array_intersect_key($objects, array_flip($names));
if ($objects) {
$output = "/**\n";
$output .= " * Implementation of hook_{$export['default hook']}()\n";
$output .= " */\n";
$output .= "function " . $name . "_{$export['default hook']}() {\n";
$output .= " \${$export['identifier']}s = array();\n\n";
foreach ($objects as $object) {
$output .= ctools_export_crud_export($table, $object, ' ');
$output .= " \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n";
}
$output .= " return \${$export['identifier']}s;\n";
$output .= "}\n";
}
return $output;
}

View File

@@ -0,0 +1,181 @@
<?php
/*******************************************************************************
* DRUSH SUPPORT
******************************************************************************/
function elysia_cron_drush_detect() {
return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))) && function_exists('drush_main');
}
function elysia_cron_drush_die() {
if (function_exists('drush_bootstrap_finish')) {
// Only in Drush5
drush_bootstrap_finish();
drush_die();
} else {
// Drush4
drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE);
exit();
}
}
function elysia_cron_drush_invoke($replace_core_cron = false) {
$args = drush_get_arguments();
array_shift($args);
// If invoked like "core-cron" i do the same as that: execute "run"
if ($replace_core_cron && empty($args)) {
$args = array("run");
}
call_user_func_array('drush_elysia_cron_run_wrapper', $args);
elysia_cron_drush_die();
}
/**
* Implementation of hook_drush_command().
*/
function elysia_cron_drush_command() {
$items = array();
$items['elysia-cron'] = array(
'description' => "Run all cron tasks in all active modules for specified site using elysia cron system. This replaces the standard \"core-cron\" drush handler.", // TODO dt
'callback' => 'drush_elysia_cron_run_wrapper',
'arguments' => array(
'op' => 'Operation: list, run, disable, enable',
'target' => 'Target of operation (optional): usually a task name or a channel name starting with "@"',
),
'examples' => array(
'elysia-cron run' => 'Run all cron tasks in all active modules (as the standard "core-cron")',
'elysia-cron run --verbose' => 'Run all cron tasks in verbose mode',
'elysia-cron run @channel' => 'Run all cron tasks in specified channel',
'elysia-cron run search_cron --ignore-time' => 'Run only search index build task (force execution)',
'elysia-cron list --elysia-cron-verbose' => 'List all channels/tasks in verbose mode',
'elysia-cron disable search_cron' => 'Disable search index build task',
),
'options' => array(
'quiet' => 'suppress all output',
'verbose' => 'enable extended output',
'elysia-cron-verbose' => 'enable extended output (the same as --verbose, but without enabling drush verbose mode)',
'ignore-disable' => 'run channels/tasks even if disabled',
'ignore-time' => 'run channels/tasks even if not ready for execution',
'ignore-running' => 'run channels/tasks even if already running',
),
);
return $items;
}
/**
* A drush command callback.
*
* wraps the elysia_cron_run function, passing manual = true
*/
function drush_elysia_cron_run_wrapper($op = false, $target = false) {
/*
drush_log("test notice", "notice");
drush_log("test ok", "ok");
drush_log("test completed", "completed");
drush_log("test warning", "warning");
drush_log("test error", "error");
drush_print("print");
*/
global $elysia_cron_drush;
$quiet = drush_get_option("quiet", false);
$verbose = drush_get_option("verbose", false);
if (!$verbose) {
$verbose = drush_get_option("elysia-cron-verbose", false);
}
$elysia_cron_drush = $quiet ? 1 : !$verbose ? 2 : 3;
switch ($op) {
case 'list':
global $elysia_cron_settings_by_channel;
elysia_cron_initialize();
foreach ($elysia_cron_settings_by_channel as $channel => $jobs) {
if (!$verbose) {
$line = array("@" . $channel);
} else {
$line = array("Channel: @" . $channel);
if ($running = elysia_cron_is_channel_running($channel)) {
$line[] = "RUNNING NOW, since " . elysia_cron_date($running);
}
if (!empty($jobs['#data']['disabled'])) {
$line[] = "DISABLED";
}
if (!$running) {
$line[] = "Last run: " . elysia_cron_date(_ec_variable_get('elysia_cron_last_run', 0));
}
}
drush_print(implode($line, ", "));
foreach ($jobs as $job => $conf) if ($job{0} != '#') {
if (!$verbose) {
$line = array($job);
} else {
$line = array("- Job: " . $job);
if (!empty($conf['running'])) {
$line[] = "RUNNING NOW, since " . elysia_cron_date($conf['running']);
}
if (!empty($conf['disabled'])) {
$line[] = "DISABLED";
}
if (empty($conf['running']) && elysia_cron_should_run($conf)) {
$line[] = "Ready to run";
}
if (empty($conf['running'])) {
$line[] = "Last run: " . elysia_cron_date($conf['last_run']);
}
}
drush_print(implode($line, ", "));
}
}
break;
case 'run':
if (empty($target)) {
elysia_cron_run(true, drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
//drush_log("Cron run complete", "completed");
}
elseif ($target{0} == '@') {
elysia_cron_initialize();
if (elysia_cron_channel_exists(substr($target, 1))) {
elysia_cron_run_channel(substr($target, 1), drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
//drush_log("Cron run complete", "completed");
} else {
drush_set_error('Channel ' . substr($target, 1) . ' does not exists');
}
}
else {
elysia_cron_initialize();
if (elysia_cron_job_exists($target)) {
elysia_cron_run_job($target, drush_get_option("ignore-disable", false), drush_get_option("ignore-time", false), drush_get_option("ignore-running", false));
//drush_log("Cron run complete", "completed");
} else {
drush_set_error('Job ' . $target . ' does not exists');
}
}
break;
case 'disable':
case 'enable':
if (!empty($target)) {
if ($target{0} == '@') {
elysia_cron_set_channel_disabled(substr($target, 1), $op == 'disable');
} else {
elysia_cron_set_job_disabled($target, $op == 'disable');
}
drush_log("Done", "ok");
} else {
drush_set_error('Target not specified');
}
break;
break;
default:
drush_print_help(drush_get_command());
}
}

View File

@@ -0,0 +1,12 @@
name = "Elysia Cron"
description = "Extended cron support with crontab-like scheduling and other features."
core = 7.x
files[] = elysia_cron_update.php
files[] = elysia_drupalconv.php
configure = admin/config/system/cron
; Information added by drupal.org packaging script on 2013-09-30
version = "7.x-2.1+9-dev"
core = "7.x"
project = "elysia_cron"
datestamp = "1380576625"

View File

@@ -0,0 +1,160 @@
<?php
function elysia_cron_schema() {
$schema['elysia_cron'] = array(
'fields' => array(
'name' => array(
'type' => 'varchar',
'length' => 120,
'not null' => TRUE,
),
'disable' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => FALSE,
),
'rule' => array(
'type' => 'varchar',
'not null' => FALSE,
'length' => 32,
),
'weight' => array(
'type' => 'int',
'not null' => FALSE,
),
'context' => array(
'type' => 'varchar',
'not null' => FALSE,
'length' => 32,
),
'running' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'last_run' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'last_aborted' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'abort_count' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'last_abort_function' => array(
'type' => 'varchar',
'length' => 32,
'no export' => TRUE,
),
'last_execution_time' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'execution_count' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'avg_execution_time' => array(
'type' => 'float',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'max_execution_time' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
'last_shutdown_time' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'no export' => TRUE,
),
),
'primary key' => array('name'),
'export' => array(
'key' => 'name',
'key name' => 'Cron job name',
'primary key' => 'name',
'identifier' => 'cron_rule',
'object factory' => 'elysia_cron_ctools_export_object_factory',
'load callback' => 'elysia_cron_ctools_export_load',
'load all callback' => 'elysia_cron_ctools_export_load_all',
//'save callback' => 'elysia_cron_ctools_export_save',
'export callback' => 'elysia_cron_ctools_export_callback',
//'import callback' => 'elysia_cron_ctools_import_callback',
'to hook code callback' => 'elysia_cron_ctools_to_hook_code',
'default hook' => 'default_elysia_cron_rules',
'api' => array(
'owner' => 'elysia_cron',
'api' => 'default_elysia_cron_rules',
'minimum_version' => 1,
'current_version' => 1,
),
),
);
return $schema;
}
/**
* Implementation of hook_install().
*/
function elysia_cron_install() {
//drupal_install_schema('elysia_cron');
// elysia_cron MUST be the first returned by module_list
// This is to ensure elysia_cron_cron is the first hook called by standard cron.php.
$min = db_query("select min(weight) from {system}")->fetchField();
if ($min > -65535) {
$min = -65535;
}
else {
$min--;
}
db_update('system')->fields(array('weight' => $min))->condition('name', 'elysia_cron')->execute();
variable_set('elysia_cron_version', elysia_cron_version());
drupal_set_message('Elysia cron installed. Setup could be found at ' . l(t('Settings page'), 'admin/config/system/cron') . '. See INSTALL.TXT for more info.');
}
/**
* Implementation of hook_uninstall().
*/
function elysia_cron_uninstall() {
$rs = db_query("select name from {variable} where name like 'elysia_cron_%%'");
foreach ($rs as $o) {
variable_del($o->name);
}
//drupal_uninstall_schema('elysia_cron');
drupal_set_message('Elysia cron uninstalled.');
}
function elysia_cron_update_1() {
$cron_key = variable_get('elysia_cron_key', false);
if ($cron_key) {
variable_set('cron_key', $cron_key);
}
variable_del('elysia_cron_key');
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,248 @@
<?php
function elysia_cron_should_run($conf, $now = -1, $ignore_disable = false, $ignore_time = false) {
$prev_rule_run = 0; // What time SHOULD the job be executed last time
if (!$ignore_disable && $conf['disabled']) {
return false;
}
if ($ignore_time) {
return true;
}
if ($now < 0) {
$now = time();
}
if ((!$conf['last_run']) || ($now - $conf['last_run'] > 365 * 86400)) {
return true;
}
$next_run = _elysia_cron_next_run($conf);
return $now >= $next_run;
}
function _elysia_cron_next_run($conf) {
if (!isset($conf['rule'])) {
return false;
}
$ranges = array(
array(0, 59),
array(0, 23),
array(1, 31), // TODO
array(1, 12),
array(0, 3000),
array(0, 6),
);
if (!preg_match('/^([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)$/', $conf['rule'], $rules)) {
elysia_cron_warning('Invalid rule found: %rule', array('%rule' => $conf['rule']));
return false;
}
$rule = array($rules[1], $rules[2], array($rules[3], $rules[5]), $rules[4]);
$ruledec = array();
$date = __cronDecodeDate($conf['last_run'], 1);
$expected_date = __cronDecodeDate(!empty($conf['last_run_expected']) ? $conf['last_run_expected'] : 0);
for ($i = 0; $i < 4; $i++) {
if ($i != 2) {
// Standard scheme for mins, hours, month
$ruledec[$i] = __cronDecodeRule($rule[$i], $ranges[$i][0], $ranges[$i][1]);
} else {
// For mday+week we follow another scheme
$ruledec[$i] = __cronDecodeRuleMday($rule[2], $date[3], $date[4]);
}
$r = $ruledec[$i];
$new = $date[$i];
if ($r['d']) {
if ($expected_date[$i] > $date[$i]) {
$expected_date[$i] -= $ranges[$i][1] + 1;
}
$new = $expected_date[$i] + ceil(($date[$i] - $expected_date[$i]) / $r['d']) * $r['d'];
}
elseif ($r['n']) {
$new = __cronNextOrEqual($date[$i], $r['n'], $ranges[$i][0], $ranges[$i][1]);
}
if ($new != $date[$i]) {
$date[$i] = $new;
if ($date[$i] > $ranges[$i][1]) {
$date[$i + 1]++;
$date[$i] = $ranges[$i][0] + $date[$i] - $ranges[$i][1] - 1;
}
for ($j = 0; $j < $i; $j++) {
if ($j == 2) {
// For mday+week decoded rule could be changed (by month+year)
$ruledec[$j] = __cronDecodeRuleMday($rule[2], $date[3], $date[4]);
}
$date[$j] = $ruledec[$j]['d'] ? ($ranges[$j][0] == 0 ? 0 : $ruledec[$j]['d']) : ($ruledec[$j]['n'] ? reset($ruledec[$j]['n']) : $ranges[$j][0]);
$expected_date[$j] = 0;
}
}
}
return __cronEncodeDate($date);
}
function __cronDecodeDate($timestamp, $min_diff = 0) {
$time = floor($timestamp / 60);
$time += $min_diff;
$date = $time ? getdate($time * 60) : 0;
$date = array(
$time ? $date['minutes'] : 0,
$time ? $date['hours'] : 0,
$time ? $date['mday'] : 0,
$time ? $date['mon'] : 0,
$time ? $date['year'] : 0,
);
return $date;
}
function __cronEncodeDate($date) {
return mktime($date[1], $date[0], 0, $date[3], $date[2], $date[4]);
}
function __cronNextOrEqual($el, $arr, $range_start, $range_end) {
if (empty($arr)) {
return $el;
}
foreach ($arr as $x) {
if ($x >= $el) {
return $x;
}
}
return $range_end + reset($arr) + 1 - $range_start;
}
function __cronDecodeRule($rule, $min, $max) {
if ($rule == '*') {
return array('n' => array(), 'd' => 0);
}
$result = array('n' => array(), 'd' => 0);
foreach (explode(',', $rule) as $token) {
if (preg_match('/^([0-9]+)-([0-9]+)$/', $token, $r)) {
$result['n'] = array_merge($result['n'], range($r[1], $r[2]));
}
elseif (preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
$result['d'] = $r[1];
}
elseif (is_numeric($token)) {
$result['n'][] = $token;
}
}
sort($result['n']);
return $result;
}
function __cronDecodeRuleMday($rule, $month, $year) {
$range_from = 1;
$range_to = $month != 2 ? (in_array($month, array(4,6,9,11)) ? 30 : 31) : ($year % 4 == 0 ? 29 : 28);
$r1 = __cronDecodeRule($rule[0], $range_from, $range_to);
$r2 = __cronDecodeRule($rule[1], $range_from, $range_to);
if ($r2['d']) {
for ($i = 0; $i < 7; $i++) {
if ($i % $r2['d'] == 0) {
$r2['n'][] = $i;
}
}
}
if ($r2['n']) {
$r2['n'] = array_unique($r2['n']);
$r1['n'] = array_merge($r1['n'], __cronMonDaysFromWeekDays($year, $month, $r2['n']), __cronMonDaysFromWeekDays($year, $month + 1, $r2['n'], 31)); // Use always "31" and not $range_to, see http://drupal.org/node/1668302
}
return $r1;
}
// Used by elysia_cron_should_run
function __cronMonDaysFromWeekDays($year, $mon, $weekdays, $offset = 0) {
if ($mon > 12) {
$year ++;
$mon = $mon - 12;
}
$result = array();
for ($i = 1; checkdate($mon, $i, $year); $i++) {
$w = date('w', mktime(12, 00, 00, $mon, $i, $year));
if (in_array($w, $weekdays)) {
$result[] = $i + $offset;
}
}
return $result;
}
/*******************************************************************************
* TESTS
******************************************************************************/
function test_elysia_cron_should_run() {
dprint("Start test");
$start = microtime(true);
//mktime: hr min sec mon day yr
dprint(" 1." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 01, 0, 1, 2, 2008))));
dprint(" 2." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(15, 00, 0, 1, 2, 2008))));
dprint(" 3." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(11, 59, 0, 1, 3, 2008))));
dprint(" 4." . (true == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 00, 0, 1, 3, 2008))));
dprint(" 5." . (false == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 3, 2008))));
dprint(" 6." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 3, 2008))));
dprint(" 7." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 4, 2008))));
dprint(" 8." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 2, 2008))));
dprint(" 9." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(0, 0, 0, 1, 3, 2008))));
dprint("10." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("11." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(0, 0, 0, 1, 6, 2008))));
dprint("12." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("13." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(00, 00, 0, 1, 7, 2008))));
dprint("14." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
dprint("15." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("16." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("17." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 6, 2008))));
dprint("18." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 28, 0, 1, 6, 2008))));
dprint("19." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 5, 2008))));
dprint("20." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 30, 0, 1, 5, 2008))));
dprint("21." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("22." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
dprint("23." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 28, 2008))));
dprint("24." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
dprint("25." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
dprint("26." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 1, 2009))));
dprint("27." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 7, 2009))));
dprint("28." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(23, 59, 0, 1, 7, 2009))));
dprint("29." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
dprint("30." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
dprint("31." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 3, 7, 2008))));
dprint("32." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 58, 0, 2, 6, 2009))));
dprint("33." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 2, 6, 2009))));
dprint("34." . (true == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 58, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
dprint("35." . (false == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 11, 2008))));
dprint("36." . (true == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 20, 2008))));
dprint("37." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("38." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("39." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("40." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 10, 2008))));
dprint("41." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
dprint("42." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 16, 2008))));
dprint("43." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("44." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("45." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 7, 2008))));
dprint("46." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 13, 2008))));
dprint("47." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 4, 2008)), mktime(23, 59, 0, 2, 5, 2008))));
dprint("48." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 5, 2008)), mktime(23, 59, 0, 2, 10, 2008))));
dprint("49." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 10, 2008)), mktime(23, 59, 0, 2, 17, 2008))));
dprint("49." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 58, 0, 2, 10, 2008)), mktime(8, 59, 0, 2, 10, 2008))));
dprint("50." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(9, 00, 0, 2, 10, 2008))));
dprint("51." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(17, 59, 0, 2, 10, 2008))));
dprint("52." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(18, 00, 0, 2, 10, 2008))));
dprint("53." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
dprint("54." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(19, 0, 0, 2, 10, 2008))));
dprint("55." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(9, 0, 0, 3, 10, 2008))));
dprint("56." . (true == elysia_cron_should_run(array('rule' => '* * * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
dprint("End test (" . (microtime(true) - $start) . ")");
}
// Remove comment to run tests
//test_elysia_cron_should_run();die();

View File

@@ -0,0 +1,230 @@
<?php
function elysia_cron_should_run($conf, $now = -1) {
if ($conf['disabled']) {
return false;
}
if ($now < 0) {
$now = time();
}
if ((!$conf['last_run']) || ($now - $conf['last_run'] > 365 * 86400)) {
return true;
}
if (!preg_match('/^([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)[ ]+([0-9*,\/-]+)$/', $conf['rule'], $rules)) {
elysia_cron_warning('Invalid rule found: %rule', array('%rule' => $conf['rule']));
return false;
}
$weekdayspec = ($rules[5] != '*');
$mondayspec = ($rules[3] != '*');
$rules[5] = _cronDecodeRule($rules[5], 0, 6);
$rules[4] = _cronDecodeRule($rules[4], 1, 12);
$rules[3] = _cronDecodeRule($rules[3], 1, 31);
$rules[2] = _cronDecodeRule($rules[2], 0, 23);
$rules[1] = _cronDecodeRule($rules[1], 0, 59);
$lastT = _cronT($conf['last_run'] + 30);
$nowT = _cronT($now);
$nowTDelta = $nowT - $lastT + ($lastT > $nowT ? 12 * 31 * 24 * 60 : 0);
$year = date('Y', $conf['last_run']);
if ($mondayspec || (!$mondayspec && !$weekdayspec)) {
$first = -1;
foreach ($rules[4] as $mon) {
foreach ($rules[3] as $d) {
if (checkdate($mon, $d, $year)) {
foreach ($rules[2] as $h) {
foreach ($rules[1] as $m) {
$t = _cronT($mon, $d, $h, $m);
//dprint("* ".$t." L:".$lastT);
if ($first < 0) {
$first = $t;
}
if ($t > $lastT) {
$nextT = $t;
break 4;
}
}
}
}
}
}
//dprint("R: ".$nextT);
if (!$nextT) {
$nextT = $first;
}
$nextTDelta = $nextT - $lastT + ($lastT > $nextT ? 12 * 31 * 24 * 60 : 0);
//dprint($nowT.' '.$nowTDelta.' '.$nextT.' '.$nextTDelta);
if ($nowTDelta >= $nextTDelta) {
return true;
}
}
if ($weekdayspec) {
foreach ($rules[4] as $mon) {
foreach (_cronMonDaysFromWeekDays($year, $mon, $rules[5]) as $d) {
foreach ($rules[2] as $h) {
foreach ($rules[1] as $m) {
$t = _cronT($mon, $d, $h, $m);
//dprint("* ".$t." L:".$lastT);
if ($t > $lastT) {
$nextT = $t;
break 4;
}
}
}
}
}
//dprint("R: ".$nextT);
if (!$nextT) {
//Must get the first of following year (if day_of_week is specified it's not the same of previous one)
foreach ($rules[4] as $mon) {
foreach (_cronMonDaysFromWeekDays($year + 1, $mon, $rules[5]) as $d) {
foreach ($rules[2] as $h) {
foreach ($rules[1] as $m) {
$nextT = _cronT($mon, $d, $h, $m);
break 4;
}
}
}
}
}
$nextTDelta = $nextT - $lastT + ($lastT > $nextT ? 12 * 31 * 24 * 60 : 0);
//dprint($nowT.' '.$nowTDelta.' '.$nextT.' '.$nextTDelta);
if ($nowTDelta >= $nextTDelta) {
return true;
}
}
return false;
}
// Used by elysia_cron_should_run
function _cronT($time, $d = -1, $h = -1, $m = -1) {
if ($d < 0) {
return date('n', $time) * 31 * 24 * 60 + date('j', $time) * 24 * 60 + date('H', $time) * 60 + date('i', $time);
}
else {
return $time * 31 * 24 * 60 + $d * 24 * 60 + $h * 60 + $m;
}
}
// Used by elysia_cron_should_run
function _cronMonDaysFromWeekDays($year, $mon, $weekdays) {
$result = array();
for ($i = 1; checkdate($mon, $i, $year); $i++) {
$w = date('w', mktime(12, 00, 00, $mon, $i, $year));
if (in_array($w, $weekdays)) {
$result[] = $i;
}
}
return $result;
}
// Used by elysia_cron_should_run
function _cronDecodeRule($rule, $min, $max) {
if ($rule == '*') {
return range($min, $max);
}
$result = array();
foreach (explode(',', $rule) as $token) {
if (preg_match('/^([0-9]+)-([0-9]+)$/', $token, $r)) {
$result = array_merge($result, range($r[1], $r[2]));
}
elseif (preg_match('/^\*\/([0-9]+)$/', $token, $r)) {
for ($i = $min; $i <= $max; $i++) {
if ($i % $r[1] == 0) {
$result[] = $i;
}
}
}
elseif (is_numeric($token)) {
$result[] = $token;
}
}
return $result;
}
/*******************************************************************************
* TESTS
******************************************************************************/
function test_elysia_cron_should_run() {
dprint("Start test");
$start = microtime(true);
//mktime: hr min sec mon day yr
dprint(" 1." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 01, 0, 1, 2, 2008))));
dprint(" 2." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(15, 00, 0, 1, 2, 2008))));
dprint(" 3." . (false == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(11, 59, 0, 1, 3, 2008))));
dprint(" 4." . (true == elysia_cron_should_run(array('rule' => '0 12 * * *', 'last_run' => mktime(12, 0, 0, 1, 2, 2008)), mktime(12, 00, 0, 1, 3, 2008))));
dprint(" 5." . (false == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 3, 2008))));
dprint(" 6." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 3, 2008))));
dprint(" 7." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 59, 0, 1, 2, 2008)), mktime(0, 00, 0, 1, 4, 2008))));
dprint(" 8." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(23, 59, 0, 1, 2, 2008))));
dprint(" 9." . (true == elysia_cron_should_run(array('rule' => '59 23 * * *', 'last_run' => mktime(23, 58, 0, 1, 2, 2008)), mktime(0, 0, 0, 1, 3, 2008))));
dprint("10." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("11." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(0, 0, 0, 1, 6, 2008))));
dprint("12." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("13." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(00, 00, 0, 1, 7, 2008))));
dprint("14." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
dprint("15." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("16." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("17." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 6, 2008))));
dprint("18." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 58, 0, 1, 5, 2008)), mktime(23, 28, 0, 1, 6, 2008))));
dprint("19." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 5, 2008))));
dprint("20." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 30, 0, 1, 5, 2008))));
dprint("21." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("22." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 0', 'last_run' => mktime(23, 28, 0, 1, 5, 2008)), mktime(23, 29, 0, 1, 6, 2008))));
dprint("23." . (false == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 28, 2008))));
dprint("24." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
dprint("25." . (true == elysia_cron_should_run(array('rule' => '29,59 23 * * 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
dprint("26." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 1, 2009))));
dprint("27." . (false == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(0, 0, 0, 1, 7, 2009))));
dprint("28." . (true == elysia_cron_should_run(array('rule' => '59 23 * * 3', 'last_run' => mktime(23, 59, 0, 12, 31, 2008)), mktime(23, 59, 0, 1, 7, 2009))));
dprint("29." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(23, 59, 0, 2, 29, 2008))));
dprint("30." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 22, 2008)), mktime(0, 0, 0, 3, 1, 2008))));
dprint("31." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 3, 7, 2008))));
dprint("32." . (false == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 58, 0, 2, 6, 2009))));
dprint("33." . (true == elysia_cron_should_run(array('rule' => '59 23 * 2 5', 'last_run' => mktime(23, 59, 0, 2, 29, 2008)), mktime(23, 59, 0, 2, 6, 2009))));
dprint("34." . (true == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 58, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
dprint("35." . (false == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 11, 2008))));
dprint("36." . (true == elysia_cron_should_run(array('rule' => '59 23 *' . '/10 * *', 'last_run' => mktime(23, 59, 0, 1, 10, 2008)), mktime(23, 59, 0, 1, 20, 2008))));
dprint("37." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("38." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("39." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("40." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 58, 0, 1, 10, 2008))));
dprint("41." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 10, 2008))));
dprint("42." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5,10-15 * *', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 16, 2008))));
dprint("43." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 4, 2008)), mktime(23, 59, 0, 1, 5, 2008))));
dprint("44." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 5, 2008)), mktime(23, 59, 0, 1, 6, 2008))));
dprint("45." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 7, 2008))));
dprint("46." . (true == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 1, 6, 2008)), mktime(23, 59, 0, 1, 13, 2008))));
dprint("47." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 4, 2008)), mktime(23, 59, 0, 2, 5, 2008))));
dprint("48." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 5, 2008)), mktime(23, 59, 0, 2, 10, 2008))));
dprint("49." . (false == elysia_cron_should_run(array('rule' => '59 23 1-5 1 0', 'last_run' => mktime(23, 59, 0, 2, 10, 2008)), mktime(23, 59, 0, 2, 17, 2008))));
dprint("49." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 58, 0, 2, 10, 2008)), mktime(8, 59, 0, 2, 10, 2008))));
dprint("50." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(9, 00, 0, 2, 10, 2008))));
dprint("51." . (false == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(17, 59, 0, 2, 10, 2008))));
dprint("52." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(8, 59, 0, 2, 10, 2008)), mktime(18, 00, 0, 2, 10, 2008))));
dprint("53." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(18, 01, 0, 2, 10, 2008))));
dprint("54." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(19, 0, 0, 2, 10, 2008))));
dprint("55." . (true == elysia_cron_should_run(array('rule' => '* 0,1,2,3,4,5,6,7,8,18,19,20,21,22,23 * * *', 'last_run' => mktime(18, 00, 0, 2, 10, 2008)), mktime(9, 0, 0, 3, 10, 2008))));
dprint("End test (" . (microtime(true) - $start) . ")");
}
// Remove comment to run tests
//test_elysia_cron_should_run();die();

View File

@@ -0,0 +1,327 @@
<?php
/*******************************************************************************
* ELYSIA CRON VERSION UPDATE
******************************************************************************/
function elysia_cron_check_version_update() {
$ver = variable_get('elysia_cron_version', 0);
if ($ver < 20111012) {
$ver = _ec_variable_get('elysia_cron_version', 0);
}
if (!$ver || $ver < 20090218) {
$unchanged = array(
'elysia_cron_last_context',
'elysia_cron_last_run',
'elysia_cron_disabled',
'elysia_cron_semaphore',
'elysia_cron_key',
'elysia_cron_allowed_hosts',
'elysia_cron_default_rule',
'elysia_cron_script',
'elysia_cron_runtime_replacement',
'elysia_cron_version',
);
$rs = db_query("select * from {variable} where name like 'elysia_cron_%%'");
while ($v = db_fetch_object($rs)) {
if (!in_array($v->name, $unchanged)) {
$vn = false;
if (preg_match('/^elysia_cron_ctx_(.*)_(running|disabled|last_run|last_aborted|abort_count|execution_count|last_execution_time|avg_execution_time|max_execution_time|last_shutdown_time|last_abort_function)/', $v->name, $r)) {
switch ($r[2]) {
case 'running':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_r';
break;
case 'disabled':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_d';
break;
case 'last_run':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_lr';
break;
case 'last_aborted':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_la';
break;
case 'abort_count':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_ac';
break;
case 'execution_count':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_ec';
break;
case 'last_execution_time':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_let';
break;
case 'avg_execution_time':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_aet';
break;
case 'max_execution_time':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_met';
break;
case 'last_shutdown_time':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_lst';
break;
case 'last_abort_function':
$vn = 'ecc_' . _ec_get_name($r[1]) . '_laf';
break;
}
}
elseif (preg_match('/^elysia_cron_(.*)_(rule|disabled|context|running|last_run|last_execution_time|execution_count|avg_execution_time|max_execution_time)/', $v->name, $r)) {
switch ($r[2]) {
case 'rule':
$vn = 'ec_' . _ec_get_name($r[1]) . '_rul';
break;
case 'disabled':
$vn = 'ec_' . _ec_get_name($r[1]) . '_d';
break;
case 'context':
$vn = 'ec_' . _ec_get_name($r[1]) . '_c';
break;
case 'running':
$vn = 'ec_' . _ec_get_name($r[1]) . '_r';
break;
case 'last_run':
$vn = 'ec_' . _ec_get_name($r[1]) . '_lr';
break;
case 'last_execution_time':
$vn = 'ec_' . _ec_get_name($r[1]) . '_let';
break;
case 'execution_count':
$vn = 'ec_' . _ec_get_name($r[1]) . '_ec';
break;
case 'avg_execution_time':
$vn = 'ec_' . _ec_get_name($r[1]) . '_aet';
break;
case 'max_execution_time':
$vn = 'ec_' . _ec_get_name($r[1]) . '_met';
break;
}
}
if ($vn) {
variable_set($vn, unserialize($v->value));
}
else {
elysia_cron_error('Error in update, cant convert %name (value: %value)', array('%name' => $v->name, '%value' => $v->value), true);
}
variable_del($v->name);
}
}
variable_set('elysia_cron_version', 20090218);
}
if ($ver < 20090920) {
variable_set('elysia_cron_version', 20090920);
}
if ($ver < 20100507) {
if (EC_DRUPAL_VERSION >= 6) {
// D6
drupal_install_schema('elysia_cron');
// In ver 20111020 disabled has been renamed to disable, revert it now
if (EC_DRUPAL_VERSION >= 7) {
// D7
// Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
db_change_field($v = 'elysia_cron', 'disable', 'disabled', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
}
elseif (EC_DRUPAL_VERSION >= 6) {
// D6
$ret = array();
db_change_field($ret, 'elysia_cron', 'disable', 'disabled', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
}
}
else {
// D5
switch ($GLOBALS['db_type']) {
case 'mysqli':
case 'mysql':
db_query("create table if not exists {elysia_cron} (
name varchar(120) not null,
disabled tinyint(1) not null default '0',
rule varchar(32),
weight int(11) not null default '0',
context varchar(32),
running int(11) not null default '0',
last_run int(11) not null default '0',
last_aborted tinyint(1) not null default '0',
abort_count int(11) not null default '0',
last_abort_function varchar(32),
last_execution_time int(11) not null default '0',
execution_count int(11) not null default '0',
avg_execution_time float(5,2) not null default '0',
max_execution_time int(11) not null default '0',
last_shutdown_time int(11) not null default '0',
primary key (name)
)");
break;
case 'pgsql':
db_query("create table {elysia_cron} (
name varchar(120) not null,
disabled smallint not null default '0',
rule varchar(32),
weight integer not null default '0',
context varchar(32),
running int not null default '0',
last_run integer not null default '0',
last_aborted smallint not null default '0',
abort_count integer not null default '0',
last_abort_function varchar(32),
last_execution_time integer not null default '0',
execution_count integer not null default '0',
avg_execution_time float not null default '0',
max_execution_time integer not null default '0',
last_shutdown_time integer not null default '0',
primary key (name)
)");
}
}
$rs = db_query("select * from {variable} where name like 'ec_%%' or name like 'ecc_%%'");
$data = array();
$todelete = array();
while ($v = db_fetch_object($rs)) {
$name = false;
if (preg_match('/^ecc_(.+)_(r|d|lr|la|ac|ec|let|aet|met|lst|laf)/', $v->name, $r)) {
$name = ':' . $r[1];
}
elseif (preg_match('/^ec_(.+)_(rul|d|c|w|r|lr|let|ec|aet|met)/', $v->name, $r)) {
$name = $r[1];
}
if ($name) {
if (!isset($data[$name])) {
$data[$name] = array('name' => $name);
}
switch ($r[2]) {
case 'r':
$f = 'running';
break;
case 'd':
$f = 'disabled';
break;
case 'rul':
$f = 'rule';
break;
case 'w':
$f = 'weight';
break;
case 'c':
$f = 'context';
break;
case 'lr':
$f = 'last_run';
break;
case 'la':
$f = 'last_aborted';
break;
case 'ac':
$f = 'abort_count';
break;
case 'laf':
$f = 'last_abort_function';
break;
case 'let':
$f = 'last_execution_time';
break;
case 'ec':
$f = 'execution_count';
break;
case 'aet':
$f = 'avg_execution_time';
break;
case 'met':
$f = 'max_execution_time';
break;
case 'lst':
$f = 'last_shutdown_time';
break;
}
$data[$name][$f] = unserialize($v->value);
$todelete[] = $v->name;
}
}
$ifields = array('disabled', 'weight', 'running', 'last_run', 'last_aborted', 'abort_count', 'last_execution_time', 'execution_count', 'avg_execution_time', 'max_execution_time', 'last_shutdown_time');
foreach ($data as $v) {
foreach ($ifields as $f) {
if (empty($v[$f])) {
$v[$f] = 0;
}
}
db_query("insert into {elysia_cron} (name, disabled, rule, weight, context, running, last_run, last_aborted, abort_count, last_abort_function, last_execution_time, execution_count, avg_execution_time, max_execution_time, last_shutdown_time)
values ('%s', %d, '%s', %d, '%s', %d, %d, %d, %d, '%s', %d, %d, %f, %d, %d)",
$v['name'], $v['disabled'], $v['rule'], $v['weight'], $v['context'], $v['running'], $v['last_run'], $v['last_aborted'], $v['abort_count'], $v['last_abort_function'], $v['last_execution_time'], $v['execution_count'], $v['avg_execution_time'], $v['max_execution_time'], $v['last_shutdown_time']
);
}
db_query("update {elysia_cron} set context = null where context = ''");
db_query("update {elysia_cron} set rule = null where rule = ''");
foreach ($todelete as $v) {
variable_del($v);
db_query("DELETE FROM {variable} WHERE name = '%s'", $v);
}
variable_set('elysia_cron_version', 20100507);
unset($GLOBALS['_ec_variables']);
}
// D7 VERSION FROM NOW ON...
if ($ver < 20110323) {
if (EC_DRUPAL_VERSION >= 7) {
// D7
// Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
db_change_field($v = 'elysia_cron', 'weight', 'weight', array('type' => 'int', 'not null' => FALSE));
}
elseif (EC_DRUPAL_VERSION >= 6) {
// D6
$ret = array();
db_change_field($ret, 'elysia_cron', 'weight', 'weight', array('type' => 'int', 'not null' => FALSE));
}
else {
// D5
db_query("alter table {elysia_cron} change weight weight int(11)");
}
variable_set('elysia_cron_version', 20110323);
}
if ($ver < 20111007) {
$default_rules = variable_get('elysia_cron_default_rules', $GLOBALS['elysia_cron_default_rules']);
if (!empty($default_rules['*/6 * * * *']) && $default_rules['*/6 * * * *'] == 'Every 6 hours') {
unset($default_rules['*/6 * * * *']);
$default_rules['0 */6 * * *'] = 'Every 6 hours';
variable_set('elysia_cron_default_rules', $default_rules);
}
variable_set('elysia_cron_version', 20111007);
}
if ($ver < 20111012) {
// I only need to rebuild variable cache, so i just set the new version
variable_set('elysia_cron_version', 20111012);
}
if ($ver < 20111020) {
if (EC_DRUPAL_VERSION >= 7) {
// D7
// Must use "$v" for PHP5.3 running D6 version (detect the error even if it doesn't pass here)
db_change_field($v = 'elysia_cron', 'disabled', 'disable', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
}
elseif (EC_DRUPAL_VERSION >= 6) {
// D6
$ret = array();
db_change_field($ret, 'elysia_cron', 'disabled', 'disable', array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE));
}
else {
// D5
db_query("alter table {elysia_cron} change disabled disable tinyint(1)");
}
db_query("update {elysia_cron} set disable = NULL where disable = 0");
variable_set('elysia_cron_version', 20111020);
}
}

View File

@@ -0,0 +1,86 @@
<?php
define('EC_DRUPAL_VERSION', 7);
/***************************************************************
* D7 VERSION
***************************************************************/
function _dcf_hook_boot($module) {
return true;
}
function _dcf_hook_init($module) {
return true;
}
function _dcf_hook_menu($items, $maycache) {
return $items;
}
function _dcr_render_array($output) {
return $output;
}
function _dcr_form(&$form) {
return $form;
}
function _dcf_internal_path($path) {
return $path;
}
function _dcf_t($string) {
return $string;
}
function _dco_watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { // WARN d7 changed WATCHDOG_ costants
return watchdog($type, $message, $variables, $severity, $link);
}
function _dco_l($text, $path, array $options = array()) {
return l($text, $path, $options);
}
function _dcf_form_validate(&$form, &$form_state) {
return array('form' => &$form, 'form_state' => &$form_state);
}
function _dco_theme($name, $args) {
return theme($name, $args);
}
function _dcf_theme_signature($args) {
return array();
}
function _dcr_hook_theme($specs) {
return $specs;
}
function _dcf_theme_form(&$args) {
return array( 'variables' => $args );
}
/***************************************************************
* D7 EXTRA FUNCTIONS
***************************************************************/
function drupal_module_get_min_weight($except_module = false) {
return !$except_module ? db_query("select min(weight) from {system}")->fetchField() :
db_query("select min(weight) from {system} where name != :name", array(':name' => $except_module))->fetchField();
}
function drupal_module_get_weight($name) {
return db_query("select weight from {system} where name = :name", array(':name' => $name))->fetchField();
}
function drupal_module_set_weight($name, $weight) {
db_update('system')->fields(array('weight' => $weight))->condition('name', $name)->execute();
}
function drupal_disable_standard_cron() {
}
function drupal_clean_after_cron_run() {
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B