FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
339
sites/all/modules/contrib/dev/prod_check/LICENSE.txt
Normal file
339
sites/all/modules/contrib/dev/prod_check/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
278
sites/all/modules/contrib/dev/prod_check/README.txt
Normal file
278
sites/all/modules/contrib/dev/prod_check/README.txt
Normal file
@@ -0,0 +1,278 @@
|
||||
|
||||
README file for the Production check & Production monitor Drupal modules.
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
When bringing a site live, you should double check a lot of settings, like the
|
||||
error logging, site e-mail, disabling the Devel module and so on.
|
||||
Next to that, you should ensure that all SEO modules are installed and properly
|
||||
configured (like Google Analytics, Page Title, XML Sitemap etc.). The Production
|
||||
check module will do all of this checking for you and present the results in a
|
||||
convenient status page accessible through /admin/reports/prod-check. Through
|
||||
this status page, you can easily navigate to all the settings pages or the
|
||||
project pages of the missing modules to rectify all you need to.
|
||||
|
||||
It would of course also be nice that these settings remain as you set them up.
|
||||
In some cases, when multiple developers make updates to a live site or with the
|
||||
odd client having somehow gotten superadmin access, stuff can get changed,
|
||||
usually unintended. That's where the Production monitor comes in the picture.
|
||||
You can open up the Production check's XMLRPC interface through its settings
|
||||
page and have the Production monitor module connect to it from a 'local'
|
||||
monitoring site in your development environment. This will allow you to monitor
|
||||
all your sites from a central server and keep an eye on them. When adding a site
|
||||
using Production monitor, you can indicate what exactly needs to be monitored
|
||||
for this site. Updates can be requested manually and are fetched automatically
|
||||
each cron run.
|
||||
|
||||
"But I like Nagios to monitor my sites!"
|
||||
|
||||
If you prefer Nagios monitoring, you can open up Production check's Nagios
|
||||
integration from its settings page. You can specify what exactly you want to
|
||||
monitor there. You will obviousely need to install the Nagios module to make
|
||||
this functionality work.
|
||||
|
||||
|
||||
Remote module update status monitoring
|
||||
======================================
|
||||
Since Production check recommends to turn of the Update module, we have
|
||||
integrated its functionality in both Production check and Production monitor.
|
||||
Production check can be configured to allow to transfer its module list with
|
||||
versioning information once a week at a given time.
|
||||
Production monitor can be configured to download this data along with all the
|
||||
rest. It will then, upon your request (still need to add this on cron, but it's
|
||||
a heavy operation, thinking about the best way to do this: the boost crawler
|
||||
code makes a good candidate), check for module updates locally for the remote
|
||||
site. Production check and Production monitor have the necessary code embedded
|
||||
so you will never need to activate the Update module, not even on the monitor
|
||||
site!
|
||||
|
||||
|
||||
Performance monitoring
|
||||
======================
|
||||
|
||||
If you install the performance module on a production site, you can use
|
||||
Production monitor to remotely monitor the collected performance data. A new
|
||||
subtab will be available displaying the module data in some nice Google charts.
|
||||
Be sure to activate the fetching of performance data in the site's config!
|
||||
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
- Nagios http://drupal.org/project/nagios
|
||||
|
||||
There are no true dependencies defined in the .info file, but naturally you need
|
||||
to install the Nagios module if you would like to integrate Production check
|
||||
with your Nagios monitoring setup.
|
||||
|
||||
- Performance logging http://drupal.org/project/performance
|
||||
|
||||
Again, no true dependencies defined, but if you want remote performance logging,
|
||||
this module can provide it for you! Install it on the remote site and enable the
|
||||
fetching of it's data when adding a site to Production monitor.
|
||||
|
||||
|
||||
Development
|
||||
===========
|
||||
See prod_check.api.php
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Production check
|
||||
----------------
|
||||
1. Extract the prod_check module and place it in /sites/all/modules/contrib
|
||||
|
||||
2. Remove the 'prod_monitor' folder and all it's contents
|
||||
|
||||
3. Upload the prod_check folder to the websites you wish to check / monitor,
|
||||
enable the module and adjust it's settings using /admin/config/system/prod-check.
|
||||
|
||||
4. You can check the /admin/reports/status page to verify if the Production
|
||||
check setup described above was executed correctly and no errors / warnings are
|
||||
reported.
|
||||
|
||||
5. You can find the result of the Production check module on
|
||||
/admin/reports/prod-check
|
||||
|
||||
Production monitor
|
||||
------------------
|
||||
1. Grab the prod_monitor folder from the package and upload it to your
|
||||
'monitoring site' and activate the module.
|
||||
2. Make sure that the site you wish to monitor is running the prod_check module
|
||||
3. Navigate to the prod_check settings page and activate XMLRPC and add an API
|
||||
key to 'secure' the connection. The key is limited to 128 characters.
|
||||
4. Add the site to the Production monitor overview page on
|
||||
/admin/reports/prod-monitor
|
||||
5. Enter the url and the API key and hit 'Get settings'. All available checks
|
||||
are now retrieved from the remote site. You can uncheck those that you do not
|
||||
wish to monitor.
|
||||
6. If you wish to fetch the data immediately, check the appropriate box and save
|
||||
the settings. Good to go!
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
When upgrading Production monitor to a newer version, always run update.php to
|
||||
verify if there are database or other updates that need to be applied!
|
||||
When ignoring this step, you might get errors and/or strange behavior!
|
||||
|
||||
Nagios
|
||||
------
|
||||
1. Download and install the Nagios module from http://drupal.org/project/nagios
|
||||
as per its readme instructions
|
||||
2. Enable Nagios support in the prod_check module on /admin/settings/prod-check
|
||||
by ticking the appropriate box.
|
||||
3. Untick the checboxes for those items you do not whish to be monitored by
|
||||
Nagios.
|
||||
4. Save the settings and you're good to go!
|
||||
|
||||
Performance logging
|
||||
-------------------
|
||||
1. Download and install the Nagios module from http://drupal.org/project/performance
|
||||
as per its readme instructions
|
||||
2. Enable fetching of performance data on /admin/reports/prod-monitor when
|
||||
adding or editing a site.
|
||||
|
||||
Drush
|
||||
-----
|
||||
You can view the Production Check statuspage using Drush, simply by using this
|
||||
command:
|
||||
|
||||
$ drush prod-check
|
||||
|
||||
or its alias:
|
||||
|
||||
$ drush pchk
|
||||
|
||||
A colour coded table will be printed. The information is limited to the name of
|
||||
the check and the status. In the Drupal version of the status page, you have an
|
||||
extra line explaining more about the curent status of a specific check.
|
||||
|
||||
You can easily make your site 'production ready' by using the following command:
|
||||
|
||||
$ drush prod-check-prodmode
|
||||
|
||||
or its alias:
|
||||
|
||||
$ drush pchk-pmode
|
||||
|
||||
This will fix most of the problems reported in the status page. You can have
|
||||
some extra control on the process by adding the --config option:
|
||||
|
||||
$ drush pchk-pmode --config
|
||||
|
||||
This will ask for some input before setting up the site.
|
||||
|
||||
For Production monitor, these commands are available:
|
||||
|
||||
$ drush prod-monitor [id]
|
||||
$ drush prod-monitor-fetch [id]
|
||||
$ drush prod-monitor-flush [id]
|
||||
$ drush prod-monitor-delete [id]
|
||||
$ drush prod-monitor-updates [id] (--check)
|
||||
|
||||
or their aliases:
|
||||
|
||||
$ drush pmon [id]
|
||||
$ drush pmon-fe [id]
|
||||
$ drush pmon-fl [id]
|
||||
$ drush pmon-rm [id]
|
||||
$ drush pmon-up [id] (--check)
|
||||
|
||||
The id parameter is optional for the prod-monitor command. The best usage is to
|
||||
first get a list of sites:
|
||||
|
||||
$ drush pmon
|
||||
|
||||
Now look up the id of a site, then use the other commands to act on that
|
||||
specific site by passing it the id:
|
||||
|
||||
$ drush pmon 3
|
||||
$ drush pmon-fl 3
|
||||
|
||||
You can pass multiple ID's by separating them with spaces:
|
||||
|
||||
$ drush pmon 3 6 19
|
||||
$ drush pmon-fl 19 4 1
|
||||
|
||||
The prod-monitor-updates command acts on one id only!
|
||||
|
||||
APC
|
||||
---
|
||||
Production Check complains about APC not being installed or misconfigured. What
|
||||
is APC you wonder? Well, APC is an opcode caching mechanism that will pre-com-
|
||||
pile PHP files and keep them stored in memory. The full manual can be found
|
||||
here: http://php.net/manual/en/book.apc.php .
|
||||
For Drupal sites, it is important to tune APC in order to achieve maximum per-
|
||||
formance there. Drupal uses a massive amount of files and therefore you should
|
||||
assign a proper amount of RAM to APC. For a dedicated setup 64Mb should be
|
||||
sufficient, in shared setups, you should easily double that!
|
||||
To tune your setup, you can use the aforementioned hidden link provided by
|
||||
Production check. You can see the memory usage there, verify your settings and
|
||||
much more.
|
||||
To help you out even further, an APC config file can be found in
|
||||
docs/apc.ini.txt. You must obviousely rename this file and omit the .txt
|
||||
extension (drupal.org CVS did not seem to accept files with .ini extension?).
|
||||
|
||||
Note: This 'hidden link' makes use of the APC supplied PHP code and is subject
|
||||
to the PHP license: http://www.php.net/license/3_01.txt .
|
||||
|
||||
|
||||
Updates
|
||||
=======
|
||||
When new checks are added to the prod_check module, the prod_monitor module will
|
||||
automatically fetch them from the remote server when you edit the settings. Upon
|
||||
displaying the edit form, XMLRPC is ALWAYS used to build op the checkboxes array
|
||||
so that you always have the latest options available.
|
||||
Cron is NOT used to do this, since we want to keep the transfer to a minimum.
|
||||
|
||||
|
||||
Hidden link
|
||||
===========
|
||||
Production check adds a 'hidden link' to the site where you can check the APC
|
||||
status of your site. This page can be found on /admin/reports/status/apc.
|
||||
This is in analogy with the system module that adds these 'hidden pages':
|
||||
/admin/reports/status/php
|
||||
/admin/reports/status/sql
|
||||
|
||||
Truely unmissable when setting up your site on a production server to check if
|
||||
all is well!
|
||||
|
||||
|
||||
The detailed report page
|
||||
========================
|
||||
|
||||
The page is divided into 4 sections:
|
||||
|
||||
- Settings: checks various Drupal settings
|
||||
- Server: checks that are 'outside of Drupal' such as APC and wether or not you
|
||||
have removed the release note files from the root.
|
||||
- Performance: checks relevant to the performance settings in Drupal such as
|
||||
page / block caching.
|
||||
- Modules: checks if certain modules are on / off
|
||||
- SEO: performs very basic SEO checks such as 'is Google Analytics activated
|
||||
and did you provide a GA account number.
|
||||
|
||||
The sections might shift over time (maybe some stuff should go under a
|
||||
'Security' section etc.).
|
||||
|
||||
The checks itself should be self explanatory to Drupal developers, so they won't
|
||||
be described in detail here.
|
||||
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
For support requests, bug reports, and feature requests, please us the issue cue
|
||||
of Menu Clone on http://drupal.org/project/issues/prod_check.
|
||||
|
||||
|
||||
Thanks
|
||||
======
|
||||
|
||||
kbahey (http://drupal.org/user/4063) for making the performance logging
|
||||
integration possible!
|
||||
bocaj (http://drupal.org/user/582042) for all the great contributions!
|
@@ -0,0 +1,8 @@
|
||||
|
||||
/* Prod monitor settings page styling */
|
||||
|
||||
.prod-check-settings{float:left;padding:0 5px;}
|
||||
.prod-check-settings.odd{background-color:#f8f8f8;}
|
||||
.prod-check-settings.even{background-color:#f1f1f1;}
|
||||
.form-item.form-item-prod-check-module-list-day, .form-item.form-item-prod-check-module-list-time{float:left;margin-right:10px;}
|
||||
.clear{clear:both;}
|
41
sites/all/modules/contrib/dev/prod_check/docs/apc.ini.txt
Normal file
41
sites/all/modules/contrib/dev/prod_check/docs/apc.ini.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
extension=apc.so
|
||||
|
||||
# commented entries are set by default
|
||||
|
||||
#apc.cache_by_default=1
|
||||
#apc.coredump_unmap=0
|
||||
|
||||
apc.enable_cli=1
|
||||
|
||||
#apc.enabled=1
|
||||
#apc.file_update_protection=2
|
||||
#apc.filters
|
||||
#apc.gc_ttl=3600
|
||||
#apc.include_once_override=0
|
||||
#apc.max_file_size=1M
|
||||
|
||||
apc.mmap_file_mask=/tmp/apc.XXXXXX
|
||||
apc.num_files_hint=4096
|
||||
|
||||
#apc.report_autofilter=0
|
||||
#apc.rfc1867=0
|
||||
#apc.rfc1867_freq=0
|
||||
#apc.rfc1867_name=APC_UPLOAD_PROGRESS
|
||||
#apc.rfc1867_prefix=upload_
|
||||
#apc.shm_segments=1
|
||||
|
||||
# 64Mb should be sufficient for dedicated single site hosting.
|
||||
# For shared hosting setups, you should double this.
|
||||
apc.shm_size=64
|
||||
#apc.shm_size=128
|
||||
|
||||
#apc.slam_defense=0
|
||||
#apc.stat=1
|
||||
#apc.stat_ctime=0
|
||||
|
||||
apc.ttl=7200
|
||||
apc.user_entries_hint=4096
|
||||
apc.user_ttl=7200
|
||||
|
||||
#apc.write_lock=1
|
@@ -0,0 +1,779 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Build status page.
|
||||
*/
|
||||
function prod_check_status() {
|
||||
drupal_set_title(t('Production check status'));
|
||||
|
||||
$output = '';
|
||||
|
||||
// Execute all functions per set as defined in the functions array in
|
||||
// _prod_check_functions().
|
||||
$functions = _prod_check_functions();
|
||||
// Not needed here.
|
||||
unset($functions['prod_mon']);
|
||||
unset($functions['perf_data']);
|
||||
|
||||
foreach ($functions as $set => $data) {
|
||||
$result = array();
|
||||
foreach ($data['functions'] as $function => $title) {
|
||||
$check = call_user_func($function);
|
||||
if (is_array($check) && !empty($check)) {
|
||||
$result = array_merge($result, $check);
|
||||
}
|
||||
}
|
||||
$output .= '<h2>' . t($data['title']) . '</h2>' . "\n";
|
||||
$output .= '<div class="description"><p><em>' . t($data['description']) . '</em></p></div>' . "\n";
|
||||
$output .= theme('prod_check_status_report', array('requirements' => $result));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build settings form.
|
||||
*/
|
||||
function prod_check_settings_form($form, &$form_state) {
|
||||
drupal_set_title(t('Production check settings'));
|
||||
$form = array();
|
||||
|
||||
// Add stylesheets & CSS.
|
||||
$base = drupal_get_path('module', 'prod_check');
|
||||
$form['#attached'] = array(
|
||||
'css' => array(
|
||||
'type' => 'file',
|
||||
'data' => $base . '/css/prod-check.css',
|
||||
),
|
||||
'js' => array(
|
||||
array (
|
||||
'type' => 'file',
|
||||
'data' => $base . '/js/jquery.equalheights.js',
|
||||
),
|
||||
array (
|
||||
'type' => 'file',
|
||||
'data' => $base . '/js/jquery.maskedinput.min.js',
|
||||
),
|
||||
array (
|
||||
'type' => 'file',
|
||||
'data' => $base . '/js/prod-check.js',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// E-mail settings.
|
||||
$form['prod_check_general'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('General settings'),
|
||||
'#description' => t('Settings to allow certain checks to function properly.'),
|
||||
'#collapsible' => FALSE,
|
||||
);
|
||||
$form['prod_check_general']['prod_check_sitemail'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Mail check'),
|
||||
'#default_value' => variable_get('prod_check_sitemail', ''),
|
||||
'#size' => 60,
|
||||
'#description' => t('Enter (part of) the e-mail address you always <strong>use when developing</strong> a website. This is used in a regular expression in the "Site e-mail", Contact and Webform modules check.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
if (module_exists('dblog')) {
|
||||
$form['prod_check_general']['prod_check_dblog_php'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Minimal watchdog severity for PHP errors'),
|
||||
'#default_value' => variable_get('prod_check_dblog_php', WATCHDOG_WARNING),
|
||||
'#options' => watchdog_severity_levels(),
|
||||
'#description' => t('Select the severity level from which to start reporting PHP errors being logged to the watchdog table.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['prod_check_general']['prod_check_dblog_php_threshold'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Threshold for PHP errors'),
|
||||
'#size' => 2,
|
||||
'#default_value' => variable_get('prod_check_dblog_php_threshold', 1),
|
||||
'#description' => t('Enter the number of times a PHP error needs to occur before reporting a problem. E.g. entering 3 here will allow 2 occurences of the exact same PHP error without an error being reported.'),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
$form['prod_check_apc'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Advanced APC settings'),
|
||||
'#description' => t('These settings are used in the !link functionality.', prod_check_link_array('advanced APC', 'admin/reports/status/apc')),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
|
||||
// Cache full count threshold
|
||||
$form['prod_check_apc']['prod_check_apc_expunge'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('APC cache full count threshold'),
|
||||
'#default_value' => variable_get('prod_check_apc_expunge', 0),
|
||||
'#size' => 2,
|
||||
'#description' => t('Issue a critical error when the cache full count is greater than the number entered here.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
// APC user.
|
||||
$form['prod_check_apc']['prod_check_apcuser'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('APC advanced features username'),
|
||||
'#default_value' => variable_get('prod_check_apcuser', 'apc'),
|
||||
'#size' => 60,
|
||||
'#description' => t('The username for logging in to the APC settings page.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
// APC password.
|
||||
$form['prod_check_apc']['prod_check_apcpass'] = array(
|
||||
'#type' => 'password_confirm',
|
||||
'#title' => t('APC advanced features password'),
|
||||
'#size' => 60,
|
||||
'#description' => t('The password for logging in to the APC settings page.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
// Disabled module settings
|
||||
$form['prod_check_disabled'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Disabled modules'),
|
||||
'#description' => t('You can choose to disable checking for updates for disabled modules here.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form['prod_check_disabled']['prod_check_exclude_disabled_modules'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t("Don't check for updates for disabled modules"),
|
||||
'#default_value' => variable_get('prod_check_exclude_disabled_modules', 0),
|
||||
'#description' => t('When checked, only enabled modules will be reported. Note that this can cause some modules that are used but not enabled (f.e. APC, memcache, domain,...) to get skipped. See the documentation on how to add disabled modules on a whitelist.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
$modules = implode(', ', _prod_check_get_disabled_modules_whitelist());
|
||||
$form['prod_check_disabled']['prod_check_disabled_modules_whitelist'] = array(
|
||||
'#markup' => t('Currently these modules will be forcefully checked even when they are disabled: %modules', array('%modules' => $modules)),
|
||||
);
|
||||
|
||||
// XMLRPC settings.
|
||||
$form['prod_check_xmlrpc'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Production monitor integration.'),
|
||||
'#description' => t('You can set up integration with the Production monitor module here.'),
|
||||
'#collapsible' => FALSE,
|
||||
);
|
||||
$form['prod_check_xmlrpc']['prod_check_enable_xmlrpc'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable XMLRPC API'),
|
||||
'#default_value' => variable_get('prod_check_enable_xmlrpc', 0),
|
||||
'#description' => t('Tick this box if you would like to the module to open up the XMLRPC api so that it can be queried externally to supply information to a base site for monitoring purposes.'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'prod_check_enable_xmlrpc',
|
||||
'wrapper' => 'prod-check-xmlrpc',
|
||||
'effect' => 'fade',
|
||||
),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
// The #value here is necessary for the markup field to be rendered :-(
|
||||
$form['prod_check_xmlrpc']['xmlrpc'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div id="prod-check-xmlrpc">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
// Only show when the checkbox above is selected.
|
||||
if (!isset($form_state['values']['prod_check_enable_xmlrpc'])) {
|
||||
$form_state['values']['prod_check_enable_xmlrpc'] = variable_get('prod_check_enable_xmlrpc', 0);
|
||||
}
|
||||
if ($form_state['values']['prod_check_enable_xmlrpc']) {
|
||||
$form['prod_check_xmlrpc']['xmlrpc']['prod_check_xmlrpc_key'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('API key'),
|
||||
'#default_value' => variable_get('prod_check_xmlrpc_key', prod_check_generate_key()),
|
||||
'#maxlength' => 128,
|
||||
'#size' => 60,
|
||||
'#description' => t('Enter a key here to ensure secure transfer of data over the API. Use a mixture of alphanumeric and special characters for increased security.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
$form['prod_check_xmlrpc']['xmlrpc']['prod_check_module_list_day'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Report module list every'),
|
||||
'#options' => array(t('Sunday'), t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday')),
|
||||
'#default_value' => variable_get('prod_check_module_list_day', 0),
|
||||
'#description' => t('Defines which day the module list will be fetchable by Production monitor for an update status check.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
$form['prod_check_xmlrpc']['xmlrpc']['prod_check_module_list_time'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('at this time'),
|
||||
'#default_value' => variable_get('prod_check_module_list_time', '03:00'),
|
||||
'#maxlength' => 5,
|
||||
'#size' => 5,
|
||||
'#description' => t('Defines what time (HH:MM) the module list will be fetchable by Production monitor for an update status check.'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
// See http://drupal.org/node/1058896#comment-5594076 .
|
||||
if (variable_get('prod_check_module_list_lastrun', 0) != -1) {
|
||||
$form['prod_check_xmlrpc']['xmlrpc']['reset'] = array(
|
||||
'#prefix' => '<div class="clear">',
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Force immediate module list reporting'),
|
||||
'#postfix' => '</div>',
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['prod_check_xmlrpc']['xmlrpc']['reset'] = array(
|
||||
'#markup' => '<div class="description clear">' . t('Production monitor will fetch the module list on next cron run or when manually invoked for this site!') . '</div>',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Nagios settings.
|
||||
if (module_exists('nagios')) {
|
||||
$form['prod_check_nagios'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Nagios integration.'),
|
||||
'#description' => t('You can set up integration with the !link module here.', prod_check_link_array('Nagios', 'http://drupal.org/project/nagios')),
|
||||
'#collapsible' => FALSE,
|
||||
);
|
||||
$form['prod_check_nagios']['prod_check_enable_nagios'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable Nagios integration'),
|
||||
'#description' => t('Tick this box if you want to enable integration with Nagios. The !link module is required for this to function.', array('!link' => l(t('Nagios'), 'http://drupal.org/project/nagios', array('attributes' => array('title' => t('Nagios')))))),
|
||||
'#default_value' => variable_get('prod_check_enable_nagios', 0),
|
||||
'#ajax' => array(
|
||||
'callback' => 'prod_check_enable_nagios',
|
||||
'wrapper' => 'prod-check-nagios',
|
||||
'effect' => 'fade',
|
||||
),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
// The #value here is necessary for the markup field to be rendered :-(
|
||||
$form['prod_check_nagios']['nagios'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div id="prod-check-nagios">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
// Only show when the checkbox above is selected.
|
||||
if (!isset($form_state['values']['prod_check_enable_nagios'])) {
|
||||
$form_state['values']['prod_check_enable_nagios'] = variable_get('prod_check_enable_nagios', 0);
|
||||
}
|
||||
// TODO: find a way to detect when this is rendered so we can adjust the
|
||||
// prod-check.js and apply equalheights/width
|
||||
if ($form_state['values']['prod_check_enable_nagios']) {
|
||||
$form['prod_check_nagios']['nagios']['settings'] = _prod_check_functions_as_form();
|
||||
$options = variable_get('prod_check_nagios_checks', array());
|
||||
if (!empty($options)) {
|
||||
// Just to increase readability of the source here.
|
||||
$monitor_settings = &$form['prod_check_nagios']['nagios']['settings']['prod_check_settings']['monitor_settings'];
|
||||
// Set default values to last saved state
|
||||
foreach (element_children($monitor_settings) as $set) {
|
||||
if (isset($options[$set])) {
|
||||
$monitor_settings[$set]['#default_value'] = $options[$set];
|
||||
}
|
||||
else {
|
||||
// No settings available, so uncheck all.
|
||||
$monitor_settings[$set]['#default_value'] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form['prod_check_nagios']['nagios']['settings']['prod_check_nagios_unique'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('When Nagios unique ID not recieved'),
|
||||
'#description' => t('Select what should happen when the Nagios unique ID is not recieved by the nagios page.'),
|
||||
'#options' => array(
|
||||
'default' => t('default Nagios module behavior'),
|
||||
'404' => t('throw a 404 error'),
|
||||
'home' => t('redirect to homepege'),
|
||||
),
|
||||
'#default_value' => variable_get('prod_check_nagios_unique', 'default'),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
$form['prod_check_nagios']['nagios']['settings']['prod_check_nagios_takeover'] = array(
|
||||
'#markup' => '<p>' . t(
|
||||
'If you want prod_check to take over the Nagios status page, you can edit the !settings and enter %callback. Only then will the setting above have any effect!',
|
||||
array(
|
||||
'!settings' => l(t('Nagios page callback'), 'admin/config/system/nagios'),
|
||||
'%callback' => 'prod_check_nagios_status_page',
|
||||
)
|
||||
) .'</p>',
|
||||
);
|
||||
|
||||
$form['prod_check_nagios']['nagios']['settings']['prod_check_nagios_verbose'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show verbose status info'),
|
||||
'#description' => t('Tick this box if you want to see detailed information about every check. Useful for debugging or first setup, but <strong>not recommended for production use!</strong>'),
|
||||
'#default_value' => variable_get('prod_check_nagios_verbose', 0),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Submit buttons.
|
||||
// Markup field for proper styling.
|
||||
$form['buttons'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div class="form-actions">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['buttons']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save configuration'),
|
||||
);
|
||||
$form['buttons']['reset'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Reset to defaults'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the functions array and return a form fieldset with different sets of
|
||||
* checkboxes so it can be inserted 'as is' in another form array for rendering.
|
||||
* This is primlarily still here for integration with Nagios. Though not in use
|
||||
* now since Nagios would create far too much needless variables in my personal
|
||||
* opinion. It's left in for easy future integration with the
|
||||
* hook_nagios_settings().
|
||||
*/
|
||||
function _prod_check_functions_as_form($compatibility = 'all') {
|
||||
$form = array();
|
||||
|
||||
$form['prod_check_settings'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Configure what data you wish to monitor with <strong>Nagios</strong> for this site.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
);
|
||||
|
||||
$form['prod_check_settings']['monitor_settings'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div id="prod-check-settings">',
|
||||
'#suffix' => '</div>',
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$i = 1;
|
||||
$functions = _prod_check_functions();
|
||||
if ($compatibility == 'all') {
|
||||
unset($functions['prod_mon']);
|
||||
unset($functions['perf_data']);
|
||||
}
|
||||
foreach ($functions as $set => $data) {
|
||||
$rest = $i % 2;
|
||||
$form['prod_check_settings']['monitor_settings'][$set] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t($data['title']),
|
||||
'#description' => t($data['description']),
|
||||
'#options' => $data['functions'],
|
||||
'#default_value' => array_keys($data['functions']),
|
||||
'#prefix' => '<div class="prod-check-settings ' . (($rest) ? 'odd' : 'even') . '">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to add xmlrpc settings.
|
||||
*/
|
||||
function prod_check_enable_xmlrpc($form, &$form_state) {
|
||||
return $form['prod_check_xmlrpc']['xmlrpc'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to add nagios settings.
|
||||
*/
|
||||
function prod_check_enable_nagios($form, &$form_state) {
|
||||
return $form['prod_check_nagios']['nagios'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation for settings form.
|
||||
*/
|
||||
function prod_check_settings_form_validate($form, &$form_state) {
|
||||
// Had to add CSS again here since it was lost on form errors. Weird, doesn't
|
||||
// seem logical...
|
||||
$base = drupal_get_path('module', 'prod_check');
|
||||
drupal_add_css($base . '/css/prod-check.css');
|
||||
drupal_add_js($base . '/js/jquery.equalheights.js', 'module', 'header');
|
||||
drupal_add_js($base . '/js/jquery.maskedinput.min.js', 'module', 'header');
|
||||
drupal_add_js($base . '/js/prod-check.js', 'module', 'header');
|
||||
|
||||
if (module_exists('dblog')) {
|
||||
if (!is_numeric($form_state['values']['prod_check_dblog_php_threshold'])) {
|
||||
form_set_error('prod_check_dblog_php_threshold', t('The PHP error threshold should be numeric!'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($form_state['values']['prod_check_enable_xmlrpc']) {
|
||||
if (empty($form_state['values']['prod_check_xmlrpc_key'])) {
|
||||
form_set_error('prod_check_xmlrpc_key', t('When enabling the XPLRPC API, you <strong>must</strong> enter an API key!'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($form_state['values']['prod_check_module_list_time'])) {
|
||||
// This check in case JavaScript is not enabled / malfunctioning.
|
||||
if (strpos($form_state['values']['prod_check_module_list_time'], ':') != 2) {
|
||||
form_set_error('prod_check_module_list_time', t('Time must be input in 24 hour format: HH:MM!'));
|
||||
}
|
||||
else {
|
||||
$time = explode(':', $form_state['values']['prod_check_module_list_time']);
|
||||
if (intval($time[0]) > 23) {
|
||||
form_set_error('prod_check_module_list_time', t('Hours must range from 00 (midnight) to 23!'));
|
||||
}
|
||||
if (intval($time[1]) > 59) {
|
||||
form_set_error('prod_check_module_list_time', t('Minutes must range from 00 to 59!'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_numeric($form_state['values']['prod_check_apc_expunge'])) {
|
||||
form_set_error('prod_check_apc_expunge', t('APC Cache full count threshold should be numeric!'));
|
||||
}
|
||||
|
||||
if (isset($form_state['values']['prod_check_enable_nagios']) && $form_state['values']['prod_check_enable_nagios']) {
|
||||
$checks = array();
|
||||
foreach ($form_state['values']['monitor_settings'] as $set => $data) {
|
||||
foreach ($data as $check => $value) {
|
||||
if ($value) {
|
||||
$checks[$set][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($checks)) {
|
||||
form_set_error('monitor_settings', t('When enabling Nagios support, you <strong>must</strong> tick at least one of the checkboxes!'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit for settings form.
|
||||
*
|
||||
* TODO: is it better to split all of these in separate functions and use
|
||||
* buttons with the #submit property? The latter is better readability wise I
|
||||
* guess.
|
||||
*/
|
||||
function prod_check_settings_form_submit($form, &$form_state) {
|
||||
switch ($form_state['values']['op']) {
|
||||
case t('Force immediate module list reporting'):
|
||||
variable_set('prod_check_module_list_lastrun', -1);
|
||||
break;
|
||||
case t('Save configuration'):
|
||||
variable_set('prod_check_sitemail', $form_state['values']['prod_check_sitemail']);
|
||||
// PHP errors.
|
||||
variable_set('prod_check_dblog_php', $form_state['values']['prod_check_dblog_php']);
|
||||
variable_set('prod_check_dblog_php_threshold', $form_state['values']['prod_check_dblog_php_threshold']);
|
||||
// APC.
|
||||
variable_set('prod_check_apc_expunge', $form_state['values']['prod_check_apc_expunge']);
|
||||
variable_set('prod_check_apcuser', $form_state['values']['prod_check_apcuser']);
|
||||
if (!empty($form_state['values']['prod_check_apcpass'])) {
|
||||
variable_set('prod_check_apcpass', $form_state['values']['prod_check_apcpass']);
|
||||
}
|
||||
else {
|
||||
variable_set('prod_check_apcpass', 'password');
|
||||
}
|
||||
variable_set('prod_check_exclude_disabled_modules', $form_state['values']['prod_check_exclude_disabled_modules']);
|
||||
if ($form_state['values']['prod_check_enable_xmlrpc']) {
|
||||
// Enable.
|
||||
variable_set('prod_check_enable_xmlrpc', $form_state['values']['prod_check_enable_xmlrpc']);
|
||||
variable_set('prod_check_xmlrpc_key', $form_state['values']['prod_check_xmlrpc_key']);
|
||||
variable_set('prod_check_module_list_day', $form_state['values']['prod_check_module_list_day']);
|
||||
variable_set('prod_check_module_list_time', $form_state['values']['prod_check_module_list_time']);
|
||||
}
|
||||
else {
|
||||
// Disable.
|
||||
variable_set('prod_check_enable_xmlrpc', 0);
|
||||
}
|
||||
// This is why we didn't use a system_settings_form().
|
||||
if (isset($form_state['values']['prod_check_enable_nagios']) && $form_state['values']['prod_check_enable_nagios']) {
|
||||
$checks = array();
|
||||
foreach ($form_state['values']['monitor_settings'] as $set => $data) {
|
||||
foreach ($data as $check => $value) {
|
||||
if ($value) {
|
||||
$checks[$set][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Enable.
|
||||
variable_set('prod_check_enable_nagios', $form_state['values']['prod_check_enable_nagios']);
|
||||
variable_set('prod_check_nagios_checks', $checks);
|
||||
variable_set('prod_check_nagios_unique', $form_state['values']['prod_check_nagios_unique']);
|
||||
variable_set('prod_check_nagios_verbose', $form_state['values']['prod_check_nagios_verbose']);
|
||||
}
|
||||
else {
|
||||
// Disable.
|
||||
variable_set('prod_check_enable_nagios', 0);
|
||||
}
|
||||
drupal_set_message(t('The configuration options have been saved.'));
|
||||
break;
|
||||
case t('Reset to defaults'):
|
||||
// This beats multiple variable_del() calls.
|
||||
// Don't delete prod_check_module_list_lastrun!
|
||||
// DELETE FROM {variable} WHERE name LIKE "prod_check\_%" AND name <> "prod_check_module_list_lastrun"'
|
||||
db_delete('variable')
|
||||
->condition('name', 'prod_check\_%', 'LIKE')
|
||||
->condition('name', 'prod_check_module_list_lastrun', '<>')
|
||||
->execute();
|
||||
cache_clear_all('variables', 'cache_bootstrap');
|
||||
drupal_set_message(t('The configuration options have been reset to their default values.'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup site for production mode.
|
||||
*/
|
||||
function prod_check_prod_mode_form() {
|
||||
drupal_set_title(t('Switch to production mode'));
|
||||
|
||||
$form = array();
|
||||
|
||||
// Put this in hook_help() first but some themes hide the help text. It's too
|
||||
// important to be hidden, so moved it here.
|
||||
$form['help'] = array(
|
||||
'#markup' => '<p>' . t('Submitting this form will switch this website to <em>production mode</em>. This means that various settings will be adjusted in order to fix most of the problems reported on the status page. <strong>Some modules will be disabled as well!</strong>') . '</p>',
|
||||
);
|
||||
|
||||
$form['site_mail'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Site e-mail address'),
|
||||
'#description' => t('This field is optional and will be used to setup the default e-mail address of this site. <strong>Currently set to %mail.</strong>', array('%mail' => variable_get('site_mail', '[not set]'))),
|
||||
);
|
||||
|
||||
if (module_exists('webform')) {
|
||||
$form['webform_default_from_address'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Webform default from e-mail address'),
|
||||
'#description' => t('This field is optional and will be used to setup the default from e-mail address for <em>Webform</em>. <strong>Currently set to %mail.</strong>', array('%mail' => variable_get('webform_default_from_address', '[not set]'))),
|
||||
);
|
||||
}
|
||||
|
||||
if (module_exists('googleanalytics')) {
|
||||
$form['googleanalytics_account'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Google Analytics Web Property ID'),
|
||||
'#description' => t('This field is optional and will be used to setup the <em>Google Analytics</em> account. <strong>Currently set to %account.</strong>', array('%account' => variable_get('googleanalytics_account', '[not set]'))),
|
||||
);
|
||||
}
|
||||
|
||||
$form['block_cache'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Block cache</em>'),
|
||||
'#description' => t("If ticked, this will enable block caching. <strong>This can cause unwanted results if custom blocks don't handle caching well!</strong>"),
|
||||
);
|
||||
|
||||
if (module_exists('dblog')) {
|
||||
$form['dblog'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Disable <em>Database logging</em>'),
|
||||
'#description' => t('If ticked, this will disable the <em>dblog</em> module wich could generate too much overhead on high traffic sites.'),
|
||||
);
|
||||
}
|
||||
|
||||
$form['nagios'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Nagios</em>'),
|
||||
'#description' => t('If ticked, this will enable the <em>Nagios</em> monitoring module if it is present.'),
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Enable production mode'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup site for production mode: validation.
|
||||
*/
|
||||
function prod_check_prod_mode_form_validate($form, &$form_state) {
|
||||
$checks = array('site_mail', 'webform_default_from_address');
|
||||
|
||||
foreach ($checks as $field) {
|
||||
if (!empty($form_state['values'][$field])) {
|
||||
if (!valid_email_address($form_state['values'][$field])) {
|
||||
form_set_error($field, t('The e-mail address %mail is not valid.', array('%mail' => $form_state['values'][$field])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Google analytics.
|
||||
if (!empty($form_state['values']['googleanalytics_account'])) {
|
||||
if (!preg_match('/^UA-\d{4,}-\d+$/', $form_state['values']['googleanalytics_account'])) {
|
||||
form_set_error('googleanalytics_account', t('A valid Google Analytics Web Property ID is case sensitive and formatted like UA-xxxxxxx-yy.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add confirm form here? In the form of:
|
||||
// The following actions will be performed: [...] Are you sure you want to
|
||||
// continue?
|
||||
|
||||
/**
|
||||
* Setup site for production mode: submit.
|
||||
*/
|
||||
function prod_check_prod_mode_form_submit($form, &$form_state) {
|
||||
// Adjust settings.
|
||||
$variables = prod_check_prod_mode_settings($form_state['values']);
|
||||
drupal_set_message(t('The following settings have been changed: %variables.', array('%variables' => implode(', ', array_keys($variables)))));
|
||||
|
||||
// Enable / disable modules.
|
||||
$modules = prod_check_prod_mode_modules($form_state['values']);
|
||||
if (!empty($modules['disable'])) {
|
||||
drupal_set_message(t('The following modules have been <strong>disabled</strong>: %modules.', array('%modules' => implode(', ', $modules['disable']))));
|
||||
}
|
||||
if (!empty($modules['enable'])) {
|
||||
drupal_set_message(t('The following modules have been <strong>enabled</strong>: %modules.', array('%modules' => implode(', ', $modules['enable']))));
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'admin/reports/prod-check';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to adjust settings. Also used by Drush.
|
||||
*/
|
||||
function prod_check_prod_mode_settings($options) {
|
||||
$variables = array(
|
||||
// Error messages to display.
|
||||
'error_level' => ERROR_REPORTING_HIDE,
|
||||
// Cache pages for anonymous users.
|
||||
'cache' => 1,
|
||||
// Aggregate and compress CSS files.
|
||||
'preprocess_css' => 1,
|
||||
// Aggregate JavaScript files.
|
||||
'preprocess_js' => 1,
|
||||
);
|
||||
|
||||
// No page compression when running Varnish.
|
||||
if (!module_exists('varnish') && !module_exists('steroids')) {
|
||||
// Compress cached pages.
|
||||
$variables['page_compression'] = 1;
|
||||
}
|
||||
|
||||
// Site e-mail address.
|
||||
if (isset($options['site_mail']) && !empty($options['site_mail'])) {
|
||||
$variables['site_mail'] = $options['site_mail'];
|
||||
}
|
||||
|
||||
// Webform default from e-mail address.
|
||||
if (isset($options['webform_default_from_address']) && !empty($options['webform_default_from_address'])) {
|
||||
$variables['webform_default_from_address'] = $options['webform_default_from_address'];
|
||||
}
|
||||
|
||||
// Google analytics account.
|
||||
if (isset($options['googleanalytics_account']) && !empty($options['googleanalytics_account'])) {
|
||||
$variables['googleanalytics_account'] = $options['googleanalytics_account'];
|
||||
}
|
||||
|
||||
// Cache blocks.
|
||||
if (isset($options['block_cache']) && !empty($options['block_cache'])) {
|
||||
$variables['block_cache'] = 1;
|
||||
}
|
||||
|
||||
// Set variables. Wanted to do this in one query, but that was impossible due
|
||||
// to the fact that it is possible a variable does not exist in the database
|
||||
// yet and still have a value. Damn that default value in variable_get()!
|
||||
foreach ($variables as $variable => $value) {
|
||||
variable_set($variable, $value);
|
||||
}
|
||||
|
||||
// Clear caches like the system_performance_settings() form does.
|
||||
drupal_clear_css_cache();
|
||||
drupal_clear_js_cache();
|
||||
// Instead of calling system_clear_page_cache_submit()
|
||||
cache_clear_all('*', 'cache_page', TRUE);
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to enable / disable modules. Also used by Drush.
|
||||
*/
|
||||
function prod_check_prod_mode_modules($options) {
|
||||
$modules['disable'] = array(
|
||||
'devel',
|
||||
'devel_generate',
|
||||
'devel_node_access',
|
||||
'devel_themer',
|
||||
'update',
|
||||
);
|
||||
|
||||
if (isset($options['dblog']) && $options['dblog']) {
|
||||
$modules['disable'][] = 'dblog';
|
||||
}
|
||||
|
||||
// We do this primarily to prepare feedback to the user. module_disable() will
|
||||
// do a module_exists() check as well but only provides feedback using
|
||||
// watchdog().
|
||||
foreach ($modules['disable'] as $id => $module) {
|
||||
if (!module_exists($module)) {
|
||||
unset($modules['disable'][$id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($modules['disable'])) {
|
||||
module_disable($modules['disable']);
|
||||
}
|
||||
|
||||
$modules['enable'] = array();
|
||||
|
||||
if ($options['nagios']) {
|
||||
$modules['enable'][] = 'nagios';
|
||||
}
|
||||
|
||||
// We cannot check if a module is available on the file system. At least,
|
||||
// there is not a function in Drupal that I know of to do this and I could not
|
||||
// find one. Hence this approach, until there's a better way.
|
||||
if (!empty($modules['enable'])) {
|
||||
module_enable($modules['enable']);
|
||||
foreach($modules['enable'] as $id => $module) {
|
||||
if (!module_exists($module)) {
|
||||
unset($modules['enable'][$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration of the APC status page.
|
||||
*/
|
||||
function prod_check_apc() {
|
||||
define('ADMIN_USERNAME', variable_get('prod_check_apcuser', 'apc'));
|
||||
define('ADMIN_PASSWORD', variable_get('prod_check_apcpass', 'password'));
|
||||
include(drupal_get_path('module', 'prod_check') . '/includes/prod_check.apc.inc');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration of the Memcache status page.
|
||||
*/
|
||||
function prod_check_memcache() {
|
||||
global $conf;
|
||||
|
||||
if (isset($conf['memcache_servers'])) {
|
||||
global $MEMCACHE_SERVERS;
|
||||
$MEMCACHE_SERVERS = array_keys($conf['memcache_servers']);
|
||||
include(drupal_get_path('module', 'prod_check') . '/includes/prod_check.memcache.inc');
|
||||
}
|
||||
else {
|
||||
print 'No memcache servers found in settings.php.';
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate default key if none is present.
|
||||
*/
|
||||
function prod_check_generate_key($length = 25) {
|
||||
$chars = 'abcdefghijklmnopqrstuxyvwzABCDEFGHIJKLMNOPQRSTUXYVWZ+-*#&@!?';
|
||||
$size = strlen($chars);
|
||||
$key = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$key .= $chars[rand(0, $size - 1)];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
1349
sites/all/modules/contrib/dev/prod_check/includes/prod_check.apc.inc
Normal file
1349
sites/all/modules/contrib/dev/prod_check/includes/prod_check.apc.inc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,906 @@
|
||||
<?php
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2004 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.0 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_0.txt. |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Harun Yayli <harunyayli at gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
$VERSION='$Id: memcache.php 310129 2011-04-11 04:44:27Z hradtke $';
|
||||
|
||||
//define('ADMIN_USERNAME','memcache'); // Admin Username
|
||||
//define('ADMIN_PASSWORD','password'); // Admin Password
|
||||
define('DATE_FORMAT','Y/m/d H:i:s');
|
||||
define('GRAPH_SIZE',200);
|
||||
define('MAX_ITEM_DUMP',50);
|
||||
|
||||
//$MEMCACHE_SERVERS[] = 'mymemcache-server1:11211'; // add more as an array
|
||||
//$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
|
||||
|
||||
|
||||
////////// END OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////// Password protect ////////////////////////////////////////////////////////////////
|
||||
//Section below commented to make this work for the Drupal 'Prod check' module
|
||||
//See also http://drupal.org/node/1860504
|
||||
/*if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
|
||||
$_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {
|
||||
Header("WWW-Authenticate: Basic realm=\"Memcache Login\"");
|
||||
Header("HTTP/1.0 401 Unauthorized");
|
||||
|
||||
echo <<<EOB
|
||||
<html><body>
|
||||
<h1>Rejected!</h1>
|
||||
<big>Wrong Username or Password!</big>
|
||||
</body></html>
|
||||
EOB;
|
||||
exit;
|
||||
}*/
|
||||
|
||||
///////////MEMCACHE FUNCTIONS /////////////////////////////////////////////////////////////////////
|
||||
|
||||
function get_host_port_from_server($server){
|
||||
$values = explode(':', $server);
|
||||
if (($values[0] == 'unix') && (!is_numeric( $values[1]))) {
|
||||
return array($server, 0);
|
||||
}
|
||||
else {
|
||||
//return values;
|
||||
//Line above modified to make this work for the Drupal 'Prod check' module
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
function sendMemcacheCommands($command){
|
||||
global $MEMCACHE_SERVERS;
|
||||
$result = array();
|
||||
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
$strs = get_host_port_from_server($server);
|
||||
$host = $strs[0];
|
||||
$port = $strs[1];
|
||||
$result[$server] = sendMemcacheCommand($host,$port,$command);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
function sendMemcacheCommand($server,$port,$command){
|
||||
|
||||
$s = @fsockopen($server,$port);
|
||||
if (!$s){
|
||||
die("Cant connect to:".$server.':'.$port);
|
||||
}
|
||||
|
||||
fwrite($s, $command."\r\n");
|
||||
|
||||
$buf='';
|
||||
while ((!feof($s))) {
|
||||
$buf .= fgets($s, 256);
|
||||
if (strpos($buf,"END\r\n")!==false){ // stat says end
|
||||
break;
|
||||
}
|
||||
if (strpos($buf,"DELETED\r\n")!==false || strpos($buf,"NOT_FOUND\r\n")!==false){ // delete says these
|
||||
break;
|
||||
}
|
||||
if (strpos($buf,"OK\r\n")!==false){ // flush_all says ok
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose($s);
|
||||
return parseMemcacheResults($buf);
|
||||
}
|
||||
function parseMemcacheResults($str){
|
||||
|
||||
$res = array();
|
||||
$lines = explode("\r\n",$str);
|
||||
$cnt = count($lines);
|
||||
for($i=0; $i< $cnt; $i++){
|
||||
$line = $lines[$i];
|
||||
$l = explode(' ',$line,3);
|
||||
if (count($l)==3){
|
||||
$res[$l[0]][$l[1]]=$l[2];
|
||||
if ($l[0]=='VALUE'){ // next line is the value
|
||||
$res[$l[0]][$l[1]] = array();
|
||||
list ($flag,$size)=explode(' ',$l[2]);
|
||||
$res[$l[0]][$l[1]]['stat']=array('flag'=>$flag,'size'=>$size);
|
||||
$res[$l[0]][$l[1]]['value']=$lines[++$i];
|
||||
}
|
||||
}elseif($line=='DELETED' || $line=='NOT_FOUND' || $line=='OK'){
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
function dumpCacheSlab($server,$slabId,$limit){
|
||||
list($host,$port) = get_host_port_from_server($server);
|
||||
$resp = sendMemcacheCommand($host,$port,'stats cachedump '.$slabId.' '.$limit);
|
||||
|
||||
return $resp;
|
||||
|
||||
}
|
||||
|
||||
function flushServer($server){
|
||||
list($host,$port) = get_host_port_from_server($server);
|
||||
$resp = sendMemcacheCommand($host,$port,'flush_all');
|
||||
return $resp;
|
||||
}
|
||||
function getCacheItems(){
|
||||
$items = sendMemcacheCommands('stats items');
|
||||
$serverItems = array();
|
||||
$totalItems = array();
|
||||
foreach ($items as $server=>$itemlist){
|
||||
$serverItems[$server] = array();
|
||||
$totalItems[$server]=0;
|
||||
if (!isset($itemlist['STAT'])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$iteminfo = $itemlist['STAT'];
|
||||
|
||||
foreach($iteminfo as $keyinfo=>$value){
|
||||
if (preg_match('/items\:(\d+?)\:(.+?)$/',$keyinfo,$matches)){
|
||||
$serverItems[$server][$matches[1]][$matches[2]] = $value;
|
||||
if ($matches[2]=='number'){
|
||||
$totalItems[$server] +=$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array('items'=>$serverItems,'counts'=>$totalItems);
|
||||
}
|
||||
function getMemcacheStats($total=true){
|
||||
$resp = sendMemcacheCommands('stats');
|
||||
if ($total){
|
||||
$res = array();
|
||||
foreach($resp as $server=>$r){
|
||||
foreach($r['STAT'] as $key=>$row){
|
||||
if (!isset($res[$key])){
|
||||
$res[$key]=null;
|
||||
}
|
||||
switch ($key){
|
||||
case 'pid':
|
||||
$res['pid'][$server]=$row;
|
||||
break;
|
||||
case 'uptime':
|
||||
$res['uptime'][$server]=$row;
|
||||
break;
|
||||
case 'time':
|
||||
$res['time'][$server]=$row;
|
||||
break;
|
||||
case 'version':
|
||||
$res['version'][$server]=$row;
|
||||
break;
|
||||
case 'pointer_size':
|
||||
$res['pointer_size'][$server]=$row;
|
||||
break;
|
||||
case 'rusage_user':
|
||||
$res['rusage_user'][$server]=$row;
|
||||
break;
|
||||
case 'rusage_system':
|
||||
$res['rusage_system'][$server]=$row;
|
||||
break;
|
||||
case 'curr_items':
|
||||
$res['curr_items']+=$row;
|
||||
break;
|
||||
case 'total_items':
|
||||
$res['total_items']+=$row;
|
||||
break;
|
||||
case 'bytes':
|
||||
$res['bytes']+=$row;
|
||||
break;
|
||||
case 'curr_connections':
|
||||
$res['curr_connections']+=$row;
|
||||
break;
|
||||
case 'total_connections':
|
||||
$res['total_connections']+=$row;
|
||||
break;
|
||||
case 'connection_structures':
|
||||
$res['connection_structures']+=$row;
|
||||
break;
|
||||
case 'cmd_get':
|
||||
$res['cmd_get']+=$row;
|
||||
break;
|
||||
case 'cmd_set':
|
||||
$res['cmd_set']+=$row;
|
||||
break;
|
||||
case 'get_hits':
|
||||
$res['get_hits']+=$row;
|
||||
break;
|
||||
case 'get_misses':
|
||||
$res['get_misses']+=$row;
|
||||
break;
|
||||
case 'evictions':
|
||||
$res['evictions']+=$row;
|
||||
break;
|
||||
case 'bytes_read':
|
||||
$res['bytes_read']+=$row;
|
||||
break;
|
||||
case 'bytes_written':
|
||||
$res['bytes_written']+=$row;
|
||||
break;
|
||||
case 'limit_maxbytes':
|
||||
$res['limit_maxbytes']+=$row;
|
||||
break;
|
||||
case 'threads':
|
||||
$res['rusage_system'][$server]=$row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// don't cache this page
|
||||
//
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache"); // HTTP/1.0
|
||||
|
||||
function duration($ts) {
|
||||
global $time;
|
||||
$years = (int)((($time - $ts)/(7*86400))/52.177457);
|
||||
$rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400));
|
||||
$weeks = (int)(($rem)/(7*86400));
|
||||
$days = (int)(($rem)/86400) - $weeks*7;
|
||||
$hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24;
|
||||
$mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60;
|
||||
$str = '';
|
||||
if($years==1) $str .= "$years year, ";
|
||||
if($years>1) $str .= "$years years, ";
|
||||
if($weeks==1) $str .= "$weeks week, ";
|
||||
if($weeks>1) $str .= "$weeks weeks, ";
|
||||
if($days==1) $str .= "$days day,";
|
||||
if($days>1) $str .= "$days days,";
|
||||
if($hours == 1) $str .= " $hours hour and";
|
||||
if($hours>1) $str .= " $hours hours and";
|
||||
if($mins == 1) $str .= " 1 minute";
|
||||
else $str .= " $mins minutes";
|
||||
return $str;
|
||||
}
|
||||
|
||||
// create graphics
|
||||
//
|
||||
function graphics_avail() {
|
||||
return extension_loaded('gd');
|
||||
}
|
||||
|
||||
function bsize($s) {
|
||||
foreach (array('','K','M','G') as $i => $k) {
|
||||
if ($s < 1024) break;
|
||||
$s/=1024;
|
||||
}
|
||||
return sprintf("%5.1f %sBytes",$s,$k);
|
||||
}
|
||||
|
||||
// create menu entry
|
||||
function menu_entry($ob,$title) {
|
||||
global $PHP_SELF;
|
||||
if ($ob==$_GET['op']){
|
||||
return "<li><a class=\"child_active\" href=\"$PHP_SELF&op=$ob\">$title</a></li>";
|
||||
}
|
||||
return "<li><a class=\"active\" href=\"$PHP_SELF&op=$ob\">$title</a></li>";
|
||||
}
|
||||
|
||||
function getHeader(){
|
||||
$header = <<<EOB
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head><title>MEMCACHE INFO</title>
|
||||
<style type="text/css"><!--
|
||||
body { background:white; font-size:100.01%; margin:0; padding:0; }
|
||||
body,p,td,th,input,submit { font-size:0.8em;font-family:arial,helvetica,sans-serif; }
|
||||
* html body {font-size:0.8em}
|
||||
* html p {font-size:0.8em}
|
||||
* html td {font-size:0.8em}
|
||||
* html th {font-size:0.8em}
|
||||
* html input {font-size:0.8em}
|
||||
* html submit {font-size:0.8em}
|
||||
td { vertical-align:top }
|
||||
a { color:black; font-weight:none; text-decoration:none; }
|
||||
a:hover { text-decoration:underline; }
|
||||
div.content { padding:1em 1em 1em 1em; position:absolute; width:97%; z-index:100; }
|
||||
|
||||
h1.memcache { background:rgb(153,153,204); margin:0; padding:0.5em 1em 0.5em 1em; }
|
||||
* html h1.memcache { margin-bottom:-7px; }
|
||||
h1.memcache a:hover { text-decoration:none; color:rgb(90,90,90); }
|
||||
h1.memcache span.logo {
|
||||
background:rgb(119,123,180);
|
||||
color:black;
|
||||
border-right: solid black 1px;
|
||||
border-bottom: solid black 1px;
|
||||
font-style:italic;
|
||||
font-size:1em;
|
||||
padding-left:1.2em;
|
||||
padding-right:1.2em;
|
||||
text-align:right;
|
||||
display:block;
|
||||
width:130px;
|
||||
}
|
||||
h1.memcache span.logo span.name { color:white; font-size:0.7em; padding:0 0.8em 0 2em; }
|
||||
h1.memcache span.nameinfo { color:white; display:inline; font-size:0.4em; margin-left: 3em; }
|
||||
h1.memcache div.copy { color:black; font-size:0.4em; position:absolute; right:1em; }
|
||||
hr.memcache {
|
||||
background:white;
|
||||
border-bottom:solid rgb(102,102,153) 1px;
|
||||
border-style:none;
|
||||
border-top:solid rgb(102,102,153) 10px;
|
||||
height:12px;
|
||||
margin:0;
|
||||
margin-top:1px;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
ol,menu { margin:1em 0 0 0; padding:0.2em; margin-left:1em;}
|
||||
ol.menu li { display:inline; margin-right:0.7em; list-style:none; font-size:85%}
|
||||
ol.menu a {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
ol.menu a.child_active {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
border-left: solid black 5px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
ol.menu span.active {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:black;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
border-left: solid black 5px;
|
||||
}
|
||||
ol.menu span.inactive {
|
||||
background:rgb(193,193,244);
|
||||
border:solid rgb(182,182,233) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
ol.menu a:hover {
|
||||
background:rgb(193,193,244);
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
|
||||
div.info {
|
||||
background:rgb(204,204,204);
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
margin-bottom:1em;
|
||||
}
|
||||
div.info h2 {
|
||||
background:rgb(204,204,204);
|
||||
color:black;
|
||||
font-size:1em;
|
||||
margin:0;
|
||||
padding:0.1em 1em 0.1em 1em;
|
||||
}
|
||||
div.info table {
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
border-spacing:0;
|
||||
width:100%;
|
||||
}
|
||||
div.info table th {
|
||||
background:rgb(204,204,204);
|
||||
color:white;
|
||||
margin:0;
|
||||
padding:0.1em 1em 0.1em 1em;
|
||||
}
|
||||
div.info table th a.sortable { color:black; }
|
||||
div.info table tr.tr-0 { background:rgb(238,238,238); }
|
||||
div.info table tr.tr-1 { background:rgb(221,221,221); }
|
||||
div.info table td { padding:0.3em 1em 0.3em 1em; }
|
||||
div.info table td.td-0 { border-right:solid rgb(102,102,153) 1px; white-space:nowrap; }
|
||||
div.info table td.td-n { border-right:solid rgb(102,102,153) 1px; }
|
||||
div.info table td h3 {
|
||||
color:black;
|
||||
font-size:1.1em;
|
||||
margin-left:-0.3em;
|
||||
}
|
||||
.td-0 a , .td-n a, .tr-0 a , tr-1 a {
|
||||
text-decoration:underline;
|
||||
}
|
||||
div.graph { margin-bottom:1em }
|
||||
div.graph h2 { background:rgb(204,204,204);; color:black; font-size:1em; margin:0; padding:0.1em 1em 0.1em 1em; }
|
||||
div.graph table { border:solid rgb(204,204,204) 1px; color:black; font-weight:normal; width:100%; }
|
||||
div.graph table td.td-0 { background:rgb(238,238,238); }
|
||||
div.graph table td.td-1 { background:rgb(221,221,221); }
|
||||
div.graph table td { padding:0.2em 1em 0.4em 1em; }
|
||||
|
||||
div.div1,div.div2 { margin-bottom:1em; width:35em; }
|
||||
div.div3 { position:absolute; left:40em; top:1em; width:580px; }
|
||||
//div.div3 { position:absolute; left:37em; top:1em; right:1em; }
|
||||
|
||||
div.sorting { margin:1.5em 0em 1.5em 2em }
|
||||
.center { text-align:center }
|
||||
.aright { position:absolute;right:1em }
|
||||
.right { text-align:right }
|
||||
.ok { color:rgb(0,200,0); font-weight:bold}
|
||||
.failed { color:rgb(200,0,0); font-weight:bold}
|
||||
|
||||
span.box {
|
||||
border: black solid 1px;
|
||||
border-right:solid black 2px;
|
||||
border-bottom:solid black 2px;
|
||||
padding:0 0.5em 0 0.5em;
|
||||
margin-right:1em;
|
||||
}
|
||||
span.green { background:#60F060; padding:0 0.5em 0 0.5em}
|
||||
span.red { background:#D06030; padding:0 0.5em 0 0.5em }
|
||||
|
||||
div.authneeded {
|
||||
background:rgb(238,238,238);
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
color:rgb(200,0,0);
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
padding:2em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
input {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:1em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
}
|
||||
//-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="head">
|
||||
<h1 class="memcache">
|
||||
<span class="logo"><a href="http://pecl.php.net/package/memcache">memcache</a></span>
|
||||
<span class="nameinfo">memcache.php by <a href="http://livebookmark.net">Harun Yayli</a></span>
|
||||
</h1>
|
||||
<hr class="memcache">
|
||||
</div>
|
||||
<div class=content>
|
||||
EOB;
|
||||
|
||||
return $header;
|
||||
}
|
||||
function getFooter(){
|
||||
global $VERSION;
|
||||
$footer = '</div><!-- Based on apc.php '.$VERSION.'--></body>
|
||||
</html>
|
||||
';
|
||||
|
||||
return $footer;
|
||||
|
||||
}
|
||||
function getMenu(){
|
||||
global $PHP_SELF;
|
||||
echo "<ol class=menu>";
|
||||
if ($_GET['op']!=4){
|
||||
echo <<<EOB
|
||||
<li><a href="$PHP_SELF&op={$_GET['op']}">Refresh Data</a></li>
|
||||
EOB;
|
||||
}
|
||||
else {
|
||||
echo <<<EOB
|
||||
<li><a href="$PHP_SELF&op=2}">Back</a></li>
|
||||
EOB;
|
||||
}
|
||||
echo
|
||||
menu_entry(1,'View Host Stats'),
|
||||
menu_entry(2,'Variables');
|
||||
|
||||
echo <<<EOB
|
||||
</ol>
|
||||
<br/>
|
||||
EOB;
|
||||
}
|
||||
|
||||
// TODO, AUTH
|
||||
|
||||
$_GET['op'] = !isset($_GET['op'])? '1':$_GET['op'];
|
||||
//$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],'')) : '';
|
||||
//Line above modified to make this work for the Drupal 'Prod check' module
|
||||
global $PHP_SELF;
|
||||
$PHP_SELF= isset($_GET['q']) ? base_path() . htmlentities(strip_tags($_GET['q'],'')) : '';
|
||||
|
||||
$PHP_SELF=$PHP_SELF.'?';
|
||||
$time = time();
|
||||
// sanitize _GET
|
||||
|
||||
foreach($_GET as $key=>$g){
|
||||
$_GET[$key]=htmlentities($g);
|
||||
}
|
||||
|
||||
|
||||
// singleout
|
||||
// when singleout is set, it only gives details for that server.
|
||||
if (isset($_GET['singleout']) && $_GET['singleout']>=0 && $_GET['singleout'] <count($MEMCACHE_SERVERS)){
|
||||
$MEMCACHE_SERVERS = array($MEMCACHE_SERVERS[$_GET['singleout']]);
|
||||
}
|
||||
|
||||
// display images
|
||||
if (isset($_GET['IMG'])){
|
||||
$memcacheStats = getMemcacheStats();
|
||||
$memcacheStatsSingle = getMemcacheStats(false);
|
||||
|
||||
if (!graphics_avail()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') {
|
||||
global $col_black;
|
||||
$x1=$x+$w-1;
|
||||
$y1=$y+$h-1;
|
||||
|
||||
imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black);
|
||||
if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2);
|
||||
else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2);
|
||||
imagerectangle($im, $x, $y1, $x1, $y, $color1);
|
||||
if ($text) {
|
||||
if ($placeindex>0) {
|
||||
|
||||
if ($placeindex<16)
|
||||
{
|
||||
$px=5;
|
||||
$py=$placeindex*12+6;
|
||||
imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2);
|
||||
imageline($im,$x,$y+$h/2,$px+90,$py,$color2);
|
||||
imagestring($im,2,$px,$py-6,$text,$color1);
|
||||
|
||||
} else {
|
||||
if ($placeindex<31) {
|
||||
$px=$x+40*2;
|
||||
$py=($placeindex-15)*12+6;
|
||||
} else {
|
||||
$px=$x+40*2+100*intval(($placeindex-15)/15);
|
||||
$py=($placeindex%15)*12+6;
|
||||
}
|
||||
imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2);
|
||||
imageline($im,$x+$w,$y+$h/2,$px,$py,$color2);
|
||||
imagestring($im,2,$px+2,$py-6,$text,$color1);
|
||||
}
|
||||
} else {
|
||||
imagestring($im,4,$x+5,$y1-16,$text,$color1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) {
|
||||
$r=$diameter/2;
|
||||
$w=deg2rad((360+$start+($end-$start)/2)%360);
|
||||
|
||||
|
||||
if (function_exists("imagefilledarc")) {
|
||||
// exists only if GD 2.0.1 is avaliable
|
||||
imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE);
|
||||
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE);
|
||||
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED);
|
||||
} else {
|
||||
imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
|
||||
imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2);
|
||||
}
|
||||
if ($text) {
|
||||
if ($placeindex>0) {
|
||||
imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
|
||||
imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
|
||||
|
||||
} else {
|
||||
imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$size = GRAPH_SIZE; // image size
|
||||
$image = imagecreate($size+50, $size+10);
|
||||
|
||||
$col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
|
||||
$col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
|
||||
$col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
|
||||
$col_black = imagecolorallocate($image, 0, 0, 0);
|
||||
|
||||
imagecolortransparent($image,$col_white);
|
||||
|
||||
switch ($_GET['IMG']){
|
||||
case 1: // pie chart
|
||||
$tsize=$memcacheStats['limit_maxbytes'];
|
||||
$avail=$tsize-$memcacheStats['bytes'];
|
||||
$x=$y=$size/2;
|
||||
$angle_from = 0;
|
||||
$fuzz = 0.000001;
|
||||
|
||||
foreach($memcacheStatsSingle as $serv=>$mcs) {
|
||||
$free = $mcs['STAT']['limit_maxbytes']-$mcs['STAT']['bytes'];
|
||||
$used = $mcs['STAT']['bytes'];
|
||||
|
||||
|
||||
if ($free>0){
|
||||
// draw free
|
||||
$angle_to = ($free*360)/$tsize;
|
||||
$perc =sprintf("%.2f%%", ($free *100) / $tsize) ;
|
||||
|
||||
fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_green,$perc);
|
||||
$angle_from = $angle_from + $angle_to ;
|
||||
}
|
||||
if ($used>0){
|
||||
// draw used
|
||||
$angle_to = ($used*360)/$tsize;
|
||||
$perc =sprintf("%.2f%%", ($used *100) / $tsize) ;
|
||||
fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_red, '('.$perc.')' );
|
||||
$angle_from = $angle_from+ $angle_to ;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2: // hit miss
|
||||
|
||||
$hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits'];
|
||||
$misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses'];
|
||||
$total = $hits + $misses ;
|
||||
|
||||
fill_box($image, 30,$size,50,-$hits*($size-21)/$total,$col_black,$col_green,sprintf("%.1f%%",$hits*100/$total));
|
||||
fill_box($image,130,$size,50,-max(4,($total-$hits)*($size-21)/$total),$col_black,$col_red,sprintf("%.1f%%",$misses*100/$total));
|
||||
break;
|
||||
|
||||
}
|
||||
header("Content-type: image/png");
|
||||
imagepng($image);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo getHeader();
|
||||
echo getMenu();
|
||||
|
||||
switch ($_GET['op']) {
|
||||
|
||||
case 1: // host stats
|
||||
$phpversion = phpversion();
|
||||
$memcacheStats = getMemcacheStats();
|
||||
$memcacheStatsSingle = getMemcacheStats(false);
|
||||
|
||||
$mem_size = $memcacheStats['limit_maxbytes'];
|
||||
$mem_used = $memcacheStats['bytes'];
|
||||
$mem_avail= $mem_size-$mem_used;
|
||||
$startTime = time()-array_sum($memcacheStats['uptime']);
|
||||
|
||||
$curr_items = $memcacheStats['curr_items'];
|
||||
$total_items = $memcacheStats['total_items'];
|
||||
$hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits'];
|
||||
$misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses'];
|
||||
$sets = $memcacheStats['cmd_set'];
|
||||
|
||||
$req_rate = sprintf("%.2f",($hits+$misses)/($time-$startTime));
|
||||
$hit_rate = sprintf("%.2f",($hits)/($time-$startTime));
|
||||
$miss_rate = sprintf("%.2f",($misses)/($time-$startTime));
|
||||
$set_rate = sprintf("%.2f",($sets)/($time-$startTime));
|
||||
|
||||
echo <<< EOB
|
||||
<div class="info div1"><h2>General Cache Information</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
<tr class=tr-1><td class=td-0>PHP Version</td><td>$phpversion</td></tr>
|
||||
EOB;
|
||||
echo "<tr class=tr-0><td class=td-0>Memcached Host". ((count($MEMCACHE_SERVERS)>1) ? 's':'')."</td><td>";
|
||||
$i=0;
|
||||
if (!isset($_GET['singleout']) && count($MEMCACHE_SERVERS)>1){
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
echo ($i+1).'. <a href="'.$PHP_SELF.'&singleout='.$i++.'">'.$server.'</a><br/>';
|
||||
}
|
||||
}
|
||||
else{
|
||||
echo '1.'.$MEMCACHE_SERVERS[0];
|
||||
}
|
||||
if (isset($_GET['singleout'])){
|
||||
echo '<a href="'.$PHP_SELF.'">(all servers)</a><br/>';
|
||||
}
|
||||
echo "</td></tr>\n";
|
||||
echo "<tr class=tr-1><td class=td-0>Total Memcache Cache</td><td>".bsize($memcacheStats['limit_maxbytes'])."</td></tr>\n";
|
||||
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div>
|
||||
|
||||
<div class="info div1"><h2>Memcache Server Information</h2>
|
||||
EOB;
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
echo '<table cellspacing=0><tbody>';
|
||||
echo '<tr class=tr-1><td class=td-1>'.$server.'</td><td><a href="'.$PHP_SELF.'&server='.array_search($server,$MEMCACHE_SERVERS).'&op=6">[<b>Flush this server</b>]</a></td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Start Time</td><td>',date(DATE_FORMAT,$memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'</td></tr>';
|
||||
echo '<tr class=tr-1><td class=td-0>Uptime</td><td>',duration($memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'</td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Memcached Server Version</td><td>'.$memcacheStatsSingle[$server]['STAT']['version'].'</td></tr>';
|
||||
echo '<tr class=tr-1><td class=td-0>Used Cache Size</td><td>',bsize($memcacheStatsSingle[$server]['STAT']['bytes']),'</td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Total Cache Size</td><td>',bsize($memcacheStatsSingle[$server]['STAT']['limit_maxbytes']),'</td></tr>';
|
||||
echo '</tbody></table>';
|
||||
}
|
||||
echo <<<EOB
|
||||
|
||||
</div>
|
||||
<div class="graph div3"><h2>Host Status Diagrams</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
EOB;
|
||||
|
||||
$size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10);
|
||||
echo <<<EOB
|
||||
<tr>
|
||||
<td class=td-0>Cache Usage</td>
|
||||
<td class=td-1>Hits & Misses</td>
|
||||
</tr>
|
||||
EOB;
|
||||
|
||||
echo
|
||||
graphics_avail() ?
|
||||
'<tr>'.
|
||||
"<td class=td-0><img alt=\"\" $size src=\"$PHP_SELF&IMG=1&".(isset($_GET['singleout'])? 'singleout='.$_GET['singleout'].'&':'')."$time\"></td>".
|
||||
"<td class=td-1><img alt=\"\" $size src=\"$PHP_SELF&IMG=2&".(isset($_GET['singleout'])? 'singleout='.$_GET['singleout'].'&':'')."$time\"></td></tr>\n"
|
||||
: "",
|
||||
'<tr>',
|
||||
'<td class=td-0><span class="green box"> </span>Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size),"</td>\n",
|
||||
'<td class=td-1><span class="green box"> </span>Hits: ',$hits.sprintf(" (%.1f%%)",$hits*100/($hits+$misses)),"</td>\n",
|
||||
'</tr>',
|
||||
'<tr>',
|
||||
'<td class=td-0><span class="red box"> </span>Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size),"</td>\n",
|
||||
'<td class=td-1><span class="red box"> </span>Misses: ',$misses.sprintf(" (%.1f%%)",$misses*100/($hits+$misses)),"</td>\n";
|
||||
echo <<< EOB
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<br/>
|
||||
<div class="info"><h2>Cache Information</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
<tr class=tr-0><td class=td-0>Current Items(total)</td><td>$curr_items ($total_items)</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Hits</td><td>{$hits}</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Misses</td><td>{$misses}</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate cache requests/second</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate cache requests/second</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate cache requests/second</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Set Rate</td><td>$set_rate cache requests/second</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
|
||||
EOB;
|
||||
|
||||
break;
|
||||
|
||||
case 2: // variables
|
||||
|
||||
$m=0;
|
||||
$cacheItems= getCacheItems();
|
||||
$items = $cacheItems['items'];
|
||||
$totals = $cacheItems['counts'];
|
||||
$maxDump = MAX_ITEM_DUMP;
|
||||
foreach($items as $server => $entries) {
|
||||
|
||||
echo <<< EOB
|
||||
|
||||
<div class="info"><table cellspacing=0><tbody>
|
||||
<tr><th colspan="2">$server</th></tr>
|
||||
<tr><th>Slab Id</th><th>Info</th></tr>
|
||||
EOB;
|
||||
|
||||
foreach($entries as $slabId => $slab) {
|
||||
$dumpUrl = $PHP_SELF.'&op=2&server='.(array_search($server,$MEMCACHE_SERVERS)).'&dumpslab='.$slabId;
|
||||
echo
|
||||
"<tr class=tr-$m>",
|
||||
"<td class=td-0><center>",'<a href="',$dumpUrl,'">',$slabId,'</a>',"</center></td>",
|
||||
"<td class=td-last><b>Item count:</b> ",$slab['number'],'<br/><b>Age:</b>',duration($time-$slab['age']),'<br/> <b>Evicted:</b>',((isset($slab['evicted']) && $slab['evicted']==1)? 'Yes':'No');
|
||||
if ((isset($_GET['dumpslab']) && $_GET['dumpslab']==$slabId) && (isset($_GET['server']) && $_GET['server']==array_search($server,$MEMCACHE_SERVERS))){
|
||||
echo "<br/><b>Items: item</b><br/>";
|
||||
$items = dumpCacheSlab($server,$slabId,$slab['number']);
|
||||
// maybe someone likes to do a pagination here :)
|
||||
$i=1;
|
||||
foreach($items['ITEM'] as $itemKey=>$itemInfo){
|
||||
$itemInfo = trim($itemInfo,'[ ]');
|
||||
|
||||
|
||||
echo '<a href="',$PHP_SELF,'&op=4&server=',(array_search($server,$MEMCACHE_SERVERS)),'&key=',base64_encode($itemKey).'">',$itemKey,'</a>';
|
||||
if ($i++ % 10 == 0) {
|
||||
echo '<br/>';
|
||||
}
|
||||
elseif ($i!=$slab['number']+1){
|
||||
echo ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "</td></tr>";
|
||||
$m=1-$m;
|
||||
}
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div><hr/>
|
||||
EOB;
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case 4: //item dump
|
||||
if (!isset($_GET['key']) || !isset($_GET['server'])){
|
||||
echo "No key set!";
|
||||
break;
|
||||
}
|
||||
// I'm not doing anything to check the validity of the key string.
|
||||
// probably an exploit can be written to delete all the files in key=base64_encode("\n\r delete all").
|
||||
// somebody has to do a fix to this.
|
||||
$theKey = htmlentities(base64_decode($_GET['key']));
|
||||
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
list($h,$p) = get_host_port_from_server($theserver);
|
||||
$r = sendMemcacheCommand($h,$p,'get '.$theKey);
|
||||
echo <<<EOB
|
||||
<div class="info"><table cellspacing=0><tbody>
|
||||
<tr><th>Server<th>Key</th><th>Value</th><th>Delete</th></tr>
|
||||
EOB;
|
||||
if (!isset($r['VALUE'])) {
|
||||
echo "<tr><td class=td-0>",$theserver,"</td><td class=td-0>",$theKey,
|
||||
"</td><td>[The requested item was not found or has expired]</td>",
|
||||
"<td></td>","</tr>";
|
||||
}
|
||||
else {
|
||||
|
||||
echo "<tr><td class=td-0>",$theserver,"</td><td class=td-0>",$theKey,
|
||||
" <br/>flag:",$r['VALUE'][$theKey]['stat']['flag'],
|
||||
" <br/>Size:",bsize($r['VALUE'][$theKey]['stat']['size']),
|
||||
"</td><td>",chunk_split($r['VALUE'][$theKey]['value'],40),"</td>",
|
||||
'<td><a href="',$PHP_SELF,'&op=5&server=',(int)$_GET['server'],'&key=',base64_encode($theKey),"\">Delete</a></td>","</tr>";
|
||||
}
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div><hr/>
|
||||
EOB;
|
||||
break;
|
||||
case 5: // item delete
|
||||
if (!isset($_GET['key']) || !isset($_GET['server'])){
|
||||
echo "No key set!";
|
||||
break;
|
||||
}
|
||||
$theKey = htmlentities(base64_decode($_GET['key']));
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
list($h,$p) = get_host_port_from_server($theserver);
|
||||
$r = sendMemcacheCommand($h,$p,'delete '.$theKey);
|
||||
echo 'Deleting '.$theKey.':'.$r;
|
||||
break;
|
||||
|
||||
case 6: // flush server
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
$r = flushServer($theserver);
|
||||
echo 'Flush '.$theserver.":".$r;
|
||||
break;
|
||||
}
|
||||
echo getFooter();
|
||||
|
||||
?>
|
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Returns HTML for the status report.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - requirements: An array of requirements.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_prod_check_status_report($variables) {
|
||||
$requirements = $variables['requirements'];
|
||||
|
||||
$severities = array(
|
||||
PROD_CHECK_REQUIREMENT_INFO => array(
|
||||
'title' => t('Info'),
|
||||
'class' => 'info',
|
||||
),
|
||||
PROD_CHECK_REQUIREMENT_OK => array(
|
||||
'title' => t('OK'),
|
||||
'class' => 'ok',
|
||||
),
|
||||
PROD_CHECK_REQUIREMENT_WARNING => array(
|
||||
'title' => t('Warning'),
|
||||
'class' => 'warning',
|
||||
),
|
||||
PROD_CHECK_REQUIREMENT_ERROR => array(
|
||||
'title' => t('Error'),
|
||||
'class' => 'error',
|
||||
),
|
||||
);
|
||||
$output = '<table class="system-status-report">';
|
||||
|
||||
foreach ($requirements as $requirement) {
|
||||
if (empty($requirement['#type'])) {
|
||||
$severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : 0];
|
||||
$severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
|
||||
|
||||
// Output table row(s)
|
||||
if (!empty($requirement['description'])) {
|
||||
$output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
|
||||
$output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
|
||||
}
|
||||
else {
|
||||
$output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</table>';
|
||||
return $output;
|
||||
}
|
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ALL code here is taken from the Drupal core's update module. prod_check
|
||||
* recommends that you turn this module off, so we provide the VERY BASIC
|
||||
* functionality to get a list of the installed modules with version info and
|
||||
* transfer this to prod_monitor over XMLRPC (alot of data here!) so that
|
||||
* prod_monitor can do all the update checking locally.
|
||||
* This approach was chosen over this approach:
|
||||
*
|
||||
* module_load_include('module', 'update', 'update');
|
||||
*
|
||||
* if ($available = update_get_available(TRUE)) {
|
||||
* $data['modules'] = update_calculate_project_data($available);
|
||||
* }
|
||||
*
|
||||
* This way, we might just as well turn on the update module and periodically
|
||||
* download the results. It's just not efficient...
|
||||
* Also note that passing TRUE to update_get_available() will bypass (refresh)
|
||||
* the caches, essentially the same as we do in _prod_check_module_list().
|
||||
* See inline comments there for more info.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.compare.inc, line 81
|
||||
*
|
||||
* Populate an array of project data.
|
||||
*
|
||||
* This iterates over a list of the installed modules or themes and groups
|
||||
* them by project and status. A few parts of this function assume that
|
||||
* enabled modules and themes are always processed first, and if disabled
|
||||
* modules or themes are being processed (there is a setting to control if
|
||||
* disabled code should be included in the Available updates report or not),
|
||||
* those are only processed after $projects has been populated with
|
||||
* information about the enabled code. 'Hidden' modules and themes are always
|
||||
* ignored. This function also records the latest change time on the .info
|
||||
* files for each module or theme, which is important data which is used when
|
||||
* deciding if the cached available update data should be invalidated.
|
||||
*
|
||||
* @param $projects
|
||||
* Reference to the array of project data of what's installed on this site.
|
||||
* @param $list
|
||||
* Array of data to process to add the relevant info to the $projects array.
|
||||
* @param $project_type
|
||||
* The kind of data in the list (can be 'module' or 'theme').
|
||||
* @param $status
|
||||
* Boolean that controls what status (enabled or disabled) to process out of
|
||||
* the $list and add to the $projects array.
|
||||
*
|
||||
* @see update_get_projects()
|
||||
*/
|
||||
function _prod_check_process_info_list(&$projects, $list, $project_type, $status) {
|
||||
foreach ($list as $file) {
|
||||
// A disabled base theme of an enabled sub-theme still has all of its code
|
||||
// run by the sub-theme, so we include it in our "enabled" projects list.
|
||||
if ($status && !$file->status && !empty($file->sub_themes)) {
|
||||
foreach ($file->sub_themes as $key => $name) {
|
||||
// Build a list of enabled sub-themes.
|
||||
if ($list[$key]->status) {
|
||||
$file->enabled_sub_themes[$key] = $name;
|
||||
}
|
||||
}
|
||||
// If there are no enabled subthemes, we should ignore this base theme
|
||||
// for the enabled case. If the site is trying to display disabled
|
||||
// themes, we'll catch it then.
|
||||
if (empty($file->enabled_sub_themes)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Otherwise, just add projects of the proper status to our list.
|
||||
elseif ($file->status != $status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if the .info file is broken.
|
||||
if (empty($file->info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if it's a hidden module or theme.
|
||||
if (!empty($file->info['hidden'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the .info doesn't define the 'project', try to figure it out.
|
||||
if (!isset($file->info['project'])) {
|
||||
$file->info['project'] = _prod_check_get_project_name($file);
|
||||
}
|
||||
|
||||
// If we still don't know the 'project', give up.
|
||||
if (empty($file->info['project'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we don't already know it, grab the change time on the .info file
|
||||
// itself. Note: we need to use the ctime, not the mtime (modification
|
||||
// time) since many (all?) tar implementations will go out of their way to
|
||||
// set the mtime on the files it creates to the timestamps recorded in the
|
||||
// tarball. We want to see the last time the file was changed on disk,
|
||||
// which is left alone by tar and correctly set to the time the .info file
|
||||
// was unpacked.
|
||||
if (!isset($file->info['_info_file_ctime'])) {
|
||||
$info_filename = dirname($file->uri) . '/' . $file->name . '.info';
|
||||
$file->info['_info_file_ctime'] = filectime($info_filename);
|
||||
}
|
||||
|
||||
if (!isset($file->info['datestamp'])) {
|
||||
$file->info['datestamp'] = 0;
|
||||
}
|
||||
|
||||
$project_name = $file->info['project'];
|
||||
|
||||
// Figure out what project type we're going to use to display this module
|
||||
// or theme. If the project name is 'drupal', we don't want it to show up
|
||||
// under the usual "Modules" section, we put it at a special "Drupal Core"
|
||||
// section at the top of the report.
|
||||
if ($project_name == 'drupal') {
|
||||
$project_display_type = 'core';
|
||||
}
|
||||
else {
|
||||
$project_display_type = $project_type;
|
||||
}
|
||||
if (empty($status) && empty($file->enabled_sub_themes)) {
|
||||
// If we're processing disabled modules or themes, append a suffix.
|
||||
// However, we don't do this to a a base theme with enabled
|
||||
// subthemes, since we treat that case as if it is enabled.
|
||||
$project_display_type .= '-disabled';
|
||||
}
|
||||
// Add a list of sub-themes that "depend on" the project and a list of base
|
||||
// themes that are "required by" the project.
|
||||
if ($project_name == 'drupal') {
|
||||
// Drupal core is always required, so this extra info would be noise.
|
||||
$sub_themes = array();
|
||||
$base_themes = array();
|
||||
}
|
||||
else {
|
||||
// Add list of enabled sub-themes.
|
||||
$sub_themes = !empty($file->enabled_sub_themes) ? $file->enabled_sub_themes : array();
|
||||
// Add list of base themes.
|
||||
$base_themes = !empty($file->base_themes) ? $file->base_themes : array();
|
||||
}
|
||||
if (!isset($projects[$project_name])) {
|
||||
// Only process this if we haven't done this project, since a single
|
||||
// project can have multiple modules or themes.
|
||||
$projects[$project_name] = array(
|
||||
'name' => $project_name,
|
||||
// Only save attributes from the .info file we care about so we do not
|
||||
// bloat our RAM usage needlessly.
|
||||
'info' => _prod_check_filter_project_info($file->info),
|
||||
'datestamp' => $file->info['datestamp'],
|
||||
'includes' => array($file->name => $file->info['name']),
|
||||
'project_type' => $project_display_type,
|
||||
'project_status' => $status,
|
||||
'sub_themes' => $sub_themes,
|
||||
'base_themes' => $base_themes,
|
||||
);
|
||||
}
|
||||
elseif ($projects[$project_name]['project_type'] == $project_display_type) {
|
||||
// Only add the file we're processing to the 'includes' array for this
|
||||
// project if it is of the same type and status (which is encoded in the
|
||||
// $project_display_type). This prevents listing all the disabled
|
||||
// modules included with an enabled project if we happen to be checking
|
||||
// for disabled modules, too.
|
||||
$projects[$project_name]['includes'][$file->name] = $file->info['name'];
|
||||
$projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
|
||||
$projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
|
||||
if (!empty($sub_themes)) {
|
||||
$projects[$project_name]['sub_themes'] += $sub_themes;
|
||||
}
|
||||
if (!empty($base_themes)) {
|
||||
$projects[$project_name]['base_themes'] += $base_themes;
|
||||
}
|
||||
}
|
||||
elseif (empty($status)) {
|
||||
// If we have a project_name that matches, but the project_display_type
|
||||
// does not, it means we're processing a disabled module or theme that
|
||||
// belongs to a project that has some enabled code. In this case, we add
|
||||
// the disabled thing into a separate array for separate display.
|
||||
$projects[$project_name]['disabled'][$file->name] = $file->info['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.compare.inc, line 238
|
||||
*
|
||||
* Given a $file object (as returned by system_get_files_database()), figure
|
||||
* out what project it belongs to.
|
||||
*
|
||||
* @see system_get_files_database()
|
||||
*/
|
||||
function _prod_check_get_project_name($file) {
|
||||
$project_name = '';
|
||||
if (isset($file->info['project'])) {
|
||||
$project_name = $file->info['project'];
|
||||
}
|
||||
elseif (isset($file->info['package']) && (strpos($file->info['package'], 'Core') === 0)) {
|
||||
$project_name = 'drupal';
|
||||
}
|
||||
return $project_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.compare.inc, line 684
|
||||
*
|
||||
* Filter the project .info data to only save attributes we need.
|
||||
*
|
||||
* @param array $info
|
||||
* Array of .info file data as returned by drupal_parse_info_file().
|
||||
*
|
||||
* @return
|
||||
* Array of .info file data we need for the Update manager.
|
||||
*
|
||||
* @see _prod_check_process_info_list()
|
||||
*/
|
||||
function _prod_check_filter_project_info($info) {
|
||||
$whitelist = array(
|
||||
'_info_file_ctime',
|
||||
'datestamp',
|
||||
'major',
|
||||
'name',
|
||||
'package',
|
||||
'project',
|
||||
'project status url',
|
||||
'version',
|
||||
);
|
||||
return array_intersect_key($info, drupal_map_assoc($whitelist));
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*--------------------------------------------------------------------
|
||||
* JQuery Plugin: "EqualHeights" & "EqualWidths"
|
||||
* by: Scott Jehl, Todd Parker, Maggie Costello Wachs (http://www.filamentgroup.com)
|
||||
*
|
||||
* Copyright (c) 2007 Filament Group
|
||||
* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
|
||||
*
|
||||
* Description: Compares the heights or widths of the top-level children of a provided element
|
||||
and sets their min-height to the tallest height (or width to widest width). Sets in em units
|
||||
by default if pxToEm() method is available.
|
||||
* Dependencies: jQuery library, pxToEm method (article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/)
|
||||
* Usage Example: $(element).equalHeights();
|
||||
Optional: to set min-height in px, pass a true argument: $(element).equalHeights(true);
|
||||
* Version: 2.0, 07.24.2008
|
||||
* Changelog:
|
||||
* 08.02.2007 initial Version 1.0
|
||||
* 07.24.2008 v 2.0 - added support for widths
|
||||
--------------------------------------------------------------------*/
|
||||
(function ($) {
|
||||
$.fn.equalHeights = function(px) {
|
||||
$(this).each(function(){
|
||||
var currentTallest = 0;
|
||||
$(this).children().each(function(i){
|
||||
if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
|
||||
});
|
||||
if (!px || !Number.prototype.pxToEm) currentTallest = currentTallest.pxToEm(); //use ems unless px is specified
|
||||
// for ie6, set height since min-height isn't supported
|
||||
if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'height': currentTallest}); }
|
||||
$(this).children().css({'min-height': currentTallest});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
// just in case you need it...
|
||||
$.fn.equalWidths = function(px) {
|
||||
$(this).each(function(){
|
||||
var currentWidest = 0;
|
||||
$(this).children().each(function(i){
|
||||
if($(this).width() > currentWidest) { currentWidest = $(this).width(); }
|
||||
});
|
||||
if(!px || !Number.prototype.pxToEm) currentWidest = currentWidest.pxToEm(); //use ems unless px is specified
|
||||
// for ie6, set width since min-width isn't supported
|
||||
if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'width': currentWidest}); }
|
||||
$(this).children().css({'min-width': currentWidest});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* javascript method: "pxToEm"
|
||||
* by:
|
||||
Scott Jehl (scott@filamentgroup.com)
|
||||
Maggie Wachs (maggie@filamentgroup.com)
|
||||
http://www.filamentgroup.com
|
||||
*
|
||||
* Copyright (c) 2008 Filament Group
|
||||
* Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
|
||||
*
|
||||
* Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.
|
||||
* Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
|
||||
* Demo: http://www.filamentgroup.com/examples/pxToEm/
|
||||
*
|
||||
* Options:
|
||||
scope: string or jQuery selector for font-size scoping
|
||||
reverse: Boolean, true reverses the conversion to em-px
|
||||
* Dependencies: jQuery library
|
||||
* Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
|
||||
*
|
||||
* Version: 2.0, 08.01.2008
|
||||
* Changelog:
|
||||
* 08.02.2007 initial Version 1.0
|
||||
* 08.01.2008 - fixed font-size calculation for IE
|
||||
--------------------------------------------------------------------*/
|
||||
(function ($) {
|
||||
Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
|
||||
//set defaults
|
||||
settings = jQuery.extend({
|
||||
scope: 'body',
|
||||
reverse: false
|
||||
}, settings);
|
||||
|
||||
var pxVal = (this == '') ? 0 : parseFloat(this);
|
||||
var scopeVal;
|
||||
var getWindowWidth = function(){
|
||||
var de = document.documentElement;
|
||||
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
|
||||
};
|
||||
|
||||
/* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
|
||||
For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
|
||||
When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
|
||||
to get an accurate em value. */
|
||||
|
||||
if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
|
||||
var calcFontSize = function(){
|
||||
return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
|
||||
};
|
||||
scopeVal = calcFontSize();
|
||||
}
|
||||
else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
|
||||
|
||||
var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
|
||||
return result;
|
||||
};
|
||||
})(jQuery);
|
7
sites/all/modules/contrib/dev/prod_check/js/jquery.maskedinput.min.js
vendored
Normal file
7
sites/all/modules/contrib/dev/prod_check/js/jquery.maskedinput.min.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
Masked Input plugin for jQuery
|
||||
Copyright (c) 2007-2009 Josh Bush (digitalbush.com)
|
||||
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
|
||||
Version: 1.2.2 (03/09/2009 22:39:06)
|
||||
*/
|
||||
(function(a){var c=(a.browser.msie?"paste":"input")+".mask";var b=(window.orientation!=undefined);a.mask={definitions:{"9":"[0-9]",a:"[A-Za-z]","*":"[A-Za-z0-9]"}};a.fn.extend({caret:function(e,f){if(this.length==0){return}if(typeof e=="number"){f=(typeof f=="number")?f:e;return this.each(function(){if(this.setSelectionRange){this.focus();this.setSelectionRange(e,f)}else{if(this.createTextRange){var g=this.createTextRange();g.collapse(true);g.moveEnd("character",f);g.moveStart("character",e);g.select()}}})}else{if(this[0].setSelectionRange){e=this[0].selectionStart;f=this[0].selectionEnd}else{if(document.selection&&document.selection.createRange){var d=document.selection.createRange();e=0-d.duplicate().moveStart("character",-100000);f=e+d.text.length}}return{begin:e,end:f}}},unmask:function(){return this.trigger("unmask")},mask:function(j,d){if(!j&&this.length>0){var f=a(this[0]);var g=f.data("tests");return a.map(f.data("buffer"),function(l,m){return g[m]?l:null}).join("")}d=a.extend({placeholder:"_",completed:null},d);var k=a.mask.definitions;var g=[];var e=j.length;var i=null;var h=j.length;a.each(j.split(""),function(m,l){if(l=="?"){h--;e=m}else{if(k[l]){g.push(new RegExp(k[l]));if(i==null){i=g.length-1}}else{g.push(null)}}});return this.each(function(){var r=a(this);var m=a.map(j.split(""),function(x,y){if(x!="?"){return k[x]?d.placeholder:x}});var n=false;var q=r.val();r.data("buffer",m).data("tests",g);function v(x){while(++x<=h&&!g[x]){}return x}function t(x){while(!g[x]&&--x>=0){}for(var y=x;y<h;y++){if(g[y]){m[y]=d.placeholder;var z=v(y);if(z<h&&g[y].test(m[z])){m[y]=m[z]}else{break}}}s();r.caret(Math.max(i,x))}function u(y){for(var A=y,z=d.placeholder;A<h;A++){if(g[A]){var B=v(A);var x=m[A];m[A]=z;if(B<h&&g[B].test(x)){z=x}else{break}}}}function l(y){var x=a(this).caret();var z=y.keyCode;n=(z<16||(z>16&&z<32)||(z>32&&z<41));if((x.begin-x.end)!=0&&(!n||z==8||z==46)){w(x.begin,x.end)}if(z==8||z==46||(b&&z==127)){t(x.begin+(z==46?0:-1));return false}else{if(z==27){r.val(q);r.caret(0,p());return false}}}function o(B){if(n){n=false;return(B.keyCode==8)?false:null}B=B||window.event;var C=B.charCode||B.keyCode||B.which;var z=a(this).caret();if(B.ctrlKey||B.altKey||B.metaKey){return true}else{if((C>=32&&C<=125)||C>186){var x=v(z.begin-1);if(x<h){var A=String.fromCharCode(C);if(g[x].test(A)){u(x);m[x]=A;s();var y=v(x);a(this).caret(y);if(d.completed&&y==h){d.completed.call(r)}}}}}return false}function w(x,y){for(var z=x;z<y&&z<h;z++){if(g[z]){m[z]=d.placeholder}}}function s(){return r.val(m.join("")).val()}function p(y){var z=r.val();var C=-1;for(var B=0,x=0;B<h;B++){if(g[B]){m[B]=d.placeholder;while(x++<z.length){var A=z.charAt(x-1);if(g[B].test(A)){m[B]=A;C=B;break}}if(x>z.length){break}}else{if(m[B]==z[x]&&B!=e){x++;C=B}}}if(!y&&C+1<e){r.val("");w(0,h)}else{if(y||C+1>=e){s();if(!y){r.val(r.val().substring(0,C+1))}}}return(e?B:i)}if(!r.attr("readonly")){r.one("unmask",function(){r.unbind(".mask").removeData("buffer").removeData("tests")}).bind("focus.mask",function(){q=r.val();var x=p();s();setTimeout(function(){if(x==j.length){r.caret(0,x)}else{r.caret(x)}},0)}).bind("blur.mask",function(){p();if(r.val()!=q){r.change()}}).bind("keydown.mask",l).bind("keypress.mask",o).bind(c,function(){setTimeout(function(){r.caret(p(true))},0)})}p()})}})})(jQuery);
|
19
sites/all/modules/contrib/dev/prod_check/js/prod-check.js
Normal file
19
sites/all/modules/contrib/dev/prod_check/js/prod-check.js
Normal file
@@ -0,0 +1,19 @@
|
||||
(function ($) {
|
||||
|
||||
// Prod check settings page styling.
|
||||
|
||||
Drupal.behaviors.prod_check = {
|
||||
attach: function(context, settings) {
|
||||
$('#edit-prod-check-module-list-time', context).mask('99:99');
|
||||
$('#prod-check-settings', context).equalHeights('px');
|
||||
$('#prod-check-settings', context).equalWidths('px');
|
||||
|
||||
$('#prod-check-nagios', context).change(function() {
|
||||
$('#prod-check-settings', context).equalHeights('px');
|
||||
$('#prod-check-settings', context).equalWidths('px');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
181
sites/all/modules/contrib/dev/prod_check/prod_check.api.php
Normal file
181
sites/all/modules/contrib/dev/prod_check/prod_check.api.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Documentation for hooks defined by prod_check.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* hook_prod_check_alter()
|
||||
*
|
||||
* You can implement hook_prod_check_alter() in your own module to add
|
||||
* additional checks or modify the core checks.
|
||||
* The hook receives the default functions divided into 7 categories:
|
||||
*
|
||||
* - settings
|
||||
* - server
|
||||
* - performance
|
||||
* - security
|
||||
* - modules
|
||||
* - seo
|
||||
* - prod_mon
|
||||
* - perf_data
|
||||
*
|
||||
* 'prod_mon' & 'perf_data' are special categories that will only be used by the
|
||||
* accompanying Production monitor module.
|
||||
*
|
||||
* Your function that implements the actual check must accept 1 string parameter
|
||||
* and return an array using the prod_check_execute_check() function.
|
||||
*
|
||||
* An example implementation (note the pass by reference in the hook!):
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_prod_check_alter()
|
||||
* @param array reference to an associative array of all available checks
|
||||
*/
|
||||
function my_module_prod_check_alter(&$checks) {
|
||||
// Add custom check to the server category:
|
||||
// function_name => title
|
||||
// Do not use t() for the title!
|
||||
$checks['server']['functions']['my_module_additional_check'] = 'Additional check title';
|
||||
|
||||
// Add custom check for Production Monitor only
|
||||
$checks['prod_mon']['functions']['my_module_prod_mon_check'] = 'My Production Monitor only check';
|
||||
|
||||
// Gather performance data
|
||||
$checks['perf_data']['functions']['my_module_prod_check_return_data'] = 'Performance logging';
|
||||
|
||||
// Add entirely new category.
|
||||
$checks['my_category'] = array(
|
||||
'title' => 'Custom category',
|
||||
'description' => 'Collection of checks I find important.',
|
||||
'functions' => array(
|
||||
'my_module_check_stuff' => 'Check stuff',
|
||||
'my_module_check_more_stuff' => 'Check more stuff',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom function to check some things.
|
||||
* @param string the caller of the function, defaults to 'internal' but can also
|
||||
* be 'xmlrpc' or 'nagios'
|
||||
* @return array you _must_ return prod_check_execute_check($check, $caller) as
|
||||
* per the example below to insure a proper status array is returned.
|
||||
*/
|
||||
function my_module_additional_check($caller = 'internal') {
|
||||
$check = array();
|
||||
|
||||
$title = 'My modules settings';
|
||||
$setting1 = t('Enable debug info');
|
||||
$setting2 = t('Disable debug info');
|
||||
$path = 'admin/settings/my-module-settings-page';
|
||||
if ($caller != 'internal') {
|
||||
$path = PRODCHECK_BASEURL . $path;
|
||||
}
|
||||
|
||||
$check['my_module_additional_check'] = array(
|
||||
'#title' => t($title),
|
||||
'#state' => variable_get('my_module_debug', 1) != 1,
|
||||
'#severity' => ($caller == 'nagios') ? NAGIOS_STATUS_CRITICAL : PROD_CHECK_REQUIREMENT_ERROR,
|
||||
'#value_ok' => $setting2,
|
||||
'#value_nok' => $setting1,
|
||||
'#description_ok' => prod_check_ok_title($title, $path),
|
||||
'#description_nok' => t('Your !link settings are set to %setting1, they should be set to %setting2 on a producion environment!',
|
||||
array(
|
||||
'!link' => '<em>'.l(t($title), $path, array('attributes' => array('title' => t($title)))).'</em>',
|
||||
'%setting1' => $setting1,
|
||||
'%setting2' => $setting2,
|
||||
)
|
||||
),
|
||||
'#nagios_key' => 'MYCHCK',
|
||||
'#nagios_type' => 'state',
|
||||
);
|
||||
|
||||
return prod_check_execute_check($check, $caller);
|
||||
}
|
||||
|
||||
function my_module_check_stuff($caller = 'internal') {
|
||||
[...]
|
||||
|
||||
return prod_check_execute_check($check, $caller);
|
||||
}
|
||||
|
||||
function my_module_check_more_stuff($caller = 'internal') {
|
||||
[...]
|
||||
|
||||
return prod_check_execute_check($check, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom callback for a prod_mon only check. Note the additional parameter in
|
||||
* the prod_check_execute_check() call!
|
||||
*/
|
||||
function my_module_prod_mon_check($caller = 'internal') {
|
||||
[...]
|
||||
|
||||
return prod_check_execute_check($check, $caller, 'prod_mon');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return performance data to Production Monitor.
|
||||
*/
|
||||
function my_module_prod_check_return_data() {
|
||||
$data = my_module_gather_summary_data();
|
||||
|
||||
if (!$data) {
|
||||
return array(
|
||||
'my_module' => array (
|
||||
'title' => 'Performance logging',
|
||||
'data' => 'No performance data found.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'my_module' => array (
|
||||
'title' => 'Performance logging',
|
||||
'data' => array(
|
||||
'Total number of page accesses' => array($data['total_accesses']),
|
||||
'Average duration per page' => array($data['ms_avg'], 'ms'),
|
||||
'Average memory per page' => array($data['mb_avg'], 'MB'),
|
||||
'Average querycount' => array($data['query_count']),
|
||||
'Average duration per query' => array($data['ms_query'], 'ms'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* hook_prod_check_disabled_modules_whitelist()
|
||||
*
|
||||
* Check for updates for these modules even if they are disabled. Some modules
|
||||
* (f.e. cache backends) are included directly but don't necessarily have the
|
||||
* module enabled in the module list. This list can be extended by other modules
|
||||
* or updated with other commonly used modules that are used in such a way.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_prod_check_disabled_modules_whitelist().
|
||||
*/
|
||||
function my_module_prod_check_disabled_modules_whitelist() {
|
||||
return array('apc', 'memcache', 'varnish');
|
||||
}
|
||||
|
||||
/**
|
||||
* hook_prod_check_disabled_modules_whitelist_alter()
|
||||
*
|
||||
* Allow other modules to add or delete modules to force check.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_prod_check_disabled_modules_whitelist_alter().
|
||||
*/
|
||||
function my_module_prod_check_disabled_modules_whitelist_alter(&$modules) {
|
||||
// Remove apc module from the whitelist.
|
||||
if ($pos = array_search('apc', $modules)) {
|
||||
unset($modules[$pos]);
|
||||
}
|
||||
}
|
111
sites/all/modules/contrib/dev/prod_check/prod_check.drush.inc
Normal file
111
sites/all/modules/contrib/dev/prod_check/prod_check.drush.inc
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implementation of hook_drush_command().
|
||||
*/
|
||||
function prod_check_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$items['prod-check'] = array(
|
||||
'callback' => 'drush_prod_check_status',
|
||||
'description' => 'Display the Production Check status page.',
|
||||
'aliases' => array('pchk'),
|
||||
);
|
||||
|
||||
$items['prod-check-prodmode'] = array(
|
||||
'callback' => 'drush_prod_check_prod_mode',
|
||||
'description' => 'Switch the site to production mode.',
|
||||
'options' => array(
|
||||
'config' => 'Ask for additional options before switching to production mode.',
|
||||
),
|
||||
'aliases' => array('pchk-pmode'),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status page callback
|
||||
*/
|
||||
function drush_prod_check_status() {
|
||||
// Map error codes to shell colours.
|
||||
$severity = array (
|
||||
PROD_CHECK_REQUIREMENT_INFO => '1',
|
||||
PROD_CHECK_REQUIREMENT_OK => '1;32',
|
||||
PROD_CHECK_REQUIREMENT_WARNING => '1;33',
|
||||
PROD_CHECK_REQUIREMENT_ERROR => '1;31',
|
||||
);
|
||||
$error = 0;
|
||||
|
||||
$functions = _prod_check_functions();
|
||||
// Not needed here.
|
||||
unset($functions['prod_mon']);
|
||||
unset($functions['perf_data']);
|
||||
|
||||
foreach ($functions as $set => $data) {
|
||||
$rows[] = array('');
|
||||
$rows[] = array("\033[1m".dt($data['title'])."\033[0m");
|
||||
foreach ($data['functions'] as $function => $title) {
|
||||
$result = call_user_func($function);
|
||||
$func = ltrim($function, '_');
|
||||
if (is_array($result) && !empty($result)) {
|
||||
$rows[] = array(
|
||||
$result[$func]['title'],
|
||||
"\033[".$severity[$result[$func]['severity']].'m'.strip_tags($result[$func]['value'])."\033[0m",
|
||||
);
|
||||
if ($error < $result[$func]['severity']) {
|
||||
$error = $result[$func]['severity'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drush_print("\033[1m".dt('Production Check status')."\033[0m", 1);
|
||||
drush_print_table($rows);
|
||||
if ($error > 0) {
|
||||
// Would be cool if we could prefix the admin path with http://<host>/ so it
|
||||
// will become a clickable link in some terminals. Any ideas?
|
||||
drush_print("\033[1m".dt('Some errors were reported!')."\033[0m ".dt('Check the full status page on')." \033[1m".'admin/reports/prod-check'."\033[0m ".dt('for details.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to production mode.
|
||||
*/
|
||||
function drush_prod_check_prod_mode() {
|
||||
$options = array();
|
||||
|
||||
// Ask extra input when the --config option is used.
|
||||
if (drush_get_option('config', FALSE)) {
|
||||
$options['site_mail'] = drush_prompt(dt('Site e-mail address'));
|
||||
|
||||
if (module_exists('webform')) {
|
||||
$options['webform_default_from_address'] = drush_prompt(dt('Webform default from e-mail address'));
|
||||
}
|
||||
|
||||
if (module_exists('googleanalytics')) {
|
||||
$options['googleanalytics_account'] = drush_prompt(dt('Google Analytics Web Property ID'));
|
||||
}
|
||||
|
||||
$options['block_cache'] = drush_confirm(dt('Enable Block cache'));
|
||||
|
||||
if (module_exists('dblog')) {
|
||||
$options['dblog'] = drush_confirm(dt('Disable Database logging'));
|
||||
}
|
||||
|
||||
$options['nagios'] = drush_confirm(dt('Enable Nagios monitoring contrib module'));
|
||||
}
|
||||
|
||||
// Adjust settings.
|
||||
module_load_include('inc', 'prod_check', 'includes/prod_check.admin');
|
||||
$variables = prod_check_prod_mode_settings($options);
|
||||
drush_print(dt('The following settings have been changed: !variables.', array('!variables' => implode(', ', array_keys($variables)))));
|
||||
|
||||
// Enable / disable modules.
|
||||
$modules = prod_check_prod_mode_modules($options);
|
||||
if (!empty($modules['disable'])) {
|
||||
drush_print(dt('The following modules have been disabled: !modules.', array('!modules' => implode(', ', $modules['disable']))));
|
||||
}
|
||||
if (!empty($modules['enable'])) {
|
||||
drush_print(dt('The following modules have been enabled: !modules.', array('!modules' => implode(', ', $modules['enable']))));
|
||||
}
|
||||
}
|
12
sites/all/modules/contrib/dev/prod_check/prod_check.info
Normal file
12
sites/all/modules/contrib/dev/prod_check/prod_check.info
Normal file
@@ -0,0 +1,12 @@
|
||||
name = Production check
|
||||
description = Check a site to see if it's properly setup for production use.
|
||||
package = Monitoring
|
||||
core = 7.x
|
||||
configure = admin/config/system/prod-check
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-10-01
|
||||
version = "7.x-1.7+0-dev"
|
||||
core = "7.x"
|
||||
project = "prod_check"
|
||||
datestamp = "1380623918"
|
||||
|
88
sites/all/modules/contrib/dev/prod_check/prod_check.install
Normal file
88
sites/all/modules/contrib/dev/prod_check/prod_check.install
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implementation of hook_install().
|
||||
*/
|
||||
function prod_check_install() {
|
||||
// Allow immediate fetching of module data by prod_mon after installation
|
||||
// for remote the module status update checking feature.
|
||||
variable_set('prod_check_module_list_lastrun', -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_uninstall().
|
||||
*/
|
||||
function prod_check_uninstall() {
|
||||
// This beats multiple variable_del() calls.
|
||||
db_delete('variable')->condition('name', 'prod_check\_%', 'LIKE')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_requirements().
|
||||
*/
|
||||
function prod_check_requirements($phase) {
|
||||
$requirements = array();
|
||||
|
||||
switch ($phase) {
|
||||
case 'runtime':
|
||||
$sitemail = variable_get('prod_check_sitemail', '');
|
||||
if (empty($sitemail)) {
|
||||
$requirements['prod_check_email'] = array(
|
||||
'title' => t('Production check'),
|
||||
'value' => t('Site e-mail check not properly configured.'),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
'description' => t('You have not changed the e-mail address on the prod-check !link. The Site e-mail check will not function properly. Please read the README.txt file.', prod_check_link_array('settings page', 'admin/settings/prod-check')),
|
||||
);
|
||||
}
|
||||
if (function_exists('apc_cache_info') && variable_get('prod_check_apcpass', 'password') == 'password') {
|
||||
$requirements['prod_check_apc'] = array(
|
||||
'title' => t('Production check'),
|
||||
'value' => t('APC password not configured.'),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
'description' => t('You have not !link for the APC status page. The page will function, but advanced options require that you set a password. Please read the README.txt file.', prod_check_link_array('changed the password', 'admin/settings/prod-check')),
|
||||
);
|
||||
}
|
||||
$nagios = variable_get('prod_check_enable_nagios', 0);
|
||||
if ($nagios && !module_exists('nagios')) {
|
||||
$requirements['prod_check_nagios'] = array(
|
||||
'title' => t('Production check'),
|
||||
'value' => t('Nagios module not installed/enabled.'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => t('You have enabled <em>Nagios integration</em> but you have not installed or enabled the !link module! Please install the !link module if you wish to use this functionality.', prod_check_link_array('Nagios', 'http://drupal.org/project/nagios')),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update prod_check_nagios_checks settings if present.
|
||||
*/
|
||||
function prod_check_update_7100() {
|
||||
if ($checks = variable_get('prod_check_nagios_checks', FALSE)) {
|
||||
$prefix = '_prod_check_';
|
||||
foreach ($checks as $set => &$calls) {
|
||||
foreach ($calls as $key => &$function) {
|
||||
if (stripos($function, $prefix) === FALSE) {
|
||||
$function = $prefix . $function;
|
||||
}
|
||||
}
|
||||
}
|
||||
variable_set('prod_check_nagios_checks', $checks);
|
||||
return t('Successfully updated prod_check_nagios_checks settings.');
|
||||
}
|
||||
|
||||
return t('No prod_check_nagios_checks found that needed an update.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove obsolete memcache settings.
|
||||
*/
|
||||
function prod_check_update_7101() {
|
||||
variable_del('prod_check_memcacheuser');
|
||||
variable_del('prod_check_memcachepass');
|
||||
|
||||
return t('Obsolete memcache settings removed.');
|
||||
}
|
1851
sites/all/modules/contrib/dev/prod_check/prod_check.module
Normal file
1851
sites/all/modules/contrib/dev/prod_check/prod_check.module
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
|
||||
/* Prod monitor settings page styling */
|
||||
|
||||
.prod-check-settings{float:left;padding:0 5px;}
|
||||
.prod-check-settings.odd{background-color:#f8f8f8;}
|
||||
.prod-check-settings.even{background-color:#f1f1f1;}
|
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,639 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Build status page.
|
||||
*/
|
||||
function prod_monitor_status($id) {
|
||||
$site = _prod_monitor_get_site($id, TRUE);
|
||||
drupal_set_title(t('Production monitor status for') .' '. _prod_monitor_sanitize_url($site['url']));
|
||||
|
||||
$functions = $site['settings']['functions'];
|
||||
$nodata = t('No data recieved yet.');
|
||||
|
||||
$output = '';
|
||||
|
||||
// General status block
|
||||
$modules = _prod_monitor_get_site_modules($id);
|
||||
if(!empty($modules) && isset($site['data']['prod_mon'])) {
|
||||
$prod_mon = $site['data']['prod_mon'];
|
||||
$output .= _prod_monitor_status_general($prod_mon, $modules);
|
||||
}
|
||||
unset($site['data']['prod_mon']);
|
||||
// Performance data not needed here.
|
||||
unset($site['data']['perf_data']);
|
||||
|
||||
// Display results of all checks.
|
||||
foreach ($functions as $set => $data) {
|
||||
if (isset($site['data'][$set])) {
|
||||
$output .= '<h2>'.t($data['title']).'</h2>'."\n";
|
||||
$output .= '<div class="description"><p><em>'.t($data['description']).'</em></p></div>'."\n";
|
||||
if (!empty($site['data'][$set])) {
|
||||
$output .= theme('prod_monitor_status_report', array('requirements' => $site['data'][$set]));
|
||||
}
|
||||
else {
|
||||
$output .= '<p>'.$nodata.'</p><p> </p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($output)) {
|
||||
$output = '<p>'.$nodata.'</p><p> </p>';
|
||||
}
|
||||
|
||||
// TODO: do not use drupal_render but change this so that hook_page_alter can
|
||||
// be used as well.
|
||||
$output .= drupal_render(drupal_get_form('_prod_monitor_update_data_form', $id, $site));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to provide general status block on status overview page
|
||||
*/
|
||||
function _prod_monitor_status_general($prod_mon, $modules) {
|
||||
// TODO: Should we hide the rows for which no data is being retrieved?
|
||||
$cron = t('Unknown');
|
||||
if (isset($prod_mon['prod_check_cron_last'])) {
|
||||
$cron = format_date($prod_mon['prod_check_cron_last'], 'large');
|
||||
}
|
||||
|
||||
$updates = _prod_monitor_generate_updates_link($modules['id'], $modules['updates']);
|
||||
|
||||
$output = '<h2>'.t('Overall status').'</h2>'."\n";
|
||||
$rows = array(
|
||||
array(
|
||||
array('data' => t('Drupal core version'), 'header' => TRUE),
|
||||
$modules['projects']['drupal']['info']['version'],
|
||||
),
|
||||
array(
|
||||
array('data' => t('Last cron run'), 'header' => TRUE),
|
||||
$cron,
|
||||
),
|
||||
array(
|
||||
array('data' => t('Update status'), 'header' => TRUE),
|
||||
$updates,
|
||||
),
|
||||
);
|
||||
$output .= theme('table', array('header' => array(), 'rows' => $rows));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate update status link for tables
|
||||
*/
|
||||
function _prod_monitor_generate_updates_link($id, $update_status) {
|
||||
$updates = t('Unknown');
|
||||
if ($update_status > 0) {
|
||||
switch ($update_status) {
|
||||
case 1:
|
||||
$class = '';
|
||||
$title = t('None');
|
||||
break;
|
||||
case 2:
|
||||
$class = 'warning';
|
||||
$title = t('Available');
|
||||
break;
|
||||
case 3:
|
||||
$class = 'error';
|
||||
$title = t('Security risk!');
|
||||
break;
|
||||
}
|
||||
$updates = array('data' => '<strong>'.l($title, 'admin/reports/prod-monitor/site/'.$id.'/view/updates', array('attributes' => array('title' => $title, 'class' => $class))).'</strong>', 'class' => $class);
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for performance page.
|
||||
*/
|
||||
function prod_monitor_performance($data) {
|
||||
drupal_set_title(t('Performance logs for') .' '. _prod_monitor_get_url($data['id']));
|
||||
|
||||
// TODO: add 'get stats now' button.
|
||||
//$site = _prod_monitor_get_site($id, TRUE);
|
||||
|
||||
return array(
|
||||
'performance_data' => array(
|
||||
'#data' => $data['data'],
|
||||
'#theme' => 'prod_monitor_performance',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for module update status page
|
||||
*/
|
||||
function prod_monitor_updates($modules) {
|
||||
$output = '';
|
||||
|
||||
$id = $modules['id'];
|
||||
|
||||
drupal_set_title(t('Module update status for') .' '. _prod_monitor_get_url($id));
|
||||
|
||||
// Only show a report if the available updates have been fetched!
|
||||
if (!empty($modules) && !empty($modules['projects']) && !empty($modules['available'])) {
|
||||
module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
|
||||
$data = _prod_monitor_calculate_project_data($id, $modules['projects'], $modules['available']);
|
||||
$output .= theme('prod_monitor_update_report', array('id' => $id, 'last' => $modules['lastupdate'], 'data' => $data));
|
||||
}
|
||||
// No data, so report this to the user and instruct him/her on how to retrieve it.
|
||||
else {
|
||||
$destination = drupal_get_destination();
|
||||
$output .= theme('prod_monitor_update_report',
|
||||
array(
|
||||
'id' => $id,
|
||||
'last' => $modules['lastupdate'],
|
||||
'data' => t(
|
||||
'No information is available about potential new releases for currently installed modules and themes. To check for updates, you may need to !cron or you can !check. Please note that checking for available updates can take a long time, so please be patient.',
|
||||
array(
|
||||
'!cron' => l(t('run cron'), 'admin/reports/status/run-cron', array('attributes' => array('title' => t('run cron')), 'query' => $destination)),
|
||||
'!check' => l(t('check manually'), 'admin/reports/prod-monitor/site/'.$id.'/update-check', array('attributes' => array('title' => t('check manually')))),
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to refresh the module update status page
|
||||
*/
|
||||
function prod_monitor_updates_check($id) {
|
||||
// Get module data.
|
||||
$modules = _prod_monitor_get_site_modules($id);
|
||||
if (!empty($modules) && !empty($modules['projects'])) {
|
||||
module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
|
||||
// ALWAYS do a full refresh.
|
||||
$result = _prod_monitor_update_refresh($id, $modules['projects'], $modules['sitekey']);
|
||||
if (!empty($result)) {
|
||||
drupal_set_message(t('Information about all available new releases and updates sucessfully fetched.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Failed to fetch all available new releases and updates!'), 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('No module data available: cannot check for updates!'), 'error');
|
||||
}
|
||||
drupal_goto('admin/reports/prod-monitor/site/'.$id.'/view/updates');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build settings form.
|
||||
*/
|
||||
function prod_monitor_overview_form($form, &$form_state, $edit = FALSE) {
|
||||
drupal_set_title(t('Production monitor settings'));
|
||||
$base = drupal_get_path('module', 'prod_monitor');
|
||||
drupal_add_css($base.'/css/prod-monitor.css', 'file');
|
||||
drupal_add_js($base.'/js/jquery.equalheights.js', 'file');
|
||||
drupal_add_js($base.'/js/prod-monitor.js', 'file');
|
||||
|
||||
$form = array();
|
||||
|
||||
$collapsed = FALSE;
|
||||
|
||||
if (!$edit) {
|
||||
// Add new site situation.
|
||||
$sites = _prod_monitor_get_sites();
|
||||
$api_key = $url = '';
|
||||
$options = array();
|
||||
$button = t('Get settings');
|
||||
if (!empty($sites)) {
|
||||
$form['table'] = array(
|
||||
'#markup' => _prod_monitor_overview_form_table($sites),
|
||||
);
|
||||
$collapsed = TRUE;
|
||||
}
|
||||
if (!empty($form_state['storage']['get_settings'])) {
|
||||
// Second step of add new site situation.
|
||||
$api_key = $form_state['values']['api_key'];
|
||||
$url = $form_state['values']['url'];
|
||||
$button = t('Add site');
|
||||
$collapsed = FALSE;
|
||||
$msg = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Edit site situation.
|
||||
$site = _prod_monitor_get_site($edit);
|
||||
$url = $site['url'];
|
||||
// Allow user to correct faulty url.
|
||||
if (isset($form_state['values']['url']) && $url != $form_state['values']['url']) {
|
||||
$url = $form_state['values']['url'];
|
||||
}
|
||||
drupal_set_title(t('Production monitor settings for !url', array('!url' => _prod_monitor_sanitize_url($url))));
|
||||
$api_key = $site['settings']['api_key'];
|
||||
$options = $site['settings']['checks'];
|
||||
if (isset($site['settings']['checks']['perf_data'])) {
|
||||
$perf_enabled = $site['settings']['checks']['perf_data'];
|
||||
}
|
||||
$button = t('Save site');
|
||||
$msg = FALSE;
|
||||
}
|
||||
|
||||
// Add/edit form.
|
||||
$form['sites'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Add a site to monitor'),
|
||||
'#description' => t('You can add sites here that you wish to monitor.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => $collapsed,
|
||||
);
|
||||
|
||||
$form['sites']['url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Website URL'),
|
||||
'#default_value' => $url,
|
||||
'#description' => t('Enter the <strong>full</strong> url of the website to be monitored. This site must be running the <em>Production check</em> module. This <strong>must</strong> include a protocol like <em>http://</em> or <em>https://</em>!'),
|
||||
'#size' => 60,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['sites']['api_key'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('The website\'s API key'),
|
||||
'#default_value' => $api_key,
|
||||
'#description' => t('Enter the API key you have configured for this site using the <em>Production check</em> module.'),
|
||||
'#size' => 60,
|
||||
'#maxlength' => 128,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
// Only show on second step of add form or when editing.
|
||||
if (!empty($form_state['storage']['get_settings']) || $edit) {
|
||||
// Get the settings from the remote site. We always do this when the form is
|
||||
// displayed and don't store this locally. Logic here is that you won't be
|
||||
// editing these settings all that much.
|
||||
$functions = _prod_monitor_retrieve_functions($url, $api_key, $msg);
|
||||
// Parse the array of functions into a form.
|
||||
if ($functions) {
|
||||
// Save data to store in DB on submit.
|
||||
$form_state['storage']['functions'] = $functions;
|
||||
// Parse functions into form data
|
||||
$form['sites']['prod_check_settings'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Configure what data you wish to monitor for this site.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
);
|
||||
|
||||
$form['sites']['prod_check_settings']['monitor_settings'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div id="prod-check-settings">',
|
||||
'#suffix' => '</div>',
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
// Single out perf_data if present to treat differently.
|
||||
if (isset($functions['perf_data'])) {
|
||||
$performance = $functions['perf_data'];
|
||||
unset($functions['perf_data']);
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
foreach ($functions as $set => $data) {
|
||||
$rest = $i%2;
|
||||
$form['sites']['prod_check_settings']['monitor_settings'][$set] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t($data['title']),
|
||||
'#description' => t($data['description']),
|
||||
'#options' => $data['functions'],
|
||||
'#default_value' => array_keys($data['functions']),
|
||||
'#prefix' => '<div class="prod-check-settings '.(($rest) ? 'odd' : 'even').'">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$i++;
|
||||
}
|
||||
if ($edit) {
|
||||
// Just to increase readability of the source code here.
|
||||
$monitor_settings = &$form['sites']['prod_check_settings']['monitor_settings'];
|
||||
// Set default values to last saved state
|
||||
foreach (element_children($monitor_settings) as $set) {
|
||||
if (isset($options[$set])) {
|
||||
$monitor_settings[$set]['#default_value'] = $options[$set];
|
||||
}
|
||||
else {
|
||||
// No settings available, so uncheck all.
|
||||
$monitor_settings[$set]['#default_value'] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add performance logging section.
|
||||
if (!empty($performance['functions'])) {
|
||||
$form['sites']['prod_check_performance'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t("Configure which module's performance data you wish to log."),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
);
|
||||
$perf_options = array();
|
||||
foreach ($performance['functions'] as $function => $title) {
|
||||
$perf_options[$function] = t($title);
|
||||
}
|
||||
$form['sites']['prod_check_performance']['performance'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $perf_options,
|
||||
'#default_value' => array(),
|
||||
'#description' => t('Indicate which performance data you want to store. This data can be provided by the modules listed here.'),
|
||||
);
|
||||
if ($edit && isset($perf_enabled)) {
|
||||
$form['sites']['prod_check_performance']['performance']['#default_value'] = $perf_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
$form['sites']['fetch'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Fetch data immediately'),
|
||||
'#default_value' => 0,
|
||||
'#description' => t('Will attempt to fetch the data immediately when the site has been added.'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Error, so show retry button.
|
||||
$button = t('Retry');
|
||||
}
|
||||
}
|
||||
|
||||
if ($edit) {
|
||||
$form['site_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $edit,
|
||||
);
|
||||
}
|
||||
|
||||
$form['sites']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $button,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to theme all sites into a table
|
||||
*/
|
||||
function _prod_monitor_overview_form_table($sites) {
|
||||
$home = array('destination' => 'admin/reports/prod-monitor');
|
||||
|
||||
// Set headers.
|
||||
$headers = array(
|
||||
t('URL'),
|
||||
t('Data'),
|
||||
t('Status'),
|
||||
t('Updates'),
|
||||
t('Date added'),
|
||||
t('Last update'),
|
||||
array('data' => t('Actions'), 'colspan' => 5),
|
||||
);
|
||||
|
||||
// Compose rows.
|
||||
$rows = array();
|
||||
foreach ($sites as $id => $site_info) {
|
||||
// Set view and flush links.
|
||||
$view = t('View');
|
||||
$flush = t('Flush');
|
||||
if ($site_info['data']) {
|
||||
$view = l(t('View'), 'admin/reports/prod-monitor/site/'.$id, array('attributes' => array('title' => t('View'))));
|
||||
$flush = l(t('Flush'), 'admin/reports/prod-monitor/site/'.$id.'/flush', array('attributes' => array('title' => t('Flush'))));
|
||||
}
|
||||
|
||||
$update_status = _prod_monitor_get_update_status($id);
|
||||
$updates = _prod_monitor_generate_updates_link($id, $update_status);
|
||||
|
||||
if (!empty($site_info['status'])) {
|
||||
$title = t(ucwords($site_info['status']));
|
||||
$status = array('data' => '<strong>'.l($title, 'admin/reports/prod-monitor/site/'.$id, array('attributes' => array('title' => $title, 'class' => $site_info['status']))).'</strong>', 'class' => array($site_info['status']));
|
||||
}
|
||||
else {
|
||||
$status = '';
|
||||
}
|
||||
|
||||
// Actually compose the rows.
|
||||
$row = array(
|
||||
'data' => array(
|
||||
_prod_monitor_sanitize_url($site_info['url']),
|
||||
(!$site_info['data']) ? t('Not yet retrieved.') : t('Stored.'),
|
||||
$status,
|
||||
$updates,
|
||||
$site_info['added'],
|
||||
(!$site_info['lastupdate']) ? t('Not yet updated.') : $site_info['lastupdate'],
|
||||
/* Compose links. */
|
||||
$view,
|
||||
l(t('Edit'), 'admin/reports/prod-monitor/site/'.$id.'/edit', array('query' => $home, 'attributes' => array('title' => t('Edit')))),
|
||||
l(t('Fetch data'), 'admin/reports/prod-monitor/site/'.$id.'/fetch', array('attributes' => array('title' => t('Fetch & View')))),
|
||||
$flush,
|
||||
l(t('Delete'), 'admin/reports/prod-monitor/site/'.$id.'/delete', array('attributes' => array('title' => t('Delete')))),
|
||||
),
|
||||
'class' => array($site_info['status']),
|
||||
);
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return theme('table', array('header' => $headers, 'rows' => $rows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation function
|
||||
*/
|
||||
function prod_monitor_overview_form_validate($form, &$form_state) {
|
||||
if (!preg_match('/^https?:\/\/.*/i', $form_state['values']['url'])) {
|
||||
form_set_error('url', t('The url must start with a valid protocol: either http:// or https://'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit function
|
||||
*/
|
||||
function prod_monitor_overview_form_submit($form, &$form_state) {
|
||||
switch ($form_state['values']['op']) {
|
||||
case t('Get settings'):
|
||||
case t('Retry'):
|
||||
// Make sure the storage is not empty so we go to step 2
|
||||
$form_state['storage']['get_settings'] = TRUE;
|
||||
$form_state['rebuild'] = TRUE;
|
||||
break;
|
||||
|
||||
case t('Add site'):
|
||||
case t('Save site'):
|
||||
// Prevent from ending on step 2 again.
|
||||
unset($form_state['storage']['get_settings']);
|
||||
$site = new stdClass();
|
||||
|
||||
// Edit situation, so force an update.
|
||||
if (isset($form_state['values']['site_id']) && is_numeric($form_state['values']['site_id'])) {
|
||||
$update = array('id');
|
||||
$site->id = $form_state['values']['site_id'];
|
||||
}
|
||||
else {
|
||||
// Add situation, insert.
|
||||
$update = array();
|
||||
$site->added = time();
|
||||
}
|
||||
|
||||
$site->url = $form_state['values']['url'];
|
||||
|
||||
// Get enabled checks.
|
||||
$checks = array();
|
||||
foreach ($form_state['values']['monitor_settings'] as $set => $data) {
|
||||
foreach ($data as $check => $value) {
|
||||
if ($value) {
|
||||
$checks[$set][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get enabled performance logs.
|
||||
if (!empty($form_state['values']['performance'])) {
|
||||
$checks['perf_data'] = array();
|
||||
foreach ($form_state['values']['performance'] as $value) {
|
||||
if ($value) {
|
||||
$checks['perf_data'][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare settings data.
|
||||
$site->settings = serialize(
|
||||
array(
|
||||
'api_key' => $form_state['values']['api_key'],
|
||||
'functions' => $form_state['storage']['functions'],
|
||||
'checks' => $checks,
|
||||
)
|
||||
);
|
||||
|
||||
$result = drupal_write_record('prod_monitor_sites', $site, $update);
|
||||
|
||||
if ($result) {
|
||||
drupal_set_message(t('Website %url correctly saved.', array('%url' => $site->url)));
|
||||
if ($form_state['values']['fetch']) {
|
||||
$site_info = _prod_monitor_get_site($site->id, TRUE);
|
||||
_prod_monitor_retrieve_data($site->id, $site_info, TRUE);
|
||||
$form_state['redirect'] = 'admin/reports/prod-monitor/site/'.$site->id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Website %url not saved! Please try again.', array('%url' => $site->url)), 'error');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to fetch site data
|
||||
*/
|
||||
function prod_monitor_fetch_data($id) {
|
||||
$site_info = _prod_monitor_get_site($id, TRUE);
|
||||
_prod_monitor_retrieve_data($id, $site_info, TRUE);
|
||||
drupal_goto('admin/reports/prod-monitor/site/'.$id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to delete a site's data
|
||||
*/
|
||||
function prod_monitor_flush_form($form, &$form_state, $id) {
|
||||
$form = array();
|
||||
|
||||
$form['site_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $id,
|
||||
);
|
||||
|
||||
$url = _prod_monitor_get_url($id);
|
||||
|
||||
$form['url'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $url,
|
||||
);
|
||||
|
||||
return confirm_form($form, t('Are you sure you wish to delete all fetched data for %url?', array('%url' => $url)), 'admin/reports/prod-monitor', t('Note that the module update status data will not be flushed!').'<br />'.t('This action cannot be undone.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a site's data
|
||||
*/
|
||||
function prod_monitor_flush_form_submit($form, &$form_state) {
|
||||
if ($form_state['values']['site_id']) {
|
||||
$result = _prod_monitor_flush_data($form_state['values']['site_id']);
|
||||
if ($result === FALSE) {
|
||||
drupal_set_message(t('Unable to flush data for %url!', array('%url' => $form_state['values']['url'])), 'error');
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Stored data for %url successfully flushed.', array('%url' => $form_state['values']['url'])));
|
||||
}
|
||||
}
|
||||
$form_state['redirect'] = 'admin/reports/prod-monitor';
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to delete a site
|
||||
*/
|
||||
function prod_monitor_delete_form($form, &$form_state, $id) {
|
||||
$form = array();
|
||||
|
||||
$form['site_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $id,
|
||||
);
|
||||
|
||||
$url = _prod_monitor_get_url($id);
|
||||
|
||||
$form['url'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $url,
|
||||
);
|
||||
|
||||
return confirm_form($form, t('Are you sure you wish to delete the website %url?', array('%url' => $url)), 'admin/reports/prod-monitor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a site
|
||||
*/
|
||||
function prod_monitor_delete_form_submit($form, &$form_state) {
|
||||
if ($form_state['values']['site_id']) {
|
||||
$result = _prod_monitor_delete_site($form_state['values']['site_id']);
|
||||
if ($result) {
|
||||
drupal_set_message(t('Website %url successfully deleted.', array('%url' => $form_state['values']['url'])));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Unable to delete %url!', array('%url' => $form_state['values']['url'])), 'error');
|
||||
}
|
||||
}
|
||||
$form_state['redirect'] = 'admin/reports/prod-monitor';
|
||||
}
|
||||
|
||||
function _prod_monitor_update_data_form($form, $form_state, $id, $site_info) {
|
||||
$form['site_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $id,
|
||||
);
|
||||
|
||||
$form['site_info'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $site_info,
|
||||
);
|
||||
|
||||
// Markup field for proper styling.
|
||||
$form['buttons'] = array(
|
||||
'#type' => 'markup',
|
||||
'#prefix' => '<div class="buttons">',
|
||||
'#value' => ' ',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
$form['buttons']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Fetch data now'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function _prod_monitor_update_data_form_submit($form, &$form_state) {
|
||||
_prod_monitor_retrieve_data($form_state['values']['site_id'], $form_state['values']['site_info'], TRUE);
|
||||
}
|
||||
|
@@ -0,0 +1,363 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Returns HTML for the project status report.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - data: An array of data about each project's status.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_prod_monitor_update_report($variables) {
|
||||
$id = $variables['id'];
|
||||
$last = $variables['last'];
|
||||
$data = $variables['data'];
|
||||
|
||||
if (!is_array($data)) {
|
||||
$output = '<p>'. $data .'</p>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
$output = '<div class="update checked">'. ($last ? t('Last checked: @time ago', array('@time' => format_interval(time() - $last))) : t('Last checked: never'));
|
||||
$output .= ' <span class="check-manually">('. l(t('Check manually'), 'admin/reports/prod-monitor/site/'.$id.'/update-check') .')</span>';
|
||||
$output .= "</div>\n";
|
||||
|
||||
$header = array();
|
||||
$rows = array();
|
||||
|
||||
$notification_level = variable_get('update_notification_threshold', 'all');
|
||||
|
||||
// Create an array of status values keyed by module or theme name, since
|
||||
// we'll need this while generating the report if we have to cross reference
|
||||
// anything (e.g. subthemes which have base themes missing an update).
|
||||
foreach ($data as $project) {
|
||||
foreach ($project['includes'] as $key => $name) {
|
||||
$status[$key] = $project['status'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $project) {
|
||||
switch ($project['status']) {
|
||||
case UPDATE_CURRENT:
|
||||
$class = 'ok';
|
||||
$icon = theme('image', array('path' => 'misc/watchdog-ok.png', 'width' => 18, 'height' => 18, 'alt' => t('ok'), 'title' => t('ok')));
|
||||
break;
|
||||
case UPDATE_UNKNOWN:
|
||||
case UPDATE_FETCH_PENDING:
|
||||
case UPDATE_NOT_FETCHED:
|
||||
$class = 'unknown';
|
||||
$icon = theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('warning'), 'title' => t('warning')));
|
||||
break;
|
||||
case UPDATE_NOT_SECURE:
|
||||
case UPDATE_REVOKED:
|
||||
case UPDATE_NOT_SUPPORTED:
|
||||
$class = 'error';
|
||||
$icon = theme('image', array('path' => 'misc/watchdog-error.png', 'width' => 18, 'height' => 18, 'alt' => t('error'), 'title' => t('error')));
|
||||
break;
|
||||
case UPDATE_NOT_CHECKED:
|
||||
case UPDATE_NOT_CURRENT:
|
||||
default:
|
||||
$class = 'warning';
|
||||
$icon = theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('warning'), 'title' => t('warning')));
|
||||
break;
|
||||
}
|
||||
|
||||
$row = '<div class="version-status">';
|
||||
$status_label = theme('prod_monitor_update_status_label', array('status' => $project['status']));
|
||||
$row .= !empty($status_label) ? $status_label : check_plain($project['reason']);
|
||||
$row .= '<span class="icon">' . $icon . '</span>';
|
||||
$row .= "</div>\n";
|
||||
|
||||
$row .= '<div class="project">';
|
||||
if (isset($project['title'])) {
|
||||
if (isset($project['link'])) {
|
||||
$row .= l($project['title'], $project['link']);
|
||||
}
|
||||
else {
|
||||
$row .= check_plain($project['title']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$row .= check_plain($project['name']);
|
||||
}
|
||||
$row .= ' ' . check_plain($project['existing_version']);
|
||||
if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) {
|
||||
$row .= ' <span class="version-date">(' . format_date($project['datestamp'], 'custom', 'Y-M-d') . ')</span>';
|
||||
}
|
||||
$row .= "</div>\n";
|
||||
|
||||
$versions_inner = '';
|
||||
$security_class = array();
|
||||
$version_class = array();
|
||||
if (isset($project['recommended'])) {
|
||||
if ($project['status'] != UPDATE_CURRENT || $project['existing_version'] !== $project['recommended']) {
|
||||
|
||||
// First, figure out what to recommend.
|
||||
// If there's only 1 security update and it has the same version we're
|
||||
// recommending, give it the same CSS class as if it was recommended,
|
||||
// but don't print out a separate "Recommended" line for this project.
|
||||
if (!empty($project['security updates']) && count($project['security updates']) == 1 && $project['security updates'][0]['version'] === $project['recommended']) {
|
||||
$security_class[] = 'version-recommended';
|
||||
$security_class[] = 'version-recommended-strong';
|
||||
}
|
||||
else {
|
||||
$version_class[] = 'version-recommended';
|
||||
// Apply an extra class if we're displaying both a recommended
|
||||
// version and anything else for an extra visual hint.
|
||||
if ($project['recommended'] !== $project['latest_version']
|
||||
|| !empty($project['also'])
|
||||
|| ($project['install_type'] == 'dev'
|
||||
&& isset($project['dev_version'])
|
||||
&& $project['latest_version'] !== $project['dev_version']
|
||||
&& $project['recommended'] !== $project['dev_version'])
|
||||
|| (isset($project['security updates'][0])
|
||||
&& $project['recommended'] !== $project['security updates'][0])
|
||||
) {
|
||||
$version_class[] = 'version-recommended-strong';
|
||||
}
|
||||
$versions_inner .= theme('prod_monitor_update_version', array('version' => $project['releases'][$project['recommended']], 'tag' => t('Recommended version:'), 'class' => $version_class));
|
||||
}
|
||||
|
||||
// Now, print any security updates.
|
||||
if (!empty($project['security updates'])) {
|
||||
$security_class[] = 'version-security';
|
||||
foreach ($project['security updates'] as $security_update) {
|
||||
$versions_inner .= theme('prod_monitor_update_version', array('version' => $security_update, 'tag' => t('Security update:'), 'class' => $security_class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($project['recommended'] !== $project['latest_version']) {
|
||||
$versions_inner .= theme('prod_monitor_update_version', array('version' => $project['releases'][$project['latest_version']], 'tag' => t('Latest version:'), 'class' => array('version-latest')));
|
||||
}
|
||||
if ($project['install_type'] == 'dev'
|
||||
&& $project['status'] != UPDATE_CURRENT
|
||||
&& isset($project['dev_version'])
|
||||
&& $project['recommended'] !== $project['dev_version']) {
|
||||
$versions_inner .= theme('prod_monitor_update_version', array('version' => $project['releases'][$project['dev_version']], 'tag' => t('Development version:'), 'class' => array('version-latest')));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($project['also'])) {
|
||||
foreach ($project['also'] as $also) {
|
||||
$versions_inner .= theme('prod_monitor_update_version', array('version' => $project['releases'][$also], 'tag' => t('Also available:'), 'class' => array('version-also-available')));
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($versions_inner)) {
|
||||
$row .= "<div class=\"versions\">\n" . $versions_inner . "</div>\n";
|
||||
}
|
||||
$row .= "<div class=\"info\">\n";
|
||||
if (!empty($project['extra'])) {
|
||||
$row .= '<div class="extra">' . "\n";
|
||||
foreach ($project['extra'] as $key => $value) {
|
||||
$row .= '<div class="' . implode(' ', $value['class']) . '">';
|
||||
$row .= check_plain($value['label']) . ': ';
|
||||
$row .= drupal_placeholder($value['data']);
|
||||
$row .= "</div>\n";
|
||||
}
|
||||
$row .= "</div>\n"; // extra div.
|
||||
}
|
||||
|
||||
$row .= '<div class="includes">';
|
||||
sort($project['includes']);
|
||||
if (!empty($project['disabled'])) {
|
||||
sort($project['disabled']);
|
||||
// Make sure we start with a clean slate for each project in the report.
|
||||
$includes_items = array();
|
||||
$row .= t('Includes:');
|
||||
$includes_items[] = t('Enabled: %includes', array('%includes' => implode(', ', $project['includes'])));
|
||||
$includes_items[] = t('Disabled: %disabled', array('%disabled' => implode(', ', $project['disabled'])));
|
||||
$row .= theme('item_list', array('items' => $includes_items));
|
||||
}
|
||||
else {
|
||||
$row .= t('Includes: %includes', array('%includes' => implode(', ', $project['includes'])));
|
||||
}
|
||||
$row .= "</div>\n";
|
||||
|
||||
if (!empty($project['base_themes'])) {
|
||||
$row .= '<div class="basethemes">';
|
||||
asort($project['base_themes']);
|
||||
$base_themes = array();
|
||||
foreach ($project['base_themes'] as $base_key => $base_theme) {
|
||||
switch ($status[$base_key]) {
|
||||
case UPDATE_NOT_SECURE:
|
||||
case UPDATE_REVOKED:
|
||||
case UPDATE_NOT_SUPPORTED:
|
||||
$base_themes[] = t('%base_theme (!base_label)', array('%base_theme' => $base_theme, '!base_label' => theme('update_status_label', array('status' => $status[$base_key]))));
|
||||
break;
|
||||
|
||||
default:
|
||||
$base_themes[] = drupal_placeholder($base_theme);
|
||||
}
|
||||
}
|
||||
$row .= t('Depends on: !basethemes', array('!basethemes' => implode(', ', $base_themes)));
|
||||
$row .= "</div>\n";
|
||||
}
|
||||
|
||||
if (!empty($project['sub_themes'])) {
|
||||
$row .= '<div class="subthemes">';
|
||||
sort($project['sub_themes']);
|
||||
$row .= t('Required by: %subthemes', array('%subthemes' => implode(', ', $project['sub_themes'])));
|
||||
$row .= "</div>\n";
|
||||
}
|
||||
|
||||
$row .= "</div>\n"; // info div.
|
||||
|
||||
if (!isset($rows[$project['project_type']])) {
|
||||
$rows[$project['project_type']] = array();
|
||||
}
|
||||
$row_key = isset($project['title']) ? drupal_strtolower($project['title']) : drupal_strtolower($project['name']);
|
||||
$rows[$project['project_type']][$row_key] = array(
|
||||
'class' => array($class),
|
||||
'data' => array($row),
|
||||
);
|
||||
}
|
||||
|
||||
$project_types = array(
|
||||
'core' => t('Drupal core'),
|
||||
'module' => t('Modules'),
|
||||
'theme' => t('Themes'),
|
||||
'module-disabled' => t('Disabled modules'),
|
||||
'theme-disabled' => t('Disabled themes'),
|
||||
);
|
||||
foreach ($project_types as $type_name => $type_label) {
|
||||
if (!empty($rows[$type_name])) {
|
||||
ksort($rows[$type_name]);
|
||||
$output .= "\n<h3>" . $type_label . "</h3>\n";
|
||||
$output .= theme('table', array('header' => $header, 'rows' => $rows[$type_name], 'attributes' => array('class' => array('update'))));
|
||||
}
|
||||
}
|
||||
drupal_add_css(drupal_get_path('module', 'update') . '/update.css');
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for a label to display for a project's update status.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - status: The integer code for a project's current update status.
|
||||
*
|
||||
* @see update_calculate_project_data()
|
||||
*/
|
||||
function theme_prod_monitor_update_status_label($variables) {
|
||||
switch ($variables['status']) {
|
||||
case UPDATE_NOT_SECURE:
|
||||
return '<span class="security-error">' . t('Security update required!') . '</span>';
|
||||
|
||||
case UPDATE_REVOKED:
|
||||
return '<span class="revoked">' . t('Revoked!') . '</span>';
|
||||
|
||||
case UPDATE_NOT_SUPPORTED:
|
||||
return '<span class="not-supported">' . t('Not supported!') . '</span>';
|
||||
|
||||
case UPDATE_NOT_CURRENT:
|
||||
return '<span class="not-current">' . t('Update available') . '</span>';
|
||||
|
||||
case UPDATE_CURRENT:
|
||||
return '<span class="current">' . t('Up to date') . '</span>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for the version display of a project.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - version: An array of data about the latest released version, containing:
|
||||
* - version: The version number.
|
||||
* - release_link: The URL for the release notes.
|
||||
* - date: The date of the release.
|
||||
* - download_link: The URL for the downloadable file.
|
||||
* - tag: The title of the project.
|
||||
* - class: A string containing extra classes for the wrapping table.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_prod_monitor_update_version($variables) {
|
||||
$version = $variables['version'];
|
||||
$tag = $variables['tag'];
|
||||
$class = implode(' ', $variables['class']);
|
||||
|
||||
$output = '';
|
||||
$output .= '<table class="version ' . $class . '">';
|
||||
$output .= '<tr>';
|
||||
$output .= '<td class="version-title">' . $tag . "</td>\n";
|
||||
$output .= '<td class="version-details">';
|
||||
$output .= l($version['version'], $version['release_link']);
|
||||
$output .= ' <span class="version-date">(' . format_date($version['date'], 'custom', 'Y-M-d') . ')</span>';
|
||||
$output .= "</td>\n";
|
||||
$output .= '<td class="version-links">';
|
||||
$links = array();
|
||||
$links['update-download'] = array(
|
||||
'title' => t('Download'),
|
||||
'href' => $version['download_link'],
|
||||
);
|
||||
$links['update-release-notes'] = array(
|
||||
'title' => t('Release notes'),
|
||||
'href' => $version['release_link'],
|
||||
);
|
||||
$output .= theme('links__update_version', array('links' => $links));
|
||||
$output .= '</td>';
|
||||
$output .= '</tr>';
|
||||
$output .= "</table>\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for the status report.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - requirements: An array of requirements.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_prod_monitor_status_report($variables) {
|
||||
$requirements = $variables['requirements'];
|
||||
|
||||
$severities = array(
|
||||
PROD_MONITOR_REQUIREMENT_INFO => array(
|
||||
'title' => t('Info'),
|
||||
'class' => 'info',
|
||||
),
|
||||
PROD_MONITOR_REQUIREMENT_OK => array(
|
||||
'title' => t('OK'),
|
||||
'class' => 'ok',
|
||||
),
|
||||
PROD_MONITOR_REQUIREMENT_WARNING => array(
|
||||
'title' => t('Warning'),
|
||||
'class' => 'warning',
|
||||
),
|
||||
PROD_MONITOR_REQUIREMENT_ERROR => array(
|
||||
'title' => t('Error'),
|
||||
'class' => 'error',
|
||||
),
|
||||
);
|
||||
$output = '<table class="system-status-report">';
|
||||
|
||||
foreach ($requirements as $requirement) {
|
||||
if (empty($requirement['#type'])) {
|
||||
$severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : 0];
|
||||
$severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
|
||||
|
||||
// Output table row(s)
|
||||
if (!empty($requirement['description'])) {
|
||||
$output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
|
||||
$output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
|
||||
}
|
||||
else {
|
||||
$output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</table>';
|
||||
|
||||
return $output;
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ALL code here is taken from the Drupal core's update module and MODIFIED for
|
||||
* integration with prod_monitor. prod_check recommends that you turn the update
|
||||
* module off. prod_check provides the VERY BASIC functionality to get a list of
|
||||
* the installed modules with version info and transfer this to prod_monitor
|
||||
* over XMLRPC (alot of data here!) so that prod_monitor can do all the update
|
||||
* checking locally.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Taken from D6 Core(!): modules/update/update.fetch.inc, line 25 and MODIFIED!
|
||||
* We chose to stick with the regular process instead of the more optimised D7
|
||||
* way. The D7 way is perfect when the update.module checks the site it is
|
||||
* running on, but we need to check several sites on an entirely different setup.
|
||||
*
|
||||
* Fetch project info via XML from a central server.
|
||||
*/
|
||||
function _prod_monitor_update_refresh($id, $projects, $site_key) {
|
||||
global $base_url;
|
||||
$fail = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
// contains update_xml_parser class
|
||||
module_load_include('inc', 'update', 'update.fetch');
|
||||
|
||||
$available = array();
|
||||
|
||||
// As replacement for DRUPAL_CORE_COMPATIBILITY since prod_check and
|
||||
// prod_monitor should be site independant.
|
||||
$core = explode('.', $projects['drupal']['info']['version']);
|
||||
$core = $core[0].'.x';
|
||||
|
||||
$max_fetch_attempts = UPDATE_MAX_FETCH_ATTEMPTS;
|
||||
|
||||
// Prepare object to store generated data to DB.
|
||||
$modules = new stdClass();
|
||||
$modules->id = $id;
|
||||
|
||||
foreach ($projects as $key => $project) {
|
||||
$url = _prod_monitor_update_build_fetch_url($project, $site_key, $core);
|
||||
$fetch_url_base = _prod_monitor_update_get_fetch_url_base($project);
|
||||
if (empty($fail[$fetch_url_base]) || count($fail[$fetch_url_base]) < $max_fetch_attempts) {
|
||||
$xml = drupal_http_request($url);
|
||||
if (isset($xml->data)) {
|
||||
$data = update_parse_xml($xml->data);
|
||||
$available[$data['short_name']] = $data;
|
||||
}
|
||||
else {
|
||||
// Connection likely broken; prepare to give up.
|
||||
$fail[$fetch_url_base][$key] = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Didn't bother trying to fetch.
|
||||
$fail[$fetch_url_base][$key] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($available) && is_array($available)) {
|
||||
// Record the projects where we failed to fetch data.
|
||||
foreach ($fail as $fetch_url_base => $failures) {
|
||||
foreach ($failures as $key => $value) {
|
||||
$available[$key]['project_status'] = 'not-fetched';
|
||||
}
|
||||
}
|
||||
$modules->available = serialize($available);
|
||||
watchdog('prod_monitor', 'Attempted to fetch information about all available new releases and updates for %link.', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_NOTICE, l(t('view'), 'admin/reports/prod-monitor/site/'.$id.'/view/updates'));
|
||||
}
|
||||
else {
|
||||
watchdog('prod_monitor', 'Unable to fetch any information about available new releases and updates for %link.', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR, l(t('view'), 'admin/reports/prod-monitor/site/'.$id.'/view/updates'));
|
||||
}
|
||||
// Whether this worked or not, we did just (try to) check for updates.
|
||||
$modules->lastupdate = time();
|
||||
$result = drupal_write_record('prod_monitor_site_modules', $modules, array('id'));
|
||||
if (!$result) {
|
||||
watchdog('prod_monitor', 'Could not update module data for %link', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR);
|
||||
}
|
||||
|
||||
return $available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.fetch.inc, line 266 and MODIFIED!
|
||||
*
|
||||
* This figures out the right URL to use, based on the project's .info file
|
||||
* and the global defaults. Appends optional query arguments when the site is
|
||||
* configured to report usage stats.
|
||||
*
|
||||
* @param $project
|
||||
* The array of project information from update_get_projects().
|
||||
* @param $site_key
|
||||
* The anonymous site key hash (optional).
|
||||
*
|
||||
* @see update_fetch_data()
|
||||
* @see _update_process_fetch_task()
|
||||
* @see update_get_projects()
|
||||
*/
|
||||
function _prod_monitor_update_build_fetch_url($project, $site_key = '', $core) {
|
||||
$name = $project['name'];
|
||||
$url = _prod_monitor_update_get_fetch_url_base($project);
|
||||
$url .= '/'. $name .'/'. $core;
|
||||
// Only append a site_key and the version information if we have a site_key
|
||||
// in the first place, and if this is not a disabled module or theme. We do
|
||||
// not want to record usage statistics for disabled code.
|
||||
if (!empty($site_key) && (strpos($project['project_type'], 'disabled') === FALSE)) {
|
||||
$url .= (strpos($url, '?') === TRUE) ? '&' : '?';
|
||||
$url .= 'site_key=';
|
||||
$url .= rawurlencode($site_key);
|
||||
if (!empty($project['info']['version'])) {
|
||||
$url .= '&version=';
|
||||
$url .= rawurlencode($project['info']['version']);
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.fetch.inc, line 297 and MODIFIED!
|
||||
*
|
||||
* Return the base of the URL to fetch available update data for a project.
|
||||
*
|
||||
* @param $project
|
||||
* The array of project information from update_get_projects().
|
||||
* @return
|
||||
* The base of the URL used for fetching available update data. This does
|
||||
* not include the path elements to specify a particular project, version,
|
||||
* site_key, etc.
|
||||
*
|
||||
* @see _update_build_fetch_url()
|
||||
*/
|
||||
function _prod_monitor_update_get_fetch_url_base($project) {
|
||||
return isset($project['info']['project status url']) ? $project['info']['project status url'] : UPDATE_DEFAULT_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from Core: modules/update/update.compare.inc, line 300 and MODIFIED!
|
||||
*
|
||||
* Calculate the current update status of all projects on the site.
|
||||
*
|
||||
* The results of this function are expensive to compute, especially on sites
|
||||
* with lots of modules or themes, since it involves a lot of comparisons and
|
||||
* other operations. Therefore, we cache the results into the {cache_update}
|
||||
* table using the 'update_project_data' cache ID. However, since this is not
|
||||
* the data about available updates fetched from the network, it is ok to
|
||||
* invalidate it somewhat quickly. If we keep this data for very long, site
|
||||
* administrators are more likely to see incorrect results if they upgrade to
|
||||
* a newer version of a module or theme but do not visit certain pages that
|
||||
* automatically clear this cache.
|
||||
*
|
||||
* @param array $available
|
||||
* Data about available project releases.
|
||||
*
|
||||
* @see update_get_available()
|
||||
* @see update_get_projects()
|
||||
* @see update_process_project_info()
|
||||
* @see update_project_cache()
|
||||
*/
|
||||
function _prod_monitor_calculate_project_data($id, $projects, $available) {
|
||||
module_load_include('inc', 'update', 'update.compare');
|
||||
update_process_project_info($projects);
|
||||
foreach ($projects as $project => $project_info) {
|
||||
if (isset($available[$project])) {
|
||||
update_calculate_project_update_status($project, $projects[$project], $available[$project]);
|
||||
}
|
||||
else {
|
||||
$projects[$project]['status'] = UPDATE_UNKNOWN;
|
||||
$projects[$project]['reason'] = t('No available releases found');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to flag a security update warning.
|
||||
// Prepare object to store generated data to DB.
|
||||
$modules = new stdClass();
|
||||
$modules->id = $id;
|
||||
// Assume there are no updates.
|
||||
$modules->updates = 1;
|
||||
// Check final status of each project.
|
||||
foreach ($projects as $project => $project_info) {
|
||||
switch ($project_info['status']) {
|
||||
case UPDATE_NOT_SECURE:
|
||||
case UPDATE_NOT_SUPPORTED:
|
||||
case UPDATE_REVOKED:
|
||||
$modules->updates = 3;
|
||||
// Stop the foreach loop as well.
|
||||
break 2;
|
||||
case UPDATE_NOT_CURRENT:
|
||||
if ($modules->updates < 2) {
|
||||
$modules->updates = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$result = drupal_write_record('prod_monitor_site_modules', $modules, array('id'));
|
||||
if (!$result) {
|
||||
watchdog('prod_monitor', 'Could not update module security status for %link', array('%link' => _prod_monitor_get_url($id)), WATCHDOG_ERROR);
|
||||
}
|
||||
|
||||
return $projects;
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*--------------------------------------------------------------------
|
||||
* JQuery Plugin: "EqualHeights" & "EqualWidths"
|
||||
* by: Scott Jehl, Todd Parker, Maggie Costello Wachs (http://www.filamentgroup.com)
|
||||
*
|
||||
* Copyright (c) 2007 Filament Group
|
||||
* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
|
||||
*
|
||||
* Description: Compares the heights or widths of the top-level children of a provided element
|
||||
and sets their min-height to the tallest height (or width to widest width). Sets in em units
|
||||
by default if pxToEm() method is available.
|
||||
* Dependencies: jQuery library, pxToEm method (article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/)
|
||||
* Usage Example: $(element).equalHeights();
|
||||
Optional: to set min-height in px, pass a true argument: $(element).equalHeights(true);
|
||||
* Version: 2.0, 07.24.2008
|
||||
* Changelog:
|
||||
* 08.02.2007 initial Version 1.0
|
||||
* 07.24.2008 v 2.0 - added support for widths
|
||||
--------------------------------------------------------------------*/
|
||||
(function ($) {
|
||||
$.fn.equalHeights = function(px) {
|
||||
$(this).each(function(){
|
||||
var currentTallest = 0;
|
||||
$(this).children().each(function(i){
|
||||
if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
|
||||
});
|
||||
if (!px || !Number.prototype.pxToEm) currentTallest = currentTallest.pxToEm(); //use ems unless px is specified
|
||||
// for ie6, set height since min-height isn't supported
|
||||
if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'height': currentTallest}); }
|
||||
$(this).children().css({'min-height': currentTallest});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
// just in case you need it...
|
||||
$.fn.equalWidths = function(px) {
|
||||
$(this).each(function(){
|
||||
var currentWidest = 0;
|
||||
$(this).children().each(function(i){
|
||||
if($(this).width() > currentWidest) { currentWidest = $(this).width(); }
|
||||
});
|
||||
if(!px || !Number.prototype.pxToEm) currentWidest = currentWidest.pxToEm(); //use ems unless px is specified
|
||||
// for ie6, set width since min-width isn't supported
|
||||
if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'width': currentWidest}); }
|
||||
$(this).children().css({'min-width': currentWidest});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* javascript method: "pxToEm"
|
||||
* by:
|
||||
Scott Jehl (scott@filamentgroup.com)
|
||||
Maggie Wachs (maggie@filamentgroup.com)
|
||||
http://www.filamentgroup.com
|
||||
*
|
||||
* Copyright (c) 2008 Filament Group
|
||||
* Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
|
||||
*
|
||||
* Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.
|
||||
* Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
|
||||
* Demo: http://www.filamentgroup.com/examples/pxToEm/
|
||||
*
|
||||
* Options:
|
||||
scope: string or jQuery selector for font-size scoping
|
||||
reverse: Boolean, true reverses the conversion to em-px
|
||||
* Dependencies: jQuery library
|
||||
* Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
|
||||
*
|
||||
* Version: 2.0, 08.01.2008
|
||||
* Changelog:
|
||||
* 08.02.2007 initial Version 1.0
|
||||
* 08.01.2008 - fixed font-size calculation for IE
|
||||
--------------------------------------------------------------------*/
|
||||
(function ($) {
|
||||
Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
|
||||
//set defaults
|
||||
settings = jQuery.extend({
|
||||
scope: 'body',
|
||||
reverse: false
|
||||
}, settings);
|
||||
|
||||
var pxVal = (this == '') ? 0 : parseFloat(this);
|
||||
var scopeVal;
|
||||
var getWindowWidth = function(){
|
||||
var de = document.documentElement;
|
||||
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
|
||||
};
|
||||
|
||||
/* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
|
||||
For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
|
||||
When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
|
||||
to get an accurate em value. */
|
||||
|
||||
if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
|
||||
var calcFontSize = function(){
|
||||
return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
|
||||
};
|
||||
scopeVal = calcFontSize();
|
||||
}
|
||||
else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
|
||||
|
||||
var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
|
||||
return result;
|
||||
};
|
||||
})(jQuery);
|
@@ -0,0 +1,12 @@
|
||||
(function ($) {
|
||||
|
||||
// Prod monitor settings page styling.
|
||||
Drupal.behaviors.prod_monitor = {
|
||||
attach: function(context, settings) {
|
||||
$('#prod-check-settings', context).equalHeights('px');
|
||||
$('#prod-check-settings', context).equalWidths('px');
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
@@ -0,0 +1,32 @@
|
||||
(function ($) {
|
||||
|
||||
// Trigger loading of the Google graphs.
|
||||
Drupal.behaviors.prod_monitor_init = {
|
||||
attach: function(context, settings) {
|
||||
var script = document.createElement('script');
|
||||
script.src = 'http://www.google.com/jsapi?callback=Drupal.behaviors.prod_monitor_performance.initGoogleDependencies';
|
||||
script.type = 'text/javascript';
|
||||
$('head').append(script);
|
||||
}
|
||||
}
|
||||
|
||||
// All functions used to setup and render the graphs.
|
||||
Drupal.behaviors.prod_monitor_performance = {
|
||||
initGoogleDependencies: function() {
|
||||
google.load('visualization', '1', {
|
||||
'callback':Drupal.behaviors.prod_monitor_performance.initGraphs,
|
||||
'packages':['annotatedtimeline']
|
||||
})
|
||||
},
|
||||
|
||||
initGraphs: function() {
|
||||
$('.performance-data').each(function() {
|
||||
var callback = $(this).attr('id').replace('-', '_');
|
||||
//console.log(Drupal.behaviors.prod_monitor_performance[callback]);
|
||||
Drupal.behaviors.prod_monitor_performance[callback]();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
|
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implementation of hook_drush_command().
|
||||
*/
|
||||
function prod_monitor_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$items['prod-monitor'] = array(
|
||||
'callback' => 'drush_prod_monitor_statusdetail',
|
||||
'description' => 'Display the Production Monitor status page',
|
||||
'aliases' => array('pmon'),
|
||||
'arguments' => array(
|
||||
'id' => "ID of the site to view it's status in detail.",
|
||||
),
|
||||
);
|
||||
$items['prod-monitor-updates'] = array(
|
||||
'callback' => '_drush_prod_monitor_updates',
|
||||
'description' => 'Display the update module status page',
|
||||
'aliases' => array('pmon-up'),
|
||||
'arguments' => array(
|
||||
'id' => 'ID of the site to view module updates for.',
|
||||
),
|
||||
'options' => array(
|
||||
'--check' => 'Check for module updates.'
|
||||
),
|
||||
);
|
||||
$items['prod-monitor-fetch'] = array(
|
||||
'callback' => 'drush_prod_monitor_fetch',
|
||||
'description' => 'Fetch the status information from a given remote site.',
|
||||
'aliases' => array('pmon-fe'),
|
||||
'arguments' => array(
|
||||
'id' => 'Space delemited list of site IDs to fetch all data for.',
|
||||
),
|
||||
);
|
||||
$items['prod-monitor-flush'] = array(
|
||||
'callback' => 'drush_prod_monitor_flush',
|
||||
'description' => "Remove all fetched data for a given site.",
|
||||
'aliases' => array('pmon-fl'),
|
||||
'arguments' => array(
|
||||
'id' => 'Space delemited list of site IDs to be flushed.',
|
||||
),
|
||||
);
|
||||
$items['prod-monitor-delete'] = array(
|
||||
'callback' => 'drush_prod_monitor_delsite',
|
||||
'description' => 'Completemy remove a site and all its data.',
|
||||
'aliases' => array('pmon-rm'),
|
||||
'arguments' => array(
|
||||
'id' => 'Space delemited list of site IDs to be deleted.',
|
||||
),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data callback.
|
||||
*/
|
||||
function drush_prod_monitor_fetch() {
|
||||
$args = func_get_args();
|
||||
foreach($args as $arg) {
|
||||
$site = _prod_monitor_get_site($arg);
|
||||
if (!empty($site['url'])) {
|
||||
$result = _prod_monitor_retrieve_data($arg, $site);
|
||||
$site['url'] = _prod_monitor_sanitize_url(rtrim($site['url'], '/'));
|
||||
if ($result === FALSE) {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('Unable to fetch data for').' '.$site['url'].'!');
|
||||
}
|
||||
else {
|
||||
drush_print(dt('Sucessfully fetched data for').' '.$site['url'].'.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('No site found with ID').' '.$arg.'!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush data callback.
|
||||
*/
|
||||
function drush_prod_monitor_flush() {
|
||||
$args = func_get_args();
|
||||
foreach ($args as $arg) {
|
||||
$url = _prod_monitor_get_url($arg);
|
||||
if (!empty($url)) {
|
||||
if (!drush_confirm(dt('Do you really want to flush all data for').' '.$url.'?')) {
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
$result = _prod_monitor_flush_data($arg);
|
||||
if ($result === FALSE) {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('Unable to flush data!'));
|
||||
}
|
||||
else {
|
||||
drush_print(dt('Stored data successfully flushed.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('No site found with ID').' '.$arg.'!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete site callback.
|
||||
*/
|
||||
function drush_prod_monitor_delsite() {
|
||||
$args = func_get_args();
|
||||
foreach ($args as $arg) {
|
||||
$url = _prod_monitor_get_url($arg);
|
||||
if (!empty($url)) {
|
||||
if (!drush_confirm(dt("Do you really want to delete").' '.$url.' '.dt('and all its data?'))) {
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
$result = _prod_monitor_delete_site($arg);
|
||||
if ($result === FALSE) {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('Unable to delete') . $url .'!');
|
||||
}
|
||||
else {
|
||||
drush_print(dt('Website successfully deleted.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('No site found with ID').' '.$arg.'!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Status page callback
|
||||
*/
|
||||
function drush_prod_monitor_statusdetail() {
|
||||
$args = func_get_args();
|
||||
|
||||
if (empty($args)) {
|
||||
_drush_prod_monitor_overview();
|
||||
}
|
||||
else if (is_numeric($args[0])) {
|
||||
_drush_prod_monitor_detail($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function _drush_prod_monitor_overview() {
|
||||
$sites = _prod_monitor_get_sites();
|
||||
|
||||
// Map error codes to shell colours.
|
||||
$severity = array (
|
||||
'ok' => '1;32',
|
||||
'warning' => '1;33',
|
||||
'error' => '1;31',
|
||||
);
|
||||
|
||||
$rows = array(array(
|
||||
dt('ID'),
|
||||
dt('URL'),
|
||||
dt('Data'),
|
||||
dt('Date added'),
|
||||
dt('Last update'),
|
||||
dt('Status'),
|
||||
));
|
||||
|
||||
// TODO: check why the colour coding messes up the tabs for the table
|
||||
// Worked around this by placing the status column last
|
||||
foreach ($sites as $id => $site_info) {
|
||||
$rows[] = array(
|
||||
$id,
|
||||
_prod_monitor_sanitize_url(rtrim($site_info['url'], '/')),
|
||||
(!$site_info['data']) ? dt('Empty') : t('Stored'),
|
||||
$site_info['added'],
|
||||
(!$site_info['lastupdate']) ? dt('Not yet updated') : $site_info['lastupdate'],
|
||||
"\033[".$severity[$site_info['status']].'m'.ucwords($site_info['status'])."\033[0m",
|
||||
);
|
||||
}
|
||||
drush_print("\033[1m".dt('Production Monitor status')."\033[0m\n", 1);
|
||||
if (count($rows) > 1) {
|
||||
drush_print_table($rows, TRUE);
|
||||
drush_print(dt('Use drush prod-monitor [id] to view the details of a specific site.'));
|
||||
}
|
||||
else {
|
||||
drush_print(dt('No sites added yet! Yo can add sites on admin/reports/prod-monitor.'), 1);
|
||||
}
|
||||
}
|
||||
|
||||
function _drush_prod_monitor_detail($id) {
|
||||
$site = _prod_monitor_get_site($id, TRUE);
|
||||
if (!isset($site['url'])) {
|
||||
drush_print("\033[1;31m".dt('Error:')." \033[0m".dt('No site found with ID').' '.$id.'!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Overall status block
|
||||
$block = array();
|
||||
$modules = _prod_monitor_get_site_modules($id);
|
||||
if(!empty($modules)) {
|
||||
$prod_mon = $site['data']['prod_mon'];
|
||||
|
||||
$cron = dt('Unknown');
|
||||
if (isset($prod_mon['prod_check_cron_last'])) {
|
||||
$cron = format_date($prod_mon['prod_check_cron_last'], 'large');
|
||||
}
|
||||
|
||||
$title = dt('Unknown');
|
||||
$color = "\033[0m";
|
||||
if ($modules['updates'] > 0) {
|
||||
switch ($modules['updates']) {
|
||||
case 1:
|
||||
$title = dt('None');
|
||||
break;
|
||||
case 2:
|
||||
$color = "\033[1;33m";
|
||||
$title = dt('Available');
|
||||
break;
|
||||
case 3:
|
||||
$color = "\033[1;31m";
|
||||
$title = dt('Security risk!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
$updates = $color . $title . "\033[0m";
|
||||
|
||||
// Construct block
|
||||
$block[] = array("\033[1m".dt('Overall status')."\033[0m");
|
||||
$block[] = array(
|
||||
dt('Drupal core version'),
|
||||
$modules['projects']['drupal']['info']['version'],
|
||||
);
|
||||
$block[] = array(
|
||||
dt('Last cron run'),
|
||||
$cron,
|
||||
);
|
||||
$block[] = array(
|
||||
dt('Updates'),
|
||||
$updates,
|
||||
);
|
||||
}
|
||||
// cleanup
|
||||
unset($site['data']['prod_mon']);
|
||||
unset($modules);
|
||||
|
||||
$functions = $site['settings']['functions'];
|
||||
$nodata = dt('No data recieved yet.');
|
||||
$url = rtrim($site['url'], '/');
|
||||
// Map error codes to shell colours.
|
||||
$severity = array (
|
||||
REQUIREMENT_INFO => '1',
|
||||
REQUIREMENT_OK => '1;32',
|
||||
REQUIREMENT_WARNING => '1;33',
|
||||
REQUIREMENT_ERROR => '1;31',
|
||||
);
|
||||
$error = 0;
|
||||
$rows = array();
|
||||
|
||||
|
||||
foreach ($functions as $set => $data) {
|
||||
if (isset($site['data'][$set])) {
|
||||
$rows[] = array('');
|
||||
$rows[] = array("\033[1m".dt($data['title'])."\033[0m");
|
||||
if (!empty($site['data'][$set])) {
|
||||
foreach ($site['data'][$set] as $check => $result) {
|
||||
$rows[] = array(
|
||||
$result['title'],
|
||||
"\033[".$severity[$result['severity']].'m'.strip_tags($result['value'])."\033[0m",
|
||||
);
|
||||
if ($error < $result['severity']) {
|
||||
$error = $result['severity'];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$rows[] = array($nodata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actual printing.
|
||||
drush_print("\033[1m".dt('Production Monitor status for').' '._prod_monitor_sanitize_url($url)."\033[0m", 1);
|
||||
if (!empty($block)) {
|
||||
drush_print_table($block);
|
||||
}
|
||||
if (!empty($rows)) {
|
||||
drush_print_table($rows);
|
||||
}
|
||||
else {
|
||||
drush_print($nodata, 1);
|
||||
}
|
||||
if ($error > 0) {
|
||||
// Would be cool if we could prefix the admin path with http://<host>/ so it
|
||||
// will become a clickable link in some terminals. Any ideas?
|
||||
drush_print("\033[1m".dt('Some errors were reported!')."\033[0m ".dt('Check the full status page on')." \033[1m".'admin/reports/prod-monitor/'.$id.'/view'."\033[0m ".dt('for details.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status page callback.
|
||||
*/
|
||||
function _drush_prod_monitor_updates() {
|
||||
$id = func_get_args();
|
||||
if (empty($id)) {
|
||||
drush_set_error('prod_monitor', dt('You must provide a site ID!'));
|
||||
return;
|
||||
}
|
||||
$id = $id['0'];
|
||||
|
||||
// Get module info.
|
||||
$modules = _prod_monitor_get_site_modules($id);
|
||||
$url = _prod_monitor_get_url($id);
|
||||
if (empty($modules)) {
|
||||
if (empty($url)) {
|
||||
drush_set_error('prod_monitor', dt('No site found with ID').' '. $id .'!');
|
||||
return;
|
||||
}
|
||||
else {
|
||||
drush_set_error('prod_monitor', dt('No module data found for') .' '. $url.'!');
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (empty($modules['available'])) {
|
||||
drush_set_error('prod_monitor', dt('No update data found for') .' '. $url.'!');
|
||||
// No data, ask for refresh.
|
||||
_drush_prod_monitor_update_refresh($id, $modules);
|
||||
}
|
||||
|
||||
// Refresh if user asked for it.
|
||||
if (drush_get_option('check')) {
|
||||
_drush_prod_monitor_update_refresh($id, $modules);
|
||||
}
|
||||
|
||||
$last = $modules['lastupdate'];
|
||||
module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
|
||||
$projects = _prod_monitor_calculate_project_data($id, $modules['projects'], $modules['available']);
|
||||
// Cleanup.
|
||||
unset($modules);
|
||||
|
||||
// Table headers.
|
||||
$rows[] = array(dt('Name'), dt('Installed version'), dt('Proposed version'), dt('Status'));
|
||||
|
||||
// Process releases, notifying user of status and building a list of proposed updates
|
||||
drush_include_engine('update_info', 'drupal', NULL, DRUSH_BASE_PATH . '/commands/pm/update_info');
|
||||
drush_include(DRUSH_BASE_PATH . '/commands/pm', 'updatecode.pm');
|
||||
$updateable = pm_project_filter($projects, $rows);
|
||||
|
||||
// Pipe preparation
|
||||
if (drush_get_context('DRUSH_PIPE')) {
|
||||
$pipe = "";
|
||||
foreach($projects as $project){
|
||||
$pipe .= $project['name']. " ";
|
||||
$pipe .= $project['existing_version']. " ";
|
||||
$pipe .= $project['candidate_version']. " ";
|
||||
$pipe .= str_replace(' ', '-', pm_update_filter($project)). "\n";
|
||||
}
|
||||
drush_print_pipe($pipe);
|
||||
// Automatically curtail update process if in pipe mode
|
||||
$updateable = FALSE;
|
||||
}
|
||||
|
||||
drush_print("\033[1m".dt('Module update status for').' '.$url."\033[0m", 1);
|
||||
drush_print(dt('Update information last refreshed:') .' '. ($last ? format_date($last) : dt('Never'))."\n", 1);
|
||||
drush_print_table($rows, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to refresh update status data.
|
||||
*/
|
||||
function _drush_prod_monitor_update_refresh($id, &$modules) {
|
||||
if (!drush_confirm(dt('Would you like to check for module updates now?'))) {
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
else {
|
||||
drush_print(dt('Refreshing update status information ...'));
|
||||
module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
|
||||
$result = _prod_monitor_update_refresh($id, $modules['projects'], $modules['sitekey']);
|
||||
if (!empty($result)) {
|
||||
drush_print();
|
||||
$modules['available'] = $result;
|
||||
$modules['lastupdate'] = time();
|
||||
}
|
||||
else {
|
||||
drush_set_error('prod_monitor', dt('Failed to refres update status information for') .' '. $url.'!');
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = Production monitor
|
||||
description = Monitors sites which make use of the Production check module.
|
||||
package = Monitoring
|
||||
core = 7.x
|
||||
configure = admin/reports/prod-monitor
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-10-01
|
||||
version = "7.x-1.7+0-dev"
|
||||
core = "7.x"
|
||||
project = "prod_check"
|
||||
datestamp = "1380623918"
|
||||
|
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implementation of hook_schema().
|
||||
*/
|
||||
function prod_monitor_schema() {
|
||||
return array(
|
||||
'prod_monitor_sites' => array(
|
||||
'description' => 'Holds all sites and data monitored by Production monitor.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'description' => 'The primary identifier for a site.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'url' => array(
|
||||
'description' => 'URL of the website to monitor.',
|
||||
'type' => 'text',
|
||||
'size' => 'normal',
|
||||
'default' => NULL,
|
||||
),
|
||||
'settings' => array(
|
||||
'description' => 'All settings related to the site.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'All data collected through XMLRPC in serialized form.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
),
|
||||
'added' => array(
|
||||
'description' => 'The Unix timestamp when the site was added.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0
|
||||
),
|
||||
'lastupdate' => array(
|
||||
'description' => 'The Unix timestamp when the data was most recently updated.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
),
|
||||
'prod_monitor_site_modules' => array(
|
||||
'description' => 'Holds all retrieved module data for a specific site.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'description' => 'The primary identifier for a site.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'projects' => array(
|
||||
'description' => 'All modules installed on the remote site.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
),
|
||||
'sitekey' => array(
|
||||
'description' => 'The unique key for the site.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'lastfetch' => array(
|
||||
'description' => 'The Unix timestamp when the data was most recently retrieved from the remote site.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'available' => array(
|
||||
'description' => 'All module updates available for the remote site.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
),
|
||||
'updates' => array(
|
||||
'description' => '0 = unknown, 1 = no updates, 2 = regular updates, 3 = security updates.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'lastupdate' => array(
|
||||
'description' => 'The Unix timestamp of the most recent module update check.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'prod_monitor_sites' => array(
|
||||
'table' => 'prod_monitor_sites',
|
||||
'columns' => array('id' => 'id'),
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
),
|
||||
'prod_monitor_site_performance' => array(
|
||||
'description' => 'Holds all retrieved performance data for a specific site.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'description' => 'The primary identifier for a site.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'module' => array(
|
||||
'description' => 'The module that reported the performance data.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'The actual performance data.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
),
|
||||
'annotation' => array(
|
||||
'description' => 'A short annotation to this specific dataset.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'default' => NULL,
|
||||
),
|
||||
'fetched' => array(
|
||||
'description' => 'The Unix timestamp when the data was retrieved from the remote site.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'prod_monitor_sites' => array(
|
||||
'table' => 'prod_monitor_sites',
|
||||
'columns' => array('id' => 'id'),
|
||||
),
|
||||
),
|
||||
'primary key' => array('id', 'module', 'fetched'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_requirements().
|
||||
*/
|
||||
function prod_monitor_requirements($phase) {
|
||||
$requirements = array();
|
||||
|
||||
switch ($phase) {
|
||||
case 'install':
|
||||
if (module_exists('update')) {
|
||||
$requirements['prod_monitor_update'] = array(
|
||||
'title' => t('Production monitor'),
|
||||
'value' => t('Update manager enabled.'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => t('You have enabled <em>Update manager</em>. You have to disable this module before enabling Production monitor!'),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of hook_uninstall().
|
||||
*/
|
||||
function prod_monitor_uninstall() {
|
||||
// This beats multiple variable_del() calls.
|
||||
db_delete('variable')->condition('name', 'prod_monitor\_%', 'LIKE')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the size of the settings field for table prod_monitor_sites.
|
||||
*/
|
||||
function prod_monitor_update_7100() {
|
||||
// Note http://drupal.org/node/150220 about not using hook_schema() here!
|
||||
db_change_field(
|
||||
'prod_monitor_sites',
|
||||
'settings',
|
||||
'settings',
|
||||
array(
|
||||
'description' => 'All settings related to the site.',
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'default' => NULL,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update xmlrpc settings for all sites.
|
||||
*/
|
||||
function prod_monitor_update_7101(&$sandbox) {
|
||||
$prefix = '_prod_check_';
|
||||
$ret = array();
|
||||
|
||||
// Update 5 sites at a time
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['current_site'] = 0;
|
||||
$sandbox['max'] = db_query('SELECT COUNT(DISTINCT id) FROM {prod_monitor_sites}')->fetchField();
|
||||
}
|
||||
|
||||
$sites = db_select('prod_monitor_sites', 'pms')
|
||||
->fields('pms', array('id', 'settings'))
|
||||
->condition('id', $sandbox['current_site'], '>')
|
||||
->range(0, 5)
|
||||
->orderBy('id', 'ASC')
|
||||
->execute();
|
||||
|
||||
foreach ($sites as $site) {
|
||||
$change = FALSE;
|
||||
$site->settings = unserialize($site->settings);
|
||||
// Adjust functions
|
||||
foreach ($site->settings['functions'] as $set => &$data) {
|
||||
foreach ($data['functions'] as $function => $title) {
|
||||
if (stripos($function, $prefix) === FALSE) {
|
||||
// Can't 'rename' keys, so we remove them and add a new one.
|
||||
unset($data['functions'][$function]);
|
||||
$data['functions'][$prefix . $function] = $title;
|
||||
$change = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prevent any chance of the next loop from going kaka cuckoo.
|
||||
// See warning at http://php.net/manual/en/control-structures.foreach.php
|
||||
unset($data, $function);
|
||||
// Adjust checks
|
||||
foreach ($site->settings['checks'] as $set => &$calls) {
|
||||
foreach ($calls as $key => &$function) {
|
||||
if (stripos($function, $prefix) === FALSE) {
|
||||
$function = $prefix . $function;
|
||||
$change = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only update record if there were changes. Added to prevent loss of data
|
||||
// when (accidentally) running this update twice.
|
||||
if ($change) {
|
||||
$site->settings = serialize($site->settings);
|
||||
db_update('prod_monitor_sites')
|
||||
->fields(array('settings' => $site->settings))
|
||||
->condition('id', $site->id)
|
||||
->execute();
|
||||
$msg = t('Successfully updated all remote site settings.');
|
||||
}
|
||||
else {
|
||||
$msg = t('No remote site settings found that needed an update.');
|
||||
}
|
||||
|
||||
$sandbox['progress']++;
|
||||
$sandbox['current_site'] = $site->id;
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
|
||||
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new table to store performance data.
|
||||
*/
|
||||
function prod_monitor_update_7102() {
|
||||
$schema = prod_monitor_schema();
|
||||
db_create_table('prod_monitor_site_performance', $schema['prod_monitor_site_performance']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update sitekey field in database to allow longer D7 values.
|
||||
*/
|
||||
function prod_check_update_7103() {
|
||||
// Note http://drupal.org/node/150220 about not using hook_schema() here!
|
||||
db_change_field(
|
||||
'prod_monitor_site_modules',
|
||||
'sitekey',
|
||||
'sitekey',
|
||||
array(
|
||||
'description' => 'The unique key for the site.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
)
|
||||
);
|
||||
}
|
@@ -0,0 +1,693 @@
|
||||
<?php
|
||||
/**
|
||||
* Our own definition of the core requirements states. These can be found in
|
||||
* includes/install.inc and are only available in hook_install(). That's why
|
||||
* we redefine them here (yes, it's double!). It's nicer than including the
|
||||
* install.inc file...
|
||||
* Let's see if this will pose problems...
|
||||
*/
|
||||
|
||||
define('PROD_MONITOR_REQUIREMENT_INFO', -1);
|
||||
define('PROD_MONITOR_REQUIREMENT_OK', 0);
|
||||
define('PROD_MONITOR_REQUIREMENT_WARNING', 1);
|
||||
define('PROD_MONITOR_REQUIREMENT_ERROR', 2);
|
||||
|
||||
/**
|
||||
* We do the same here for the update module constants: redefine them so that we
|
||||
* do not need to run the update module entirely!
|
||||
*/
|
||||
define('UPDATE_DEFAULT_URL', 'http://updates.drupal.org/release-history');
|
||||
define('UPDATE_NOT_SECURE', 1);
|
||||
define('UPDATE_REVOKED', 2);
|
||||
define('UPDATE_NOT_SUPPORTED', 3);
|
||||
define('UPDATE_NOT_CURRENT', 4);
|
||||
define('UPDATE_CURRENT', 5);
|
||||
define('UPDATE_NOT_CHECKED', -1);
|
||||
define('UPDATE_UNKNOWN', -2);
|
||||
define('UPDATE_NOT_FETCHED', -3);
|
||||
define('UPDATE_FETCH_PENDING', -4);
|
||||
define('UPDATE_MAX_FETCH_ATTEMPTS', 2);
|
||||
define('UPDATE_MAX_FETCH_TIME', 5);
|
||||
|
||||
/**
|
||||
* Implementation of hook_help().
|
||||
*/
|
||||
function prod_monitor_help($path, $arg) {
|
||||
$output = '';
|
||||
switch ($path) {
|
||||
case 'admin/help#prod_monitor':
|
||||
$output .= '<p>'.t('Production monitor is a module that can connect to the <strong>Production check</strong> module using <strong>XMLRPC</strong> and an <strong>API key</strong>. It will retrieve all specified data from the remote site to create a satus page and monitoring facility in a central place.').'<br />';
|
||||
$output .= t('You can add multiple sites and configure per site what data you wish (not) to monitor, allowing you to setup a central Drupal site that will monitor all of your sites that have the <em>Production check</em> module with <em>XMLRPC</em> enabled.').'<br />';
|
||||
$output .= t('The <strong>data retrieval</strong> mechanism can be called <strong>manually</strong> and is integrated with the <strong>cron</strong>, so you get a fresh update of data each cron run.').'</p>';
|
||||
break;
|
||||
case 'admin/reports/prod-monitor':
|
||||
$output .= '<p><strong>'.t('Site overview table').'</strong><br />';
|
||||
$output .= t('The overview table gives you an overview of what sites you have added together with their status. The status will be the highest error detected in the retrieved data set.').'<br />';
|
||||
$output .= t('The per site functions <strong>View</strong>, <strong>Edit</strong>, <strong>Fetch data</strong>, <strong>Flush</strong> and <strong>Delete</strong> should be self explanatory.').'</p>';
|
||||
// No break!
|
||||
case 'admin/reports/prod-monitor/site/%/edit':
|
||||
$output .= '<p><strong>'.t('Website URL & API key').'</strong><br />';
|
||||
$output .= t('To add a site, enter it\'s <strong>full url</strong>, including the protocol, but omitting the <em>xmlrpc.php</em> part and the <strong>API key</strong> that you have configured for it using the <strong>Production check</strong> module. Now click the <strong>Get settings</strong> button.').'<br />';
|
||||
$output .= t('All of the checks that the <em>Production check</em> module can perform are fetched from the remote site and presented as an array of checkboxes. Finally you can configure what exactly you wish to monitor for this site, then hit the <strong>Add site</strong> button.').'<br />';
|
||||
$output .= t('Each time you edit a site, the settings are fetched from the remote server so that any new checks that might have been added to the <em>Production check</em> module there are always up to date in the monitoring section.').'<br />'; $output .= t('<strong>Fetch data immediately</strong> does exactly what it says and fetches all the configured data from the remote site and will direct you to the report page.').'</p>';
|
||||
break;
|
||||
case 'admin/reports/prod-monitor/site/%':
|
||||
case 'admin/reports/prod-monitor/site/%/view':
|
||||
$output .= '<p>'.t('This is an overview of all checks performed by the <em>Production check</em> module and their status <strong>on the remote site</strong>. You can click the links inside the report to jump to the module\'s settings page, or to go to the project page of a module, in case you need to download it for installation.').'</p>';
|
||||
break;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_permission()
|
||||
*/
|
||||
function prod_monitor_permission() {
|
||||
return array(
|
||||
'access production monitor' => array(
|
||||
'title' => t('Administer the Production Monitor module'),
|
||||
'description' => t('Perform adiminister tasks for the Production Monitor module'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_menu().
|
||||
* Note: do not use t() in this hook! Translation is handled by core!
|
||||
*/
|
||||
function prod_monitor_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/reports/prod-monitor'] = array(
|
||||
'title' => 'Production monitor',
|
||||
'description' => 'Setup the Production monitor.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('prod_monitor_overview_form'),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
);
|
||||
|
||||
// This hook_menu() thing, still can't fully see the logic in it. However,
|
||||
// this here is what I want to achieve. It would be nice to see the /view/ bit
|
||||
// in the path dissapear, that would finish it entirely. I'll settle for this
|
||||
// now, caused me enough headache already ;-)
|
||||
|
||||
// The actual callback used by the default primary & secondary tabs,
|
||||
// they trickle upwards seeking for a callback to end up here.
|
||||
$items['admin/reports/prod-monitor/site/%'] = array(
|
||||
'title' => 'View',
|
||||
'description' => 'View the Production monitor report page.',
|
||||
'page callback' => 'prod_monitor_status',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 10,
|
||||
);
|
||||
|
||||
// Default primary tab (callback for this is it's parent path).
|
||||
$items['admin/reports/prod-monitor/site/%/view'] = array(
|
||||
'title' => 'View',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
// Default secondary (sub) tab (callback for this is it's parent path).
|
||||
$items['admin/reports/prod-monitor/site/%/view/status'] = array(
|
||||
'title' => 'Status',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
// Performance secondary (sub) tab.
|
||||
$items['admin/reports/prod-monitor/site/%prod_monitor_perf/view/performance'] = array(
|
||||
'title' => 'Performance',
|
||||
'description' => t('View the performance data for this site.'),
|
||||
'page callback' => 'prod_monitor_performance',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 12,
|
||||
);
|
||||
|
||||
// Updates secondary (sub) tab.
|
||||
$items['admin/reports/prod-monitor/site/%prod_monitor/view/updates'] = array(
|
||||
'title' => 'Updates',
|
||||
'description' => 'View the Production monitor modules update page.',
|
||||
'page callback' => 'prod_monitor_updates',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 15,
|
||||
);
|
||||
|
||||
$items['admin/reports/prod-monitor/site/%/update-check'] = array(
|
||||
'title' => 'Updates',
|
||||
'description' => 'Refresh Production monitor modules update page.',
|
||||
'page callback' => 'prod_monitor_updates_check',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_CALLBACK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
);
|
||||
|
||||
$items['admin/reports/prod-monitor/site/%/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'description' => 'Edit website',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('prod_monitor_overview_form', 4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 20,
|
||||
);
|
||||
|
||||
$items['admin/reports/prod-monitor/site/%/flush'] = array(
|
||||
'title' => 'Flush',
|
||||
'description' => "Flush website's data.",
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('prod_monitor_flush_form', 4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 30,
|
||||
);
|
||||
|
||||
$items['admin/reports/prod-monitor/site/%/fetch'] = array(
|
||||
'title' => 'Fetch',
|
||||
'description' => "Fetch website's data.",
|
||||
'page callback' => 'prod_monitor_fetch_data',
|
||||
'page arguments' => array(4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 35,
|
||||
);
|
||||
|
||||
$items['admin/reports/prod-monitor/site/%/delete'] = array(
|
||||
'title' => 'Delete',
|
||||
'description' => 'Delete website',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('prod_monitor_delete_form', 4),
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('access production monitor'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'includes/prod_monitor.admin.inc',
|
||||
'weight' => 40,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_load()
|
||||
*/
|
||||
function prod_monitor_load($id) {
|
||||
// Get module data.
|
||||
$modules = _prod_monitor_get_site_modules($id);
|
||||
// Hide tab if no module data found.
|
||||
if (!isset($modules['projects']) || empty($modules['projects'])) {
|
||||
$modules = FALSE;
|
||||
}
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_load()
|
||||
*/
|
||||
function prod_monitor_perf_load($id) {
|
||||
$data = array();
|
||||
$data['id'] = $id;
|
||||
|
||||
// Get performance data.
|
||||
$data['data'] = _prod_monitor_get_performance_data($id);
|
||||
// Hide tab if no module data found.
|
||||
if (empty($data['data'])) {
|
||||
return FALSE;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_theme()
|
||||
*/
|
||||
function prod_monitor_theme() {
|
||||
return array(
|
||||
'prod_monitor_update_report' => array(
|
||||
'variables' => array('id' => NULL, 'last' => NULL, 'data' => NULL),
|
||||
'file' => 'includes/prod_monitor.theme.inc',
|
||||
),
|
||||
'prod_monitor_update_status_label' => array(
|
||||
'variables' => array('status' => NULL),
|
||||
'file' => 'includes/prod_monitor.theme.inc',
|
||||
),
|
||||
'prod_monitor_update_version' => array(
|
||||
'variables' => array('version' => NULL, 'tag' => NULL, 'class' => NULL),
|
||||
'file' => 'includes/prod_monitor.theme.inc',
|
||||
),
|
||||
'prod_monitor_status_report' => array(
|
||||
'variables' => array('requirements' => NULL),
|
||||
'file' => 'includes/prod_monitor.theme.inc',
|
||||
),
|
||||
'prod_monitor_performance' => array(
|
||||
'render element' => 'elements',
|
||||
'template' => 'templates/prod-monitor-performance',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of theme_preprocess_hook()
|
||||
*/
|
||||
function template_preprocess_prod_monitor_performance(&$vars) {
|
||||
$vars['data'] = $vars['elements']['#data'];
|
||||
// This array holds all graphs per module, per timestamp and per unit (MB, ms,
|
||||
// ...).
|
||||
$vars['graphs'] = array();
|
||||
foreach ($vars['data'] as $module => $data_set) {
|
||||
// Counters to make sure we only add columns once.
|
||||
$count = $i = 0;
|
||||
foreach ($data_set as $time => $params) {
|
||||
// Store title for this modules graphs.
|
||||
$vars['graphs'][$module]['title'] = $params['title'];
|
||||
if (is_array($params['data'])) {
|
||||
// Count all rows.
|
||||
$count = count($params['data']);
|
||||
foreach ($params['data'] as $title => $row) {
|
||||
// Rows without a specified unit.
|
||||
if (!isset($row[1])) {
|
||||
$row[1] = '';
|
||||
}
|
||||
if ($i < $count) {
|
||||
// Setup the columns for the graph in such a way that they are
|
||||
// organised by unit so we can draw one graph per unit.
|
||||
$vars['graphs'][$module][$row[1]]['cols'][] = $title;
|
||||
$i++;
|
||||
}
|
||||
// Cast empty strings to 0 and store the data per timestamp and unit so
|
||||
// that we can draw one graph per unit.
|
||||
$vars['graphs'][$module][$row[1]]['rows'][$time][] = (int) $row[0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$vars['graphs'][$module]['message'] = $params['data'];
|
||||
}
|
||||
}
|
||||
if (count($vars['graphs'][$module]) > 2) {
|
||||
unset($vars['graphs'][$module]['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_cron()
|
||||
*/
|
||||
function prod_monitor_cron() {
|
||||
if (variable_get('prod_monitor_cron_running', FALSE)) {
|
||||
watchdog('prod_monitor', 'Last cron run was not properly terminated!', array(), WATCHDOG_ERROR);
|
||||
}
|
||||
|
||||
$sites = _prod_monitor_get_sites(variable_get('prod_monitor_cron_start_at', 0));
|
||||
|
||||
// Indicate we're running.
|
||||
variable_set('prod_monitor_cron_running', TRUE);
|
||||
$cron_start = REQUEST_TIME;
|
||||
// 180 seconds run max!
|
||||
$time_limit = 180;
|
||||
$elapsed = $process = 0;
|
||||
|
||||
foreach ($sites as $id => $site_info) {
|
||||
$elapsed = REQUEST_TIME - $cron_start;
|
||||
if ($elapsed < $time_limit) {
|
||||
//TODO: add module status update check here.
|
||||
_prod_monitor_retrieve_data($id, $site_info);
|
||||
$process++;
|
||||
}
|
||||
else {
|
||||
// Time's up! Start with this site next time.
|
||||
variable_set('prod_monitor_cron_start_at', $id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all was processed, make sure we start from the top next time
|
||||
if ($process >= count($sites)) {
|
||||
variable_set('prod_monitor_cron_start_at', 0);
|
||||
}
|
||||
|
||||
watchdog('prod_monitor', '!count sites updated successfully in !time seconds.', array('!count' => $process, '!time' => $elapsed), WATCHDOG_NOTICE);
|
||||
|
||||
// Indicate we've stopped.
|
||||
variable_del('prod_monitor_cron_running');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve settings form from Prod check using XMLRPC
|
||||
*/
|
||||
function _prod_monitor_retrieve_functions($url, $api_key, $msg = TRUE) {
|
||||
$url = rtrim($url, '/') . '/xmlrpc.php';
|
||||
|
||||
$functions = xmlrpc($url, array('prod_check.get_settings' => array($api_key)));
|
||||
if (!$functions) {
|
||||
drupal_set_message(
|
||||
t('Failed to retrieve settings form from !link, please verify the given URL and try again!',
|
||||
array('!link' => l('remote site', $url, array('attributes' => array('title' => t('remote site')))))
|
||||
),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
else if ($msg) {
|
||||
drupal_set_message(t('Settings form updated, please adjust your settings.'));
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve data form from Prod check using XMLRPC and store it in the database.
|
||||
*
|
||||
* @param $id id of the site the data is being fetched for
|
||||
* @param $site_info associative array containing api_key and checks to execute
|
||||
* @param $msg wether or not to give feedback to the user of the action
|
||||
*/
|
||||
function _prod_monitor_retrieve_data($id, $site_info, $msg = FALSE) {
|
||||
$url = rtrim($site_info['url'], '/') . '/xmlrpc.php';
|
||||
$api_key = $site_info['settings']['api_key'];
|
||||
$checks = $site_info['settings']['checks'];
|
||||
|
||||
// Do requests.
|
||||
$data = xmlrpc($url, array('prod_check.get_data' => array($api_key, $checks)));
|
||||
if (!$data) {
|
||||
watchdog('prod_monitor', 'Could not retrieve settings data for %link', array('%link' => $site_info['url']), WATCHDOG_ERROR);
|
||||
if ($msg) {
|
||||
drupal_set_message(
|
||||
t('Data for %link not successfully fetched. Errors have been !link.',
|
||||
array(
|
||||
'%link' => $site_info['url'],
|
||||
'!link' => l(t('logged'), 'admin/reports/dblog'),
|
||||
)
|
||||
), 'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Extract the module list data to be stored in a different table
|
||||
$module_list = array();
|
||||
if (isset($data['prod_mon']['prod_check_module_list'])) {
|
||||
$module_list = $data['prod_mon']['prod_check_module_list'];
|
||||
unset($data['prod_mon']['prod_check_module_list']);
|
||||
}
|
||||
|
||||
// Extract the performance data to be stored in a different table
|
||||
$perf_data = array();
|
||||
if (isset($data['perf_data'])) {
|
||||
$perf_data = $data['perf_data'];
|
||||
unset($data['perf_data']);
|
||||
}
|
||||
|
||||
// Store site data
|
||||
$site = new stdClass();
|
||||
$site->id = $id;
|
||||
$site->data = serialize($data);
|
||||
$site->lastupdate = REQUEST_TIME;
|
||||
$result = drupal_write_record('prod_monitor_sites', $site, array('id'));
|
||||
// TODO: pour this into a function, it's thrice the same!
|
||||
if (!$result) {
|
||||
watchdog('prod_monitor', 'Could not update data for %link', array('%link' => $site_info['url']), WATCHDOG_ERROR);
|
||||
if ($msg) {
|
||||
drupal_set_message(
|
||||
t('Data for %link not successfully saved. Errors have been !link.',
|
||||
array(
|
||||
'%link' => $site_info['url'],
|
||||
'!link' => l(t('logged'), 'admin/reports/dblog'),
|
||||
)
|
||||
), 'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($msg) {
|
||||
drupal_set_message(t('Data for %link successfully updated.', array('%link' => $site_info['url'])));
|
||||
}
|
||||
// Store module data if there is an update.
|
||||
if (!empty($module_list)) {
|
||||
// Check if data present, so we can update.
|
||||
$modules = _prod_monitor_get_site_modules($id, TRUE);
|
||||
$update = array();
|
||||
if (!empty($modules)) {
|
||||
$update = array('id');
|
||||
}
|
||||
|
||||
$modules = new stdClass();
|
||||
$modules->id = $id;
|
||||
$modules->projects = serialize($module_list['projects']);
|
||||
$modules->sitekey = $module_list['site_key'];
|
||||
$modules->lastfetch = $module_list['last_update'];
|
||||
$result = drupal_write_record('prod_monitor_site_modules', $modules, $update);
|
||||
// TODO: pour this into a function, it's thrice the same!
|
||||
if (!$result) {
|
||||
watchdog('prod_monitor', 'Could not update module data for %link', array('%link' => $site_info['url']), WATCHDOG_ERROR);
|
||||
if ($msg) {
|
||||
drupal_set_message(
|
||||
t('Module data for %link not successfully saved. Errors have been !link.',
|
||||
array(
|
||||
'%link' => $site_info['url'],
|
||||
'!link' => l(t('logged'), 'admin/reports/dblog'),
|
||||
)
|
||||
), 'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($msg) {
|
||||
drupal_set_message(t('Module data for %link successfully updated.', array('%link' => $site_info['url'])));
|
||||
}
|
||||
}
|
||||
if (!empty($perf_data)) {
|
||||
foreach ($perf_data as $module => $module_data) {
|
||||
$performance = new stdClass();
|
||||
$performance->id = $id;
|
||||
$performance->module = $module;
|
||||
$performance->data = serialize($module_data);
|
||||
$performance->fetched = time();
|
||||
$result = drupal_write_record('prod_monitor_site_performance', $performance);
|
||||
// TODO: pour this into a function, it's thrice the same!
|
||||
if (!$result) {
|
||||
watchdog('prod_monitor', 'Could not update performance data for %link', array('%link' => $site_info['url']), WATCHDOG_ERROR);
|
||||
if ($msg) {
|
||||
drupal_set_message(
|
||||
t('Performance data for %link not successfully saved. Errors have been !link.',
|
||||
array(
|
||||
'%link' => $site_info['url'],
|
||||
'!link' => l(t('logged'), 'admin/reports/dblog'),
|
||||
)
|
||||
), 'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($msg) {
|
||||
drupal_set_message(t('Performance data for %link successfully updated.', array('%link' => $site_info['url'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get all sites.
|
||||
*/
|
||||
function _prod_monitor_get_sites($start_id = FALSE) {
|
||||
if ($start_id) {
|
||||
// When called from hook_cron
|
||||
$result = db_query("SELECT * FROM {prod_monitor_sites} WHERE id >= :start_id ORDER BY id ASC", array(':start_id' => $start_id));
|
||||
}
|
||||
else {
|
||||
$result = db_query("SELECT * FROM {prod_monitor_sites} ORDER BY added DESC");
|
||||
}
|
||||
|
||||
$sites = array();
|
||||
foreach ($result as $row) {
|
||||
$id = $row->id;
|
||||
$row->data = unserialize($row->data);
|
||||
|
||||
// Get highest error level
|
||||
$status = -1;
|
||||
if (!empty($row->data)) {
|
||||
foreach ($row->data as $set => $checks) {
|
||||
foreach ($checks as $check => $results) {
|
||||
$status = ($results['severity'] > $status) ? $results['severity'] : $status;
|
||||
}
|
||||
}
|
||||
$data_status = TRUE;
|
||||
}
|
||||
else {
|
||||
$data_status = FALSE;
|
||||
}
|
||||
|
||||
switch ($status) {
|
||||
case 0: $status = 'ok';
|
||||
break;
|
||||
case 1: $status = 'warning';
|
||||
break;
|
||||
case 2: $status = 'error';
|
||||
break;
|
||||
default: $status = '';
|
||||
}
|
||||
|
||||
$sites[$id]['url'] = $row->url;
|
||||
$sites[$id]['settings'] = unserialize($row->settings);
|
||||
$sites[$id]['data'] = $data_status;
|
||||
$sites[$id]['status'] = $status;
|
||||
$sites[$id]['added'] = format_date($row->added, 'small');
|
||||
$sites[$id]['lastupdate'] = (empty($row->lastupdate)) ? FALSE : format_date($row->lastupdate, 'small');
|
||||
}
|
||||
|
||||
return $sites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a site by ID.
|
||||
*
|
||||
* @param $id
|
||||
* int site id.
|
||||
* @param $all
|
||||
* Boolean whether or not to return all fields or just the url and settings.
|
||||
*/
|
||||
function _prod_monitor_get_site($id, $all = FALSE) {
|
||||
if (!$all) {
|
||||
$site = db_query("SELECT url, settings FROM {prod_monitor_sites} WHERE id = :id", array(':id' => $id))->fetchAssoc();
|
||||
}
|
||||
else {
|
||||
$site = db_query("SELECT * FROM {prod_monitor_sites} WHERE id = :id", array(':id' => $id))->fetchAssoc();
|
||||
}
|
||||
|
||||
if (!empty($site)) {
|
||||
$site['settings'] = unserialize($site['settings']);
|
||||
if ($all) {
|
||||
$site['data'] = unserialize($site['data']);
|
||||
}
|
||||
}
|
||||
|
||||
return $site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a site's modules by ID.
|
||||
*
|
||||
* @param $id
|
||||
* int site id.
|
||||
* @param $exists
|
||||
* Boolean wether to return just the ID (to check if there is module info)
|
||||
* or all fields.
|
||||
*/
|
||||
function _prod_monitor_get_site_modules($id, $exists = FALSE) {
|
||||
if (!$exists) {
|
||||
$modules = db_query('SELECT * FROM {prod_monitor_site_modules} WHERE id = :id', array (':id' => $id))->fetchAssoc();
|
||||
}
|
||||
else {
|
||||
$modules = db_query('SELECT id FROM {prod_monitor_site_modules} WHERE id = :id', array (':id' => $id))->fetchAssoc();
|
||||
}
|
||||
|
||||
if (!empty($modules) && !$exists) {
|
||||
$modules['projects'] = unserialize($modules['projects']);
|
||||
$modules['available'] = unserialize($modules['available']);
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the module status of a site by ID.
|
||||
*
|
||||
* @param $id
|
||||
* int site id.
|
||||
*/
|
||||
function _prod_monitor_get_update_status($id) {
|
||||
return db_query('SELECT updates FROM {prod_monitor_site_modules} WHERE id = :id', array(':id' => $id))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the performance data of a site by ID.
|
||||
*
|
||||
* @param $id
|
||||
* int site id.
|
||||
*/
|
||||
function _prod_monitor_get_performance_data($id) {
|
||||
$result = db_query('SELECT module, data, fetched FROM {prod_monitor_site_performance} WHERE id = :id', array(':id' => $id));
|
||||
|
||||
$data = array();
|
||||
foreach ($result as $row) {
|
||||
$data[$row->module][$row->fetched] = unserialize($row->data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to flush data for a site.
|
||||
* Added for easy implementation of Drush functionality.
|
||||
*/
|
||||
function _prod_monitor_flush_data($id) {
|
||||
$site = new stdClass();
|
||||
$site->id = $id;
|
||||
// Setting data to NULL would be preferred, but then drupal_write_record
|
||||
// fails!
|
||||
$site->data = serialize(array());
|
||||
$site->lastupdate = 0;
|
||||
|
||||
return drupal_write_record('prod_monitor_sites', $site, array('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to delete a site.
|
||||
* Added for easy implementation of Drush functionality.
|
||||
*/
|
||||
function _prod_monitor_delete_site($id) {
|
||||
$txn = db_transaction();
|
||||
|
||||
// If anyone has an idea on how to do this on one single query, like we did in
|
||||
// the D6 version, drop us a line!
|
||||
try {
|
||||
$query = db_delete('prod_monitor_sites')
|
||||
->condition('id', $id)
|
||||
->execute();
|
||||
$query = db_delete('prod_monitor_site_modules')
|
||||
->condition('id', $id)
|
||||
->execute();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$txn->rollback();
|
||||
watchdog_exception('prod_monitor', $e);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for Drush to show proper info when deleting a site.
|
||||
*/
|
||||
function _prod_monitor_get_url($id) {
|
||||
$url = db_query('SELECT url FROM {prod_monitor_sites} WHERE id = :id', array(':id' => $id))->fetchField();
|
||||
|
||||
return _prod_monitor_sanitize_url(rtrim($url, '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove (optional) password from URL.
|
||||
*/
|
||||
function _prod_monitor_sanitize_url($url) {
|
||||
return preg_replace('/(:\/\/[^:]+:)[^@]+(@)/', "$1...$2", $url);
|
||||
}
|
||||
|
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Template that renders a google graph. Feel free to override it if you prefer
|
||||
* another graphing mechanism by placing it in your themes folder.
|
||||
*
|
||||
* Available variables:
|
||||
* $data contains raw data fetched from the prod_monitor_performance table.
|
||||
* $graphs contains preprocessed data to allow (more) easy output.
|
||||
*
|
||||
* TODO: add preloader element to the Graphs div so the users see something is
|
||||
* loading.
|
||||
*/
|
||||
|
||||
// Output all graphs.
|
||||
$scripts = '';
|
||||
foreach ($graphs as $module => $data) {
|
||||
?>
|
||||
<h2><?php print $data['title'] ?></h2>
|
||||
|
||||
<?php
|
||||
unset($data['title']);
|
||||
if (isset($data['message'])) {
|
||||
print $data['message'];
|
||||
}
|
||||
else {
|
||||
foreach ($data as $unit => $numbers) {
|
||||
$unit = strtolower($unit);
|
||||
?>
|
||||
<div id="<?php print $module . (!empty($unit) ? '-' . $unit : ''); ?>" style="width: 960px; height: 300px; text-align: center;" class="performance-data"><img style="padding-top: 140px;" src="<?php print base_path() . drupal_get_path('module', 'prod_monitor'); ?>/images/spinner.gif" width="20" height="20" /></div>
|
||||
<?php
|
||||
$scripts .= ' Drupal.behaviors.prod_monitor_performance.' . $module . (!empty($unit) ? '_' . $unit : '') . " = function() {\n";
|
||||
$scripts .= ' var data = new google.visualization.DataTable();'."\n";
|
||||
$scripts .= " data.addColumn('datetime', 'Date');\n";
|
||||
|
||||
// Add columns.
|
||||
foreach ($numbers['cols'] as $col) {
|
||||
$scripts .= " data.addColumn('number', '$col');\n";
|
||||
}
|
||||
|
||||
// Add column data.
|
||||
$scripts .= ' data.addRows(['."\n";
|
||||
foreach ($numbers['rows'] as $time => $row) {
|
||||
$scripts .= ' [new Date(' . date('Y, n, j, G, i, s', $time) . '), ' . implode(', ', $row ) . "],\n";
|
||||
}
|
||||
$scripts .= " ]);\n\n";
|
||||
|
||||
$scripts .= " var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('". $module . (!empty($unit) ? '-' . $unit : '') . "'));\n";
|
||||
$scripts .= ' chart.draw(data, {displayAnnotations: false});'."\n";
|
||||
$scripts .= " }\n\n";
|
||||
?>
|
||||
<p> </p>
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
// Actually add the JS files to the page. Order is important!
|
||||
drupal_add_js(drupal_get_path('module', 'prod_monitor') . '/js/prod-monitor.performance.js', array('type' => 'file', 'weight' => 1));
|
||||
drupal_add_js($scripts, array('type' => 'inline', 'weight' => 2));
|
||||
?>
|
Reference in New Issue
Block a user