Codebase list icingaweb2-module-businessprocess / a1a5f99
Adding upstream version 2.1.0. Signed-off-by: David Kunz <david.kunz@dknet.ch> David Kunz 5 years ago
199 changed file(s) with 17228 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 stages:
1 - Coding Standards
2 - Unit-Tests
3 - Build Packages
4
5 variables:
6 BASE_VERSION: "2.0.0"
7 VERSION_SUFFIX: "-b${CI_BUILD_ID}-${CI_BUILD_REF_SLUG}"
8
9 PSR2 CS Test:
10 stage: Coding Standards
11 tags:
12 - xenial
13 script:
14 - phpcs --report-width=auto --report-full --report-gitblame --report-summary -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test
15
16 Ubuntu Xenial:
17 stage: Unit-Tests
18 tags:
19 - xenial
20 - businessprocess
21 script:
22 - phpunit --testdox --coverage-html=coverage || phpunit --verbose
23 artifacts:
24 expire_in: 1 week
25 name: code-coverage
26 paths:
27 - coverage/*
28
29 Debian Jessie:
30 stage: Unit-Tests
31 tags:
32 - jessie
33 - businessprocess
34 script:
35 - phpunit --testdox || phpunit --verbose
36
37 CentOS 6:
38 stage: Unit-Tests
39 tags:
40 - centos6
41 - businessprocess
42 script:
43 - phpunit --testdox || phpunit --verbose
44
45 CentOS 7:
46 stage: Unit-Tests
47 tags:
48 - centos7
49 - businessprocess
50 script:
51 - phpunit --testdox || phpunit --verbose
52
53 Xenial Packages:
54 stage: Build Packages
55 tags:
56 - xenial
57 - businessprocess
58 script:
59 - cp -a packaging/debian debian
60 - dch --no-conf -U -M --empty -v "${BASE_VERSION}${VERSION_SUFFIX}-${CI_BUILD_REF:0:7}" "Automated build triggered by ${GITLAB_USER_ID} <${GITLAB_USER_EMAIL}>"
61 - cp LICENSE debian/copyright
62 - dpkg-buildpackage -us -uc
63 - mkdir build
64 - mv ../icingaweb2-module-businessprocess*.deb build/
65 artifacts:
66 expire_in: 1 week
67 paths:
68 - build/*
69
70 Jessie Packages:
71 stage: Build Packages
72 tags:
73 - jessie
74 - businessprocess
75 script:
76 - cp -a packaging/debian debian
77 - dch --no-conf -U -M --empty -v "${BASE_VERSION}${VERSION_SUFFIX}-${CI_BUILD_REF:0:7}" "Automated build triggered by ${GITLAB_USER_ID} <${GITLAB_USER_EMAIL}>"
78 - cp LICENSE debian/copyright
79 - dpkg-buildpackage -us -uc
80 - mkdir build
81 - mv ../icingaweb2-module-businessprocess*.deb build/
82 artifacts:
83 expire_in: 1 week
84 paths:
85 - build/*
0 language: php
1 php:
2 - '5.3'
3 - '5.4'
4 - '5.5'
5 - '5.6'
6 - '7.0'
7 - '7.1'
8 - nightly
9
10 before_script:
11 - curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
12 - wget https://github.com/Icinga/icingaweb2/archive/v2.4.0.tar.gz
13 - tar xfz v2.4.0.tar.gz
14 - ln -s icingaweb2-2.4.0/library/Icinga
15 - ln -s icingaweb2-2.4.0/library/vendor/Zend
16
17 script:
18 - php phpcs.phar --report-width=auto --report-full --report-gitblame --report-summary -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test
19 - phpunit --testdox || phpunit --verbose
0 GNU GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
4 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The licenses for most software are designed to take away your
11 freedom to share and change it. By contrast, the GNU General Public
12 License is intended to guarantee your freedom to share and change free
13 software--to make sure the software is free for all its users. This
14 General Public License applies to most of the Free Software
15 Foundation's software and to any other program whose authors commit to
16 using it. (Some other Free Software Foundation software is covered by
17 the GNU Lesser General Public License instead.) You can apply it to
18 your programs, too.
19
20 When we speak of free software, we are referring to freedom, not
21 price. Our General Public Licenses are designed to make sure that you
22 have the freedom to distribute copies of free software (and charge for
23 this service if you wish), that you receive source code or can get it
24 if you want it, that you can change the software or use pieces of it
25 in new free programs; and that you know you can do these things.
26
27 To protect your rights, we need to make restrictions that forbid
28 anyone to deny you these rights or to ask you to surrender the rights.
29 These restrictions translate to certain responsibilities for you if you
30 distribute copies of the software, or if you modify it.
31
32 For example, if you distribute copies of such a program, whether
33 gratis or for a fee, you must give the recipients all the rights that
34 you have. You must make sure that they, too, receive or can get the
35 source code. And you must show them these terms so they know their
36 rights.
37
38 We protect your rights with two steps: (1) copyright the software, and
39 (2) offer you this license which gives you legal permission to copy,
40 distribute and/or modify the software.
41
42 Also, for each author's protection and ours, we want to make certain
43 that everyone understands that there is no warranty for this free
44 software. If the software is modified by someone else and passed on, we
45 want its recipients to know that what they have is not the original, so
46 that any problems introduced by others will not reflect on the original
47 authors' reputations.
48
49 Finally, any free program is threatened constantly by software
50 patents. We wish to avoid the danger that redistributors of a free
51 program will individually obtain patent licenses, in effect making the
52 program proprietary. To prevent this, we have made it clear that any
53 patent must be licensed for everyone's free use or not licensed at all.
54
55 The precise terms and conditions for copying, distribution and
56 modification follow.
57
58 GNU GENERAL PUBLIC LICENSE
59 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
60
61 0. This License applies to any program or other work which contains
62 a notice placed by the copyright holder saying it may be distributed
63 under the terms of this General Public License. The "Program", below,
64 refers to any such program or work, and a "work based on the Program"
65 means either the Program or any derivative work under copyright law:
66 that is to say, a work containing the Program or a portion of it,
67 either verbatim or with modifications and/or translated into another
68 language. (Hereinafter, translation is included without limitation in
69 the term "modification".) Each licensee is addressed as "you".
70
71 Activities other than copying, distribution and modification are not
72 covered by this License; they are outside its scope. The act of
73 running the Program is not restricted, and the output from the Program
74 is covered only if its contents constitute a work based on the
75 Program (independent of having been made by running the Program).
76 Whether that is true depends on what the Program does.
77
78 1. You may copy and distribute verbatim copies of the Program's
79 source code as you receive it, in any medium, provided that you
80 conspicuously and appropriately publish on each copy an appropriate
81 copyright notice and disclaimer of warranty; keep intact all the
82 notices that refer to this License and to the absence of any warranty;
83 and give any other recipients of the Program a copy of this License
84 along with the Program.
85
86 You may charge a fee for the physical act of transferring a copy, and
87 you may at your option offer warranty protection in exchange for a fee.
88
89 2. You may modify your copy or copies of the Program or any portion
90 of it, thus forming a work based on the Program, and copy and
91 distribute such modifications or work under the terms of Section 1
92 above, provided that you also meet all of these conditions:
93
94 a) You must cause the modified files to carry prominent notices
95 stating that you changed the files and the date of any change.
96
97 b) You must cause any work that you distribute or publish, that in
98 whole or in part contains or is derived from the Program or any
99 part thereof, to be licensed as a whole at no charge to all third
100 parties under the terms of this License.
101
102 c) If the modified program normally reads commands interactively
103 when run, you must cause it, when started running for such
104 interactive use in the most ordinary way, to print or display an
105 announcement including an appropriate copyright notice and a
106 notice that there is no warranty (or else, saying that you provide
107 a warranty) and that users may redistribute the program under
108 these conditions, and telling the user how to view a copy of this
109 License. (Exception: if the Program itself is interactive but
110 does not normally print such an announcement, your work based on
111 the Program is not required to print an announcement.)
112
113 These requirements apply to the modified work as a whole. If
114 identifiable sections of that work are not derived from the Program,
115 and can be reasonably considered independent and separate works in
116 themselves, then this License, and its terms, do not apply to those
117 sections when you distribute them as separate works. But when you
118 distribute the same sections as part of a whole which is a work based
119 on the Program, the distribution of the whole must be on the terms of
120 this License, whose permissions for other licensees extend to the
121 entire whole, and thus to each and every part regardless of who wrote it.
122
123 Thus, it is not the intent of this section to claim rights or contest
124 your rights to work written entirely by you; rather, the intent is to
125 exercise the right to control the distribution of derivative or
126 collective works based on the Program.
127
128 In addition, mere aggregation of another work not based on the Program
129 with the Program (or with a work based on the Program) on a volume of
130 a storage or distribution medium does not bring the other work under
131 the scope of this License.
132
133 3. You may copy and distribute the Program (or a work based on it,
134 under Section 2) in object code or executable form under the terms of
135 Sections 1 and 2 above provided that you also do one of the following:
136
137 a) Accompany it with the complete corresponding machine-readable
138 source code, which must be distributed under the terms of Sections
139 1 and 2 above on a medium customarily used for software interchange; or,
140
141 b) Accompany it with a written offer, valid for at least three
142 years, to give any third party, for a charge no more than your
143 cost of physically performing source distribution, a complete
144 machine-readable copy of the corresponding source code, to be
145 distributed under the terms of Sections 1 and 2 above on a medium
146 customarily used for software interchange; or,
147
148 c) Accompany it with the information you received as to the offer
149 to distribute corresponding source code. (This alternative is
150 allowed only for noncommercial distribution and only if you
151 received the program in object code or executable form with such
152 an offer, in accord with Subsection b above.)
153
154 The source code for a work means the preferred form of the work for
155 making modifications to it. For an executable work, complete source
156 code means all the source code for all modules it contains, plus any
157 associated interface definition files, plus the scripts used to
158 control compilation and installation of the executable. However, as a
159 special exception, the source code distributed need not include
160 anything that is normally distributed (in either source or binary
161 form) with the major components (compiler, kernel, and so on) of the
162 operating system on which the executable runs, unless that component
163 itself accompanies the executable.
164
165 If distribution of executable or object code is made by offering
166 access to copy from a designated place, then offering equivalent
167 access to copy the source code from the same place counts as
168 distribution of the source code, even though third parties are not
169 compelled to copy the source along with the object code.
170
171 4. You may not copy, modify, sublicense, or distribute the Program
172 except as expressly provided under this License. Any attempt
173 otherwise to copy, modify, sublicense or distribute the Program is
174 void, and will automatically terminate your rights under this License.
175 However, parties who have received copies, or rights, from you under
176 this License will not have their licenses terminated so long as such
177 parties remain in full compliance.
178
179 5. You are not required to accept this License, since you have not
180 signed it. However, nothing else grants you permission to modify or
181 distribute the Program or its derivative works. These actions are
182 prohibited by law if you do not accept this License. Therefore, by
183 modifying or distributing the Program (or any work based on the
184 Program), you indicate your acceptance of this License to do so, and
185 all its terms and conditions for copying, distributing or modifying
186 the Program or works based on it.
187
188 6. Each time you redistribute the Program (or any work based on the
189 Program), the recipient automatically receives a license from the
190 original licensor to copy, distribute or modify the Program subject to
191 these terms and conditions. You may not impose any further
192 restrictions on the recipients' exercise of the rights granted herein.
193 You are not responsible for enforcing compliance by third parties to
194 this License.
195
196 7. If, as a consequence of a court judgment or allegation of patent
197 infringement or for any other reason (not limited to patent issues),
198 conditions are imposed on you (whether by court order, agreement or
199 otherwise) that contradict the conditions of this License, they do not
200 excuse you from the conditions of this License. If you cannot
201 distribute so as to satisfy simultaneously your obligations under this
202 License and any other pertinent obligations, then as a consequence you
203 may not distribute the Program at all. For example, if a patent
204 license would not permit royalty-free redistribution of the Program by
205 all those who receive copies directly or indirectly through you, then
206 the only way you could satisfy both it and this License would be to
207 refrain entirely from distribution of the Program.
208
209 If any portion of this section is held invalid or unenforceable under
210 any particular circumstance, the balance of the section is intended to
211 apply and the section as a whole is intended to apply in other
212 circumstances.
213
214 It is not the purpose of this section to induce you to infringe any
215 patents or other property right claims or to contest validity of any
216 such claims; this section has the sole purpose of protecting the
217 integrity of the free software distribution system, which is
218 implemented by public license practices. Many people have made
219 generous contributions to the wide range of software distributed
220 through that system in reliance on consistent application of that
221 system; it is up to the author/donor to decide if he or she is willing
222 to distribute software through any other system and a licensee cannot
223 impose that choice.
224
225 This section is intended to make thoroughly clear what is believed to
226 be a consequence of the rest of this License.
227
228 8. If the distribution and/or use of the Program is restricted in
229 certain countries either by patents or by copyrighted interfaces, the
230 original copyright holder who places the Program under this License
231 may add an explicit geographical distribution limitation excluding
232 those countries, so that distribution is permitted only in or among
233 countries not thus excluded. In such case, this License incorporates
234 the limitation as if written in the body of this License.
235
236 9. The Free Software Foundation may publish revised and/or new versions
237 of the General Public License from time to time. Such new versions will
238 be similar in spirit to the present version, but may differ in detail to
239 address new problems or concerns.
240
241 Each version is given a distinguishing version number. If the Program
242 specifies a version number of this License which applies to it and "any
243 later version", you have the option of following the terms and conditions
244 either of that version or of any later version published by the Free
245 Software Foundation. If the Program does not specify a version number of
246 this License, you may choose any version ever published by the Free Software
247 Foundation.
248
249 10. If you wish to incorporate parts of the Program into other free
250 programs whose distribution conditions are different, write to the author
251 to ask for permission. For software which is copyrighted by the Free
252 Software Foundation, write to the Free Software Foundation; we sometimes
253 make exceptions for this. Our decision will be guided by the two goals
254 of preserving the free status of all derivatives of our free software and
255 of promoting the sharing and reuse of software generally.
256
257 NO WARRANTY
258
259 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
260 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
261 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
262 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
263 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
264 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
265 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
266 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
267 REPAIR OR CORRECTION.
268
269 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
270 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
271 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
272 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
273 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
274 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
275 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
276 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
277 POSSIBILITY OF SUCH DAMAGES.
278
279 END OF TERMS AND CONDITIONS
280
281 How to Apply These Terms to Your New Programs
282
283 If you develop a new program, and you want it to be of the greatest
284 possible use to the public, the best way to achieve this is to make it
285 free software which everyone can redistribute and change under these terms.
286
287 To do so, attach the following notices to the program. It is safest
288 to attach them to the start of each source file to most effectively
289 convey the exclusion of warranty; and each file should have at least
290 the "copyright" line and a pointer to where the full notice is found.
291
292 <one line to give the program's name and a brief idea of what it does.>
293 Copyright (C) <year> <name of author>
294
295 This program is free software; you can redistribute it and/or modify
296 it under the terms of the GNU General Public License as published by
297 the Free Software Foundation; either version 2 of the License, or
298 (at your option) any later version.
299
300 This program is distributed in the hope that it will be useful,
301 but WITHOUT ANY WARRANTY; without even the implied warranty of
302 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303 GNU General Public License for more details.
304
305 You should have received a copy of the GNU General Public License along
306 with this program; if not, write to the Free Software Foundation, Inc.,
307 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
308
309 Also add information on how to contact you by electronic and paper mail.
310
311 If the program is interactive, make it output a short notice like this
312 when it starts in an interactive mode:
313
314 Gnomovision version 69, Copyright (C) year name of author
315 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
316 This is free software, and you are welcome to redistribute it
317 under certain conditions; type `show c' for details.
318
319 The hypothetical commands `show w' and `show c' should show the appropriate
320 parts of the General Public License. Of course, the commands you use may
321 be called something other than `show w' and `show c'; they could even be
322 mouse-clicks or menu items--whatever suits your program.
323
324 You should also get your employer (if you work as a programmer) or your
325 school, if any, to sign a "copyright disclaimer" for the program, if
326 necessary. Here is a sample; alter the names:
327
328 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
329 `Gnomovision' (which makes passes at compilers) written by James Hacker.
330
331 <signature of Ty Coon>, 1 April 1989
332 Ty Coon, President of Vice
333
334 This General Public License does not permit incorporating your program into
335 proprietary programs. If your program is a subroutine library, you may
336 consider it more useful to permit linking proprietary applications with the
337 library. If this is what you want to do, use the GNU Lesser General
338 Public License instead of this License.
0 Business Processes - Icinga Web 2 module
1 ========================================
2
3 If you want to visualize and monitor hierarchical business processes based on
4 any or all objects monitored by Icinga, the Icinga Web 2 business process
5 module is the way to go.
6
7 [![Tile View](doc/screenshot/00_preview/0005_preview-smaller-tile-view.png)](doc/13-Web-Components-Tile-Renderer.md)
8 [![Tree View](doc/screenshot/00_preview/0006_preview-smaller-tree-view.png)](doc/14-Web-Components-Tree-Renderer.md)
9 [![Dashboard](doc/screenshot/00_preview/0007_preview-smallerbusinessprocesses-on-dashboard.png)](doc/16-Add-To-Dashboard.md)
10
11 Want to create custom process-based dashboards? Trigger notifications at
12 process or sub-process level? Provide a quick top-level view for thousands of
13 components on a single screen? That's what this module has been designed for!
14
15 You're running a huge cloud, want to get rid of the monitoring noise triggered
16 by your auto-scaling platform but still want to have detailed information just
17 a couple of clicks away in case you need them? You will love this little module!
18
19 [![Build Status](https://travis-ci.org/Icinga/icingaweb2-module-businessprocess.svg?branch=master)](https://travis-ci.org/Icinga/icingaweb2-module-businessprocess)
20
21 Documentation
22 -------------
23
24 ### Basics
25 * [Installation](doc/01-Installation.md)
26 * [Getting Started](doc/02-Getting-Started.md)
27 * [Create your first process node](doc/03-Create-your-first-process-node.md)
28
29 ### Web Components
30 * [Breadcrumb](doc/12-Web-Components-Breadcrumb.md)
31 * [Tile Renderer](doc/13-Web-Components-Tile-Renderer.md)
32 * [Tree Renderer](doc/14-Web-Components-Tree-Renderer.md)
33 * [Show Processes on a Dashboard](doc/16-Add-To-Dashboard.md)
34
35 ### Storage
36 * [Store your Configuration](doc/21-Store-Config.md)
37 * [Upload an existing Configuration](doc/22-Upload-Config.md)
38
39 ### Monitoring
40 * [~~Discover the CLI~~](doc/31-Discover-the-CLI.md)
41 * [~~Create related Service Checks~~](doc/32-Create-Service-Checks.md)
42
43 ### Business Impact
44 * [~~Apply Simulations~~](doc/41-Simulations.md)
45 * [~~Show Business Impact~~](doc/42-Business-Impact.md)
46
47 ### The Project
48 * [Project History](doc/81-History.md)
49 * [Changelog](doc/82-Changelog.md)
50 * [Contributing](doc/84-Contributing.md)
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Clicommands;
3
4 class CheckCommand extends ProcessCommand
5 {
6 public function listActions()
7 {
8 return array('process');
9 }
10
11 /**
12 * 'check process' is DEPRECATED, please use 'process check' instead
13 *
14 * USAGE
15 *
16 * icingacli businessprocess check process [--config <name>] <process>
17 */
18 public function processAction()
19 {
20 $this->checkAction();
21 }
22 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Clicommands;
3
4 use Icinga\Cli\Command;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\BpNode;
7 use Icinga\Module\Businessprocess\HostNode;
8 use Icinga\Module\Businessprocess\Node;
9 use Icinga\Module\Businessprocess\State\MonitoringState;
10 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
11
12 class ProcessCommand extends Command
13 {
14 /**
15 * @var LegacyStorage
16 */
17 protected $storage;
18
19 protected $hostColors = array(
20 0 => array('black', 'lightgreen'),
21 1 => array('lightgray', 'lightred'),
22 2 => array('black', 'brown'),
23 99 => array('black', 'lightgray'),
24 );
25
26 protected $serviceColors = array(
27 0 => array('black', 'lightgreen'),
28 1 => array('black', 'yellow'),
29 2 => array('lightgray', 'lightred'),
30 3 => array('black', 'lightpurple'),
31 99 => array('black', 'lightgray'),
32 );
33
34 public function init()
35 {
36 $this->storage = new LegacyStorage($this->Config()->getSection('global'));
37 }
38
39 /**
40 * List all available Business Process Configurations
41 *
42 * ...or their BusinessProcess Nodes in case a Configuration name is given
43 *
44 * USAGE
45 *
46 * icingacli businessprocess list processes [<config-name>] [options]
47 *
48 * OPTIONS
49 *
50 * <config-name>
51 * --no-title Show only names and no related title
52 */
53 public function listAction()
54 {
55 if ($config = $this->params->shift()) {
56 $this->listBpNames($this->storage->loadProcess($config));
57 } else {
58 $this->listConfigNames(! (bool) $this->params->shift('no-title'));
59 }
60 }
61
62 /**
63 * Check a specific process
64 *
65 * USAGE
66 *
67 * icingacli businessprocess process check <process> [options]
68 *
69 * OPTIONS
70 *
71 * --config <configname> Name of the config that contains <process>
72 * --details Show problem details as a tree
73 * --colors Show colored output
74 * --state-type <type> Define which state type to look at. Could be
75 * either soft or hard, overrides an eventually
76 * configured default
77 */
78 public function checkAction()
79 {
80 $name = $this->params->get('config');
81 if ($name === null) {
82 $name = $this->getFirstProcessName();
83 }
84
85 $bp = $this->storage->loadProcess($name);
86
87 if (null !== ($stateType = $this->params->get('state-type'))) {
88 if ($stateType === 'soft') {
89 $bp->useSoftStates();
90 }
91 if ($stateType === 'hard') {
92 $bp->useHardStates();
93 }
94 }
95
96 /** @var BpNode $node */
97 $node = $bp->getNode($this->params->shift());
98 MonitoringState::apply($bp);
99 if ($bp->hasErrors()) {
100 printf(
101 "Checking Business Process %s failed: %s\n",
102 $node->getAlias(),
103 implode("\n", $bp->getErrors())
104 );
105 exit(3);
106 }
107
108 printf("Business Process %s: %s\n", $node->getStateName(), $node->getAlias());
109 if ($this->params->shift('details')) {
110 echo $this->renderProblemTree($node->getProblemTree(), $this->params->shift('colors'));
111 }
112
113 exit($node->getState());
114 }
115
116 protected function listConfigNames($withTitle)
117 {
118 foreach ($this->storage->listProcesses() as $key => $title) {
119 if ($withTitle) {
120 echo $title . "\n";
121 } else {
122 echo $key . "\n";
123 }
124 }
125 }
126
127 protected function listBpNames(BpConfig $config)
128 {
129 foreach ($config->listBpNodes() as $title) {
130 echo $title . "\n";
131 }
132 }
133
134 protected function renderProblemTree($tree, $useColors = false, $depth = 0)
135 {
136 $output = '';
137
138 foreach ($tree as $name => $subtree) {
139 /** @var Node $node */
140 $node = $subtree['node'];
141
142 if ($node instanceof HostNode) {
143 $colors = $this->hostColors[$node->getState()];
144 } else {
145 $colors = $this->serviceColors[$node->getState()];
146 }
147
148 $state = sprintf('[%s]', $node->getStateName());
149 if ($useColors) {
150 $state = $this->screen->colorize($state, $colors[0], $colors[1]);
151 }
152
153 $output .= sprintf(
154 "%s%s %s %s\n",
155 str_repeat(' ', $depth),
156 $node instanceof BpNode ? $node->getOperator() : '-',
157 $state,
158 $node->getAlias()
159 );
160 $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1);
161 }
162
163 return $output;
164 }
165
166 protected function getFirstProcessName()
167 {
168 $list = $this->storage->listProcessNames();
169 return key($list);
170 }
171 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Controllers;
3
4 use Icinga\Module\Businessprocess\Web\Controller;
5 use Icinga\Module\Businessprocess\Web\Component\Dashboard;
6
7 class IndexController extends Controller
8 {
9 /**
10 * Show an overview page
11 */
12 public function indexAction()
13 {
14 $this->setTitle($this->translate('Business Process Overview'));
15 $this->controls()->add($this->overviewTab());
16 $this->content()->add(Dashboard::create($this->Auth(), $this->storage()));
17 $this->setAutorefreshInterval(15);
18 }
19 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Controllers;
3
4 use Icinga\Module\Businessprocess\Renderer\Breadcrumb;
5 use Icinga\Module\Businessprocess\Renderer\TileRenderer;
6 use Icinga\Module\Businessprocess\Simulation;
7 use Icinga\Module\Businessprocess\State\MonitoringState;
8 use Icinga\Module\Businessprocess\Web\Controller;
9 use Icinga\Module\Businessprocess\Web\Url;
10
11 class NodeController extends Controller
12 {
13 public function impactAction()
14 {
15 $this->setAutorefreshInterval(10);
16 $content = $this->content();
17 $this->controls()->add(
18 $this->singleTab($this->translate('Node Impact'))
19 );
20 $name = $this->params->get('name');
21 $this->addTitle($this->translate('Business Impact (%s)'), $name);
22
23 $simulation = Simulation::fromSession($this->session());
24 foreach ($this->storage()->listProcessNames() as $configName) {
25 $config = $this->storage()->loadProcess($configName);
26
27 // TODO: Fix issues with children, they do not exist unless resolved :-/
28 // This is a workaround:
29 foreach ($config->getRootNodes() as $node) {
30 $node->getState();
31 }
32 foreach ($config->getRootNodes() as $node) {
33 $node->clearState();
34 }
35
36 if (! $config->hasNode($name)) {
37 continue;
38 }
39
40 MonitoringState::apply($config);
41 $config->applySimulation($simulation);
42
43 foreach ($config->getNode($name)->getPaths() as $path) {
44 array_pop($path);
45 $node = array_pop($path);
46 $renderer = new TileRenderer($config, $config->getNode($node));
47 $renderer->setUrl(
48 Url::fromPath(
49 'businessprocess/process/show',
50 array('config' => $configName)
51 )
52 )->setPath($path);
53
54 $bc = Breadcrumb::create($renderer);
55 $bc->attributes()->set('data-base-target', '_next');
56 $content->add($bc);
57 }
58 }
59 }
60 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Controllers;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\State\MonitoringState;
6 use Icinga\Module\Businessprocess\Storage\ConfigDiff;
7 use Icinga\Module\Businessprocess\Html\Element;
8 use Icinga\Module\Businessprocess\Html\HtmlString;
9 use Icinga\Module\Businessprocess\Html\HtmlTag;
10 use Icinga\Module\Businessprocess\Html\Icon;
11 use Icinga\Module\Businessprocess\Html\Link;
12 use Icinga\Module\Businessprocess\Node;
13 use Icinga\Module\Businessprocess\Renderer\Breadcrumb;
14 use Icinga\Module\Businessprocess\Renderer\Renderer;
15 use Icinga\Module\Businessprocess\Renderer\TileRenderer;
16 use Icinga\Module\Businessprocess\Renderer\TreeRenderer;
17 use Icinga\Module\Businessprocess\Simulation;
18 use Icinga\Module\Businessprocess\Storage\LegacyConfigRenderer;
19 use Icinga\Module\Businessprocess\Web\Component\ActionBar;
20 use Icinga\Module\Businessprocess\Web\Component\RenderedProcessActionBar;
21 use Icinga\Module\Businessprocess\Web\Component\Tabs;
22 use Icinga\Module\Businessprocess\Web\Controller;
23 use Icinga\Module\Businessprocess\Web\Url;
24 use Icinga\Web\Notification;
25 use Icinga\Web\Widget\Tabextension\DashboardAction;
26
27 class ProcessController extends Controller
28 {
29 /** @var Renderer */
30 protected $renderer;
31
32 /**
33 * Create a new Business Process Configuration
34 */
35 public function createAction()
36 {
37 $this->assertPermission('businessprocess/create');
38
39 $title = $this->translate('Create a new Business Process');
40 $this->setTitle($title);
41 $this->controls()
42 ->add($this->tabsForCreate()->activate('create'))
43 ->add(HtmlTag::h1($title));
44
45 $this->content()->add(
46 $this->loadForm('bpConfig')
47 ->setStorage($this->storage())
48 ->setSuccessUrl('businessprocess/process/show')
49 ->handleRequest()
50 );
51 }
52
53 /**
54 * Upload an existing Business Process Configuration
55 */
56 public function uploadAction()
57 {
58 $this->assertPermission('businessprocess/create');
59
60 $title = $this->translate('Upload a Business Process Config file');
61 $this->setTitle($title);
62 $this->controls()
63 ->add($this->tabsForCreate()->activate('upload'))
64 ->add(HtmlTag::h1($title));
65
66 $this->content()->add(
67 $this->loadForm('BpUpload')
68 ->setStorage($this->storage())
69 ->setSuccessUrl('businessprocess/process/show')
70 ->handleRequest()
71 );
72 }
73
74 /**
75 * Show a business process
76 */
77 public function showAction()
78 {
79 $bp = $this->loadModifiedBpConfig();
80 $node = $this->getNode($bp);
81
82 MonitoringState::apply($bp);
83 $this->handleSimulations($bp);
84
85 $this->setTitle($this->translate('Business Process "%s"'), $bp->getTitle());
86
87 $renderer = $this->prepareRenderer($bp, $node);
88
89 if ($this->params->get('unlocked')) {
90 $renderer->unlock();
91 }
92
93 if ($bp->isEmpty() && $renderer->isLocked()) {
94 $this->redirectNow($this->url()->with('unlocked', true));
95 }
96
97 $this->prepareControls($bp, $renderer);
98 $missing = $bp->getMissingChildren();
99 if (! empty($missing)) {
100 $bp->addError(sprintf('There are missing nodes: %s', implode(', ', $missing)));
101 }
102 $this->content()->addContent($this->showHints($bp));
103 $this->content()->addContent($this->showWarnings($bp));
104 $this->content()->addContent($this->showErrors($bp));
105 $this->content()->add($renderer);
106 $this->loadActionForm($bp, $node);
107 $this->setDynamicAutorefresh();
108 }
109
110 protected function prepareControls($bp, $renderer)
111 {
112 $controls = $this->controls();
113
114 if ($this->showFullscreen) {
115 $controls->attributes()->add('class', 'want-fullscreen');
116 $controls->add(
117 Link::create(
118 Icon::create('resize-small'),
119 $this->url()->without('showFullscreen')->without('view'),
120 null,
121 array(
122 'style' => 'float: right',
123 'title' => $this->translate(
124 'Leave full screen and switch back to normal mode'
125 )
126 )
127 )
128 );
129 }
130
131 if (! ($this->showFullscreen || $this->view->compact)) {
132 $controls->add($this->getProcessTabs($bp, $renderer));
133 }
134 if (! $this->view->compact) {
135 $controls->add(Element::create('h1')->setContent($this->view->title));
136 }
137 $controls->add(Breadcrumb::create($renderer));
138 if (! $this->showFullscreen && ! $this->view->compact) {
139 $controls->add(
140 new RenderedProcessActionBar($bp, $renderer, $this->Auth(), $this->url())
141 );
142 }
143 }
144
145 protected function getNode(BpConfig $bp)
146 {
147 if ($nodeName = $this->params->get('node')) {
148 return $bp->getNode($nodeName);
149 } else {
150 return null;
151 }
152 }
153
154 protected function prepareRenderer($bp, $node)
155 {
156 if ($this->renderer === null) {
157 if ($this->params->get('mode') === 'tree') {
158 $renderer = new TreeRenderer($bp, $node);
159 } else {
160 $renderer = new TileRenderer($bp, $node);
161 }
162 $renderer->setUrl($this->url())
163 ->setPath($this->params->getValues('path'));
164
165 $this->renderer = $renderer;
166 }
167
168 return $this->renderer;
169 }
170
171 protected function getProcessTabs(BpConfig $bp, Renderer $renderer)
172 {
173 $tabs = $this->singleTab($bp->getTitle());
174 if ($renderer->isLocked()) {
175 $tabs->extend(new DashboardAction());
176 }
177
178 return $tabs;
179 }
180
181 protected function handleSimulations(BpConfig $bp)
182 {
183 $simulation = Simulation::fromSession($this->session());
184
185 if ($this->params->get('dismissSimulations')) {
186 Notification::success(
187 sprintf(
188 $this->translate('%d applied simulation(s) have been dropped'),
189 $simulation->count()
190 )
191 );
192 $simulation->clear();
193 $this->redirectNow($this->url()->without('dismissSimulations')->without('unlocked'));
194 }
195
196 $bp->applySimulation($simulation);
197 }
198
199 protected function loadActionForm(BpConfig $bp, Node $node = null)
200 {
201 $action = $this->params->get('action');
202 $form = null;
203 if ($this->showFullscreen) {
204 return;
205 }
206
207 $canEdit = $bp->getMetadata()->canModify();
208
209 if ($action === 'add' && $canEdit) {
210 $form = $this->loadForm('AddNode')
211 ->setProcess($bp)
212 ->setParentNode($node)
213 ->setSession($this->session())
214 ->handleRequest();
215 } elseif ($action === 'delete' && $canEdit) {
216 $form =$this->loadForm('DeleteNode')
217 ->setProcess($bp)
218 ->setNode($bp->getNode($this->params->get('deletenode')))
219 ->setParentNode($node)
220 ->setSession($this->session())
221 ->handleRequest();
222 } elseif ($action === 'edit' && $canEdit) {
223 $form =$this->loadForm('Process')
224 ->setProcess($bp)
225 ->setNode($bp->getNode($this->params->get('editnode')))
226 ->setSession($this->session())
227 ->handleRequest();
228 } elseif ($action === 'simulation') {
229 $form = $this->loadForm('simulation')
230 ->setNode($bp->getNode($this->params->get('simulationnode')))
231 ->setSimulation(Simulation::fromSession($this->session()))
232 ->handleRequest();
233 }
234
235 if ($form) {
236 $this->content()->prependContent(HtmlString::create((string) $form));
237 }
238 }
239
240 protected function setDynamicAutorefresh()
241 {
242 if (! $this->isXhr()) {
243 // This will trigger the very first XHR refresh immediately on page
244 // load. Please not that this may hammer the server in case we would
245 // decide to use autorefreshInterval for HTML meta-refreshes also.
246 $this->setAutorefreshInterval(1);
247 return;
248 }
249
250 if ($this->params->get('action')) {
251 $this->setAutorefreshInterval(45);
252 } else {
253 $this->setAutorefreshInterval(10);
254 }
255 }
256
257 protected function showWarnings(BpConfig $bp)
258 {
259 if ($bp->hasWarnings()) {
260 $ul = Element::create('ul', array('class' => 'warning'));
261 foreach ($bp->getWarnings() as $warning) {
262 $ul->createElement('li')->addContent($warning);
263 }
264
265 return $ul;
266 } else {
267 return null;
268 }
269 }
270
271 protected function showErrors(BpConfig $bp)
272 {
273 if ($bp->hasWarnings()) {
274 $ul = Element::create('ul', array('class' => 'error'));
275 foreach ($bp->getErrors() as $msg) {
276 $ul->createElement('li')->addContent($msg);
277 }
278
279 return $ul;
280 } else {
281 return null;
282 }
283 }
284
285 protected function showHints(BpConfig $bp)
286 {
287 $ul = Element::create('ul', array('class' => 'error'));
288 foreach ($bp->getErrors() as $error) {
289 $ul->createElement('li')->addContent($error);
290 }
291 if ($bp->hasChanges()) {
292 $ul->createElement('li')->setSeparator(' ')->addContent(sprintf(
293 $this->translate('This process has %d pending change(s).'),
294 $bp->countChanges()
295 ))->addContent(
296 Link::create(
297 $this->translate('Store'),
298 'businessprocess/process/config',
299 array('config' => $bp->getName())
300 )
301 )->addContent(
302 Link::create(
303 $this->translate('Dismiss'),
304 $this->url()->with('dismissChanges', true),
305 null
306 )
307 );
308 }
309
310 if ($bp->hasSimulations()) {
311 $ul->createElement('li')->setSeparator(' ')->addContent(sprintf(
312 $this->translate('This process shows %d simulated state(s).'),
313 $bp->countSimulations()
314 ))->addContent(Link::create(
315 $this->translate('Dismiss'),
316 $this->url()->with('dismissSimulations', true)
317 ));
318 }
319
320 if ($ul->hasContent()) {
321 return $ul;
322 } else {
323 return null;
324 }
325 }
326
327 /**
328 * Show the source code for a process
329 */
330 public function sourceAction()
331 {
332 $this->assertPermission('businessprocess/modify');
333
334 $bp = $this->loadModifiedBpConfig();
335 $this->view->showDiff = $showDiff = (bool) $this->params->get('showDiff', false);
336
337 $this->view->source = LegacyConfigRenderer::renderConfig($bp);
338 if ($this->view->showDiff) {
339 $this->view->diff = ConfigDiff::create(
340 $this->storage()->getSource($this->view->configName),
341 $this->view->source
342 );
343 $title = sprintf(
344 $this->translate('%s: Source Code Differences'),
345 $bp->getTitle()
346 );
347 } else {
348 $title = sprintf(
349 $this->translate('%s: Source Code'),
350 $bp->getTitle()
351 );
352 }
353
354 $this->setTitle($title);
355 $this->controls()
356 ->add($this->tabsForConfig($bp)->activate('source'))
357 ->add(HtmlTag::h1($title))
358 ->add($this->createConfigActionBar($bp, $showDiff));
359
360 $this->setViewScript('process/source');
361 }
362
363 /**
364 * Download a process configuration file
365 */
366 public function downloadAction()
367 {
368 $this->assertPermission('businessprocess/modify');
369
370 $config = $this->loadModifiedBpConfig();
371 $response = $this->getResponse();
372 $response->setHeader(
373 'Content-Disposition',
374 sprintf(
375 'attachment; filename="%s.conf";',
376 $config->getName()
377 )
378 );
379 $response->setHeader('Content-Type', 'text/plain');
380
381 echo LegacyConfigRenderer::renderConfig($config);
382 $this->doNotRender();
383 }
384
385 /**
386 * Modify a business process configuration
387 */
388 public function configAction()
389 {
390 $this->assertPermission('businessprocess/modify');
391
392 $bp = $this->loadModifiedBpConfig();
393
394 $title = sprintf(
395 $this->translate('%s: Configuration'),
396 $bp->getTitle()
397 );
398 $this->setTitle($title);
399 $this->controls()
400 ->add($this->tabsForConfig($bp)->activate('config'))
401 ->add(HtmlTag::h1($title))
402 ->add($this->createConfigActionBar($bp));
403
404 $url = Url::fromPath(
405 'businessprocess/process/show?unlocked',
406 array('config' => $bp->getName())
407 );
408 $this->content()->add(
409 $this->loadForm('bpConfig')
410 ->setProcessConfig($bp)
411 ->setStorage($this->storage())
412 ->setSuccessUrl($url)
413 ->handleRequest()
414 );
415 }
416
417 protected function createConfigActionBar(BpConfig $config, $showDiff = false)
418 {
419 $actionBar = new ActionBar();
420
421 if ($showDiff) {
422 $params = array('config' => $config->getName());
423 $actionBar->add(
424 Link::create(
425 $this->translate('Source'),
426 'businessprocess/process/source',
427 $params,
428 array(
429 'class' => 'icon-doc-text',
430 'title' => $this->translate('Show source code'),
431 )
432 )
433 );
434 } else {
435 $params = array(
436 'config' => $config->getName(),
437 'showDiff' => true
438 );
439
440 $actionBar->add(
441 Link::create(
442 $this->translate('Diff'),
443 'businessprocess/process/source',
444 $params,
445 array(
446 'class' => 'icon-flapping',
447 'title' => $this->translate('Highlight changes'),
448 )
449 )
450 );
451 }
452
453 $actionBar->add(
454 Link::create(
455 $this->translate('Download'),
456 'businessprocess/process/download',
457 array('config' => $config->getName()),
458 array(
459 'target' => '_blank',
460 'class' => 'icon-download',
461 'title' => $this->translate('Download process configuration')
462 )
463 )
464 );
465
466 return $actionBar;
467 }
468
469 protected function tabsForShow()
470 {
471 return $this->tabs()->add('show', array(
472 'label' => $this->translate('Business Process'),
473 'url' => $this->url()
474 ));
475 }
476
477 /**
478 * @return Tabs
479 */
480 protected function tabsForCreate()
481 {
482 return $this->tabs()->add('create', array(
483 'label' => $this->translate('Create'),
484 'url' => 'businessprocess/process/create'
485 ))->add('upload', array(
486 'label' => $this->translate('Upload'),
487 'url' => 'businessprocess/process/upload'
488 ));
489 }
490
491 protected function tabsForConfig(BpConfig $config)
492 {
493 $params = array(
494 'config' => $config->getName()
495 );
496
497 $tabs = $this->tabs()->add('config', array(
498 'label' => $this->translate('Process Configuration'),
499 'url' =>Url::fromPath('businessprocess/process/config', $params)
500 ));
501
502 if ($this->params->get('showDiff')) {
503 $params['showDiff'] = true;
504 }
505
506 $tabs->add('source', array(
507 'label' => $this->translate('Source'),
508 'url' =>Url::fromPath('businessprocess/process/source', $params)
509 ));
510
511 return $tabs;
512 }
513 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Modification\ProcessChanges;
7 use Icinga\Module\Businessprocess\Web\Form\QuickForm;
8 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
9 use Icinga\Web\Session\SessionNamespace;
10
11 class AddNodeForm extends QuickForm
12 {
13 /** @var MonitoringBackend */
14 protected $backend;
15
16 /** @var BpConfig */
17 protected $bp;
18
19 /** @var BpNode */
20 protected $parent;
21
22 protected $objectList = array();
23
24 protected $processList = array();
25
26 /** @var SessionNamespace */
27 protected $session;
28
29 public function setup()
30 {
31 $view = $this->getView();
32 if ($this->hasParentNode()) {
33 $this->addHtml(
34 '<h2>' . $view->escape(
35 sprintf($this->translate('Add a node to %s'), $this->parent->getAlias())
36 ) . '</h2>'
37 );
38 } else {
39 $this->addHtml(
40 '<h2>' . $this->translate('Add a new root node') . '</h2>'
41 );
42 }
43
44 $type = $this->selectNodeType();
45 switch ($type) {
46 case 'host':
47 $this->selectHost();
48 break;
49 case 'service':
50 $this->selectService();
51 break;
52 case 'process':
53 $this->selectProcess();
54 break;
55 case 'new-process':
56 $this->addNewProcess();
57 break;
58 case null:
59 $this->setSubmitLabel($this->translate('Next'));
60 return;
61 }
62 }
63
64 protected function addNewProcess()
65 {
66 $this->addElement('text', 'name', array(
67 'label' => $this->translate('Name'),
68 'required' => true,
69 'description' => $this->translate(
70 'This is the unique identifier of this process'
71 ),
72 ));
73
74 $this->addElement('text', 'alias', array(
75 'label' => $this->translate('Title'),
76 'description' => $this->translate(
77 'Usually this title will be shown for this node. Equals name'
78 . ' if not given'
79 ),
80 ));
81
82 $this->addElement('select', 'operator', array(
83 'label' => $this->translate('Operator'),
84 'required' => true,
85 'multiOptions' => array(
86 '&' => $this->translate('AND'),
87 '|' => $this->translate('OR'),
88 '!' => $this->translate('NOT'),
89 '<' => $this->translate('DEG'),
90 '1' => $this->translate('MIN 1'),
91 '2' => $this->translate('MIN 2'),
92 '3' => $this->translate('MIN 3'),
93 '4' => $this->translate('MIN 4'),
94 '5' => $this->translate('MIN 5'),
95 '6' => $this->translate('MIN 6'),
96 '7' => $this->translate('MIN 7'),
97 '8' => $this->translate('MIN 8'),
98 '9' => $this->translate('MIN 9'),
99 )
100 ));
101
102 $this->addElement('select', 'display', array(
103 'label' => $this->translate('Visualization'),
104 'required' => true,
105 'description' => $this->translate(
106 'Where to show this process'
107 ),
108 'value' => $this->hasParentNode() ? '0' : '1',
109 'multiOptions' => array(
110 '1' => $this->translate('Toplevel Process'),
111 '0' => $this->translate('Subprocess only'),
112 )
113 ));
114
115 $this->addElement('text', 'infoUrl', array(
116 'label' => $this->translate('Info URL'),
117 'description' => $this->translate(
118 'URL pointing to more information about this node'
119 )
120 ));
121 }
122
123 /**
124 * @return string|null
125 */
126 protected function selectNodeType()
127 {
128 $types = array();
129 if ($this->hasParentNode()) {
130 $types['host'] = $this->translate('Host');
131 $types['service'] = $this->translate('Service');
132 } elseif (! $this->hasProcesses()) {
133 $this->addElement('hidden', 'node_type', array(
134 'ignore' => true,
135 'decorators' => array('ViewHelper'),
136 'value' => 'new-process'
137 ));
138
139 return 'new-process';
140 }
141
142 if ($this->hasProcesses()) {
143 $types['process'] = $this->translate('Existing Process');
144 }
145
146 $types['new-process'] = $this->translate('New Process Node');
147
148 $this->addElement('select', 'node_type', array(
149 'label' => $this->translate('Node type'),
150 'required' => true,
151 'description' => $this->translate(
152 'The node type you want to add'
153 ),
154 'ignore' => true,
155 'class' => 'autosubmit',
156 'multiOptions' => $this->optionalEnum($types)
157 ));
158
159 return $this->getSentValue('node_type');
160 }
161
162 protected function selectHost()
163 {
164 $this->addElement('multiselect', 'children', array(
165 'label' => $this->translate('Hosts'),
166 'required' => true,
167 'size' => 8,
168 'style' => 'width: 25em',
169 'multiOptions' => $this->enumHostList(),
170 'description' => $this->translate(
171 'Hosts that should be part of this business process node'
172 )
173 ));
174 }
175
176 protected function selectService()
177 {
178 $this->addHostElement();
179 if ($host = $this->getSentValue('host')) {
180 $this->addServicesElement($host);
181 } else {
182 $this->setSubmitLabel($this->translate('Next'));
183 }
184 }
185
186 protected function addHostElement()
187 {
188 $this->addElement('select', 'host', array(
189 'label' => $this->translate('Host'),
190 'required' => true,
191 'ignore' => true,
192 'class' => 'autosubmit',
193 'multiOptions' => $this->optionalEnum($this->enumHostForServiceList()),
194 ));
195 }
196
197 protected function addServicesElement($host)
198 {
199 $this->addElement('multiselect', 'children', array(
200 'label' => $this->translate('Services'),
201 'required' => true,
202 'size' => 8,
203 'style' => 'width: 25em',
204 'multiOptions' => $this->enumServiceList($host),
205 'description' => $this->translate(
206 'Services that should be part of this business process node'
207 )
208 ));
209 }
210
211 protected function selectProcess()
212 {
213 $this->addElement('multiselect', 'children', array(
214 'label' => $this->translate('Process nodes'),
215 'required' => true,
216 'size' => 8,
217 'style' => 'width: 25em',
218 'multiOptions' => $this->enumProcesses(),
219 'description' => $this->translate(
220 'Other processes that should be part of this business process node'
221 )
222 ));
223 }
224
225 /**
226 * @param MonitoringBackend $backend
227 * @return $this
228 */
229 public function setBackend(MonitoringBackend $backend)
230 {
231 $this->backend = $backend;
232 return $this;
233 }
234
235 /**
236 * @param BpConfig $process
237 * @return $this
238 */
239 public function setProcess(BpConfig $process)
240 {
241 $this->bp = $process;
242 $this->setBackend($process->getBackend());
243 return $this;
244 }
245
246 /**
247 * @param BpNode|null $node
248 * @return $this
249 */
250 public function setParentNode(BpNode $node = null)
251 {
252 $this->parent = $node;
253 return $this;
254 }
255
256 /**
257 * @return bool
258 */
259 public function hasParentNode()
260 {
261 return $this->parent !== null;
262 }
263
264 /**
265 * @param SessionNamespace $session
266 * @return $this
267 */
268 public function setSession(SessionNamespace $session)
269 {
270 $this->session = $session;
271 return $this;
272 }
273
274 protected function enumHostForServiceList()
275 {
276 $names = $this->backend->select()->from('hostStatus', array(
277 'hostname' => 'host_name',
278 ))->order('host_name')->getQuery()->fetchColumn();
279
280 // fetchPairs doesn't seem to work when using the same column with
281 // different aliases twice
282
283 return array_combine((array) $names, (array) $names);
284 }
285
286 protected function enumHostList()
287 {
288 $names = $this->backend->select()->from('hostStatus', array(
289 'hostname' => 'host_name',
290 ))->order('host_name')->getQuery()->fetchColumn();
291
292 // fetchPairs doesn't seem to work when using the same column with
293 // different aliases twice
294 $res = array();
295 $suffix = ';Hoststatus';
296 foreach ($names as $name) {
297 $res[$name . $suffix] = $name;
298 }
299
300 return $res;
301 }
302
303 protected function enumServiceList($host)
304 {
305 $query = $this->backend->select()->from(
306 'serviceStatus',
307 array('service' => 'service_description')
308 )->where('host_name', $host);
309 $query->order('service_description');
310 $names = $query->getQuery()->fetchColumn();
311
312 $services = array();
313 foreach ($names as $name) {
314 $services[$host . ';' . $name] = $name;
315 }
316
317 return $services;
318 }
319
320 protected function hasProcesses()
321 {
322 return count($this->enumProcesses()) > 0;
323 }
324
325 protected function enumProcesses()
326 {
327 $list = array();
328
329 foreach ($this->bp->getNodes() as $node) {
330 if ($node instanceof BpNode) {
331 // TODO: Blacklist parents
332 $list[(string) $node] = (string) $node; // display name?
333 }
334 }
335
336 natsort($list);
337 return $list;
338 }
339
340 protected function fetchObjectList()
341 {
342 $this->objectList = array();
343 $hosts = $this->backend->select()->from('hostStatus', array(
344 'hostname' => 'host_name',
345 'in_downtime' => 'host_in_downtime',
346 'ack' => 'host_acknowledged',
347 'state' => 'host_state'
348 ))->order('host_name')->getQuery()->fetchAll();
349
350 $services = $this->backend->select()->from('serviceStatus', array(
351 'hostname' => 'host_name',
352 'service' => 'service_description',
353 'in_downtime' => 'service_in_downtime',
354 'ack' => 'service_acknowledged',
355 'state' => 'service_state'
356 ))->order('host_name')->order('service_description')->getQuery()->fetchAll();
357
358 foreach ($hosts as $host) {
359 $this->objectList[$host->hostname] = array(
360 $host->hostname . ';Hoststatus' => 'Host Status'
361 );
362 }
363
364 foreach ($services as $service) {
365 $this->objectList[$service->hostname][
366 $service->hostname . ';' . $service->service
367 ] = $service->service;
368 }
369
370 return $this;
371 }
372
373 public function onSuccess()
374 {
375 $changes = ProcessChanges::construct($this->bp, $this->session);
376 switch ($this->getValue('node_type')) {
377 case 'host':
378 case 'service':
379 case 'process':
380 $changes->addChildrenToNode($this->getValue('children'), $this->parent);
381 break;
382 case 'new-process':
383 $properties = $this->getValues();
384 unset($properties['name']);
385 if ($this->hasParentNode()) {
386 $properties['parentName'] = $this->parent->getName();
387 }
388 $changes->createNode($this->getValue('name'), $properties);
389 break;
390 }
391
392 // Trigger session destruction to make sure it get's stored.
393 // TODO: figure out why this is necessary, might be an unclean shutdown on redirect
394 unset($changes);
395
396 parent::onSuccess();
397 }
398 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Icinga\Authentication\Auth;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Web\Form\BpConfigBaseForm;
7
8 class BpConfigForm extends BpConfigBaseForm
9 {
10 protected $deleteButtonName;
11
12 public function setup()
13 {
14 $this->addElement('text', 'name', array(
15 'label' => $this->translate('Name'),
16 'required' => true,
17 'validators' => array(
18 array(
19 'validator' => 'StringLength',
20 'options' => array(
21 'min' => 2,
22 'max' => 40
23 )
24 ),
25 array(
26 'validator' => 'Regex',
27 'options' => array(
28 'pattern' => '/^[a-zA-Z0-9](?:[a-zA-Z0-9 ._-]*)?[a-zA-Z0-9_]$/'
29 )
30 )
31 ),
32 'description' => $this->translate(
33 'This is the unique identifier of this process'
34 ),
35 ));
36
37 $this->addElement('text', 'Title', array(
38 'label' => $this->translate('Title'),
39 'description' => $this->translate(
40 'Usually this title will be shown for this process. Equals name'
41 . ' if not given'
42 ),
43 ));
44
45 $this->addElement('textarea', 'Description', array(
46 'label' => $this->translate('Description'),
47 'description' => $this->translate(
48 'A slightly more detailed description for this process, about 100-150 characters long'
49 ),
50 'rows' => 4,
51 ));
52
53 $this->addElement('select', 'Backend', array(
54 'label' => $this->translate('Backend'),
55 'description' => $this->translate(
56 'Icinga Web Monitoring Backend where current object states for'
57 . ' this process should be retrieved from'
58 ),
59 'multiOptions' => array(
60 null => $this->translate('Use the configured default backend'),
61 ) + $this->listAvailableBackends()
62 ));
63
64 $this->addElement('select', 'Statetype', array(
65 'label' => $this->translate('State Type'),
66 'required' => true,
67 'description' => $this->translate(
68 'Whether this process should be based on Icinga hard or soft states'
69 ),
70 'multiOptions' => array(
71 'soft' => $this->translate('Use SOFT states'),
72 'hard' => $this->translate('Use HARD states'),
73 )
74 ));
75
76 $this->addElement('select', 'AddToMenu', array(
77 'label' => $this->translate('Add to menu'),
78 'required' => true,
79 'description' => $this->translate(
80 'Whether this process should be linked in the main Icinga Web 2 menu'
81 ),
82 'multiOptions' => array(
83 'yes' => $this->translate('Yes'),
84 'no' => $this->translate('No'),
85 )
86 ));
87
88 if ($this->config === null) {
89 $this->setSubmitLabel(
90 $this->translate('Add')
91 );
92 } else {
93 $config = $this->config;
94
95 $meta = $config->getMetadata();
96 foreach ($meta->getProperties() as $k => $v) {
97 if ($el = $this->getElement($k)) {
98 $el->setValue($v);
99 }
100 }
101 $this->getElement('name')
102 ->setValue($config->getName())
103 ->setAttrib('readonly', true);
104
105 $this->setSubmitLabel(
106 $this->translate('Store')
107 );
108
109 $label = $this->translate('Delete');
110 $el = $this->createElement('submit', $label, array(
111 'data-base-target' => '_main'
112 ))->setLabel($label)->setDecorators(array('ViewHelper'));
113 $this->deleteButtonName = $el->getName();
114 $this->addElement($el);
115 }
116 }
117
118 protected function onRequest()
119 {
120 $name = $this->getValue('name');
121
122 if ($this->shouldBeDeleted()) {
123 $this->config->clearAppliedChanges();
124 $this->storage->deleteProcess($name);
125 $this->setSuccessUrl('businessprocess');
126 $this->redirectOnSuccess(sprintf('Process %s has been deleted', $name));
127 }
128 }
129
130 public function onSuccess()
131 {
132 $name = $this->getValue('name');
133
134 if ($this->config === null) {
135 // New config
136 $config = new BpConfig();
137 $config->setName($name);
138
139 if (! $this->prepareMetadata($config)) {
140 return;
141 }
142
143 $this->setSuccessUrl(
144 $this->getSuccessUrl()->setParams(
145 array('config' => $name, 'unlocked' => true)
146 )
147 );
148 $this->setSuccessMessage(sprintf('Process %s has been created', $name));
149 } else {
150 $config = $this->config;
151 $this->setSuccessMessage(sprintf('Process %s has been stored', $name));
152 }
153 $meta = $config->getMetadata();
154 foreach ($this->getValues() as $key => $value) {
155 if ($value === null || $value === '') {
156 continue;
157 }
158 if ($meta->hasKey($key)) {
159 $meta->set($key, $value);
160 }
161 }
162
163 $this->storage->storeProcess($config);
164 $config->clearAppliedChanges();
165 $this->setSuccessUrl('businessprocess/process/show', array('config' => $name));
166 parent::onSuccess();
167 }
168
169 public function hasDeleteButton()
170 {
171 return $this->deleteButtonName !== null;
172 }
173
174 public function shouldBeDeleted()
175 {
176 if (! $this->hasDeleteButton()) {
177 return false;
178 }
179
180 $name = $this->deleteButtonName;
181 return $this->getSentValue($name) === $this->getElement($name)->getLabel();
182 }
183 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Exception;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Storage\LegacyConfigParser;
7 use Icinga\Module\Businessprocess\Web\Form\BpConfigBaseForm;
8 use Icinga\Web\Notification;
9
10 class BpUploadForm extends BpConfigBaseForm
11 {
12 protected $backend;
13
14 protected $node;
15
16 protected $objectList = array();
17
18 protected $processList = array();
19
20 protected $deleteButtonName;
21
22 private $sourceCode;
23
24 /** @var BpConfig */
25 private $uploadedConfig;
26
27 public function setup()
28 {
29 $this->showUpload();
30 if ($this->hasSource()) {
31 $this->showDetails();
32 }
33 }
34
35 protected function showDetails()
36 {
37 $this->addElement('text', 'name', array(
38 'label' => $this->translate('Name'),
39 'required' => true,
40 'description' => $this->translate(
41 'This is the unique identifier of this process'
42 ),
43 'validators' => array(
44 array(
45 'validator' => 'StringLength',
46 'options' => array(
47 'min' => 2,
48 'max' => 40
49 )
50 ),
51 array(
52 'validator' => 'Regex',
53 'options' => array(
54 'pattern' => '/^[a-zA-Z0-9](?:[a-zA-Z0-9 ._-]*)?[a-zA-Z0-9_]$/'
55 )
56 )
57 ),
58 ));
59
60 $this->addElement('textarea', 'source', array(
61 'label' => $this->translate('Source'),
62 'description' => $this->translate(
63 'Business process source code'
64 ),
65 'value' => $this->sourceCode,
66 'class' => 'preformatted smaller',
67 'rows' => 7,
68 ));
69
70 $this->getUploadedConfig();
71
72 $this->setSubmitLabel(
73 $this->translate('Store')
74 );
75 }
76
77 public function getUploadedConfig()
78 {
79 if ($this->uploadedConfig === null) {
80 $this->uploadedConfig = $this->parseSubmittedSourceCode();
81 }
82
83 return $this->uploadedConfig;
84 }
85
86 protected function parseSubmittedSourceCode()
87 {
88 $code = $this->getSentValue('source');
89 $name = $this->getSentValue('name', '<new config>');
90 if (empty($code)) {
91 $code = $this->sourceCode;
92 }
93
94 try {
95 $config = LegacyConfigParser::parseString($name, $code);
96
97 if ($config->hasErrors()) {
98 foreach ($config->getErrors() as $error) {
99 $this->addError($error);
100 }
101 }
102 } catch (Exception $e) {
103 $this->addError($e->getMessage());
104 return null;
105 }
106
107 return $config;
108 }
109
110 protected function hasSource()
111 {
112 if ($this->hasBeenSent() && $source = $this->getSentValue('source')) {
113 $this->sourceCode = $source;
114 } else {
115 $this->processUploadedSource();
116 }
117
118 if (empty($this->sourceCode)) {
119 return false;
120 } else {
121 $this->removeElement('uploaded_file');
122 return true;
123 }
124 }
125
126 protected function showUpload()
127 {
128 $this->setAttrib('enctype', 'multipart/form-data');
129
130 $this->addElement('file', 'uploaded_file', array(
131 'label' => $this->translate('File'),
132 'destination' => $this->getTempDir(),
133 'required' => true,
134 ));
135
136 /** @var \Zend_Form_Element_File $el */
137 $el = $this->getElement('uploaded_file');
138 $el->setValueDisabled(true);
139
140 $this->setSubmitLabel(
141 $this->translate('Next')
142 );
143 }
144
145 protected function getTempDir()
146 {
147 return sys_get_temp_dir();
148 }
149
150 protected function processUploadedSource()
151 {
152 /** @var \Zend_Form_Element_File $el */
153 $el = $this->getElement('uploaded_file');
154
155 if ($el && $this->hasBeenSent()) {
156 $tmpdir = $this->getTempDir();
157 $tmpfile = tempnam($tmpdir, 'bpupload_');
158
159 // TODO: race condition, try to do this without unlinking here
160 unlink($tmpfile);
161
162 $el->addFilter('Rename', $tmpfile);
163 if ($el->receive()) {
164 $this->sourceCode = file_get_contents($tmpfile);
165 unlink($tmpfile);
166 } else {
167 foreach ($el->file->getMessages() as $error) {
168 $this->addError($error);
169 }
170 }
171 }
172
173 return $this;
174 }
175
176 public function onSuccess()
177 {
178 $config = $this->getUploadedConfig();
179 $name = $config->getName();
180
181 if ($this->storage->hasProcess($name)) {
182 $this->addError(sprintf(
183 $this->translate('A process named "%s" already exists'),
184 $name
185 ));
186
187 return;
188 }
189
190 if (! $this->prepareMetadata($config)) {
191 return;
192 }
193
194 $this->storage->storeProcess($config);
195 Notification::success(sprintf('Process %s has been stored', $name));
196 }
197 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Modification\ProcessChanges;
7 use Icinga\Module\Businessprocess\Node;
8 use Icinga\Module\Businessprocess\Web\Form\QuickForm;
9 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
10 use Icinga\Web\Session\SessionNamespace;
11
12 class DeleteNodeForm extends QuickForm
13 {
14 /** @var MonitoringBackend */
15 protected $backend;
16
17 /** @var BpConfig */
18 protected $bp;
19
20 /** @var Node */
21 protected $node;
22
23 /** @var BpNode */
24 protected $parentNode;
25
26 /** @var SessionNamespace */
27 protected $session;
28
29 public function setup()
30 {
31 $node = $this->node;
32 $view = $this->getView();
33 $this->addHtml(
34 '<h2>' . $view->escape(
35 sprintf($this->translate('Delete "%s"'), $node->getAlias())
36 ) . '</h2>'
37 );
38
39 $biLink = $view->qlink(
40 $node->getAlias(),
41 'businessprocess/node/impact',
42 array('name' => $node->getName()),
43 array('data-base-target' => '_next')
44 );
45 $this->addHtml(
46 '<p>' . sprintf(
47 $view->escape(
48 $this->translate('Unsure? Show business impact of "%s"')
49 ),
50 $biLink
51 ) . '</p>'
52 );
53
54 if ($this->parentNode) {
55 $yesMsg = sprintf(
56 $this->translate('Delete from %s'),
57 $this->parentNode->getAlias()
58 );
59 } else {
60 $yesMsg = sprintf(
61 $this->translate('Delete root node "%s"'),
62 $this->node->getAlias()
63 );
64 }
65
66 $this->addElement('select', 'confirm', array(
67 'label' => $this->translate('Are you sure?'),
68 'required' => true,
69 'description' => $this->translate(
70 'Do you really want to delete this node?'
71 ),
72 'multiOptions' => $this->optionalEnum(array(
73 'no' => $this->translate('No'),
74 'yes' => $yesMsg,
75 'all' => sprintf($this->translate('Delete all occurrences of %s'), $node->getAlias()),
76 ))
77 ));
78 }
79
80 /**
81 * @param MonitoringBackend $backend
82 * @return $this
83 */
84 public function setBackend(MonitoringBackend $backend)
85 {
86 $this->backend = $backend;
87 return $this;
88 }
89
90 /**
91 * @param BpConfig $process
92 * @return $this
93 */
94 public function setProcess(BpConfig $process)
95 {
96 $this->bp = $process;
97 $this->setBackend($process->getBackend());
98 return $this;
99 }
100
101 /**
102 * @param Node $node
103 * @return $this
104 */
105 public function setNode(Node $node)
106 {
107 $this->node = $node;
108 return $this;
109 }
110
111 /**
112 * @param BpNode|null $node
113 * @return $this
114 */
115 public function setParentNode(BpNode $node = null)
116 {
117 $this->parentNode = $node;
118 return $this;
119 }
120
121 /**
122 * @param SessionNamespace $session
123 * @return $this
124 */
125 public function setSession(SessionNamespace $session)
126 {
127 $this->session = $session;
128 return $this;
129 }
130
131 public function onSuccess()
132 {
133 $changes = ProcessChanges::construct($this->bp, $this->session);
134
135 switch ($this->getValue('confirm')) {
136 case 'yes':
137 $changes->deleteNode($this->node, $this->parentNode->getName());
138 break;
139 case 'all':
140 $changes->deleteNode($this->node);
141 break;
142 case 'no':
143 $this->setSuccessMessage($this->translate('Well, maybe next time'));
144 }
145 // Trigger session desctruction to make sure it get's stored.
146 // TODO: figure out why this is necessary, might be an unclean shutdown on redirect
147 unset($changes);
148
149 parent::onSuccess();
150 }
151 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Modification\ProcessChanges;
7 use Icinga\Module\Businessprocess\Web\Form\QuickForm;
8 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
9 use Icinga\Web\Notification;
10 use Icinga\Web\Session\SessionNamespace;
11
12 class ProcessForm extends QuickForm
13 {
14 /** @var MonitoringBackend */
15 protected $backend;
16
17 /** @var BpConfig */
18 protected $bp;
19
20 /** @var BpNode */
21 protected $node;
22
23 protected $objectList = array();
24
25 protected $processList = array();
26
27 /** @var SessionNamespace */
28 protected $session;
29
30 public function setup()
31 {
32 if ($this->node === null) {
33 $this->addElement('text', 'name', array(
34 'label' => $this->translate('Name'),
35 'required' => true,
36 'description' => $this->translate(
37 'This is the unique identifier of this process'
38 ),
39 ));
40 } else {
41 $this->addHtml(
42 '<h2>' . $this->getView()->escape(
43 sprintf($this->translate('Modify "%s"'), $this->node->getAlias())
44 ) . '</h2>'
45 );
46 }
47
48 $this->addElement('text', 'alias', array(
49 'label' => $this->translate('Title'),
50 'description' => $this->translate(
51 'Usually this title will be shown for this node. Equals name'
52 . ' if not given'
53 ),
54 ));
55
56 $this->addElement('select', 'operator', array(
57 'label' => $this->translate('Operator'),
58 'required' => true,
59 'multiOptions' => array(
60 '&' => $this->translate('AND'),
61 '|' => $this->translate('OR'),
62 '!' => $this->translate('NOT'),
63 '1' => $this->translate('MIN 1'),
64 '2' => $this->translate('MIN 2'),
65 '3' => $this->translate('MIN 3'),
66 '4' => $this->translate('MIN 4'),
67 '5' => $this->translate('MIN 5'),
68 '6' => $this->translate('MIN 6'),
69 '7' => $this->translate('MIN 7'),
70 '8' => $this->translate('MIN 8'),
71 '9' => $this->translate('MIN 9'),
72 )
73 ));
74
75 $this->addElement('select', 'display', array(
76 'label' => $this->translate('Visualization'),
77 'required' => true,
78 'description' => $this->translate(
79 'Where to show this process'
80 ),
81 'multiOptions' => array(
82 '1' => $this->translate('Toplevel Process'),
83 '0' => $this->translate('Subprocess only'),
84 )
85 ));
86
87 $this->addElement('text', 'url', array(
88 'label' => $this->translate('Info URL'),
89 'description' => $this->translate(
90 'URL pointing to more information about this node'
91 )
92 ));
93
94 if ($node = $this->node) {
95 if ($node->hasAlias()) {
96 $this->getElement('alias')->setValue($node->getAlias());
97 }
98 $this->getElement('operator')->setValue($node->getOperator());
99 $this->getElement('display')->setValue($node->getDisplay());
100 if ($node->hasInfoUrl()) {
101 $this->getElement('url')->setValue($node->getInfoUrl());
102 }
103 }
104 }
105
106 /**
107 * @param MonitoringBackend $backend
108 * @return $this
109 */
110 public function setBackend(MonitoringBackend $backend)
111 {
112 $this->backend = $backend;
113 return $this;
114 }
115
116 /**
117 * @param BpConfig $process
118 * @return $this
119 */
120 public function setProcess(BpConfig $process)
121 {
122 $this->bp = $process;
123 $this->setBackend($process->getBackend());
124 return $this;
125 }
126
127 /**
128 * @param BpNode $node
129 * @return $this
130 */
131 public function setNode(BpNode $node)
132 {
133 $this->node = $node;
134 return $this;
135 }
136
137 /**
138 * @param SessionNamespace $session
139 * @return $this
140 */
141 public function setSession(SessionNamespace $session)
142 {
143 $this->session = $session;
144 return $this;
145 }
146
147 public function onSuccess()
148 {
149 $changes = ProcessChanges::construct($this->bp, $this->session);
150
151 $modifications = array();
152 $alias = $this->getValue('alias');
153 $operator = $this->getValue('operator');
154 $display = $this->getValue('display');
155 $url = $this->getValue('url');
156 if (empty($url)) {
157 $url = null;
158 }
159 if (empty($alias)) {
160 $alias = null;
161 }
162 // TODO: rename
163
164 if ($node = $this->node) {
165 if ($display !== $node->getDisplay()) {
166 $modifications['display'] = $display;
167 }
168 if ($operator !== $node->getOperator()) {
169 $modifications['operator'] = $operator;
170 }
171 if ($url !== $node->getInfoUrl()) {
172 $modifications['infoUrl'] = $url;
173 }
174 if ($alias !== $node->getAlias()) {
175 $modifications['alias'] = $alias;
176 }
177 } else {
178 $modifications = array(
179 'display' => $display,
180 'operator' => $operator,
181 'infoUrl' => $url,
182 'alias' => $alias,
183 );
184 }
185
186 if (! empty($modifications)) {
187 if ($this->node === null) {
188 $changes->createNode($this->getValue('name'), $modifications);
189 } else {
190 $changes->modifyNode($this->node, $modifications);
191 }
192
193 Notification::success(
194 sprintf(
195 'Process %s has been modified',
196 $this->bp->getName()
197 )
198 );
199 }
200 }
201 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Forms;
3
4 use Icinga\Module\Businessprocess\MonitoredNode;
5 use Icinga\Module\Businessprocess\Simulation;
6 use Icinga\Module\Businessprocess\Web\Form\QuickForm;
7
8 class SimulationForm extends QuickForm
9 {
10 /** @var MonitoredNode */
11 protected $node;
12
13 /** @var MonitoredNode */
14 protected $simulatedNode;
15
16 /** @var Simulation */
17 protected $simulation;
18
19 public function setup()
20 {
21 $states = $this->enumStateNames();
22
23 // TODO: Fetch state from object
24 if ($this->simulatedNode) {
25 $simulatedState = $this->simulatedNode->getState();
26 $states[$simulatedState] = sprintf(
27 '%s (%s)',
28 $this->node->getStateName($simulatedState),
29 $this->translate('Current simulation')
30 );
31 $node = $this->simulatedNode;
32 $hasSimulation = true;
33 } else {
34 $hasSimulation = false;
35 $node = $this->node;
36 }
37
38 $view = $this->getView();
39 if ($hasSimulation) {
40 $title = $this->translate('Modify simulation for %s');
41 } else {
42 $title = $this->translate('Add simulation for %s');
43 }
44 $this->addHtml(
45 '<h2>'
46 . $view->escape(sprintf($title, $node->getAlias()))
47 . '</h2>'
48 );
49
50 $this->addElement('select', 'state', array(
51 'label' => $this->translate('State'),
52 'multiOptions' => $states,
53 'class' => 'autosubmit',
54 'value' => $this->simulatedNode ? $node->getState() : null,
55 ));
56 if (in_array($this->getSentValue('state'), array('0', '99'))) {
57 return;
58 }
59 if ($hasSimulation || ctype_digit($this->getSentValue('state'))) {
60 $this->addElement('checkbox', 'acknowledged', array(
61 'label' => $this->translate('Acknowledged'),
62 'value' => $node->isAcknowledged(),
63 ));
64
65 $this->addElement('checkbox', 'in_downtime', array(
66 'label' => $this->translate('In downtime'),
67 'value' => $node->isInDowntime(),
68 ));
69 }
70
71 $this->setSubmitLabel($this->translate('Apply'));
72 }
73
74 public function setNode($node)
75 {
76 $this->node = $node;
77 return $this;
78 }
79
80 public function setSimulation(Simulation $simulation)
81 {
82 $this->simulation = $simulation;
83
84 $name = $this->node->getName();
85 if ($simulation->hasNode($name)) {
86 $this->simulatedNode = clone($this->node);
87 $s = $simulation->getNode($name);
88 $this->simulatedNode->setState($s->state)
89 ->setAck($s->acknowledged)
90 ->setDowntime($s->in_downtime)
91 ->setMissing(false);
92 }
93
94 return $this;
95 }
96
97 public function onSuccess()
98 {
99 $nodeName = $this->node->getName();
100
101 if (ctype_digit($this->getValue('state'))) {
102 $this->notifySuccess($this->translate('Simulation has been set'));
103 $this->simulation->set($nodeName, (object) array(
104 'state' => $this->getValue('state'),
105 'acknowledged' => $this->getValue('acknowledged'),
106 'in_downtime' => $this->getValue('in_downtime'),
107 ));
108 } else {
109 if ($this->simulation->remove($nodeName)) {
110 $this->notifySuccess($this->translate('Simulation has been removed'));
111 }
112 }
113 $this->redirectOnSuccess();
114 }
115
116 /**
117 * @return array
118 */
119 protected function enumStateNames()
120 {
121 $states = array(
122 null => sprintf(
123 $this->translate('Use current state (%s)'),
124 $this->translate($this->node->getStateName())
125 )
126 ) + $this->node->enumStateNames();
127
128 return $states;
129 }
130 }
0 # Icinga Web 2 - Head for multiple monitoring backends.
1 # Copyright (C) 2017 Icinga Development Team
2 # This file is distributed under the same license as Businessprocess Module.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 #
5 msgid ""
6 msgstr ""
7 "Project-Id-Version: Businessprocess Module (2.1.0)\n"
8 "Report-Msgid-Bugs-To: dev@icinga.org\n"
9 "POT-Creation-Date: 2017-02-20 15:18+0100\n"
10 "PO-Revision-Date: 2017-02-20 15:20+0100\n"
11 "Last-Translator: Thomas Gelf <thomas@gelf.net>\n"
12 "Language: de_DE\n"
13 "MIME-Version: 1.0\n"
14 "Content-Type: text/plain; charset=UTF-8\n"
15 "Content-Transfer-Encoding: 8bit\n"
16 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17 "Language-Team: \n"
18 "X-Generator: Poedit 1.8.7.1\n"
19
20 #: application/controllers/ProcessController.php:189
21 #, php-format
22 msgid "%d applied simulation(s) have been dropped"
23 msgstr "%d angewendete Simulation(en) wurde(n) entfernt"
24
25 #: library/Businessprocess/Web/Controller.php:201
26 #, php-format
27 msgid "%d pending change(s) have been dropped"
28 msgstr "%d vorgenommene Änderung(en) wurde(n) verworfen"
29
30 #: application/controllers/ProcessController.php:396
31 #, php-format
32 msgid "%s: Configuration"
33 msgstr "%s: Konfiguration"
34
35 #: application/controllers/ProcessController.php:350
36 #, php-format
37 msgid "%s: Source Code"
38 msgstr "%s: Quellcode"
39
40 #: application/controllers/ProcessController.php:345
41 #, php-format
42 msgid "%s: Source Code Differences"
43 msgstr "%s: Quellcode Unterschiede"
44
45 #: library/Businessprocess/ProvidedHook/Director/DataType/BusinessProcess.php:17
46 msgid "(no process config chosen)"
47 msgstr "(keine Prozess-Konfiguration gewählt)"
48
49 #: library/Businessprocess/Web/Form/QuickBaseForm.php:113
50 msgid "- please choose -"
51 msgstr "- bitte wählen -"
52
53 #: application/forms/BpUploadForm.php:184
54 #, php-format
55 msgid "A process named \"%s\" already exists"
56 msgstr "Ein Prozess namens \"%s\" existiert bereits"
57
58 #: application/forms/BpConfigForm.php:49
59 msgid ""
60 "A slightly more detailed description for this process, about 100-150 "
61 "characters long"
62 msgstr ""
63 "Eine etwas detailliertere Beschreibung dieses Prozesses, etwa 100-150 "
64 "Zeichen lang"
65
66 #: application/forms/AddNodeForm.php:87 application/forms/ProcessForm.php:61
67 msgid "AND"
68 msgstr "UND"
69
70 #: application/forms/SimulationForm.php:62
71 msgid "Acknowledged"
72 msgstr "Bestätigt"
73
74 #: application/forms/BpConfigForm.php:91
75 #: library/Businessprocess/Renderer/TreeRenderer.php:246
76 #: library/Businessprocess/Renderer/TileRenderer.php:95
77 msgid "Add"
78 msgstr "Hinzufügen"
79
80 #: library/Businessprocess/Renderer/TreeRenderer.php:251
81 #: library/Businessprocess/Renderer/TileRenderer.php:99
82 #: library/Businessprocess/Renderer/TileRenderer.php:108
83 msgid "Add a new business process node"
84 msgstr "Neuen Business-Prozess hinzufügen"
85
86 #: application/forms/AddNodeForm.php:41
87 msgid "Add a new root node"
88 msgstr "Neuen Wurzelknoten hinzufügen"
89
90 #: application/forms/AddNodeForm.php:36
91 #, php-format
92 msgid "Add a node to %s"
93 msgstr "Neuen Knoten zu %s hinzufügen"
94
95 #: application/forms/SimulationForm.php:43
96 #, php-format
97 msgid "Add simulation for %s"
98 msgstr "Simulation für %s hinzufügen"
99
100 #: application/forms/BpConfigForm.php:78
101 msgid "Add to menu"
102 msgstr "Zum Menü hinzufügen"
103
104 #: configuration.php:50
105 msgid "Allow to create whole new process configuration (files)"
106 msgstr "Erlaube das erstellen einer neuen Prozess-Konfiguration (Datei)"
107
108 #: configuration.php:54
109 msgid "Allow to modify process definitions, to add and remove nodes"
110 msgstr ""
111 "Erlaubt es Prozessdefinitionen zu modifizieren, sowie Knoten hinzuzufügen "
112 "und zu entfernen"
113
114 #: configuration.php:46
115 msgid ""
116 "Allow to see all available processes, regardless of configured restrictions"
117 msgstr ""
118 "Erlaubt es, alle verfügbaren Prozesse unabhängig von konfigurierten "
119 "Restriktionen zu sehen"
120
121 #: application/forms/SimulationForm.php:72
122 msgid "Apply"
123 msgstr "Anwenden"
124
125 #: application/forms/DeleteNodeForm.php:68
126 msgid "Are you sure?"
127 msgstr "Sind Sie sicher?"
128
129 #: application/forms/BpConfigForm.php:55
130 msgid "Backend"
131 msgstr "Backend"
132
133 #: library/Businessprocess/ProvidedHook/Monitoring/HostActions.php:12
134 #: library/Businessprocess/ProvidedHook/Monitoring/ServiceActions.php:15
135 msgid "Business Impact"
136 msgstr "Business Impact"
137
138 #: application/controllers/NodeController.php:22
139 #, php-format
140 msgid "Business Impact (%s)"
141 msgstr "Business Impact (%s)"
142
143 #: application/controllers/ProcessController.php:473
144 #: library/Businessprocess/Web/Controller.php:133
145 #: library/Businessprocess/Web/Controller.php:144
146 msgid "Business Process"
147 msgstr "Business-Prozess"
148
149 #: application/controllers/ProcessController.php:86
150 #, php-format
151 msgid "Business Process \"%s\""
152 msgstr "Business-Prozess \"%s\""
153
154 #: application/controllers/IndexController.php:15
155 msgid "Business Process Overview"
156 msgstr "Business-Prozessübersicht"
157
158 #: configuration.php:6
159 msgid "Business Processes"
160 msgstr "Business-Prozesse"
161
162 #: application/forms/BpUploadForm.php:64
163 msgid "Business process source code"
164 msgstr "Business-Prozess Quellcode"
165
166 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:90
167 msgid "Config"
168 msgstr "Konfiguration"
169
170 #: library/Businessprocess/State/MonitoringState.php:40
171 #, php-format
172 msgid "Could not retrieve process state: %s"
173 msgstr "Konnte den Prozess-Status nicht ermitteln: %s"
174
175 #: application/controllers/ProcessController.php:484
176 #: library/Businessprocess/Web/Component/Dashboard.php:55
177 msgid "Create"
178 msgstr "Erstelle"
179
180 #: application/controllers/ProcessController.php:40
181 msgid "Create a new Business Process"
182 msgstr "Neuen Business-Prozess erstellen"
183
184 #: library/Businessprocess/Web/Component/Dashboard.php:56
185 msgid "Create a new Business Process configuration"
186 msgstr "Erstelle eine neue Business-Prozesskonfiguration"
187
188 #: application/forms/SimulationForm.php:30
189 msgid "Current simulation"
190 msgstr "Aktuelle Simulation"
191
192 #: application/forms/AddNodeForm.php:90
193 msgid "DEG"
194 msgstr "DEG"
195
196 #: application/forms/BpConfigForm.php:110
197 msgid "Delete"
198 msgstr "Löschen"
199
200 #: application/forms/DeleteNodeForm.php:36
201 #, php-format
202 msgid "Delete \"%s\""
203 msgstr "Lösche \"%s\""
204
205 #: application/forms/DeleteNodeForm.php:76
206 #, php-format
207 msgid "Delete all occurrences of %s"
208 msgstr "Lösche alle Vorkommen von %s"
209
210 #: application/forms/DeleteNodeForm.php:57
211 #, php-format
212 msgid "Delete from %s"
213 msgstr "Entferne von %s"
214
215 #: application/forms/DeleteNodeForm.php:62
216 #, php-format
217 msgid "Delete root node \"%s\""
218 msgstr "Lösche den Wurzelknoten \"%s\""
219
220 #: library/Businessprocess/Renderer/TileRenderer/NodeTile.php:282
221 msgid "Delete this node"
222 msgstr "Lösche diesen Knoten"
223
224 #: application/forms/BpConfigForm.php:47
225 msgid "Description"
226 msgstr "Beschreibung"
227
228 #: application/controllers/ProcessController.php:443
229 msgid "Diff"
230 msgstr "Unterschied"
231
232 #: application/controllers/ProcessController.php:304
233 #: application/controllers/ProcessController.php:316
234 msgid "Dismiss"
235 msgstr "Verwerfen"
236
237 #: application/forms/DeleteNodeForm.php:71
238 msgid "Do you really want to delete this node?"
239 msgstr "Möchtest du diesen Knoten wirklich löschen?"
240
241 #: application/controllers/ProcessController.php:456
242 msgid "Download"
243 msgstr "Download"
244
245 #: application/controllers/ProcessController.php:462
246 msgid "Download process configuration"
247 msgstr "Prozesskonfiguration herunterladen"
248
249 #: application/forms/AddNodeForm.php:144
250 msgid "Existing Process"
251 msgstr "Existierender Prozess"
252
253 #: application/forms/BpUploadForm.php:132
254 msgid "File"
255 msgstr "Datei"
256
257 #: library/Businessprocess/Web/Form/QuickForm.php:389
258 #: library/Businessprocess/Web/Form/QuickForm.php:414
259 msgid "Form has successfully been sent"
260 msgstr "Das Formular wurde erfolgreich versandt"
261
262 #: library/Businessprocess/Web/Component/Dashboard.php:47
263 msgid ""
264 "From here you can reach all your defined Business Process configurations, "
265 "create new or modify existing ones"
266 msgstr ""
267 "Von hier kannst du alle definierten Business-Prozesskonfigurationen "
268 "erreichen, neue erstellen oder bestehende bearbeiten"
269
270 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:46
271 msgid "Fullscreen"
272 msgstr "Vollbild"
273
274 #: application/controllers/ProcessController.php:448
275 msgid "Highlight changes"
276 msgstr "Änderungen hervorheben"
277
278 #: application/forms/AddNodeForm.php:131 application/forms/AddNodeForm.php:190
279 msgid "Host"
280 msgstr "Host"
281
282 #: application/forms/AddNodeForm.php:166
283 msgid "Hosts"
284 msgstr "Hosts"
285
286 #: application/forms/AddNodeForm.php:172
287 msgid "Hosts that should be part of this business process node"
288 msgstr "Hosts welche Teil dieses Business-Prozesses sein sollen"
289
290 #: application/forms/BpConfigForm.php:57
291 msgid ""
292 "Icinga Web Monitoring Backend where current object states for this process "
293 "should be retrieved from"
294 msgstr ""
295 "Das Icinga Web Monitoring Backend von welchem die Status-Informationen für "
296 "diesen Prozess bezogen werden sollen"
297
298 #: application/forms/SimulationForm.php:67
299 msgid "In downtime"
300 msgstr "In Downtime"
301
302 #: application/forms/AddNodeForm.php:117 application/forms/ProcessForm.php:89
303 msgid "Info URL"
304 msgstr "Info-URL"
305
306 #: application/controllers/ProcessController.php:125
307 msgid "Leave full screen and switch back to normal mode"
308 msgstr "Vollbildmodus verlassen, zurück zur normalen Ansicht"
309
310 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:74
311 msgid "Lock"
312 msgstr "Sperren"
313
314 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:79
315 msgid "Lock this process"
316 msgstr "Sperre diesen Prozess"
317
318 #: application/forms/AddNodeForm.php:91 application/forms/ProcessForm.php:64
319 msgid "MIN 1"
320 msgstr "MIN 1"
321
322 #: application/forms/AddNodeForm.php:92 application/forms/ProcessForm.php:65
323 msgid "MIN 2"
324 msgstr "MIN 2"
325
326 #: application/forms/AddNodeForm.php:93 application/forms/ProcessForm.php:66
327 msgid "MIN 3"
328 msgstr "MIN 3"
329
330 #: application/forms/AddNodeForm.php:94 application/forms/ProcessForm.php:67
331 msgid "MIN 4"
332 msgstr "MIN 4"
333
334 #: application/forms/AddNodeForm.php:95 application/forms/ProcessForm.php:68
335 msgid "MIN 5"
336 msgstr "MIN 5"
337
338 #: application/forms/AddNodeForm.php:96 application/forms/ProcessForm.php:69
339 msgid "MIN 6"
340 msgstr "MIN 6"
341
342 #: application/forms/AddNodeForm.php:97 application/forms/ProcessForm.php:70
343 msgid "MIN 7"
344 msgstr "MIN 7"
345
346 #: application/forms/AddNodeForm.php:98 application/forms/ProcessForm.php:71
347 msgid "MIN 8"
348 msgstr "MIN 8"
349
350 #: application/forms/AddNodeForm.php:99 application/forms/ProcessForm.php:72
351 msgid "MIN 9"
352 msgstr "MIN 9"
353
354 #: application/forms/ProcessForm.php:44
355 #, php-format
356 msgid "Modify \"%s\""
357 msgstr "Bearbeite \"%s\""
358
359 #: application/forms/SimulationForm.php:41
360 #, php-format
361 msgid "Modify simulation for %s"
362 msgstr "Bearbeite die Simulation für %s"
363
364 #: library/Businessprocess/Renderer/TileRenderer/NodeTile.php:269
365 msgid "Modify this business process node"
366 msgstr "Bearbeite diesen Business-Prozessknoten"
367
368 #: library/Businessprocess/Renderer/TreeRenderer.php:203
369 msgid "Modify this node"
370 msgstr "Bearbeite diesen Knoten"
371
372 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:95
373 msgid "Modify this process"
374 msgstr "Bearbeite diesen Prozess"
375
376 #: library/Businessprocess/Renderer/TreeRenderer.php:226
377 msgid "More information"
378 msgstr "Weitere Informationen"
379
380 #: application/forms/AddNodeForm.php:89 application/forms/ProcessForm.php:63
381 msgid "NOT"
382 msgstr "NICHT"
383
384 #: application/forms/BpConfigForm.php:16 application/forms/AddNodeForm.php:68
385 #: application/forms/BpUploadForm.php:39 application/forms/ProcessForm.php:35
386 msgid "Name"
387 msgstr "Name"
388
389 #: library/Businessprocess/BpNode.php:295
390 #, php-format
391 msgid "Nesting error detected: %s"
392 msgstr "Verschachtelungsfehler erkannt: %s"
393
394 #: application/forms/AddNodeForm.php:147
395 msgid "New Process Node"
396 msgstr "Neuer Prozessknoten"
397
398 #: application/forms/AddNodeForm.php:60 application/forms/AddNodeForm.php:183
399 #: application/forms/BpUploadForm.php:142
400 msgid "Next"
401 msgstr "Weiter"
402
403 #: application/forms/BpConfigForm.php:85
404 #: application/forms/DeleteNodeForm.php:74
405 msgid "No"
406 msgstr "Nein"
407
408 #: library/Businessprocess/Web/Component/Dashboard.php:76
409 msgid "No Business Process has been defined for you"
410 msgstr "Es wurde noch kein Business-Prozess für dich definiert"
411
412 #: library/Businessprocess/BpConfig.php:728
413 #, php-format
414 msgid "No business process nodes for \"%s\" have been defined yet"
415 msgstr "Es wurden noch keine Business-Prozess Knoten für \"%s\" definiert"
416
417 #: library/Businessprocess/Web/Controller.php:226
418 #, php-format
419 msgid "No such process config: \"%s\""
420 msgstr "Keine entsprechende Prozesskonfiguration gefunden: \"%s\""
421
422 #: library/Businessprocess/BpConfig.php:595
423 #, php-format
424 msgid "Node \"%s\" has been defined twice"
425 msgstr "Der Knoten \"%s\" wurde doppelt definiert"
426
427 #: application/controllers/NodeController.php:19
428 msgid "Node Impact"
429 msgstr "Impact des Knotens"
430
431 #: application/forms/AddNodeForm.php:150
432 msgid "Node type"
433 msgstr "Knotentyp"
434
435 #: library/Businessprocess/Web/Component/Dashboard.php:75
436 msgid "Not available"
437 msgstr "NIcht verfügbar"
438
439 #: application/forms/AddNodeForm.php:88 application/forms/ProcessForm.php:62
440 msgid "OR"
441 msgstr "ODER"
442
443 #: application/forms/AddNodeForm.php:84 application/forms/ProcessForm.php:58
444 msgid "Operator"
445 msgstr "Operator"
446
447 #: application/forms/AddNodeForm.php:221
448 msgid "Other processes that should be part of this business process node"
449 msgstr "Andere Prozesse welche Teil dieses Business-Prozesses sein sollen"
450
451 #: library/Businessprocess/Web/Form/BpConfigBaseForm.php:45
452 #, php-format
453 msgid "Please prefix the name with \"%s\""
454 msgstr "Bitte stelle \"%s\" dem Namen voran"
455
456 #: library/Businessprocess/Web/Form/BpConfigBaseForm.php:50
457 #, php-format
458 msgid "Please prefix the name with one of \"%s\""
459 msgstr "Bitte stelle dem Namen eines von \"%s\" voran"
460
461 #: application/controllers/ProcessController.php:499
462 msgid "Process Configuration"
463 msgstr "Prozess-Konfiguration"
464
465 #: application/forms/AddNodeForm.php:215
466 msgid "Process nodes"
467 msgstr "Prozess-Knoten"
468
469 #: configuration.php:58
470 msgid "Restrict access to configurations with the given prefix"
471 msgstr "Beschränke den Zugriff auf Konfigurationen mit dem gegebenen Präfix"
472
473 #: application/forms/AddNodeForm.php:132
474 msgid "Service"
475 msgstr "Service"
476
477 #: application/forms/AddNodeForm.php:201
478 msgid "Services"
479 msgstr "Services"
480
481 #: application/forms/AddNodeForm.php:207
482 msgid "Services that should be part of this business process node"
483 msgstr "Services welche Teil dieses Business-Prozesses sein sollen"
484
485 #: configuration.php:26
486 msgid "Show all"
487 msgstr "Alle anzeigen"
488
489 #: application/controllers/ProcessController.php:431
490 msgid "Show source code"
491 msgstr "Quellcode anzeigen"
492
493 #: library/Businessprocess/Renderer/TileRenderer/NodeTile.php:255
494 msgid "Show the business impact of this node by simulating a specific state"
495 msgstr ""
496 "Zeige den Business Impact dieses Knoten durch Simulation eines gewünschten "
497 "Zustandes"
498
499 #: library/Businessprocess/Renderer/TileRenderer/NodeTile.php:220
500 msgid "Show this subtree as a tree"
501 msgstr "Zeige diesen Teilbaum als eigenen Baum"
502
503 #: library/Businessprocess/Renderer/TileRenderer/NodeTile.php:212
504 msgid "Show tiles for this subtree"
505 msgstr "Zeige Kacheln für diesen Teilbaum"
506
507 #: library/Businessprocess/Renderer/TreeRenderer.php:216
508 msgid "Simulate a specific state"
509 msgstr "Einen bestimmten Zustand simulieren"
510
511 #: application/forms/SimulationForm.php:111
512 msgid "Simulation has been removed"
513 msgstr "Simulation wurde entfernt"
514
515 #: application/forms/SimulationForm.php:103
516 msgid "Simulation has been set"
517 msgstr "Simulation wurde gesetzt"
518
519 #: application/controllers/ProcessController.php:426
520 #: application/controllers/ProcessController.php:508
521 #: application/forms/BpUploadForm.php:62
522 msgid "Source"
523 msgstr "Quelle"
524
525 #: application/forms/SimulationForm.php:52
526 msgid "State"
527 msgstr "Status"
528
529 #: application/forms/BpConfigForm.php:66
530 msgid "State Type"
531 msgstr "Statustyp"
532
533 #: application/controllers/ProcessController.php:298
534 #: application/forms/BpConfigForm.php:107 application/forms/BpUploadForm.php:74
535 msgid "Store"
536 msgstr "Speichern"
537
538 #: library/Businessprocess/Web/Form/QuickForm.php:177
539 msgid "Submit"
540 msgstr "Absenden"
541
542 #: application/forms/AddNodeForm.php:112 application/forms/ProcessForm.php:84
543 msgid "Subprocess only"
544 msgstr "Nur Unterprozess"
545
546 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:26
547 msgid "Switch to Tile view"
548 msgstr "Zur Kachelansicht wechseln"
549
550 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:38
551 msgid "Switch to Tree view"
552 msgstr "Zur Baumansicht wechseln"
553
554 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:51
555 msgid "Switch to fullscreen mode"
556 msgstr "Zum Vollbildmodus wechseln"
557
558 #: application/forms/AddNodeForm.php:153
559 msgid "The node type you want to add"
560 msgstr "Der gewünschte Knotentyp"
561
562 #: application/forms/BpConfigForm.php:34 application/forms/AddNodeForm.php:71
563 #: application/forms/BpUploadForm.php:42 application/forms/ProcessForm.php:38
564 msgid "This is the unique identifier of this process"
565 msgstr "Das ist der eindeutige Bezeichner dieses Prozesses"
566
567 #: application/controllers/ProcessController.php:294
568 #, php-format
569 msgid "This process has %d pending change(s)."
570 msgstr "Dieser Prozess hat %d ungesicherte Änderung(en)"
571
572 #: application/controllers/ProcessController.php:313
573 #, php-format
574 msgid "This process shows %d simulated state(s)."
575 msgstr "Dieser Prozess zeigt %d simulierte Zustände"
576
577 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:21
578 msgid "Tiles"
579 msgstr "Kacheln"
580
581 #: application/forms/BpConfigForm.php:39 application/forms/AddNodeForm.php:76
582 #: application/forms/ProcessForm.php:50
583 msgid "Title"
584 msgstr "Titel"
585
586 #: application/forms/AddNodeForm.php:111 application/forms/ProcessForm.php:83
587 msgid "Toplevel Process"
588 msgstr "Toplevel-Prozess"
589
590 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:33
591 msgid "Tree"
592 msgstr "Baum"
593
594 #: application/forms/AddNodeForm.php:119 application/forms/ProcessForm.php:91
595 msgid "URL pointing to more information about this node"
596 msgstr "URL zu mehr Informationen über diesen Knoten"
597
598 #: library/Businessprocess/BpConfig.php:538
599 msgid "Unbound nodes"
600 msgstr "Ungebundene Knoten"
601
602 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:62
603 msgid "Unlock"
604 msgstr "Entsperren"
605
606 #: library/Businessprocess/Web/Component/RenderedProcessActionBar.php:67
607 msgid "Unlock this process"
608 msgstr "Entsperre diesen Prozess"
609
610 #: application/forms/DeleteNodeForm.php:49
611 #, php-format
612 msgid "Unsure? Show business impact of \"%s\""
613 msgstr "Nicht sicher? Zeige den Business Impact von \"%s\""
614
615 #: application/controllers/ProcessController.php:487
616 #: library/Businessprocess/Web/Component/Dashboard.php:64
617 msgid "Upload"
618 msgstr "Hochladen"
619
620 #: application/controllers/ProcessController.php:61
621 msgid "Upload a Business Process Config file"
622 msgstr "Lade eine Business-Prozess Konfigurationsdatei hoch"
623
624 #: library/Businessprocess/Web/Component/Dashboard.php:65
625 msgid "Upload an existing Business Process configuration"
626 msgstr "Lade eine existierende Business-Prozess Konfigurationsdatei hoch"
627
628 #: application/forms/BpConfigForm.php:73
629 msgid "Use HARD states"
630 msgstr "HARD-States benutzen"
631
632 #: application/forms/BpConfigForm.php:72
633 msgid "Use SOFT states"
634 msgstr "SOFT-States benutzen"
635
636 #: application/forms/SimulationForm.php:124
637 #, php-format
638 msgid "Use current state (%s)"
639 msgstr "Aktuellen Status benutzen (%s)"
640
641 #: application/forms/BpConfigForm.php:61
642 msgid "Use the configured default backend"
643 msgstr "Benutze das konfigurierte Standard-Backend"
644
645 #: application/forms/AddNodeForm.php:78 application/forms/ProcessForm.php:52
646 msgid ""
647 "Usually this title will be shown for this node. Equals name if not given"
648 msgstr ""
649 "Für gewöhnlich wird dieser Titel für diesen Knoten angezeigt. Entspricht dem "
650 "Namen, wenn nicht angegeben"
651
652 #: application/forms/BpConfigForm.php:41
653 msgid ""
654 "Usually this title will be shown for this process. Equals name if not given"
655 msgstr ""
656 "Für gewöhnlich wird dieser Titel für diesen Prozess angezeigt. Entspricht "
657 "dem Namen, wenn nicht angegeben"
658
659 #: application/forms/AddNodeForm.php:104 application/forms/ProcessForm.php:77
660 msgid "Visualization"
661 msgstr "Darstellung"
662
663 #: library/Businessprocess/Web/Component/Dashboard.php:42
664 msgid "Welcome to your Business Process Overview"
665 msgstr "Willkommen zur Übersicht deiner Business-Prozesse"
666
667 #: application/forms/DeleteNodeForm.php:144
668 msgid "Well, maybe next time"
669 msgstr "Na gut, vielleicht nächstes Mal"
670
671 #: application/forms/AddNodeForm.php:107 application/forms/ProcessForm.php:80
672 msgid "Where to show this process"
673 msgstr "Wo dieser Prozess angezeigt werden soll"
674
675 #: application/forms/BpConfigForm.php:69
676 msgid "Whether this process should be based on Icinga hard or soft states"
677 msgstr "Ob dieser Prozess auf Icinga's Hard- oder Softstates basieren soll"
678
679 #: application/forms/BpConfigForm.php:81
680 msgid "Whether this process should be linked in the main Icinga Web 2 menu"
681 msgstr "Ob dieser Prozess ins Icinga Web 2 Hauptmenü verlinkt werden soll"
682
683 #: application/forms/BpConfigForm.php:84
684 msgid "Yes"
685 msgstr "Ja"
686
687 #~ msgid "Add children"
688 #~ msgstr "Nachfolger hinzufügen"
689
690 #~ msgid "Another process"
691 #~ msgstr "Ein anderer Prozess"
692
693 #~ msgid "External process"
694 #~ msgstr "Externer Prozess"
695
696 #~ msgid "Modify process node: %s"
697 #~ msgstr "Bearbeite Knoten: %s"
698
699 #~ msgid ""
700 #~ "As no business process has been defined yet you might want to create a %s "
701 #~ "or upload an %s."
702 #~ msgstr ""
703 #~ "Nachdem noch kein Business-Prozess definiert wurde, möchtest du "
704 #~ "vermutlich einen %s erstellen oder einen %s hochladen."
705
706 #~ msgid "CRITICAL"
707 #~ msgstr "KRITISCH"
708
709 #~ msgid "Configure your first business process"
710 #~ msgstr "Konfiguriere deinen ersten Business-Prozess"
711
712 #~ msgid "OK"
713 #~ msgstr "OK"
714
715 #~ msgid "PENDING"
716 #~ msgstr "PENDING"
717
718 #~ msgid "Parser waring on %s:%s: %s"
719 #~ msgstr "Parse-Warnung an %s:%s: %s"
720
721 #~ msgid "Plugin output"
722 #~ msgstr "Plugin-Ausgabe"
723
724 #~ msgid "Reporting"
725 #~ msgstr "Reporting"
726
727 #~ msgid "This has not been implemented yet"
728 #~ msgstr "Das wurde noch nicht implementiert"
729
730 #~ msgid "UNKNOWN"
731 #~ msgstr "UNBEKANNT"
732
733 #~ msgid "Use current default backend"
734 #~ msgstr "Aktuelles Standard-Backend benutzen"
735
736 #~ msgid "WARNING"
737 #~ msgstr "WARNUNG"
738
739 #~ msgid "Warnings"
740 #~ msgstr "Warnungen"
741
742 #~ msgid "min"
743 #~ msgstr "min"
0 # Icinga Web 2 - Head for multiple monitoring backends.
1 # Copyright (C) 2015 Icinga Development Team
2 # This file is distributed under the same license as Businessprocess Module.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 #
5 msgid ""
6 msgstr ""
7 "Project-Id-Version: Businessprocess Module (2.0.0-beta1)\n"
8 "Report-Msgid-Bugs-To: dev@icinga.org\n"
9 "POT-Creation-Date: 2015-02-13 01:29+0100\n"
10 "PO-Revision-Date: 2015-02-13 01:46+0100\n"
11 "Last-Translator: Thomas Gelf <thomas@gelf.net>\n"
12 "Language: it_IT\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "MIME-Version: 1.0\n"
15 "Content-Type: text/plain; charset=UTF-8\n"
16 "Content-Transfer-Encoding: 8bit\n"
17 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18 "X-Generator: Poedit 1.5.4\n"
19
20 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:38
21 msgid "AND"
22 msgstr "E"
23
24 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:44
25 msgid "Acknowledged"
26 msgstr "Gestito"
27
28 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:51
29 msgid "Apply"
30 msgstr "Applica"
31
32 #: /usr/local/icingaweb-modules/businessprocess/configuration.php:4
33 msgid "Business Processes"
34 msgstr "Processi Business"
35
36 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:34
37 msgid "CRITICAL"
38 msgstr "CRITICO"
39
40 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:49
41 msgid ""
42 "Hosts, services or other processes that should be part of this business "
43 "process"
44 msgstr ""
45 "Host, servizi o altri processi che devono far parte di questo processo "
46 "business"
47
48 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:48
49 msgid "In downtime"
50 msgstr "In manutenzione"
51
52 #: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Node.php:304
53 msgid "More information"
54 msgstr "Ulteriori informazioni"
55
56 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:32
57 msgid "OK"
58 msgstr "OK"
59
60 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:39
61 msgid "OR"
62 msgstr "O"
63
64 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:35
65 msgid "Operator"
66 msgstr "Operatore"
67
68 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:66
69 msgid "Other Business Processes"
70 msgstr "Altri processi business"
71
72 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:40
73 msgid "Plugin output"
74 msgstr "Output del plugin"
75
76 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:45
77 msgid "Process components"
78 msgstr "Componenti del processo"
79
80 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:30
81 msgid "Process name"
82 msgstr "Nome processo"
83
84 #: /usr/local/icingaweb-modules/businessprocess/configuration.php:3
85 msgid "Reporting"
86 msgstr "Reportistica"
87
88 #: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Controller.php:40
89 msgid "Show"
90 msgstr "Mostra"
91
92 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:111
93 msgid "Simulation has been removed"
94 msgstr "La simulazione è stata rimossa"
95
96 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:29
97 msgid "State"
98 msgstr "Stato"
99
100 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:51
101 msgid "Store"
102 msgstr "Salva"
103
104 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:35
105 msgid "UNKNOWN"
106 msgstr "SCONOSCIUTO"
107
108 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:31
109 msgid "Use current state"
110 msgstr "Usa lo stato attuale"
111
112 #: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:33
113 msgid "WARNING"
114 msgstr "AVVISO"
115
116 #: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/warnings.phtml:2
117 msgid "Warnings"
118 msgstr "Avvisi"
119
120 #: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:40
121 msgid "min"
122 msgstr "min"
0 <?php
1
2 // Avoid complaints about missing namespace and invalid class name
3 // @codingStandardsIgnoreStart
4 class Zend_View_Helper_FormSimpleNote extends Zend_View_Helper_FormElement
5 {
6 // @codingStandardsIgnoreEnd
7
8 public function formSimpleNote($name, $value = null)
9 {
10 $info = $this->_getInfo($name, $value);
11 extract($info); // name, value, attribs, options, listsep, disable
12 return $value;
13 }
14 }
0 <?php
1
2 /**
3 * @deprecated
4 * @codingStandardsIgnoreStart
5 */
6 class Zend_View_Helper_RenderStateBadges extends Zend_View_Helper_Abstract
7 {
8 // @codingStandardsIgnoreEnd
9 public function renderStateBadges($summary)
10 {
11 $html = '';
12
13 foreach ($summary as $state => $cnt) {
14 if ($cnt === 0
15 || $state === 'OK'
16 || $state === 'UP'
17 ) {
18 continue;
19 }
20
21 $html .= '<span class="badge badge-' . strtolower($state)
22 . '" title="' . mt('monitoring', $state) . '">'
23 . $cnt . '</span>';
24 }
25
26 if ($html !== '') {
27 $html = '<div class="badges">' . $html . '</div>';
28 }
29
30 return $html;
31 }
32 }
0 <?= $this->controls->render() ?>
1 <?= $this->content->render() ?>
0 <?= $this->controls->render() ?>
1
2 <div class="content">
3 <?php if ($this->showDiff): ?>
4 <div class="diff">
5 <?= $this->diff->render() ?>
6 </div>
7 <?php else: ?>
8 <table class="sourcecode">
9 <?php
10
11 $cnt = 0;
12 $lines = preg_split('~\r?\n~', $this->source);
13 $len = ceil(log(count($lines), 10));
14 $rowhtml = sprintf('<tr><th>%%0%dd: </th><td>%%s<br></td></tr>', $len);
15
16 foreach ($lines as $line) {
17 $cnt++;
18 printf($rowhtml, $cnt, $this->escape($line));
19 }
20
21 ?>
22 </table>
23 <?php endif ?>
24 </div>
0 <?php
1
2 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
3
4 /** @var \Icinga\Application\Modules\Module $this */
5 $section = $this->menuSection(N_('Business Processes'), array(
6 'url' => 'businessprocess',
7 'icon' => 'sitemap',
8 'priority' => 46
9 ));
10
11 try {
12 $storage = new LegacyStorage(
13 $this->getConfig()->getSection('global')
14 );
15
16 $prio = 0;
17 foreach ($storage->listProcessNames() as $name) {
18 $meta = $storage->loadMetadata($name);
19 if ($meta->get('AddToMenu') === 'no') {
20 continue;
21 }
22 $prio++;
23
24 if ($prio > 5) {
25 $section->add(N_('Show all'), array(
26 'url' => 'businessprocess',
27 'priority' => $prio
28 ));
29
30 break;
31 }
32
33 $section->add($meta->getTitle(), array(
34 'url' => 'businessprocess/process/show',
35 'urlParameters' => array('config' => $name),
36 'priority' => $prio
37 ));
38 }
39 } catch (Exception $e) {
40 // Well... there is not much we could do here
41 }
42
43 $this->providePermission(
44 'businessprocess/showall',
45 $this->translate('Allow to see all available processes, regardless of configured restrictions')
46 );
47 $this->providePermission(
48 'businessprocess/create',
49 $this->translate('Allow to create whole new process configuration (files)')
50 );
51 $this->providePermission(
52 'businessprocess/modify',
53 $this->translate('Allow to modify process definitions, to add and remove nodes')
54 );
55 $this->provideRestriction(
56 'businessprocess/prefix',
57 $this->translate('Restrict access to configurations with the given prefix')
58 );
0 <a id="Installation"></a>Installation
1 =====================================
2
3 Requirements
4 ------------
5
6 * Icinga Web 2 (&gt;= 2.4.1)
7 * PHP (&gt;= 5.3 or 7.x)
8
9 The Icinga Web 2 `monitoring` module needs to be configured and enabled.
10
11 Installation from .tar.gz
12 -------------------------
13
14 Download the latest version and extract it to a folder named `businessprocess`
15 in one of your Icinga Web 2 module path directories.
16
17 You might want to use a script as follows for this task:
18 ```sh
19 ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
20 REPO_URL="https://github.com/Icinga/icingaweb2-module-businessprocess"
21 TARGET_DIR="${ICINGAWEB_MODULEPATH}/businessprocess"
22 MODULE_VERSION="2.1.0"
23 URL="${REPO_URL}/archive/v${MODULE_VERSION}.tar.gz"
24 install -d -m 0755 "${TARGET_DIR}"
25 wget -q -O - "$URL" | tar xfz - -C "${TARGET_DIR}" --strip-components 1
26 ```
27
28 Installation from GIT repository
29 --------------------------------
30
31 Another convenient method is the installation directly from our GIT repository.
32 Just clone the repository to one of your Icinga Web 2 module path directories.
33 It will be immediately ready for use:
34
35 ```sh
36 ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
37 REPO_URL="https://github.com/Icinga/icingaweb2-module-businessprocess"
38 TARGET_DIR="${ICINGAWEB_MODULEPATH}/businessprocess"
39 MODULE_VERSION="2.1.0"
40 git clone "${REPO_URL}" "${TARGET_DIR}"
41 ```
42
43 You can now directly use our current GIT master or check out a specific version.
44
45 Enable the newly installed module
46 ---------------------------------
47
48 Enable the `businessprocess` module either on the CLI by running
49
50 ```sh
51 icingacli module enable businessprocess
52 ```
53
54 Or go to your Icinga Web 2 frontend, choose `Configuration` -&gt; `Modules`...
55
56 ![Choose Configuration - Modules](screenshot/01_installation/101_menu-configuration-modules.png)
57
58 ...choose the `businessprocess` module and `enable` it:
59
60 ![Enable the module](screenshot/01_installation/102_enable-module.png)
61
62 It might afterwards be necessary to refresh your web browser to be sure that
63 newly provided styling is loaded.
64
65 Create your first Business Process definition
66 ---------------------------------------------
67
68 That's it, *Business Process* is now ready for use. Please read more on [how to get started](02-Getting-Started.md).
0 <a id="Getting-Started"></a>Getting Started
1 ===========================================
2
3 Once you enable the *Business Process* module, it will pop up in your menu.
4 When you click on it, it will show you a new Dashboard:
5
6 ![Empty Dashboard](screenshot/02_getting-started/0201_empty-dashboard.png)
7
8 A new Business Process configuration
9 -------------------------------------------
10
11 From here we choose to create a new *Business Process configuration*:
12
13 ![New Business Process](screenshot/02_getting-started/0202_create-new-configuration.png)
14
15 Let's have a look at the single fields:
16
17 ### Configuration name
18
19 ![Configuration name](screenshot/02_getting-started/0203_create-new_name.png)
20
21 The Business Process definition will be stored with this name. This is going to
22 be used when referencing this process in URLs and in Check Commands.
23
24 ### Title
25
26 ![Configuration name](screenshot/02_getting-started/0204_create-new_title.png)
27
28 You might optionally want to provide an additional title. In that case the title
29 is shown in the GUI, while the name is still used as a reference. The title will
30 default to the name.
31
32 ### Description
33
34 ![Description](screenshot/02_getting-started/0205_create-new_description.png)
35
36 Provide a short description explaining within 100-150 character what this
37 configuration provides. This will be shown on the Dashboard.
38
39 ### Backend
40
41 ![Backend](screenshot/02_getting-started/0206_create-new_backend.png)
42
43 **Hint:** *Usually this should not be changed*
44
45 Icinga Web 2 currently uses only one Monitoring Backend, but in theory you
46 could configure multiple ones. They won't be usable in a meaningful way at the
47 time of this writing. Still, you might want to use a different backend as a data
48 provider for your Business Process.
49
50 ### State Type
51
52 ![State Type](screenshot/02_getting-started/0207_create-new_state-type.png)
53
54 You can decide whether `SOFT` or `HARD` states should be the used as a base when
55 calculating the state of a Business Process definition.
56
57 ### Add to menu
58
59 Business Process configurations can be linked to the Icinga Web 2 menu. Only the
60 first five configurations a user is allowed to see will be shown there:
61
62 ![Add to menu](screenshot/02_getting-started/0208_create-new_add-to-menu.png)
63
64 That's all for now, click `Add` to store your new (still empty) Business Process
65 configuration.
66
67 Empty configuration
68 ===================
69
70 You are redirected to your newly created Business Process configuration:
71
72 ![Empty configuration](screenshot/02_getting-started/0209_new-empty-configuration.png)
73
74 From here we can now add as many deeply nested Business Processes as we want.
75 But let's first have a look at our Dashboard once again:
76
77 ![New on Dashboard](screenshot/02_getting-started/0210_new-on-dashboard.png)
78
79 Now let's move on and [create your first Nodes](03-Create-your-first-process-node.md).
0 <a id="Create-your-first-process-node"></a>Create your first Business Process Node
1 ==================================================================================
2
3 A *Business Process Node* consists of a *name*, *title*, an *operator* and one or
4 more child nodes. It can be a Root Node, child node of other Business Process
5 Nodes - or both.
6
7 ![Empty Config](screenshot/03_first-root-node/0301_empty-config.png)
8
9 Configuring our first node
10 --------------------------
11
12 To create our first *Business Process Node* we click the *Add* button. This
13 leads to the related configuration form:
14
15 ![Add new Node](screenshot/03_first-root-node/0302_add-new-node.png)
16
17 First setting is the *Node name*, an identifier that must be unique throughout
18 all Nodes that are going to be defined. This identifier will be used in every
19 link and also in *Check Commands* referring this node from an Icinga *Service
20 Check*.
21
22 ### Set a title
23
24 As uniqueness sometimes leads to not-so-beautiful names, you are additionally
25 allowed to specify a title. This is what the frontend is going to show:
26
27 ![Node Title](screenshot/03_first-root-node/0303_node-title.png)
28
29 ### Choose an operator
30
31 Every Business Process requires an *Operator*. This operator defines it's
32 behaviour, this specifies how it's very own state is going to be calculated:
33
34 ![Operator](screenshot/03_first-root-node/0304_operator.png)
35
36 ### Specify where to display
37
38 The form suggests to create a *Toplevel Process*. It does so as we are about
39 to create a new *root node*. We could alternatively also create a sub process.
40 As we are currently not adding it to another Node, this would lead to an *Unbound
41 Node* that could be linked later on.
42
43 ![Node Display](screenshot/03_first-root-node/0305_display.png)
44
45 ### Provide an optional Info URL
46
47 One might also want to provide a link to additional information related to a
48 specific process. This could be instructions with more technical details or
49 hints telling what should happen if outage occurs. You might not want to do so
50 for every single Node, but it might come in handy for your most important (top
51 level?) nodes:
52
53 ![Node Info Url](screenshot/03_first-root-node/0306_info-url.png)
54
55 That's it, your are ready to submit the form.
56
57 ### First Business Process Node ready
58
59 You are now shown your first Business Process Node. A red bar reminds you that
60 your pending changes have not been stored yet:
61
62 ![First Node created](screenshot/03_first-root-node/0307_first-node-created.png)
63
64 You could now *Store the Configuration* or move on with adding additional nodes
65 to complete your configuration.
66
67 **Hint**: the blue arrow makes part of a breadcrumb showing your current position.
68 You might want to learn more about [breadcrumbs](12-Web-Components-Breadcrumb.md).
0 <a id="Web-Components-Breadcrumb"></a>Web Components: Breadcrumb
1 ================================================================
2
3 All Business Process renderers show a **breadcrumb** component to always give
4 you a quick indication of your current location.
5
6 ![Símple Breadcrumb](screenshot/12_web-components_breadcrumb/1201_simple-breadcrumb.png)
7
8 The left-most section shows the title of the current *Business Process Configuration*.
9 The remaining sections show the path to the current *Business Process Node* currently
10 being shown.
11
12 Hovering the Breadcrumb with your mouse shows you that all of it sections are
13 highlighted, as they are links pointing to either the root level when clicking
14 on the *Configuration Node* itself or to the corresponding *Business Process Node*.
15
16 All but the last section, showing your current position in the tree. Even if
17 not being highlighted, it is still a link an can be clicked in case you neeed
18 so.
19
20 In case you're showing some related details in a split-screen view of *Icinga
21 Web 2*, a click on any *Breadcrumb* section will switch back to a wide single
22 column view to make it obvious that you moved to another context. It is also
23 perfectly legal to open any of the available links in a new browser tab or
24 window.
25
26 Available actions below the Breadcrumb
27 --------------------------------------
28
29 ### Choose a renderer
30
31 The first link allows to toggle the used Renderer. Currently a *Tree* and a
32 *Tile* renderer are available.
33
34 ### Move to Full Screen Mode
35
36 Every view can be shown in *Full Screen Mode*. Full screen means that left and
37 upper menu together with some other details are hidden. Your Business Process
38 will be able to use all of the available space. Want even more? Then please
39 additionally switch your browser to full screen mode. This is usually done by
40 pressing the `F11` key.
41
42 Once being in full screen mode you'll find an icon on the right side that will
43 allow you to switch back to normal view:
44
45 ![Return from fullscreen](screenshot/12_web-components_breadcrumb/1202_return-from-fullscreen.png)
46
47 **Hint:** We know that the web application might request real full screen mode
48 on their own. We refused doing so as many people find this being an annoying
49 feature.
50
51 ### Unlock the Configuration
52
53 When clicking `Unlock`, additional actions are shown. One of them is immediately
54 shown next to the `Unlock` link and reads `Config`. It allows you to reach Configuration
55 settings for the your currently loaded *Business Process Configutation*:
56
57 ![Unlocked config](screenshot/12_web-components_breadcrumb/1204_unlocked_config.png)
58
59 But there is more. When unlocked, all nodes provide links allowing to modify or
60 to delete them. Host/Service Nodes now allow you to simulate a specific state.
61
62 Other main actions
63 ------------------
64
65 ### Add content to your Dashboard
66
67 When being in *locked* mode, you are allowed to add the currently shown process
68 at the given path with the active renderer in the main (or a custom) [Icinga Web 2
69 Dashboard](16-Add-To-Dashboard.md):
70
71 ![Add to Dashboard](screenshot/12_web-components_breadcrumb/1203_add-to-dashboard.png)
0 <a id="Web-Components-Tile-Renderer"></a>Web Components: Tile Renderer
1 ======================================================================
2
3 The default Business Process *Renderer* is the *Tile Renderer*. It always shows
4 one level of your tree, enriched with badges giving some hint on lower level
5 node problems. This is what it looks like:
6
7 ![Tile Renderer](screenshot/13_web-components-tile-renderer/1301_tile-view.png)
8
9 Please click on *Tree* below the [breadcrumb](12-Web-Components-Breadcrumb.md)
10 to switch to the [Tree View](14-Web-Components-Tree-Renderer.md). In the left
11 corner of every *Tile* you can find two icons, both of them will show the related
12 sub-process. On your mobile phone this usually replaces your current view, please
13 use the [breadcrumb](12-Web-Components-Breadcrumb.md) to navigate back to a higher
14 level.
15
16 On a Notebook or Desktop Computer this usually leady to a split-screen view:
17
18 ![Split View - Tiles and Tree](screenshot/13_web-components-tile-renderer/1302_tile-and-subtree.png)
19
20 This example shows a subtree shown with the [Tree Renderer](14-Web-Components-Tree-Renderer.md),
21 it is of course also perfectly legal to drill down using the *Tile Renderer*
22 only.
0 <a id="Web-Components-Tree-Renderer"></a>Web Components: Tree Renderer
1 ======================================================================
2
3 The main advantage of the *Tree Renderer* is that it is able to show all nodes
4 of Business Process trees at once. This works fine even for huge trees with lots
5 of nodes. Please have a look at this screenshot to get an idea of how the tree
6 view looks like:
7
8 ![Tree View](screenshot/14_web-components-tree-renderer/1401_tree-view.png)
9
10 Clicking Business Process Nodes collapses or unfolds them, clicking single hosts
11 or services show the related monitored object. You can of course always switch
12 back to the [Tile Renderer](13-Web-Components-Tile-Renderer.md) with a single
13 click at any time.
0 <a id="Add-To-Dashboard"></a>Show Processes on a Dashboard
1 ==========================================================
2
3 When being in *Locked mode*, you can add any Business Process at top or sub level
4 to any Icinga Web 2 Dashboard. The related link can be found in the Tab bar:
5
6 ![Add to Dashboard - Link](screenshot/16_dashboard/1601_add-to-dashboard-link.png)
7
8 This leads to the standard Icinga Web 2 *Add Dashlet to Dashboard* form. Feel
9 free to add your Business Process View to any existing Dashboard. You might also
10 want to create a dedicated Dashboard as shown in this example:
11
12 ![Add to Dashboard - Form](screenshot/16_dashboard/1602_add_to_dashboard-form.png)
13
14
15 Want more?
16 ----------
17
18 Head on and add multiple Business Processes to your Dashboard to show all of
19 them at once:
20
21 ![Sample Dashboard](screenshot/16_dashboard/1603_businessprocesses_on_dashboard.png)
0 <a id="Store-Config"></a>Store your Configuration
1 =================================================
2
3 Changes to your *Business Process Configuration* are added to a stack and will
4 not be stored immediately. In case there are pending unstored changes, this will
5 be shown on every screen:
6
7 ![Pending Changes](screenshot/21_store-config/2101_Pending-Changes.png)
8
9 A click on *Dismiss* immediately throws away all unstored changes. A click on
10 *Store* brings you to the configuration form. You have seen this before, once
11 you created your [very first configuration](02-Getting-Started.md):
12
13 ![Store Config](screenshot/21_store-config/2102_Store-Config.png)
14
15 Config Diff
16 -----------
17
18 If unsure what changes you're going to store, you can still check the *Config Diff*
19 before finally storing to disk:
20
21 ![Show Diff](screenshot/21_store-config/2103_Show-Diff.png)
22
23 You can also download your existing configuration to safe it elsewhere or to
24 apply manual modifications with our favourite plaintext editor.
0 <a id="Upload-Config"></a>Upload a Configuration File
1 =====================================================
2
3 You can upload a formerly downloaded or even a manually created file directly
4 through the web frontend. Given sufficient permissions, the Dashboard provides
5 a related link:
6
7 ![From Dashboard to Upload](screenshot/22_upload-config/2201_go-to-upload.png)
8
9 Chose a file
10 ------------
11
12 This can be any file:
13
14 ![Choose a File](screenshot/22_upload-config/2202_choose-file.png)
15
16 It should be valid of course, but don't worry - the *Business Process* module
17 protects you from syntax errors:
18
19 ![Syntax Error](screenshot/22_upload-config/2203_syntax-error.png)
20
21 Just for fun you could try to upload an image or whatever you want - it will not
22 break. It will also protect you from accidentally overwriting existing files:
23
24 ![Duplicate Name](screenshot/22_upload-config/2204_duplicate-name.png)
25
26 So in case you need to replace an existing process, please delete it before
27 uploading a new one.
0 Project History
1 ===============
2
3 The Business Process module is based on the ideas of the Nagios(tm) [Business
4 Process AddOn](http://bp-addon.monitoringexchange.org/) written by Bernd
5 Strößenreuther. We always loved it's simplicity, and while it looks pretty
6 oldschool right now there are still many shops happily using it in production.
7
8 ![BpAddOn Overview](screenshot/81_history/8101_bpaddon-overview.png)
9
10 Compatibility
11 -------------
12
13 We fully support the BPaddon configuration language and will continue to do so.
14 It's also perfectly valid to run both products in parallel based on the very same
15 config files. New features are (mostly) added in a compatible way.
16
17 Configuration titles and descriptions, properties related to state types or
18 permissions are examples for new features that didn't formerly exist. They are
19 stored as commented metadata in the file header and therefore invisible to the
20 old AddOn.
21
22 The only way to break compatibility is to use newly introduced operators like
23 `ǸOT`. Once you do so, the old AddOn will no longer be able to parse your
24 configuration.
25
26 ![BpAddOn Details](screenshot/81_history/8102_bpaddon-detail.png)
27
28 Lot's of changes went on and are still going on under the hood. We have more
29 features and new language constructs. We separated the config reader from the
30 state fetcher in our code base. This will allow us to eventually support config
31 backends like SQL databases or the Icinga 2 DSL.
32
33 This would make it easier to distribute configuration in large environments.
34
35 Improvements
36 ------------
37
38 Major focus has been put on execution speed. So while the Web integration shows
39 much more details at once and is able to display huge unfolded trees, it should
40 still render and refresh faster. Same goes for the Check Plugin.
41
42 Behaviour for all operators is now strictly specified and Unit-tested. You still
43 can manually edit your configuration files. But much better, you also delegate
44 this to your co-workers, as Business Process definitions can now be built directly
45 in the GUI.
0 <a id="Changelog"></a>Changelog
1 ===============================
2
3 2.1.0
4 -----
5
6 ### Fixed issues and related features
7 * You can find issues and feature requests related to this release on our
8 [roadmap](https://github.com/Icinga/icingaweb2-module-businessprocess/milestone/4?closed=1)
9
10 ### Usability and visualization
11 * Missing nodes are now shown in a generic error notice
12 * `Unbound nodes` (nodes not being shown at top level and not in use as a sub
13 node) are now reachable through a fake node
14 * A bug with the Chrome browser showing messed up checkboxes has been fixed
15
16 ### State calculation
17 * Missing nodes are now considered being `UNKNOWN` (or `UNREACHABLE` when
18 being a host node). The former behaviour comes from Icinga 1.x, as a reload
19 there had the potential to trigger false alarms. This is no longer an issue
20 with Icinga 2.x, allowing us to be strict once again when it goes to missing
21 nodes
22 * Linking nodes from other process configuration files (still an undocumented
23 feature) has been broken shortly before 2.0.0, this has now been fixed
24
25 ### Permissions
26 * Permissions have not been enforced as they should have been in 2.0.0, some
27 links and operations have been accessible to roles that haven't been granted
28 such. This has now been fixed
29 * While we allow for granular permissions that can be persisted in every process
30 configuration file (still an undocumented feature), there is now also a pretty
31 simple but effective way of restricting access to your business processes based
32 on process name prefixes.
33
34 2.0.0
35 -----
36
37 * First officially stable version
0 Contributing
1 ============
2
3 The Businessprocess module is an Open Source project and lives from your contributions.
4 No matter whether these are feature requests, issues, translations, documentation or
5 code.
6
7 * Please check whether a related issue alredy exists on our [Issue Tracker](https://github.com/icinga/icingaweb2-module-businessprocess/issues)
8 * Make sure your code conforms to the [PSR-2: Coding Style Guide](http://www.php-fig.org/psr/psr-2/)
9 * Unit-Tests would be great
10 * Send a [Pull Request](https://github.com/Icinga/icingaweb2-module-businessprocess/pulls)
11 (it will automatically be tested on Travis-CI, PSR-2 is enforced)
12 * We try hard to keep our master always green: [![Build Status](https://travis-ci.org/Icinga/icingaweb2-module-businessprocess.svg?branch=master)](https://travis-ci.org/Icinga/icingaweb2-module-businessprocess)
13
14 Compatibility
15 -------------
16
17 The Business Process module is tested on PHP versions 5.3 up to 7.1 (including
18 nightly builds).
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Exception\IcingaException;
5 use Icinga\Exception\NotFoundError;
6 use Icinga\Module\Businessprocess\Exception\NestingError;
7 use Icinga\Module\Businessprocess\Modification\ProcessChanges;
8 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
9 use Exception;
10
11 class BpConfig
12 {
13 const SOFT_STATE = 0;
14
15 const HARD_STATE = 1;
16
17 /**
18 * Name of the configured monitoring backend
19 *
20 * @var string
21 */
22 protected $backendName;
23
24 /**
25 * Monitoring backend to retrieve states from
26 *
27 * @var MonitoringBackend
28 */
29 protected $backend;
30
31 /** @var Metadata */
32 protected $metadata;
33
34 /**
35 * Business process name
36 *
37 * @var string
38 */
39 protected $name;
40
41 /**
42 * Business process title
43 *
44 * @var string
45 */
46 protected $title;
47
48 /**
49 * State type, soft or hard
50 *
51 * @var int
52 */
53 protected $state_type = self::HARD_STATE;
54
55 /**
56 * Warnings, usually filled at process build time
57 *
58 * @var array
59 */
60 protected $warnings = array();
61
62 /**
63 * Errors, usually filled at process build time
64 *
65 * @var array
66 */
67 protected $errors = array();
68
69 /**
70 * All used node objects
71 *
72 * @var array
73 */
74 protected $nodes = array();
75
76 /**
77 * Root node objects
78 *
79 * @var array
80 */
81 protected $root_nodes = array();
82
83 /**
84 * All host names { 'hostA' => true, ... }
85 *
86 * @var array
87 */
88 protected $hosts = array();
89
90 /** @var bool Whether catchable errors should be thrown nonetheless */
91 protected $throwErrors = false;
92
93 protected $loopDetection = array();
94
95 /**
96 * Applied state simulation
97 *
98 * @var Simulation
99 */
100 protected $simulation;
101
102 protected $changeCount = 0;
103
104 protected $simulationCount = 0;
105
106 /** @var ProcessChanges */
107 protected $appliedChanges;
108
109 public function __construct()
110 {
111 }
112
113 /**
114 * Retrieve metadata for this configuration
115 *
116 * @return Metadata
117 */
118 public function getMetadata()
119 {
120 if ($this->metadata === null) {
121 $this->metadata = new Metadata($this->name);
122 }
123
124 return $this->metadata;
125 }
126
127 /**
128 * Set metadata
129 *
130 * @param Metadata $metadata
131 *
132 * @return $this
133 */
134 public function setMetadata(Metadata $metadata)
135 {
136 $this->metadata = $metadata;
137 return $this;
138 }
139
140 /**
141 * Apply pending process changes
142 *
143 * @param ProcessChanges $changes
144 *
145 * @return $this
146 */
147 public function applyChanges(ProcessChanges $changes)
148 {
149 $cnt = 0;
150 foreach ($changes->getChanges() as $change) {
151 $cnt++;
152 $change->applyTo($this);
153 }
154 $this->changeCount = $cnt;
155
156 $this->appliedChanges = $changes;
157
158 return $this;
159 }
160
161 /**
162 * Apply a state simulation
163 *
164 * @param Simulation $simulation
165 *
166 * @return $this
167 */
168 public function applySimulation(Simulation $simulation)
169 {
170 $cnt = 0;
171
172 foreach ($simulation->simulations() as $node => $s) {
173 if (! $this->hasNode($node)) {
174 continue;
175 }
176 $cnt++;
177 $this->getNode($node)
178 ->setState($s->state)
179 ->setAck($s->acknowledged)
180 ->setDowntime($s->in_downtime)
181 ->setMissing(false);
182 }
183
184 $this->simulationCount = $cnt;
185
186 return $this;
187 }
188
189 /**
190 * Number of applied changes
191 *
192 * @return int
193 */
194 public function countChanges()
195 {
196 return $this->changeCount;
197 }
198
199 /**
200 * Whether changes have been applied to this configuration
201 *
202 * @return int
203 */
204 public function hasChanges()
205 {
206 return $this->countChanges() > 0;
207 }
208
209 /**
210 * @param $name
211 *
212 * @return $this
213 */
214 public function setName($name)
215 {
216 $this->name = $name;
217 return $this;
218 }
219
220 /**
221 * @return string
222 */
223 public function getName()
224 {
225 return $this->name;
226 }
227
228 /**
229 * @return string
230 */
231 public function getHtmlId()
232 {
233 return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
234 }
235
236 public function setTitle($title)
237 {
238 $this->title = $title;
239 return $this;
240 }
241
242 public function getTitle()
243 {
244 return $this->getMetadata()->getTitle();
245 }
246
247 public function hasTitle()
248 {
249 return $this->getMetadata()->has('Title');
250 }
251
252 public function getBackendName()
253 {
254 return $this->getMetadata()->get('Backend');
255 }
256
257 public function hasBackendName()
258 {
259 return $this->getMetadata()->has('Backend');
260 }
261
262 public function setBackend(MonitoringBackend $backend)
263 {
264 $this->backend = $backend;
265 return $this;
266 }
267
268 public function getBackend()
269 {
270 if ($this->backend === null) {
271 $this->backend = MonitoringBackend::instance(
272 $this->getBackendName()
273 );
274 }
275
276 return $this->backend;
277 }
278
279 public function hasBackend()
280 {
281 return $this->backend !== null;
282 }
283
284 public function hasBeenChanged()
285 {
286 return false;
287 }
288
289 public function hasSimulations()
290 {
291 return $this->countSimulations() > 0;
292 }
293
294 public function countSimulations()
295 {
296 return $this->simulationCount;
297 }
298
299 public function clearAppliedChanges()
300 {
301 if ($this->appliedChanges !== null) {
302 $this->appliedChanges->clear();
303 }
304 return $this;
305 }
306
307 public function useSoftStates()
308 {
309 $this->state_type = self::SOFT_STATE;
310 return $this;
311 }
312
313 public function useHardStates()
314 {
315 $this->state_type = self::HARD_STATE;
316 return $this;
317 }
318
319 public function usesSoftStates()
320 {
321 return $this->state_type === self::SOFT_STATE;
322 }
323
324 public function usesHardStates()
325 {
326 return $this->state_type === self::HARD_STATE;
327 }
328
329 public function addRootNode($name)
330 {
331 $this->root_nodes[$name] = $this->getNode($name);
332 return $this;
333 }
334
335 public function removeRootNode($name)
336 {
337 if ($this->isRootNode($name)) {
338 unset($this->root_nodes[$name]);
339 }
340
341 return $this;
342 }
343
344 public function isRootNode($name)
345 {
346 return array_key_exists($name, $this->root_nodes);
347 }
348
349 /**
350 * @return BpNode[]
351 */
352 public function getChildren()
353 {
354 return $this->getRootNodes();
355 }
356
357 /**
358 * @return int
359 */
360 public function countChildren()
361 {
362 return count($this->root_nodes);
363 }
364
365 /**
366 * @return BpNode[]
367 */
368 public function getRootNodes()
369 {
370 ksort($this->root_nodes);
371 return $this->root_nodes;
372 }
373
374 public function listRootNodes()
375 {
376 $names = array_keys($this->root_nodes);
377 sort($names);
378 return $names;
379 }
380
381 public function getNodes()
382 {
383 return $this->nodes;
384 }
385
386 public function hasNode($name)
387 {
388 return array_key_exists($name, $this->nodes);
389 }
390
391 public function hasRootNode($name)
392 {
393 return array_key_exists($name, $this->root_nodes);
394 }
395
396 public function createService($host, $service)
397 {
398 $node = new ServiceNode(
399 $this,
400 (object) array(
401 'hostname' => $host,
402 'service' => $service
403 )
404 );
405 $this->nodes[$host . ';' . $service] = $node;
406 $this->hosts[$host] = true;
407 return $node;
408 }
409
410 public function createHost($host)
411 {
412 $node = new HostNode($this, (object) array('hostname' => $host));
413 $this->nodes[$host . ';Hoststatus'] = $node;
414 $this->hosts[$host] = true;
415 return $node;
416 }
417
418 public function calculateAllStates()
419 {
420 foreach ($this->getRootNodes() as $node) {
421 $node->getState();
422 }
423
424 return $this;
425 }
426
427 public function clearAllStates()
428 {
429 foreach ($this->getBpNodes() as $node) {
430 $node->clearState();
431 }
432
433 return $this;
434 }
435
436 public function listInvolvedHostNames()
437 {
438 return array_keys($this->hosts);
439 }
440
441 /**
442 * Create and attach a new process (BpNode)
443 *
444 * @param string $name Process name
445 * @param string $operator Operator (defaults to &)
446 *
447 * @return BpNode
448 */
449 public function createBp($name, $operator = '&')
450 {
451 $node = new BpNode($this, (object) array(
452 'name' => $name,
453 'operator' => $operator,
454 'child_names' => array(),
455 ));
456
457 $this->addNode($name, $node);
458 return $node;
459 }
460
461 public function createMissingBp($name)
462 {
463 return $this->createBp($name)->setMissing();
464 }
465
466 public function getMissingChildren()
467 {
468 $missing = array();
469 foreach ($this->getRootNodes() as $root) {
470 $missing += $root->getMissingChildren();
471 }
472
473 return $missing;
474 }
475
476 public function createImportedNode($config, $name = null)
477 {
478 $params = (object) array('configName' => $config);
479 if ($name !== null) {
480 $params->node = $name;
481 }
482
483 $node = new ImportedNode($this, $params);
484 $this->nodes[$node->getName()] = $node;
485 return $node;
486 }
487
488 /**
489 * @param $name
490 * @return Node
491 * @throws Exception
492 */
493 public function getNode($name)
494 {
495 if ($name === '__unbound__') {
496 return $this->getUnboundBaseNode();
497 }
498
499 if (array_key_exists($name, $this->nodes)) {
500 return $this->nodes[$name];
501 }
502
503 // Fallback: if it is a service, create an empty one:
504 $this->warn(sprintf('The node "%s" doesn\'t exist', $name));
505 $pos = strpos($name, ';');
506 if ($pos !== false) {
507 $host = substr($name, 0, $pos);
508 $service = substr($name, $pos + 1);
509 // TODO: deactivated, this scares me, test it
510 if ($service === 'Hoststatus') {
511 return $this->createHost($host);
512 } else {
513 return $this->createService($host, $service);
514 }
515 }
516
517 throw new Exception(
518 sprintf('The node "%s" doesn\'t exist', $name)
519 );
520 }
521
522 /**
523 * @return BpNode
524 */
525 public function getUnboundBaseNode()
526 {
527 // Hint: state is useless here, but triggers parent/child "calculation"
528 // This is an ugly workaround and should be made obsolete
529 $this->calculateAllStates();
530
531 $names = array_keys($this->getUnboundNodes());
532 $bp = new BpNode($this, (object) array(
533 'name' => '__unbound__',
534 'operator' => '&',
535 'child_names' => $names
536 ));
537 $bp->setAlias($this->translate('Unbound nodes'));
538 return $bp;
539 }
540
541 /**
542 * @param $name
543 * @return BpNode
544 *
545 * @throws NotFoundError
546 */
547 public function getBpNode($name)
548 {
549 if ($this->hasBpNode($name)) {
550 return $this->nodes[$name];
551 } else {
552 throw new NotFoundError('Trying to access a missing business process node "%s"', $name);
553 }
554 }
555
556 /**
557 * @param $name
558 *
559 * @return bool
560 */
561 public function hasBpNode($name)
562 {
563 return array_key_exists($name, $this->nodes)
564 && $this->nodes[$name] instanceof BpNode;
565 }
566
567 /**
568 * Set the state for a specific node
569 *
570 * @param string $name Node name
571 * @param int $state Desired state
572 *
573 * @return $this
574 */
575 public function setNodeState($name, $state)
576 {
577 $this->getNode($name)->setState($state);
578 return $this;
579 }
580
581 /**
582 * Add the given node to the given BpNode
583 *
584 * @param $name
585 * @param BpNode $node
586 *
587 * @return $this
588 */
589 public function addNode($name, BpNode $node)
590 {
591 if (array_key_exists($name, $this->nodes)) {
592 $this->warn(
593 sprintf(
594 mt('businessprocess', 'Node "%s" has been defined twice'),
595 $name
596 )
597 );
598 }
599
600 $this->nodes[$name] = $node;
601
602 if ($node->getDisplay() > 0) {
603 if (! $this->isRootNode($name)) {
604 $this->addRootNode($name);
605 }
606 } else {
607 if ($this->isRootNode($name)) {
608 $this->removeRootNode($name);
609 }
610 }
611
612
613 return $this;
614 }
615
616 /**
617 * Remove all occurrences of a specific node by name
618 *
619 * @param $name
620 */
621 public function removeNode($name)
622 {
623 unset($this->nodes[$name]);
624 if (array_key_exists($name, $this->root_nodes)) {
625 unset($this->root_nodes[$name]);
626 }
627
628 foreach ($this->getBpNodes() as $node) {
629 if ($node->hasChild($name)) {
630 $node->removeChild($name);
631 }
632 }
633 }
634
635 /**
636 * Get all business process nodes
637 *
638 * @return BpNode[]
639 */
640 public function getBpNodes()
641 {
642 $nodes = array();
643
644 foreach ($this->nodes as $node) {
645 if ($node instanceof BpNode) {
646 $nodes[$node->getName()] = $node;
647 }
648 }
649
650 return $nodes;
651 }
652
653 /**
654 * List all business process node names
655 *
656 * @return array
657 */
658 public function listBpNodes()
659 {
660 $nodes = array();
661
662 foreach ($this->getBpNodes() as $name => $node) {
663 $alias = $node->getAlias();
664 $nodes[$name] = $name === $alias ? $name : sprintf('%s (%s)', $alias, $node);
665 }
666
667 natsort($nodes);
668 return $nodes;
669 }
670
671 /**
672 * All business process nodes defined in this config but not
673 * assigned to any parent
674 *
675 * @return BpNode[]
676 */
677 public function getUnboundNodes()
678 {
679 $nodes = array();
680
681 foreach ($this->getBpNodes() as $name => $node) {
682 if ($node->hasParents()) {
683 continue;
684 }
685
686 if ($node->getDisplay() === 0) {
687 $nodes[$name] = $node;
688 }
689 }
690
691 return $nodes;
692 }
693
694 /**
695 * @return bool
696 */
697 public function hasWarnings()
698 {
699 return ! empty($this->warnings);
700 }
701
702 /**
703 * @return array
704 */
705 public function getWarnings()
706 {
707 return $this->warnings;
708 }
709
710 /**
711 * @return bool
712 */
713 public function hasErrors()
714 {
715 return ! empty($this->errors) || $this->isEmpty();
716 }
717
718 /**
719 * @return array
720 */
721 public function getErrors()
722 {
723 $errors = $this->errors;
724 if ($this->isEmpty()) {
725 $errors[] = sprintf(
726 $this->translate(
727 'No business process nodes for "%s" have been defined yet'
728 ),
729 $this->getTitle()
730 );
731 }
732 return $errors;
733 }
734
735 /**
736 * Translation helper
737 *
738 * @param $msg
739 *
740 * @return mixed|string
741 */
742 public function translate($msg)
743 {
744 return mt('businessprocess', $msg);
745 }
746
747 /**
748 * Add a message to our warning stack
749 *
750 * @param $msg
751 */
752 protected function warn($msg)
753 {
754 $args = func_get_args();
755 array_shift($args);
756 $this->warnings[] = vsprintf($msg, $args);
757 }
758
759 /**
760 * @param string $msg,...
761 *
762 * @return $this
763 *
764 * @throws IcingaException
765 */
766 public function addError($msg)
767 {
768 $args = func_get_args();
769 array_shift($args);
770 $msg = vsprintf($msg, $args);
771 if ($this->throwErrors) {
772 throw new IcingaException($msg);
773 }
774
775 $this->errors[] = $msg;
776 return $this;
777 }
778
779 /**
780 * Decide whether errors should be thrown or collected
781 *
782 * @param bool $throw
783 *
784 * @return $this
785 */
786 public function throwErrors($throw = true)
787 {
788 $this->throwErrors = $throw;
789 return $this;
790 }
791
792 /**
793 * Begin loop detection for the given name
794 *
795 * Will throw a NestingError in case this node will be met again below itself
796 *
797 * @param $name
798 *
799 * @throws NestingError
800 */
801 public function beginLoopDetection($name)
802 {
803 // echo "Begin loop $name\n";
804 if (array_key_exists($name, $this->loopDetection)) {
805 $loop = array_keys($this->loopDetection);
806 $loop[] = $name;
807 $this->loopDetection = array();
808 throw new NestingError('Loop detected: %s', implode(' -> ', $loop));
809 }
810
811 $this->loopDetection[$name] = true;
812 }
813
814 /**
815 * Remove the given name from the loop detection stack
816 *
817 * @param $name
818 */
819 public function endLoopDetection($name)
820 {
821 // echo "End loop $this->name\n";
822 unset($this->loopDetection[$name]);
823 }
824
825 /**
826 * Whether this configuration has any Nodes
827 *
828 * @return bool
829 */
830 public function isEmpty()
831 {
832 // This is faster
833 if (! empty($this->root_nodes)) {
834 return false;
835 }
836
837 return count($this->listBpNodes()) === 0;
838 }
839 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Exception\ConfigurationError;
5 use Icinga\Exception\NotFoundError;
6 use Icinga\Module\Businessprocess\Exception\NestingError;
7
8 class BpNode extends Node
9 {
10 const OP_AND = '&';
11 const OP_OR = '|';
12 const OP_NOT = '!';
13 protected $operator = '&';
14 protected $url;
15 protected $info_command;
16 protected $display = 0;
17
18 /** @var Node[] */
19 protected $children;
20
21 /** @var array */
22 protected $childNames = array();
23 protected $alias;
24 protected $counters;
25 protected $missing = null;
26 protected $missingChildren;
27
28 protected static $emptyStateSummary = array(
29 'OK' => 0,
30 'WARNING' => 0,
31 'CRITICAL' => 0,
32 'UNKNOWN' => 0,
33 'PENDING' => 0,
34 'UP' => 0,
35 'DOWN' => 0,
36 'UNREACHABLE' => 0,
37 'MISSING' => 0,
38 );
39
40 protected static $sortStateInversionMap = array(
41 4 => 0,
42 3 => 0,
43 2 => 2,
44 1 => 1,
45 0 => 4
46 );
47
48 protected $className = 'process';
49
50 public function __construct(BpConfig $bp, $object)
51 {
52 $this->bp = $bp;
53 $this->name = $object->name;
54 $this->setOperator($object->operator);
55 $this->setChildNames($object->child_names);
56 }
57
58 public function getStateSummary()
59 {
60 if ($this->counters === null) {
61 $this->getState();
62 $this->counters = self::$emptyStateSummary;
63
64 foreach ($this->getChildren() as $child) {
65 if ($child instanceof BpNode) {
66 $counters = $child->getStateSummary();
67 foreach ($counters as $k => $v) {
68 $this->counters[$k] += $v;
69 }
70 } else {
71 $state = $child->getStateName();
72 $this->counters[$state]++;
73 }
74 }
75 }
76 return $this->counters;
77 }
78
79 public function hasProblems()
80 {
81 if ($this->isProblem()) {
82 return true;
83 }
84
85 $okStates = array('OK', 'UP', 'PENDING', 'MISSING');
86
87 foreach ($this->getStateSummary() as $state => $cnt) {
88 if ($cnt !== 0 && ! in_array($state, $okStates)) {
89 return true;
90 }
91 }
92
93 return false;
94 }
95
96 /**
97 * @param Node $node
98 * @return $this
99 * @throws ConfigurationError
100 */
101 public function addChild(Node $node)
102 {
103 if ($this->children === null) {
104 $this->getChildren();
105 }
106
107 $name = $node->getName();
108 if (array_key_exists($name, $this->children)) {
109 throw new ConfigurationError(
110 'Node "%s" has been defined more than once',
111 $name
112 );
113 }
114 $this->children[$name] = $node;
115 $this->childNames[] = $name;
116 $node->addParent($this);
117 return $this;
118 }
119
120 public function getProblematicChildren()
121 {
122 $problems = array();
123
124 foreach ($this->getChildren() as $child) {
125 if ($child->isProblem()
126 || ($child instanceof BpNode && $child->hasProblems())
127 ) {
128 $problems[] = $child;
129 }
130 }
131
132 return $problems;
133 }
134
135 public function hasChild($name)
136 {
137 return in_array($name, $this->childNames);
138 }
139
140 public function removeChild($name)
141 {
142 if (($key = array_search($name, $this->childNames)) !== false) {
143 unset($this->childNames[$key]);
144
145 if (! empty($this->children)) {
146 unset($this->children[$name]);
147 }
148 }
149
150 return $this;
151 }
152
153 public function getProblemTree()
154 {
155 $tree = array();
156
157 foreach ($this->getProblematicChildren() as $child) {
158 $name = (string) $child;
159 $tree[$name] = array(
160 'node' => $child,
161 'children' => array()
162 );
163 if ($child instanceof BpNode) {
164 $tree[$name]['children'] = $child->getProblemTree();
165 }
166 }
167
168 return $tree;
169 }
170
171 public function isMissing()
172 {
173 if ($this->missing === null) {
174 $exists = false;
175 foreach ($this->getChildren() as $child) {
176 if (! $child->isMissing()) {
177 $exists = true;
178 }
179 }
180 $this->missing = ! $exists;
181 }
182 return $this->missing;
183 }
184
185 public function getMissingChildren()
186 {
187 if ($this->missingChildren === null) {
188 $missing = array();
189
190 foreach ($this->getChildren() as $child) {
191 if ($child->isMissing()) {
192 $missing[(string) $child] = $child;
193 }
194
195 foreach ($child->getMissingChildren() as $m) {
196 $missing[(string) $m] = $m;
197 }
198 }
199
200 $this->missingChildren = $missing;
201 }
202
203 return $this->missingChildren;
204 }
205
206 public function getOperator()
207 {
208 return $this->operator;
209 }
210
211 public function setOperator($operator)
212 {
213 $this->assertValidOperator($operator);
214 $this->operator = $operator;
215 return $this;
216 }
217
218 protected function assertValidOperator($operator)
219 {
220 switch ($operator) {
221 case self::OP_AND:
222 case self::OP_OR:
223 case self::OP_NOT:
224 return;
225 default:
226 if (is_numeric($operator)) {
227 return;
228 }
229 }
230
231 throw new ConfigurationError(
232 'Got invalid operator: %s',
233 $operator
234 );
235 }
236
237 public function setInfoUrl($url)
238 {
239 $this->url = $url;
240 return $this;
241 }
242
243 public function hasInfoUrl()
244 {
245 return ! empty($this->url);
246 }
247
248 public function getInfoUrl()
249 {
250 return $this->url;
251 }
252
253 public function setInfoCommand($cmd)
254 {
255 $this->info_command = $cmd;
256 }
257
258 public function hasInfoCommand()
259 {
260 return $this->info_command !== null;
261 }
262
263 public function getInfoCommand()
264 {
265 return $this->info_command;
266 }
267
268 public function hasAlias()
269 {
270 return $this->alias !== null;
271 }
272
273 public function getAlias()
274 {
275 return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name;
276 }
277
278 public function setAlias($name)
279 {
280 $this->alias = $name;
281 return $this;
282 }
283
284 /**
285 * @return int
286 */
287 public function getState()
288 {
289 if ($this->state === null) {
290 try {
291 $this->reCalculateState();
292 } catch (NestingError $e) {
293 $this->bp->addError(
294 $this->bp->translate('Nesting error detected: %s'),
295 $e->getMessage()
296 );
297
298 // Failing nodes are unknown
299 $this->state = 3;
300 }
301 }
302
303 return $this->state;
304 }
305
306 public function getHtmlId()
307 {
308 return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', (string) $this);
309 }
310
311 protected function invertSortingState($state)
312 {
313 return self::$sortStateInversionMap[$state >> self::SHIFT_FLAGS] << self::SHIFT_FLAGS;
314 }
315
316 /**
317 * @return $this
318 */
319 public function reCalculateState()
320 {
321 $bp = $this->bp;
322
323 $sort_states = array();
324 $lastStateChange = 0;
325
326 if (!$this->hasChildren()) {
327 // TODO: delegate this to operators, should mostly fail
328 $this->setState(self::ICINGA_UNKNOWN);
329 $this->setMissing();
330 return $this;
331 }
332
333 foreach ($this->getChildren() as $child) {
334 $bp->beginLoopDetection($this->name);
335 if ($child instanceof MonitoredNode && $child->isMissing()) {
336 if ($child instanceof HostNode) {
337 $child->setState(self::ICINGA_UNREACHABLE);
338 } else {
339 $child->setState(self::ICINGA_UNKNOWN);
340 }
341
342 $child->setMissing();
343 }
344 $sort_states[] = $child->getSortingState();
345 $lastStateChange = max($lastStateChange, $child->getLastStateChange());
346 $bp->endLoopDetection($this->name);
347 }
348
349 $this->setLastStateChange($lastStateChange);
350
351 switch ($this->operator) {
352 case self::OP_AND:
353 $sort_state = max($sort_states);
354 break;
355 case self::OP_NOT:
356 $sort_state = $this->invertSortingState(max($sort_states));
357 break;
358 case self::OP_OR:
359 $sort_state = min($sort_states);
360 break;
361 default:
362 // MIN:
363 sort($sort_states);
364
365 // default -> unknown
366 $sort_state = 3 << self::SHIFT_FLAGS;
367
368 for ($i = 1; $i <= $this->operator; $i++) {
369 $sort_state = array_shift($sort_states);
370 }
371 }
372 if ($sort_state & self::FLAG_DOWNTIME) {
373 $this->setDowntime(true);
374 }
375 if ($sort_state & self::FLAG_ACK) {
376 $this->setAck(true);
377 }
378
379 $this->state = $this->sortStateTostate($sort_state);
380 return $this;
381 }
382
383 public function checkForLoops()
384 {
385 $bp = $this->bp;
386 foreach ($this->getChildren() as $child) {
387 $bp->beginLoopDetection($this->name);
388 if ($child instanceof BpNode) {
389 $child->checkForLoops();
390 }
391 $bp->endLoopDetection($this->name);
392 }
393
394 return $this;
395 }
396
397 public function setDisplay($display)
398 {
399 $this->display = (int) $display;
400 return $this;
401 }
402
403 public function getDisplay()
404 {
405 return $this->display;
406 }
407
408 public function setChildNames($names)
409 {
410 sort($names);
411 $this->childNames = $names;
412 $this->children = null;
413 return $this;
414 }
415
416 public function hasChildren($filter = null)
417 {
418 return !empty($this->childNames);
419 }
420
421 public function getChildNames()
422 {
423 return $this->childNames;
424 }
425
426 public function getChildren($filter = null)
427 {
428 if ($this->children === null) {
429 $this->children = array();
430 natsort($this->childNames);
431 foreach ($this->childNames as $name) {
432 $this->children[$name] = $this->bp->getNode($name);
433 $this->children[$name]->addParent($this);
434 }
435 }
436
437 return $this->children;
438 }
439
440 /**
441 * return BpNode[]
442 */
443 public function getChildBpNodes()
444 {
445 $children = array();
446
447 foreach ($this->getChildren() as $name => $child) {
448 if ($child instanceof BpNode) {
449 $children[$name] = $child;
450 }
451 }
452
453 return $children;
454 }
455
456 /**
457 * @param $childName
458 * @return Node
459 * @throws NotFoundError
460 */
461 public function getChildByName($childName)
462 {
463 foreach ($this->getChildren() as $name => $child) {
464 if ($name === $childName) {
465 return $child;
466 }
467 }
468
469 throw new NotFoundError('Trying to get missing child %s', $childName);
470 }
471
472 protected function assertNumericOperator()
473 {
474 if (! is_numeric($this->operator)) {
475 throw new ConfigurationError('Got invalid operator: %s', $this->operator);
476 }
477 }
478
479 public function operatorHtml()
480 {
481 switch ($this->operator) {
482 case self::OP_AND:
483 return 'and';
484 break;
485 case self::OP_OR:
486 return 'or';
487 break;
488 case self::OP_NOT:
489 return 'not';
490 break;
491 default:
492 // MIN
493 $this->assertNumericOperator();
494 return 'min:' . $this->operator;
495 }
496 }
497 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Director;
3
4 use Icinga\Application\Config;
5 use Icinga\Module\Director\Hook\ShipConfigFilesHook;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7
8 class ShipConfigFiles extends ShipConfigFilesHook
9 {
10 public function fetchFiles()
11 {
12 $files = array();
13
14 $storage = new LegacyStorage(
15 Config::module('businessprocess')->getSection('global')
16 );
17
18 foreach ($storage->listProcesses() as $name => $title) {
19 $files['processes/' . $name . '.bp'] = $storage->getSource($name);
20 }
21
22 return $files;
23 }
24 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Exception;
3
4 use Icinga\Exception\IcingaException;
5
6 class NestingError extends IcingaException
7 {
8 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Web\Request;
5 use Icinga\Web\Form as WebForm;
6
7 class Form extends WebForm
8 {
9 public function __construct($options = null)
10 {
11 parent::__construct($options);
12 $this->setup();
13 }
14
15 public function addHidden($name, $value = null)
16 {
17 $this->addElement('hidden', $name);
18 $this->getElement($name)->setDecorators(array('ViewHelper'));
19 if ($value !== null) {
20 $this->setDefault($name, $value);
21 }
22 return $this;
23 }
24
25 public function handleRequest(Request $request = null)
26 {
27 parent::handleRequest();
28 return $this;
29 }
30
31 public static function construct()
32 {
33 return new static;
34 }
35 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\Html\Link;
5 use Icinga\Module\Businessprocess\Web\Url;
6
7 class HostNode extends MonitoredNode
8 {
9 protected $sortStateToStateMap = array(
10 4 => self::ICINGA_DOWN,
11 3 => self::ICINGA_UNREACHABLE,
12 1 => self::ICINGA_PENDING,
13 0 => self::ICINGA_UP
14 );
15
16 protected $stateToSortStateMap = array(
17 self::ICINGA_PENDING => 1,
18 self::ICINGA_UNREACHABLE => 3,
19 self::ICINGA_DOWN => 4,
20 self::ICINGA_UP => 0,
21 );
22
23 protected $stateNames = array(
24 'UP',
25 'DOWN',
26 'UNREACHABLE',
27 99 => 'PENDING'
28 );
29
30 protected $hostname;
31
32 protected $className = 'host';
33
34 public function __construct(BpConfig $bp, $object)
35 {
36 $this->name = $object->hostname . ';Hoststatus';
37 $this->hostname = $object->hostname;
38 $this->bp = $bp;
39 if (isset($object->state)) {
40 $this->setState($object->state);
41 } else {
42 $this->setState(0)->setMissing();
43 }
44 }
45
46 public function getAlias()
47 {
48 return $this->getHostname();
49 }
50
51 public function getHostname()
52 {
53 return $this->hostname;
54 }
55
56 public function getUrl()
57 {
58 $params = array(
59 'host' => $this->getHostname(),
60 );
61
62 if ($this->bp->hasBackendName()) {
63 $params['backend'] = $this->bp->getBackendName();
64 }
65
66 return Url::fromPath('monitoring/host/show', $params);
67 }
68
69 public function getLink()
70 {
71 return Link::create($this->hostname, $this->getUrl());
72 }
73 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class Attribute
5 {
6 /** @var string */
7 protected $name;
8
9 /** @var string|array */
10 protected $value;
11
12 /**
13 * Attribute constructor.
14 *
15 * @param $name
16 * @param $value
17 */
18 public function __construct($name, $value)
19 {
20 $this->name = $name;
21 $this->value = $value;
22 }
23
24 /**
25 * @param $name
26 * @param $value
27 * @return static
28 */
29 public static function create($name, $value)
30 {
31 return new static($name, $value);
32 }
33
34 /**
35 * @return string
36 */
37 public function getName()
38 {
39 return $this->name;
40 }
41
42 /**
43 * @return string
44 */
45 public function getValue()
46 {
47 return $this->value;
48 }
49
50 /**
51 * @param string|array $value
52 * @return $this
53 */
54 public function setValue($value)
55 {
56 $this->value = $value;
57 return $this;
58 }
59
60 /**
61 * @param string $value
62 * @return $this
63 */
64 public function addValue($value)
65 {
66 if (! is_array($this->value)) {
67 $this->value = array($this->value);
68 }
69
70 if (is_array($value)) {
71 $this->value = array_merge($this->value, $value);
72 } else {
73 $this->value[] = $value;
74 }
75 return $this;
76 }
77
78 /**
79 * @return string
80 */
81 public function render()
82 {
83 return sprintf(
84 '%s="%s"',
85 $this->renderName(),
86 $this->renderValue()
87 );
88 }
89
90 /**
91 * @return string
92 */
93 public function renderName()
94 {
95 return static::escapeName($this->name);
96 }
97
98 /**
99 * @return string
100 */
101 public function renderValue()
102 {
103 return static::escapeValue($this->value);
104 }
105
106 /**
107 * @param $name
108 * @return string
109 */
110 public static function escapeName($name)
111 {
112 // TODO: escape
113 return (string) $name;
114 }
115
116 /**
117 * @param $value
118 * @return string
119 */
120 public static function escapeValue($value)
121 {
122 // TODO: escape differently
123 if (is_array($value)) {
124 return Util::escapeForHtml(implode(' ', $value));
125 } else {
126 return Util::escapeForHtml((string) $value);
127 }
128 }
129 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Exception\IcingaException;
5 use Icinga\Exception\ProgrammingError;
6
7 class Attributes
8 {
9 /** @var Attribute[] */
10 protected $attributes = array();
11
12 /** @var callable */
13 protected $callbacks = array();
14
15 /**
16 * Attributes constructor.
17 * @param Attribute[] $attributes
18 */
19 public function __construct(array $attributes = null)
20 {
21 if (empty($attributes)) {
22 return;
23 }
24
25 foreach ($attributes as $key => $value) {
26 if ($value instanceof Attribute) {
27 $this->addAttribute($value);
28 } elseif (is_string($key)) {
29 $this->add($key, $value);
30 } elseif (is_array($value) && count($value) === 2) {
31 $this->add(array_shift($value), array_shift($value));
32 }
33 }
34 }
35
36 /**
37 * @param Attribute[] $attributes
38 * @return static
39 */
40 public static function create(array $attributes = null)
41 {
42 return new static($attributes);
43 }
44
45 /**
46 * @param Attributes|array|null $attributes
47 * @return Attributes
48 * @throws IcingaException
49 */
50 public static function wantAttributes($attributes)
51 {
52 if ($attributes instanceof Attributes) {
53 return $attributes;
54 } else {
55 $self = new static();
56 if (is_array($attributes)) {
57 foreach ($attributes as $k => $v) {
58 $self->add($k, $v);
59 }
60
61 return $self;
62 } elseif ($attributes !== null) {
63 throw new IcingaException(
64 'Attributes, Array or Null expected, got %s',
65 Util::getPhpTypeName($attributes)
66 );
67 }
68 return $self;
69 }
70 }
71
72 /**
73 * @return Attribute[]
74 */
75 public function getAttributes()
76 {
77 return $this->attributes;
78 }
79
80 /**
81 * @param Attribute|string $attribute
82 * @param string|array $value
83 * @return $this
84 */
85 public function add($attribute, $value = null)
86 {
87 if ($attribute instanceof static) {
88 foreach ($attribute->getAttributes() as $a) {
89 $this->add($a);
90 }
91 } elseif ($attribute instanceof Attribute) {
92 $this->addAttribute($attribute);
93 } elseif (is_array($attribute)) {
94 foreach ($attribute as $name => $value) {
95 $this->add($name, $value);
96 }
97 } else {
98 $this->addAttribute(Attribute::create($attribute, $value));
99 }
100
101 return $this;
102 }
103
104 /**
105 * @param Attribute|string $attribute
106 * @param string|array $value
107 * @return $this
108 */
109 public function set($attribute, $value = null)
110 {
111 if ($attribute instanceof static) {
112 foreach ($attribute as $a) {
113 $this->setAttribute($a);
114 }
115
116 return $this;
117 } elseif ($attribute instanceof Attribute) {
118 return $this->setAttribute($attribute);
119 } else {
120 return $this->setAttribute(new Attribute($attribute, $value));
121 }
122 }
123
124 /**
125 * @param Attribute $attribute
126 * @return $this
127 */
128 public function addAttribute(Attribute $attribute)
129 {
130 $name = $attribute->getName();
131 if (array_key_exists($name, $this->attributes)) {
132 $this->attributes[$name]->addValue($attribute->getValue());
133 } else {
134 $this->attributes[$name] = $attribute;
135 }
136
137 return $this;
138 }
139
140 /**
141 * @param Attribute $attribute
142 * @return $this
143 */
144 public function setAttribute(Attribute $attribute)
145 {
146 $name = $attribute->getName();
147 $this->attributes[$name] = $attribute;
148 return $this;
149 }
150
151 /**
152 * Callback must return an instance of Attribute
153 *
154 * @param string $name
155 * @param callable $callback
156 * @return $this
157 */
158 public function registerCallbackFor($name, $callback)
159 {
160 if (! is_callable($callback)) {
161 throw new ProgrammingError('registerCallBack expects a callable callback');
162 }
163 $this->callbacks[$name] = $callback;
164 return $this;
165 }
166
167 /**
168 * @inheritdoc
169 */
170 public function render()
171 {
172 if (empty($this->attributes) && empty($this->callbacks)) {
173 return '';
174 }
175
176 $parts = array();
177 foreach ($this->callbacks as $callback) {
178 $attribute = call_user_func($callback);
179 if (! $attribute instanceof Attribute) {
180 throw new ProgrammingError(
181 'A registered attribute callback must return an Attribute'
182 );
183 }
184
185 $parts[] = $attribute->render();
186 }
187
188 foreach ($this->attributes as $attribute) {
189 $parts[] = $attribute->render();
190 }
191 return ' ' . implode(' ', $parts);
192 }
193 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Exception;
5
6 abstract class BaseElement extends Html
7 {
8 /** @var array You may want to set default attributes when extending this class */
9 protected $defaultAttributes;
10
11 /** @var Attributes */
12 protected $attributes;
13
14 /** @var string */
15 protected $tag;
16
17 /**
18 * @return Attributes
19 */
20 public function attributes()
21 {
22 if ($this->attributes === null) {
23 $default = $this->getDefaultAttributes();
24 if (empty($default)) {
25 $this->attributes = new Attributes();
26 } else {
27 $this->attributes = Attributes::wantAttributes($default);
28 }
29 }
30
31 return $this->attributes;
32 }
33
34 /**
35 * @param Attributes|array|null $attributes
36 * @return $this
37 */
38 public function setAttributes($attributes)
39 {
40 $this->attributes = Attributes::wantAttributes($attributes);
41 return $this;
42 }
43
44 /**
45 * @param Attributes|array|null $attributes
46 * @return $this
47 */
48 public function addAttributes($attributes)
49 {
50 $this->attributes = Attributes::wantAttributes($attributes);
51 return $this;
52 }
53
54 public function getDefaultAttributes()
55 {
56 return $this->defaultAttributes;
57 }
58
59 public function setTag($tag)
60 {
61 $this->tag = $tag;
62 return $this;
63 }
64
65 public function getTag()
66 {
67 return $this->tag;
68 }
69
70 /**
71 * Container constructor.
72 *
73 * @param string $tag
74 * @param Attributes|array $attributes
75 *
76 * @return Element
77 */
78 public function createElement($tag, $attributes = null)
79 {
80 $element = Element::create($tag, $attributes);
81 $this->add($element);
82 return $element;
83 }
84
85 public function renderContent()
86 {
87 return parent::render();
88 }
89
90 /**
91 * @return string
92 */
93 public function render()
94 {
95 $tag = $this->getTag();
96
97 return sprintf(
98 '<%s%s>%s</%s>',
99 $tag,
100 $this->attributes()->render(),
101 $this->renderContent(),
102 $tag
103 );
104 }
105
106 /**
107 * Whether the given something can be rendered
108 *
109 * @param mixed $any
110 * @return bool
111 */
112 protected function canBeRendered($any)
113 {
114 return is_string($any) || is_int($any) || is_null($any);
115 }
116 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class Container extends BaseElement
5 {
6 /** @var string */
7 protected $contentSeparator = "\n";
8
9 /** @var string */
10 protected $tag = 'div';
11
12 protected function __construct()
13 {
14 }
15
16 /**
17 * @param Renderable|array|string $content
18 * @param Attributes|array $attributes
19 * @param string $tag
20 *
21 * @return static
22 */
23 public static function create($attributes = null, $content = null, $tag = null)
24 {
25 $container = new static();
26 if ($content !== null) {
27 $container->setContent($content);
28 }
29
30 if ($attributes !== null) {
31 $container->setAttributes($attributes);
32 }
33 if ($tag !== null) {
34 $container->setTag($tag);
35 }
36
37 return $container;
38 }
39 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class Element extends BaseElement
5 {
6 /**
7 * Container constructor.
8 *
9 * @param string $tag
10 * @param Attributes|array $attributes
11 */
12 public function __construct($tag, $attributes = null)
13 {
14 $this->tag = $tag;
15
16 if ($attributes !== null) {
17 $this->attributes = $this->attributes()->add($attributes);
18 }
19 }
20
21 /**
22 * Container constructor.
23 *
24 * @param string $tag
25 * @param Attributes|array $attributes
26 *
27 * @return static
28 */
29 public static function create($tag, $attributes = null)
30 {
31 return new static($tag, $attributes);
32 }
33 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Exception\ProgrammingError;
5
6 class Html implements Renderable
7 {
8 protected $contentSeparator = '';
9
10 /**
11 * @var Renderable[]
12 */
13 private $content = array();
14
15 public static function escape($any)
16 {
17 return Util::wantHtml($any);
18 }
19
20 /**
21 * @param Renderable $element
22 * @return $this
23 */
24 public function add(Renderable $element)
25 {
26 $this->content[] = $element;
27 return $this;
28 }
29
30 /**
31 * @param Renderable $element
32 * @return $this
33 */
34 public function prepend(Renderable $element)
35 {
36 array_unshift($this->content, $element);
37 return $this;
38 }
39
40 /**
41 * @param Renderable|array|string $content
42 * @return $this
43 */
44 public function setContent($content)
45 {
46 $this->content = array(
47 static::escape($content)
48 );
49
50 return $this;
51 }
52
53 /**
54 * @param Renderable|array|string $content
55 * @return $this
56 */
57 public function addContent($content)
58 {
59 $this->content[] = static::escape($content);
60 return $this;
61 }
62
63 /**
64 * @param Renderable|array|string $content
65 * @return $this
66 */
67 public function prependContent($content)
68 {
69 array_unshift($this->content, static::escape($content));
70 return $this;
71 }
72
73 /**
74 * return Html
75 */
76 public function getContent()
77 {
78 if ($this->content === null) {
79 $this->content = array(new Html());
80 }
81
82 return $this->content;
83 }
84
85 public function hasContent()
86 {
87 if ($this->content === null) {
88 return false;
89 }
90
91 // TODO: unfinished
92 // return $this->content->isEmpty();
93 return true;
94 }
95
96 /**
97 * @param $separator
98 * @return $this
99 */
100 public function setSeparator($separator)
101 {
102 $this->contentSeparator = $separator;
103 return $this;
104 }
105
106 /**
107 * @inheritdoc
108 */
109 public function render()
110 {
111 $html = array();
112
113 foreach ($this->content as $element) {
114 if (is_string($element)) {
115 var_dump($this->content);
116 }
117 $html[] = $element->render();
118 }
119
120 return implode($this->contentSeparator, $html);
121 }
122
123 protected function translate($msg)
124 {
125 // TODO: Not so nice
126 return mt('businessprocess', $msg);
127 }
128
129 public static function element($name, $attributes = null)
130 {
131 // TODO: This might be anything here, add a better check
132 if (! ctype_alnum($name)) {
133 throw new ProgrammingError('Invalid element requested');
134 }
135
136 $class = __NAMESPACE__ . '\\' . $name;
137 /** @var Element $element */
138 $element = new $class();
139 if ($attributes !== null) {
140 $element->setAttributes($attributes);
141 }
142
143 return $element;
144 }
145
146 /**
147 * @param Exception|string $error
148 * @return string
149 */
150 protected function renderError($error)
151 {
152 return Util::renderError($error);
153 }
154
155 /**
156 * @return string
157 */
158 public function __toString()
159 {
160 try {
161 return $this->render();
162 } catch (Exception $e) {
163 return $this->renderError($e);
164 }
165 }
166 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class HtmlString extends Text
5 {
6 protected $escaped = true;
7 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 abstract class HtmlTag
5 {
6 /**
7 * @param $content
8 * @param Attributes|array $attributes
9 *
10 * @return Element
11 */
12 public static function h1($content, $attributes = null)
13 {
14 return Element::create('h1', $attributes)->setContent($content);
15 }
16
17 /**
18 * @param $content
19 * @param Attributes|array $attributes
20 *
21 * @return Element
22 */
23 public static function p($content, $attributes = null)
24 {
25 return Element::create('p', $attributes)->setContent($content);
26 }
27 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class Icon extends BaseElement
5 {
6 protected $tag = 'i';
7
8 protected function __construct()
9 {
10 }
11
12 /**
13 * @param string $name
14 * @param array $attributes
15 *
16 * @return static
17 */
18 public static function create($name, array $attributes = null)
19 {
20 $icon = new static();
21 $icon->setAttributes($attributes);
22 $icon->attributes()->add('class', array('icon', 'icon-' . $name));
23 return $icon;
24 }
25 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Web\Url;
5 use Icinga\Web\Url as WebUrl;
6
7 class Img extends BaseElement
8 {
9 protected $tag = 'img';
10
11 /** @var Url */
12 protected $url;
13
14 protected $defaultAttributes = array('alt' => '');
15
16 protected function __construct()
17 {
18 }
19
20 /**
21 * @param Url|string $url
22 * @param array $urlParams
23 * @param array $attributes
24 *
25 * @return static
26 */
27 public static function create($url, $urlParams = null, array $attributes = null)
28 {
29 $img = new static();
30 $img->setAttributes($attributes);
31 $img->attributes()->registerCallbackFor('src', array($img, 'getSrcAttribute'));
32 $img->setUrl($url, $urlParams);
33 return $img;
34 }
35
36 public function setUrl($url, $urlParams)
37 {
38 if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl
39 if ($urlParams !== null) {
40 $url->addParams($urlParams);
41 }
42
43 $this->url = $url;
44 } else {
45 if ($urlParams === null) {
46 $this->url = Url::fromPath($url);
47 } else {
48 $this->url = Url::fromPath($url, $urlParams);
49 }
50 }
51
52 $this->url->getParams();
53 }
54
55 /**
56 * @return Attribute
57 */
58 public function getSrcAttribute()
59 {
60 return new Attribute('src', $this->getUrl()->getAbsoluteUrl('&'));
61 }
62
63 /**
64 * @return Url
65 */
66 public function getUrl()
67 {
68 // TODO: What if null? #?
69 return $this->url;
70 }
71 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Web\Url;
5 use Icinga\Web\Url as WebUrl;
6
7 class Link extends BaseElement
8 {
9 protected $tag = 'a';
10
11 /** @var Url */
12 protected $url;
13
14 protected function __construct()
15 {
16 }
17
18 /**
19 * @param Renderable|array|string $content
20 * @param Url|string $url
21 * @param array $urlParams
22 * @param array $attributes
23 *
24 * @return static
25 */
26 public static function create($content, $url, $urlParams = null, array $attributes = null)
27 {
28 $link = new static();
29 $link->setContent($content);
30 $link->setAttributes($attributes);
31 $link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute'));
32 $link->setUrl($url, $urlParams);
33 return $link;
34 }
35
36 public function setUrl($url, $urlParams)
37 {
38 if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl
39 if ($urlParams !== null) {
40 $url->addParams($urlParams);
41 }
42
43 $this->url = $url;
44 } else {
45 if ($urlParams === null) {
46 $this->url = Url::fromPath($url);
47 } else {
48 $this->url = Url::fromPath($url, $urlParams);
49 }
50 }
51
52 $this->url->getParams();
53 }
54
55 /**
56 * @return Attribute
57 */
58 public function getHrefAttribute()
59 {
60 return new Attribute('href', $this->getUrl()->getAbsoluteUrl('&'));
61 }
62
63 /**
64 * @return Url
65 */
66 public function getUrl()
67 {
68 // TODO: What if null? #?
69 return $this->url;
70 }
71 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 interface Renderable
5 {
6 public function render();
7 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 class Text implements Renderable
5 {
6 /** @var string */
7 protected $string;
8
9 protected $escaped = false;
10
11 /**
12 * Text constructor.
13 *
14 * @param $text
15 */
16 public function __construct($string)
17 {
18 $this->string = (string) $string;
19 }
20
21 /**
22 * @return string
23 */
24 public function getText()
25 {
26 return $this->string;
27 }
28
29 /**
30 * @param bool $escaped
31 * @return $this
32 */
33 public function setEscaped($escaped = true)
34 {
35 $this->escaped = $escaped;
36 return $this;
37 }
38
39 /**
40 * @param $text
41 *
42 * @return static
43 */
44 public static function create($text)
45 {
46 return new static($text);
47 }
48
49 /**
50 * @return string
51 */
52 public function render()
53 {
54 if ($this->escaped) {
55 return $this->string;
56 } else {
57 return Util::escapeForHtml($this->string);
58 }
59 }
60
61 /**
62 * @param Exception|string $error
63 * @return string
64 */
65 protected function renderError($error)
66 {
67 return Util::renderError($error);
68 }
69
70 /**
71 * @return string
72 */
73 public function __toString()
74 {
75 try {
76 return $this->render();
77 } catch (Exception $e) {
78 return $this->renderError($e);
79 }
80 }
81 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Html;
3
4 use Exception;
5 use Icinga\Exception\IcingaException;
6
7 class Util
8 {
9 /**
10 * Charset to be used - we only support UTF-8
11 */
12 const CHARSET = 'UTF-8';
13
14 /**
15 * The flags we use for htmlspecialchars depend on our PHP version
16 */
17 protected static $htmlEscapeFlags;
18
19 /**
20 * Escape the given value top be safely used in view scripts
21 *
22 * @param string $value The output to be escaped
23 * @return string
24 */
25 public static function escapeForHtml($value)
26 {
27 return htmlspecialchars(
28 $value,
29 static::htmlEscapeFlags(),
30 self::CHARSET,
31 true
32 );
33 }
34
35 /**
36 * @param Exception|string $error
37 * @return string
38 */
39 public static function renderError($error)
40 {
41 if ($error instanceof Exception) {
42 $file = preg_split('/[\/\\\]/', $error->getFile(), -1, PREG_SPLIT_NO_EMPTY);
43 $file = array_pop($file);
44 $msg = sprintf(
45 '%s (%s:%d)',
46 $error->getMessage(),
47 $file,
48 $error->getLine()
49 );
50 } elseif (is_string($error)) {
51 $msg = $error;
52 } else {
53 $msg = 'Got an invalid error'; // TODO: translate?
54 }
55
56 return sprintf(
57 'ERROR: %s', // TODO: translate?
58 static::escapeForHtml($msg)
59 );
60 }
61
62 /**
63 * @param $any
64 * @return Renderable
65 * @throws IcingaException
66 */
67 public static function wantHtml($any)
68 {
69 if ($any instanceof Renderable) {
70 return $any;
71 } elseif (static::canBeRenderedAsString($any)) {
72 return new Text($any);
73 } elseif (is_array($any)) {
74 $html = new Html();
75 foreach ($any as $el) {
76 $html->add(static::wantHtml($el));
77 }
78
79 return $html;
80 } else {
81 // TODO: Should we add a dedicated Exception class?
82 throw new IcingaException(
83 'String, Html Element or Array of such expected, got "%s"',
84 Util::getPhpTypeName($any)
85 );
86 }
87 }
88
89 public static function canBeRenderedAsString($any)
90 {
91 return is_string($any) || is_int($any) || is_null($any);
92 }
93
94 /**
95 * @param $any
96 * @return string
97 */
98 public static function getPhpTypeName($any)
99 {
100 if (is_object($any)) {
101 return get_class($any);
102 } else {
103 return gettype($any);
104 }
105 }
106
107 /**
108 * This defines the flags used when escaping for HTML
109 *
110 * - Single quotes are not escaped (ENT_COMPAT)
111 * - With PHP >= 5.4, invalid characters are replaced with � (ENT_SUBSTITUTE)
112 * - With PHP 5.3 they are ignored (ENT_IGNORE, less secure)
113 * - Uses HTML5 entities for PHP >= 5.4, disallowing &#013;
114 *
115 * @return int
116 */
117 protected static function htmlEscapeFlags()
118 {
119 if (self::$htmlEscapeFlags === null) {
120 if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
121 self::$htmlEscapeFlags = ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5;
122 } else {
123 self::$htmlEscapeFlags = ENT_COMPAT | ENT_IGNORE;
124 }
125 }
126
127 return self::$htmlEscapeFlags;
128 }
129 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Application\Config;
5 use Icinga\Module\Businessprocess\Html\Link;
6 use Icinga\Module\Businessprocess\State\MonitoringState;
7 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
8 use Icinga\Module\Businessprocess\Web\Url;
9 use Exception;
10
11 class ImportedNode extends Node
12 {
13 /** @var string */
14 protected $configName;
15
16 /** @var string */
17 protected $nodeName;
18
19 /** @var BpNode */
20 private $node;
21
22 protected $className = 'subtree';
23
24 /** @var BpConfig */
25 private $config;
26
27 /**
28 * @inheritdoc
29 */
30 public function __construct(BpConfig $bp, $object)
31 {
32 $this->bp = $bp;
33 $this->configName = $object->configName;
34 $this->name = '@' . $object->configName;
35 if (property_exists($object, 'node')) {
36 $this->nodeName = $object->node;
37 $this->name .= ':' . $object->node;
38 } else {
39 $this->useAllRootNodes();
40 }
41
42 if (isset($object->state)) {
43 $this->setState($object->state);
44 }
45 }
46
47 public function hasNode()
48 {
49 return $this->nodeName !== null;
50 }
51
52 /**
53 * @return string
54 */
55 public function getConfigName()
56 {
57 return $this->configName;
58 }
59
60 /**
61 * @inheritdoc
62 */
63 public function getState()
64 {
65 if ($this->state === null) {
66 try {
67 MonitoringState::apply($this->importedConfig());
68 } catch (Exception $e) {
69 }
70
71 $this->state = $this->importedNode()->getState();
72 $this->setMissing(false);
73 }
74
75 return $this->state;
76 }
77
78 /**
79 * @inheritdoc
80 */
81 public function getAlias()
82 {
83 return $this->importedNode()->getAlias();
84 }
85
86 public function getUrl()
87 {
88 $params = array(
89 'config' => $this->getConfigName(),
90 'node' => $this->importedNode()->getName()
91 );
92
93 return Url::fromPath('businessprocess/process/show', $params);
94 }
95
96 /**
97 * @inheritdoc
98 */
99 public function isMissing()
100 {
101 $this->getState();
102 // Probably doesn't work, as we create a fake node with worse state
103 return $this->missing;
104 }
105
106 /**
107 * @inheritdoc
108 */
109 public function isInDowntime()
110 {
111 if ($this->downtime === null) {
112 $this->getState();
113 $this->downtime = $this->importedNode()->isInDowntime();
114 }
115
116 return $this->downtime;
117 }
118
119 /**
120 * @inheritdoc
121 */
122 public function isAcknowledged()
123 {
124 if ($this->ack === null) {
125 $this->getState();
126 $this->downtime = $this->importedNode()->isAcknowledged();
127 }
128
129 return $this->ack;
130 }
131
132 /**
133 * @return BpNode
134 */
135 protected function importedNode()
136 {
137 if ($this->node === null) {
138 $this->node = $this->loadImportedNode();
139 }
140
141 return $this->node;
142 }
143
144 /**
145 * @return BpNode
146 */
147 protected function loadImportedNode()
148 {
149 try {
150 $import = $this->importedConfig();
151
152 return $import->getNode($this->nodeName);
153 } catch (Exception $e) {
154 return $this->createFailedNode($e);
155 }
156 }
157
158 protected function useAllRootNodes()
159 {
160 try {
161 $bp = $this->importedConfig();
162 $this->node = new BpNode($bp, (object) array(
163 'name' => $this->getName(),
164 'operator' => '&',
165 'child_names' => $bp->listRootNodes(),
166 ));
167 } catch (Exception $e) {
168 $this->createFailedNode($e);
169 }
170 }
171
172 /**
173 * @return BpConfig
174 */
175 protected function importedConfig()
176 {
177 if ($this->config === null) {
178 $import = $this->storage()->loadProcess($this->configName);
179 if ($this->bp->usesSoftStates()) {
180 $import->useSoftStates();
181 } else {
182 $import->useHardStates();
183 }
184
185 $this->config = $import;
186 }
187
188 return $this->config;
189 }
190
191 /**
192 * @return LegacyStorage
193 */
194 protected function storage()
195 {
196 return new LegacyStorage(
197 Config::module('businessprocess')->getSection('global')
198 );
199 }
200
201 /**
202 * @param Exception $e
203 *
204 * @return BpNode
205 */
206 protected function createFailedNode(Exception $e)
207 {
208 $this->bp->addError($e->getMessage());
209 $node = new BpNode($this->importedConfig(), (object) array(
210 'name' => $this->getName(),
211 'operator' => '&',
212 'child_names' => array()
213 ));
214 $node->setState(2);
215 $node->setMissing(false)
216 ->setDowntime(false)
217 ->setAck(false)
218 ->setAlias($e->getMessage());
219
220 return $node;
221 }
222
223 /**
224 * @inheritdoc
225 */
226 public function getLink()
227 {
228 return Link::create(
229 $this->getAlias(),
230 'businessprocess/process/show',
231 array(
232 'config' => $this->configName,
233 'node' => $this->nodeName
234 )
235 );
236 }
237 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Authentication\Auth;
6 use Icinga\Exception\ProgrammingError;
7 use Icinga\User;
8
9 class Metadata
10 {
11 /** @var string Configuration name */
12 protected $name;
13
14 protected $properties = array(
15 'Title' => null,
16 'Description' => null,
17 'Owner' => null,
18 'AllowedUsers' => null,
19 'AllowedGroups' => null,
20 'AllowedRoles' => null,
21 'AddToMenu' => null,
22 'Backend' => null,
23 'Statetype' => null,
24 // 'SLAHosts' => null
25 );
26
27 public function __construct($name)
28 {
29 $this->name = $name;
30 }
31
32 public function getTitle()
33 {
34 if ($this->has('Title')) {
35 return $this->get('Title');
36 } else {
37 return $this->name;
38 }
39 }
40
41 public function getExtendedTitle()
42 {
43 $title = $this->getTitle();
44
45 if ($title === $this->name) {
46 return $title;
47 } else {
48 return sprintf('%s (%s)', $title, $this->name);
49 }
50 }
51
52 public function getProperties()
53 {
54 return $this->properties;
55 }
56
57 public function hasKey($key)
58 {
59 return array_key_exists($key, $this->properties);
60 }
61
62 public function get($key, $default = null)
63 {
64 $this->assertKeyExists($key);
65 if ($this->properties[$key] === null) {
66 return $default;
67 }
68
69 return $this->properties[$key];
70 }
71
72 public function set($key, $value)
73 {
74 $this->assertKeyExists($key);
75 $this->properties[$key] = $value;
76
77 return $this;
78 }
79
80 public function isNull($key)
81 {
82 return null === $this->get($key);
83 }
84
85 public function has($key)
86 {
87 return null !== $this->get($key);
88 }
89
90 protected function assertKeyExists($key)
91 {
92 if (! $this->hasKey($key)) {
93 throw new ProgrammingError('Trying to access invalid header key: %s', $key);
94 }
95
96 return $this;
97 }
98
99 public function hasRestrictions()
100 {
101 return ! (
102 $this->isNull('AllowedUsers')
103 && $this->isNull('AllowedGroups')
104 && $this->isNull('AllowedRoles')
105 );
106 }
107
108 protected function getAuth()
109 {
110 return Auth::getInstance();
111 }
112
113 public function canModify(Auth $auth = null)
114 {
115 if ($auth === null) {
116 if (Icinga::app()->isCli()) {
117 return true;
118 } else {
119 $auth = $this->getAuth();
120 }
121 }
122
123 return $this->canRead($auth) && (
124 $auth->hasPermission('businessprocess/modify')
125 || $this->ownerIs($auth->getUser()->getUsername())
126 );
127 }
128
129 public function canRead(Auth $auth = null)
130 {
131 if ($auth === null) {
132 if (Icinga::app()->isCli()) {
133 return true;
134 } else {
135 $auth = $this->getAuth();
136 }
137 }
138
139 $prefixes = $auth->getRestrictions('businessprocess/prefix');
140 if (! empty($prefixes)) {
141 if (! $this->nameIsPrefixedWithOneOf($prefixes)) {
142 return false;
143 }
144 }
145
146 if ($auth->hasPermission('businessprocess/showall')) {
147 return true;
148 }
149
150 if (! $this->hasRestrictions()) {
151 return true;
152 }
153
154 if (! $auth->isAuthenticated()) {
155 return false;
156 }
157
158 return $this->userCanRead($auth->getUser());
159 }
160
161 public function nameIsPrefixedWithOneOf(array $prefixes)
162 {
163 foreach ($prefixes as $prefix) {
164 if (substr($this->name, 0, strlen($prefix)) === $prefix) {
165 return true;
166 }
167 }
168
169 return false;
170 }
171
172 protected function userCanRead(User $user)
173 {
174 $username = $user->getUsername();
175
176 return $this->ownerIs($username)
177 || $this->isInAllowedUserList($username)
178 || $this->isMemberOfAllowedGroups($user)
179 || $this->hasOneOfTheAllowedRoles($user);
180 }
181
182 public function ownerIs($username)
183 {
184 return $this->get('Owner') === $username;
185 }
186
187 public function listAllowedUsers()
188 {
189 // TODO: $this->get('AllowedUsers', array());
190 $list = $this->get('AllowedUsers');
191 if ($list === null) {
192 return array();
193 } else {
194 return $this->splitCommaSeparated($list);
195 }
196 }
197
198 public function listAllowedGroups()
199 {
200 $list = $this->get('AllowedGroups');
201 if ($list === null) {
202 return array();
203 } else {
204 return $this->splitCommaSeparated($list);
205 }
206 }
207
208 public function listAllowedRoles()
209 {
210 $list = $this->get('AllowedRoles');
211 if ($list === null) {
212 return array();
213 } else {
214 return $this->splitCommaSeparated($list);
215 }
216 }
217
218 public function isInAllowedUserList($username)
219 {
220 foreach ($this->listAllowedUsers() as $allowedUser) {
221 if ($username === $allowedUser) {
222 return true;
223 }
224 }
225
226 return false;
227 }
228
229 public function isMemberOfAllowedGroups(User $user)
230 {
231 foreach ($this->listAllowedGroups() as $group) {
232 if ($user->isMemberOf($group)) {
233 return true;
234 }
235 }
236
237 return false;
238 }
239
240 public function hasOneOfTheAllowedRoles(User $user)
241 {
242 foreach ($this->listAllowedRoles() as $roleName) {
243 foreach ($user->getRoles() as $role) {
244 if ($role->getName() === $roleName) {
245 return true;
246 }
247 }
248 }
249
250 return false;
251 }
252
253 protected function splitCommaSeparated($string)
254 {
255 return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY);
256 }
257 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Node;
6 use Icinga\Exception\ProgrammingError;
7
8 /**
9 * Abstract NodeAction class
10 *
11 * Every instance of a NodeAction represents a single applied change. Changes are pushed to
12 * a stack and consumed from there. When persisted, NodeActions are serialized with their name,
13 * node name and optionally additional properties according preserveProperties. For each property
14 * that should be preserved, getter and setter methods have to be defined.
15 *
16 * @package Icinga\Module\Businessprocess
17 */
18 abstract class NodeAction
19 {
20 /** @var string Name of this action (currently create, modify, remove) */
21 protected $actionName;
22
23 /** @var string Name of the node this action applies to */
24 protected $nodeName;
25
26 /** @var array Properties which should be preserved when serializing this action */
27 protected $preserveProperties = array();
28
29 /**
30 * NodeAction constructor.
31 *
32 * @param Node|string $node
33 */
34 public function __construct($node = null)
35 {
36 if ($node !== null) {
37 $this->nodeName = (string) $node;
38 }
39 }
40
41 /**
42 * Every NodeAction must be able to apply itself to a BusinessProcess
43 *
44 * @param BpConfig $config
45 * @return mixed
46 */
47 abstract public function applyTo(BpConfig $config);
48
49 /**
50 * Every NodeAction must be able to tell whether it could be applied to a BusinessProcess
51 *
52 * @param BpConfig $config
53 * @return bool
54 */
55 abstract public function appliesTo(BpConfig $config);
56
57 /**
58 * The name of the node this modification applies to
59 *
60 * @return string
61 */
62 public function getNodeName()
63 {
64 return $this->nodeName;
65 }
66
67 public function hasNode()
68 {
69 return $this->nodeName !== null;
70 }
71
72 /**
73 * Whether this is an instance of a given action name
74 *
75 * @param string $actionName
76 * @return bool
77 */
78 public function is($actionName)
79 {
80 return $this->getActionName() === $actionName;
81 }
82
83 /**
84 * Create an instance of a given actionName for a specific Node
85 *
86 * @param string $actionName
87 * @param string $nodeName
88 *
89 * @return static
90 */
91 public static function create($actionName, $nodeName)
92 {
93 $className = __NAMESPACE__ . '\\Node' . ucfirst($actionName) . 'Action';
94 $object = new $className($nodeName);
95 return $object;
96 }
97
98 /**
99 * Returns a JSON-encoded serialized NodeAction
100 *
101 * @return string
102 */
103 public function serialize()
104 {
105 $object = (object) array(
106 'actionName' => $this->getActionName(),
107 'nodeName' => $this->getNodeName(),
108 'properties' => array()
109 );
110
111 foreach ($this->preserveProperties as $key) {
112 $func = 'get' . ucfirst($key);
113 $object->properties[$key] = $this->$func();
114 }
115
116 return json_encode($object);
117 }
118
119 /**
120 * Decodes a JSON-serialized NodeAction and returns an object instance
121 *
122 * @param $string
123 * @return NodeAction
124 */
125 public static function unSerialize($string)
126 {
127 $object = json_decode($string);
128 $action = self::create($object->actionName, $object->nodeName);
129
130 foreach ($object->properties as $key => $val) {
131 $func = 'set' . ucfirst($key);
132 $action->$func($val);
133 }
134
135 return $action;
136 }
137
138 /**
139 * Returns the defined action name or determines such from the class name
140 *
141 * @return string The action name
142 *
143 * @throws ProgrammingError when no such class exists
144 */
145 public function getActionName()
146 {
147 if ($this->actionName === null) {
148 if (! preg_match('/\\\Node(\w+)Action$/', get_class($this), $m)) {
149 throw new ProgrammingError(
150 '"%s" is not a NodeAction class',
151 get_class($this)
152 );
153 }
154 $this->actionName = lcfirst($m[1]);
155 }
156
157 return $this->actionName;
158 }
159 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\BpNode;
6
7 class NodeAddChildrenAction extends NodeAction
8 {
9 protected $children = array();
10
11 protected $preserveProperties = array('children');
12
13 /**
14 * @inheritdoc
15 */
16 public function appliesTo(BpConfig $config)
17 {
18 $name = $this->getNodeName();
19
20 if (! $config->hasNode($name)) {
21 return false;
22 }
23
24 return $config->getNode($name) instanceof BpNode;
25 }
26
27 /**
28 * @inheritdoc
29 */
30 public function applyTo(BpConfig $config)
31 {
32 /** @var BpNode $node */
33 if (! $this->hasNode()) {
34 // TODO: We end up here when defining "top nodes", but that would probably
35 // be a different action
36 return $this;
37 }
38
39 $node = $config->getBpNode($this->getNodeName());
40
41 foreach ($this->children as $name) {
42 if (! $config->hasNode($name)) {
43 if (strpos($name, ';') !== false) {
44 list($host, $service) = preg_split('/;/', $name, 2);
45
46 if ($service === 'Hoststatus') {
47 $config->createHost($host);
48 } else {
49 $config->createService($host, $service);
50 }
51 }
52 }
53 $node->addChild($config->getNode($name));
54 }
55
56 return $this;
57 }
58
59 /**
60 * @param array|string $children
61 * @return $this
62 */
63 public function setChildren($children)
64 {
65 if (is_string($children)) {
66 $children = array($children);
67 }
68 $this->children = $children;
69 return $this;
70 }
71
72 /**
73 * @return array
74 */
75 public function getChildren()
76 {
77 return $this->children;
78 }
79 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\BpNode;
6 use Icinga\Module\Businessprocess\Node;
7
8 class NodeCreateAction extends NodeAction
9 {
10 /** @var string */
11 protected $parentName;
12
13 /** @var array */
14 protected $properties = array();
15
16 /** @var array */
17 protected $preserveProperties = array('parentName', 'properties');
18
19 /**
20 * @param Node $name
21 */
22 public function setParent(Node $name)
23 {
24 $this->parentName = (string) $name;
25 }
26
27 /**
28 * @return bool
29 */
30 public function hasParent()
31 {
32 return $this->parentName !== null;
33 }
34
35 /**
36 * @return string
37 */
38 public function getParentName()
39 {
40 return $this->parentName;
41 }
42
43 /**
44 * @param string $name
45 */
46 public function setParentName($name)
47 {
48 $this->parentName = $name;
49 }
50
51 /**
52 * @return array
53 */
54 public function getProperties()
55 {
56 return $this->properties;
57 }
58
59 /**
60 * @param array $properties
61 * @return $this
62 */
63 public function setProperties($properties)
64 {
65 $this->properties = (array) $properties;
66 return $this;
67 }
68
69 /**
70 * @inheritdoc
71 */
72 public function appliesTo(BpConfig $config)
73 {
74 return ! $config->hasNode($this->getNodeName());
75 }
76
77 /**
78 * @inheritdoc
79 */
80 public function applyTo(BpConfig $config)
81 {
82 $name = $this->getNodeName();
83
84 $properties = array(
85 'name' => $name,
86 'operator' => $this->properties['operator'],
87 );
88 if (array_key_exists('childNames', $this->properties)) {
89 $properties['child_names'] = $this->properties['childNames'];
90 } else {
91 $properties['child_names'] = array();
92 }
93 $node = new BpNode($config, (object) $properties);
94
95 foreach ($this->getProperties() as $key => $val) {
96 if ($key === 'parentName') {
97 $config->getBpNode($val)->addChild($node);
98 continue;
99 }
100 $func = 'set' . ucfirst($key);
101 $node->$func($val);
102 }
103
104 $config->addNode($name, $node);
105
106 return $node;
107 }
108 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Node;
6
7 class NodeModifyAction extends NodeAction
8 {
9 protected $properties = array();
10
11 protected $formerProperties = array();
12
13 protected $preserveProperties = array('formerProperties', 'properties');
14
15 /**
16 * Set properties for a specific node
17 *
18 * Can be called multiple times
19 *
20 * @param Node $node
21 * @param array $properties
22 *
23 * @return $this
24 */
25 public function setNodeProperties(Node $node, array $properties)
26 {
27 foreach (array_keys($properties) as $key) {
28 $this->properties[$key] = $properties[$key];
29
30 if (array_key_exists($key, $this->formerProperties)) {
31 continue;
32 }
33
34 $func = 'get' . ucfirst($key);
35 $this->formerProperties[$key] = $node->$func();
36 }
37
38 return $this;
39 }
40
41 /**
42 * @inheritdoc
43 */
44 public function appliesTo(BpConfig $config)
45 {
46 $name = $this->getNodeName();
47
48 if (! $config->hasNode($name)) {
49 return false;
50 }
51
52 $node = $config->getNode($name);
53
54 foreach ($this->properties as $key => $val) {
55 $func = 'get' . ucfirst($key);
56 if ($this->formerProperties[$key] !== $node->$func()) {
57 return false;
58 }
59 }
60
61 return true;
62 }
63
64 /**
65 * @inheritdoc
66 */
67 public function applyTo(BpConfig $config)
68 {
69 $node = $config->getNode($this->getNodeName());
70
71 foreach ($this->properties as $key => $val) {
72 $func = 'set' . ucfirst($key);
73 $node->$func($val);
74 }
75
76 return $this;
77 }
78
79 /**
80 * @param $properties
81 * @return $this
82 */
83 public function setProperties($properties)
84 {
85 $this->properties = $properties;
86 return $this;
87 }
88
89 /**
90 * @param $properties
91 * @return $this
92 */
93 public function setFormerProperties($properties)
94 {
95 $this->formerProperties = $properties;
96 return $this;
97 }
98
99 /**
100 * @return array
101 */
102 public function getProperties()
103 {
104 return $this->properties;
105 }
106
107 /**
108 * @return array
109 */
110 public function getFormerProperties()
111 {
112 return $this->formerProperties;
113 }
114 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5
6 /**
7 * NodeRemoveAction
8 *
9 * Tracks removed nodes
10 *
11 * @package Icinga\Module\Businessprocess
12 */
13 class NodeRemoveAction extends NodeAction
14 {
15 protected $preserveProperties = array('parentName');
16
17 protected $parentName;
18
19 /**
20 * @param $parentName
21 * @return $this
22 */
23 public function setParentName($parentName = null)
24 {
25 $this->parentName = $parentName;
26 return $this;
27 }
28
29 /**
30 * @return mixed
31 */
32 public function getParentName()
33 {
34 return $this->parentName;
35 }
36
37 /**
38 * @inheritdoc
39 */
40 public function appliesTo(BpConfig $config)
41 {
42 $parent = $this->getParentName();
43 if ($parent === null) {
44 return $config->hasNode($this->getNodeName());
45 } else {
46 return $config->hasNode($this->getNodeName()) && $config->hasNode($this->getParentName());
47 }
48 }
49
50 /**
51 * @inheritdoc
52 */
53 public function applyTo(BpConfig $config)
54 {
55 $config->calculateAllStates();
56 $name = $this->getNodeName();
57 $parentName = $this->getParentName();
58 if ($parentName === null) {
59 $config->removeNode($name);
60 } else {
61 $node = $config->getNode($name);
62 $parent = $config->getBpNode($parentName);
63 $parent->getState();
64 $parent->removeChild($name);
65 $node->removeParent($parentName);
66 if (! $node->hasParents()) {
67 $config->removeNode($name);
68 }
69 }
70 }
71 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Modification;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Node;
6 use Icinga\Web\Session\SessionNamespace as Session;
7
8 class ProcessChanges
9 {
10 /** @var NodeAction[] */
11 protected $changes = array();
12
13 /** @var Session */
14 protected $session;
15
16 /** @var bool */
17 protected $hasBeenModified = false;
18
19 /** @var string Session storage key for this processes changes */
20 protected $sessionKey;
21
22 /**
23 * ProcessChanges constructor.
24 *
25 * Direct access is not allowed
26 */
27 private function __construct()
28 {
29 }
30
31 /**
32 * @param BpConfig $bp
33 * @param Session $session
34 *
35 * @return ProcessChanges
36 */
37 public static function construct(BpConfig $bp, Session $session)
38 {
39 $key = 'changes.' . $bp->getName();
40 $changes = new ProcessChanges();
41 $changes->sessionKey = $key;
42
43 if ($actions = $session->get($key)) {
44 foreach ($actions as $string) {
45 $changes->push(NodeAction::unSerialize($string));
46 }
47 }
48 $changes->session = $session;
49
50 return $changes;
51 }
52
53 /**
54 * @param Node $node
55 * @param $properties
56 *
57 * @return $this
58 */
59 public function modifyNode(Node $node, $properties)
60 {
61 $action = new NodeModifyAction($node);
62 $action->setNodeProperties($node, $properties);
63 return $this->push($action);
64 }
65
66 /**
67 * @param Node $node
68 * @param $properties
69 *
70 * @return $this
71 */
72 public function addChildrenToNode($children, Node $node = null)
73 {
74 $action = new NodeAddChildrenAction($node);
75 $action->setChildren($children);
76 return $this->push($action);
77 }
78
79 /**
80 * @param Node|string $nodeName
81 * @param array $properties
82 * @param Node $parent
83 *
84 * @return $this
85 */
86 public function createNode($nodeName, $properties, Node $parent = null)
87 {
88 $action = new NodeCreateAction($nodeName);
89 $action->setProperties($properties);
90 if ($parent !== null) {
91 $action->setParent($parent);
92 }
93 return $this->push($action);
94 }
95
96 /**
97 * @param Node $node
98 * @param string $parentName
99 * @return $this
100 */
101 public function deleteNode(Node $node, $parentName = null)
102 {
103 $action = new NodeRemoveAction($node);
104 if ($parentName !== null) {
105 $action->setParentName($parentName);
106 }
107
108 return $this->push($action);
109 }
110
111 /**
112 * Add a new action to the stack
113 *
114 * @param NodeAction $change
115 *
116 * @return $this
117 */
118 public function push(NodeAction $change)
119 {
120 $this->changes[] = $change;
121 $this->hasBeenModified = true;
122 return $this;
123 }
124
125 /**
126 * Get all stacked actions
127 *
128 * @return NodeAction[]
129 */
130 public function getChanges()
131 {
132 return $this->changes;
133 }
134
135 /**
136 * Forget all changes and remove them from the Session
137 *
138 * @return $this
139 */
140 public function clear()
141 {
142 $this->hasBeenModified = true;
143 $this->changes = array();
144 $this->session->set($this->getSessionKey(), null);
145 return $this;
146 }
147
148 /**
149 * Whether there are no stacked changes
150 *
151 * @return bool
152 */
153 public function isEmpty()
154 {
155 return $this->count() === 0;
156 }
157
158 /**
159 * Number of stacked changes
160 *
161 * @return bool
162 */
163 public function count()
164 {
165 return count($this->changes);
166 }
167
168 /**
169 * Get the first change on the stack, false if empty
170 *
171 * @return NodeAction|boolean
172 */
173 public function shift()
174 {
175 if ($this->isEmpty()) {
176 return false;
177 }
178
179 $this->hasBeenModified = true;
180 return array_shift($this->changes);
181 }
182
183 /**
184 * Get the last change on the stack, false if empty
185 *
186 * @return NodeAction|boolean
187 */
188 public function pop()
189 {
190 if ($this->isEmpty()) {
191 return false;
192 }
193
194 $this->hasBeenModified = true;
195 return array_pop($this->changes);
196 }
197
198 /**
199 * The identifier used for this processes changes in our Session storage
200 *
201 * @return string
202 */
203 protected function getSessionKey()
204 {
205 return $this->sessionKey;
206 }
207
208 protected function hasBeenModified()
209 {
210 return $this->hasBeenModified;
211 }
212
213 /**
214 * @return array
215 */
216 public function serialize()
217 {
218 $serialized = array();
219 foreach ($this->getChanges() as $change) {
220 $serialized[] = $change->serialize();
221 }
222
223 return $serialized;
224 }
225
226 /**
227 * Persist to session on destruction
228 */
229 public function __destruct()
230 {
231 if (! $this->hasBeenModified()) {
232 unset($this->session);
233 return;
234 }
235 $session = $this->session;
236 $key = $this->getSessionKey();
237 if (! $this->isEmpty()) {
238 $session->set($key, $this->serialize());
239 }
240 unset($this->session);
241 }
242 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\Html\Link;
5
6 abstract class MonitoredNode extends Node
7 {
8 abstract public function getUrl();
9
10 public function getLink()
11 {
12 if ($this->isMissing()) {
13 return Link::create($this->getAlias(), '#');
14 } else {
15 return Link::create($this->getAlias(), $this->getUrl());
16 }
17 }
18 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Exception\ProgrammingError;
5 use Icinga\Module\Businessprocess\Html\Link;
6
7 abstract class Node
8 {
9 const FLAG_DOWNTIME = 1;
10 const FLAG_ACK = 2;
11 const FLAG_MISSING = 4;
12 const FLAG_NONE = 8;
13 const SHIFT_FLAGS = 4;
14
15 const ICINGA_OK = 0;
16 const ICINGA_WARNING = 1;
17 const ICINGA_CRITICAL = 2;
18 const ICINGA_UNKNOWN = 3;
19 const ICINGA_UP = 0;
20 const ICINGA_DOWN = 1;
21 const ICINGA_UNREACHABLE = 2;
22 const ICINGA_PENDING = 99;
23
24 protected $sortStateToStateMap = array(
25 4 => self::ICINGA_CRITICAL,
26 3 => self::ICINGA_UNKNOWN,
27 2 => self::ICINGA_WARNING,
28 1 => self::ICINGA_PENDING,
29 0 => self::ICINGA_OK
30 );
31
32 protected $stateToSortStateMap = array(
33 self::ICINGA_PENDING => 1,
34 self::ICINGA_UNKNOWN => 3,
35 self::ICINGA_CRITICAL => 4,
36 self::ICINGA_WARNING => 2,
37 self::ICINGA_OK => 0,
38 );
39
40 /**
41 * Main business process object
42 *
43 * @var BpConfig
44 */
45 protected $bp;
46
47 /**
48 * Parent nodes
49 *
50 * @var array
51 */
52 protected $parents = array();
53
54 /**
55 * Node identifier
56 *
57 * @var string
58 */
59 protected $name;
60
61 /**
62 * Node state
63 *
64 * @var int
65 */
66 protected $state;
67
68 /**
69 * Whether this nodes state has been acknowledged
70 *
71 * @var bool
72 */
73 protected $ack;
74
75 /**
76 * Whether this node is in a scheduled downtime
77 *
78 * @var bool
79 */
80 protected $downtime;
81
82 // obsolete
83 protected $duration;
84
85 /**
86 * Last state change, unix timestamp
87 *
88 * @var int
89 */
90 protected $lastStateChange;
91
92 protected $missing = false;
93
94 protected $className = 'unknown';
95
96 protected $stateNames = array(
97 'OK',
98 'WARNING',
99 'CRITICAL',
100 'UNKNOWN',
101 99 => 'PENDING'
102 );
103
104 abstract public function __construct(BpConfig $bp, $object);
105
106 public function setMissing($missing = true)
107 {
108 $this->missing = $missing;
109 return $this;
110 }
111
112 public function isProblem()
113 {
114 return $this->getState() > 0;
115 }
116
117 public function hasBeenChanged()
118 {
119 return false;
120 }
121
122 public function isMissing()
123 {
124 return $this->missing;
125 }
126
127 public function hasMissingChildren()
128 {
129 return count($this->getMissingChildren()) > 0;
130 }
131
132 public function getMissingChildren()
133 {
134 return array();
135 }
136
137 public function hasInfoUrl()
138 {
139 return false;
140 }
141
142 public function setState($state)
143 {
144 $this->state = (int) $state;
145 $this->missing = false;
146 return $this;
147 }
148
149 /**
150 * Forget my state
151 *
152 * @return $this
153 */
154 public function clearState()
155 {
156 $this->state = null;
157 return $this;
158 }
159
160 public function setAck($ack = true)
161 {
162 $this->ack = $ack;
163 return $this;
164 }
165
166 public function setDowntime($downtime = true)
167 {
168 $this->downtime = $downtime;
169 return $this;
170 }
171
172 public function getStateName($state = null)
173 {
174 $states = $this->enumStateNames();
175 if ($state === null) {
176 return $states[ $this->getState() ];
177 } else {
178 return $states[ $state ];
179 }
180 }
181
182 public function enumStateNames()
183 {
184 return $this->stateNames;
185 }
186
187 public function getState()
188 {
189 if ($this->state === null) {
190 throw new ProgrammingError(
191 sprintf(
192 'Node %s is unable to retrieve it\'s state',
193 $this->name
194 )
195 );
196 }
197
198 return $this->state;
199 }
200
201 public function getSortingState()
202 {
203 $sort = $this->stateToSortState($this->getState());
204 $sort = ($sort << self::SHIFT_FLAGS)
205 + ($this->isInDowntime() ? self::FLAG_DOWNTIME : 0)
206 + ($this->isAcknowledged() ? self::FLAG_ACK : 0);
207 if (! ($sort & (self::FLAG_DOWNTIME | self::FLAG_ACK))) {
208 $sort |= self::FLAG_NONE;
209 }
210 return $sort;
211 }
212
213 public function getLastStateChange()
214 {
215 return $this->lastStateChange;
216 }
217
218 public function setLastStateChange($timestamp)
219 {
220 $this->lastStateChange = $timestamp;
221 return $this;
222 }
223
224 public function addParent(Node $parent)
225 {
226 $this->parents[] = $parent;
227 return $this;
228 }
229
230 public function getDuration()
231 {
232 return $this->duration;
233 }
234
235 public function isHandled()
236 {
237 return $this->isInDowntime() || $this->isAcknowledged();
238 }
239
240 public function isInDowntime()
241 {
242 if ($this->downtime === null) {
243 $this->getState();
244 }
245 return $this->downtime;
246 }
247
248 public function isAcknowledged()
249 {
250 if ($this->ack === null) {
251 $this->getState();
252 }
253 return $this->ack;
254 }
255
256 public function getChildren($filter = null)
257 {
258 return array();
259 }
260
261 public function countChildren($filter = null)
262 {
263 return count($this->getChildren($filter));
264 }
265
266 public function hasChildren($filter = null)
267 {
268 return $this->countChildren($filter) > 0;
269 }
270
271 public function isEmpty()
272 {
273 return $this->countChildren() === 0;
274 }
275
276 public function hasAlias()
277 {
278 return false;
279 }
280
281 public function getAlias()
282 {
283 return $this->name;
284 }
285
286 public function hasParents()
287 {
288 return count($this->parents) > 0;
289 }
290
291 public function hasParentName($name)
292 {
293 foreach ($this->getParents() as $parent) {
294 if ($parent->getName() === $name) {
295 return true;
296 }
297 }
298
299 return false;
300 }
301
302 public function removeParent($name)
303 {
304 $this->parents = array_filter(
305 $this->parents,
306 function (BpNode $parent) use ($name) {
307 return $parent->getName() !== $name;
308 }
309 );
310
311 return $this;
312 }
313
314 /**
315 * @return BpNode[]
316 */
317 public function getParents()
318 {
319 return $this->parents;
320 }
321
322 /**
323 * @return array
324 */
325 public function getPaths()
326 {
327 if ($this->bp->hasRootNode($this->getName())) {
328 return array(array($this->getName()));
329 }
330
331 $paths = array();
332 foreach ($this->parents as $parent) {
333 foreach ($parent->getPaths() as $path) {
334 $path[] = $this->getName();
335 $paths[] = $path;
336 }
337 }
338
339 return $paths;
340 }
341
342 protected function stateToSortState($state)
343 {
344 if (array_key_exists($state, $this->stateToSortStateMap)) {
345 return $this->stateToSortStateMap[$state];
346 }
347
348 throw new ProgrammingError(
349 'Got invalid state for node %s: %s',
350 $this->getName(),
351 var_export($state, 1) . var_export($this->stateToSortStateMap, 1)
352 );
353 }
354
355 protected function sortStateTostate($sortState)
356 {
357 $sortState = $sortState >> self::SHIFT_FLAGS;
358 if (array_key_exists($sortState, $this->sortStateToStateMap)) {
359 return $this->sortStateToStateMap[$sortState];
360 }
361
362 throw new ProgrammingError('Got invalid sorting state %s', $sortState);
363 }
364
365 public function getObjectClassName()
366 {
367 return $this->className;
368 }
369
370 /**
371 * @return Link
372 */
373 public function getLink()
374 {
375 return Link::create($this->getAlias(), '#');
376 }
377
378 public function operatorHtml()
379 {
380 return '&nbsp;';
381 }
382
383 public function getName()
384 {
385 return $this->name;
386 }
387
388 public function __toString()
389 {
390 return $this->getName();
391 }
392
393 public function __destruct()
394 {
395 unset($this->parents);
396 }
397 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\ProvidedHook\Monitoring;
3
4 use Icinga\Module\Monitoring\Hook\HostActionsHook;
5 use Icinga\Module\Monitoring\Object\Host;
6
7 class HostActions extends HostActionsHook
8 {
9 public function getActionsForHost(Host $host)
10 {
11 $label = mt('businessprocess', 'Business Impact');
12 return array(
13 $label => 'businessprocess/node/impact?name='
14 . rawurlencode($host->getName() . ';Hoststatus')
15 );
16 }
17 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\ProvidedHook\Monitoring;
3
4 use Exception;
5 use Icinga\Application\Config;
6 use Icinga\Module\Monitoring\Hook\ServiceActionsHook;
7 use Icinga\Module\Monitoring\Object\Service;
8 use Icinga\Web\Url;
9
10 class ServiceActions extends ServiceActionsHook
11 {
12 public function getActionsForService(Service $service)
13 {
14 $label = mt('businessprocess', 'Business Impact');
15 return array(
16 $label => sprintf(
17 'businessprocess/node/impact?name=%s',
18 rawurlencode(
19 sprintf('%s;%s', $service->getHost()->getName(), $service->getName())
20 )
21 )
22 );
23 }
24 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Renderer;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\Html\BaseElement;
6 use Icinga\Module\Businessprocess\Html\Element;
7 use Icinga\Module\Businessprocess\Html\Link;
8 use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile;
9
10 class Breadcrumb extends BaseElement
11 {
12 protected $tag = 'ul';
13
14 protected $defaultAttributes = array(
15 'class' => 'breadcrumb',
16 'data-base-target' => '_main'
17 );
18
19 /**
20 * @param Renderer $renderer
21 * @return static
22 */
23 public static function create(Renderer $renderer)
24 {
25 $bp = $renderer->getBusinessProcess();
26 $breadcrumb = new static;
27 $bpUrl = $renderer->getBaseUrl();
28 if ($bpUrl->getParam('action') === 'delete') {
29 $bpUrl->remove('action');
30 }
31 $breadcrumb->add(Element::create('li')->add(
32 Link::create($bp->getTitle(), $bpUrl)
33 ));
34 $path = $renderer->getCurrentPath();
35
36 $parts = array();
37 while ($node = array_pop($path)) {
38 array_unshift(
39 $parts,
40 static::renderNode($bp->getNode($node), $path, $renderer)
41 );
42 }
43 $breadcrumb->addContent($parts);
44
45 return $breadcrumb;
46 }
47
48 /**
49 * @param BpNode $node
50 * @param array $path
51 * @param Renderer $renderer
52 *
53 * @return NodeTile
54 */
55 protected static function renderNode(BpNode $node, $path, Renderer $renderer)
56 {
57 // TODO: something more generic than NodeTile?
58 $renderer = clone($renderer);
59 $renderer->lock()->setIsBreadcrumb();
60 $p = new NodeTile($renderer, (string) $node, $node, $path);
61 $p->attributes()->add('class', $renderer->getNodeClasses($node));
62 $p->setTag('li');
63 return $p;
64 }
65 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Renderer;
3
4 use Icinga\Date\DateFormatter;
5 use Icinga\Exception\ProgrammingError;
6 use Icinga\Module\Businessprocess\BpNode;
7 use Icinga\Module\Businessprocess\BpConfig;
8 use Icinga\Module\Businessprocess\Html\Container;
9 use Icinga\Module\Businessprocess\Html\Element;
10 use Icinga\Module\Businessprocess\Html\Html;
11 use Icinga\Module\Businessprocess\Html\HtmlString;
12 use Icinga\Module\Businessprocess\Node;
13 use Icinga\Module\Businessprocess\Web\Url;
14
15 abstract class Renderer extends Html
16 {
17 /** @var BpConfig */
18 protected $config;
19
20 /** @var BpNode */
21 protected $parent;
22
23 /** @var bool Administrative actions are hidden unless unlocked */
24 protected $locked = true;
25
26 /** @var Url */
27 protected $url;
28
29 /** @var Url */
30 protected $baseUrl;
31
32 /** @var array */
33 protected $path = array();
34
35 /** @var bool */
36 protected $isBreadcrumb = false;
37
38 /**
39 * Renderer constructor.
40 *
41 * @param BpConfig $config
42 * @param BpNode|null $parent
43 */
44 public function __construct(BpConfig $config, BpNode $parent = null)
45 {
46 $this->config = $config;
47 $this->parent = $parent;
48 }
49
50 /**
51 * @return BpConfig
52 */
53 public function getBusinessProcess()
54 {
55 return $this->config;
56 }
57
58 /**
59 * Whether this will render all root nodes
60 *
61 * @return bool
62 */
63 public function wantsRootNodes()
64 {
65 return $this->parent === null;
66 }
67
68 /**
69 * Whether this will only render parts of given config
70 *
71 * @return bool
72 */
73 public function rendersSubNode()
74 {
75 return $this->parent !== null;
76 }
77
78 /**
79 * @return BpNode
80 */
81 public function getParentNode()
82 {
83 return $this->parent;
84 }
85
86 /**
87 * @return BpNode[]
88 */
89 public function getParentNodes()
90 {
91 if ($this->wantsRootNodes()) {
92 return array();
93 }
94
95 return $this->parent->getParents();
96 }
97
98 /**
99 * @return BpNode[]
100 */
101 public function getChildNodes()
102 {
103 if ($this->wantsRootNodes()) {
104 return $this->config->getRootNodes();
105 } else {
106 return $this->parent->getChildren();
107 }
108 }
109
110 /**
111 * @return int
112 */
113 public function countChildNodes()
114 {
115 if ($this->wantsRootNodes()) {
116 return $this->config->countChildren();
117 } else {
118 return $this->parent->countChildren();
119 }
120 }
121
122 /**
123 * @param $summary
124 * @return Container
125 */
126 public function renderStateBadges($summary)
127 {
128 $container = Container::create(
129 array('class' => 'badges')
130 )/* ->renderIfEmpty(false) */;
131
132 foreach ($summary as $state => $cnt) {
133 if ($cnt === 0
134 || $state === 'OK'
135 || $state === 'UP'
136 ) {
137 continue;
138 }
139
140 $container->addContent(
141 Element::create(
142 'span',
143 array(
144 'class' => array(
145 'badge',
146 'badge-' . strtolower($state)
147 ),
148 // TODO: We should translate this in this module
149 'title' => mt('monitoring', $state)
150 )
151 )->setContent($cnt)
152 );
153 }
154
155 return $container;
156 }
157
158 public function getNodeClasses(Node $node)
159 {
160 if ($node->isMissing()) {
161 $classes = array('missing');
162 } else {
163 $classes = array(
164 strtolower($node->getStateName())
165 );
166 if ($node->hasMissingChildren()) {
167 $classes[] = 'missing-children';
168 }
169 }
170
171 if ($node->isHandled()) {
172 $classes[] = 'handled';
173 }
174
175 if ($node instanceof BpNode) {
176 $classes[] = 'process-node';
177 } else {
178 $classes[] = 'monitored-node';
179 }
180 // TODO: problem?
181 return $classes;
182 }
183
184 public function setPath(array $path)
185 {
186 $this->path = $path;
187 return $this;
188 }
189
190 /**
191 * @return string|null
192 */
193 public function getPath()
194 {
195 return $this->path;
196 }
197
198 public function getCurrentPath()
199 {
200 $path = $this->getPath();
201 if ($this->rendersSubNode()) {
202 $path[] = (string) $this->parent;
203 }
204 return $path;
205 }
206
207 /**
208 * @param Url $url
209 * @return $this
210 */
211 public function setUrl(Url $url)
212 {
213 $this->url = $url->without(array(
214 'deletenode',
215 'deleteparent',
216 'editnode',
217 'simulationnode',
218 'view'
219 ));
220 $this->setBaseUrl($url);
221 return $this;
222 }
223
224 /**
225 * @param Url $url
226 * @return $this
227 */
228 public function setBaseUrl(Url $url)
229 {
230 $this->baseUrl = $url->without(array('node', 'path', 'view'));
231 return $this;
232 }
233
234 public function getUrl()
235 {
236 return $this->url;
237 }
238
239 /**
240 * @return Url
241 * @throws ProgrammingError
242 */
243 public function getBaseUrl()
244 {
245 if ($this->baseUrl === null) {
246 throw new ProgrammingError('Renderer has no baseUrl');
247 }
248
249 return clone($this->baseUrl);
250 }
251
252 /**
253 * @return bool
254 */
255 public function isLocked()
256 {
257 return $this->locked;
258 }
259
260 /**
261 * @return $this
262 */
263 public function lock()
264 {
265 $this->locked = true;
266 return $this;
267 }
268
269 /**
270 * @return $this
271 */
272 public function unlock()
273 {
274 $this->locked = false;
275 return $this;
276 }
277
278 /**
279 * TODO: Get rid of this
280 *
281 * @return $this
282 */
283 public function setIsBreadcrumb()
284 {
285 $this->isBreadcrumb = true;
286 return $this;
287 }
288
289 public function isBreadcrumb()
290 {
291 return $this->isBreadcrumb;
292 }
293
294 public function timeSince($time, $timeOnly = false)
295 {
296 if (! $time) {
297 return HtmlString::create('');
298 }
299
300 return Element::create(
301 'span',
302 array(
303 'class' => array('relative-time', 'time-since'),
304 'title' => DateFormatter::formatDateTime($time),
305 )
306 )->setContent(DateFormatter::timeSince($time, $timeOnly));
307 }
308
309 protected function createUnboundParent(BpConfig $bp)
310 {
311 return $bp->getNode('__unbound__');
312 }
313
314 /**
315 * Just to be on the safe side
316 */
317 public function __destruct()
318 {
319 unset($this->parent);
320 unset($this->config);
321 }
322 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Renderer\TileRenderer;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\HostNode;
6 use Icinga\Module\Businessprocess\Html\BaseElement;
7 use Icinga\Module\Businessprocess\Html\Container;
8 use Icinga\Module\Businessprocess\Html\HtmlString;
9 use Icinga\Module\Businessprocess\Html\Icon;
10 use Icinga\Module\Businessprocess\Html\Link;
11 use Icinga\Module\Businessprocess\ImportedNode;
12 use Icinga\Module\Businessprocess\MonitoredNode;
13 use Icinga\Module\Businessprocess\Node;
14 use Icinga\Module\Businessprocess\Renderer\Renderer;
15 use Icinga\Module\Businessprocess\ServiceNode;
16
17 class NodeTile extends BaseElement
18 {
19 protected $tag = 'div';
20
21 protected $renderer;
22
23 protected $name;
24
25 protected $node;
26
27 protected $path;
28
29 /**
30 * @var Container
31 */
32 private $actions;
33
34 /**
35 * NodeTile constructor.
36 * @param Renderer $renderer
37 * @param $name
38 * @param Node $node
39 * @param null $path
40 */
41 public function __construct(Renderer $renderer, $name, Node $node, $path = null)
42 {
43 $this->renderer = $renderer;
44 $this->name = $name;
45 $this->node = $node;
46 $this->path = $path;
47 }
48
49 protected function actions()
50 {
51 if ($this->actions === null) {
52 $this->addActions();
53 }
54 return $this->actions;
55 }
56
57 protected function addActions()
58 {
59 $this->actions = Container::create(
60 array(
61 'class' => 'actions',
62 'data-base-target' => '_self'
63 )
64 );
65
66 return $this->add($this->actions);
67 }
68
69 public function render()
70 {
71 $renderer = $this->renderer;
72 $node = $this->node;
73
74 $attributes = $this->attributes();
75 $attributes->add('class', $renderer->getNodeClasses($node));
76 $attributes->add('id', 'bp-' . (string) $node);
77
78 $this->addActions();
79
80 $link = $this->getMainNodeLink();
81 $this->add($link);
82
83 if ($node instanceof BpNode) {
84 if ($renderer->isBreadcrumb()) {
85 $link->addContent($renderer->renderStateBadges($node->getStateSummary()));
86 } else {
87 $this->addContent($renderer->renderStateBadges($node->getStateSummary()));
88 }
89 }
90
91 if (! $renderer->isBreadcrumb()) {
92 $this->addDetailsActions();
93 }
94
95 if (! $renderer->isLocked()) {
96 $this->addActionLinks();
97 }
98
99 return parent::render();
100 }
101
102 protected function getMainNodeUrl(Node $node)
103 {
104 if ($node instanceof BpNode) {
105 return $this->makeBpUrl($node);
106 } else {
107 /** @var MonitoredNode $node */
108 return $node->getUrl();
109 }
110 }
111
112 protected function buildBaseNodeUrl(Node $node)
113 {
114 $path = $this->path;
115 $name = $this->name; // TODO: ??
116 $renderer = $this->renderer;
117
118 $bp = $renderer->getBusinessProcess();
119 $params = array(
120 'config' => $node instanceof ImportedNode ?
121 $node->getConfigName() :
122 $bp->getName()
123 );
124
125 if ($name !== null) {
126 $params['node'] = $name;
127 }
128
129 $url = $renderer->getBaseUrl();
130 $p = $url->getParams();
131 $p->mergeValues($params);
132 if (! empty($path)) {
133 $p->addValues('path', $path);
134 }
135
136 return $url;
137 }
138
139 protected function makeBpUrl(BpNode $node)
140 {
141 return $this->buildBaseNodeUrl($node);
142 }
143
144 protected function makeMonitoredNodeUrl(MonitoredNode $node)
145 {
146 $path = $this->path;
147 $name = $this->name; // TODO: ??
148 $renderer = $this->renderer;
149
150 $bp = $renderer->getBusinessProcess();
151 $params = array(
152 'config' => $bp->getName()
153 );
154
155 if ($name !== null) {
156 $params['node'] = $node->getName();
157 }
158
159 $url = $renderer->getBaseUrl();
160 $p = $url->getParams();
161 $p->mergeValues($params);
162 if (! empty($path)) {
163 $p->addValues('path', $path);
164 }
165
166 return $url;
167 }
168
169 /**
170 * @return Link
171 */
172 protected function getMainNodeLink()
173 {
174 $node = $this->node;
175 $url = $this->getMainNodeUrl($node);
176 if ($node instanceof ServiceNode) {
177 $link = Link::create(
178 $node->getAlias(),
179 $url,
180 null,
181 array('data-base-target' => '_next')
182 );
183 } elseif ($node instanceof HostNode) {
184 $link = Link::create(
185 $node->getHostname(),
186 $url,
187 null,
188 array('data-base-target' => '_next')
189 );
190 } else {
191 $link = Link::create($node->getAlias(), $url);
192 if ($node instanceof ImportedNode) {
193 $link->attributes()->add('data-base-target', '_next');
194 }
195 }
196
197 return $link;
198 }
199
200 protected function addDetailsActions()
201 {
202 $node = $this->node;
203 $url = $this->getMainNodeUrl($node);
204
205 if ($node instanceof BpNode) {
206 $this->actions()->add(Link::create(
207 Icon::create('dashboard'),
208 $url->with('mode', 'tile'),
209 null,
210 array(
211 'title' => $this->translate('Show tiles for this subtree'),
212 'data-base-target' => '_next'
213 )
214 ))->add(Link::create(
215 Icon::create('sitemap'),
216 $url->with('mode', 'tree'),
217 null,
218 array(
219 'title' => $this->translate('Show this subtree as a tree'),
220 'data-base-target' => '_next'
221 )
222 ));
223 } else {
224 // $url = $this->makeMonitoredNodeUrl($node);
225 if ($node instanceof ServiceNode) {
226 $this->actions()->add(Link::create(
227 Icon::create('service'),
228 $node->getUrl(),
229 null,
230 array('data-base-target' => '_next')
231 ));
232 } elseif ($node instanceof HostNode) {
233 $this->actions()->add(Link::create(
234 Icon::create('host'),
235 $node->getUrl(),
236 null,
237 array('data-base-target' => '_next')
238 ));
239 }
240 }
241 }
242
243 protected function addActionLinks()
244 {
245 $node = $this->node;
246 $renderer = $this->renderer;
247 if ($node instanceof MonitoredNode) {
248 $this->actions()->add(Link::create(
249 Icon::create('magic'),
250 $renderer->getUrl()->with('action', 'simulation')
251 ->with('simulationnode', $this->name),
252 null,
253 array('title' => $this->translate(
254 'Show the business impact of this node by simulating a specific state'
255 ))
256 ));
257 }
258
259 if (! $this->renderer->getBusinessProcess()->getMetadata()->canModify()) {
260 return;
261 }
262
263 if ($node instanceof BpNode) {
264 $this->actions()->add(Link::create(
265 Icon::create('edit'),
266 $renderer->getUrl()->with('action', 'edit')->with('editnode', $node->getName()),
267 null,
268 array('title' => $this->translate('Modify this business process node'))
269 ));
270 }
271
272 $params = array(
273 'action' => 'delete',
274 'deletenode' => $node->getName(),
275 );
276
277 $this->actions()->add(Link::create(
278 Icon::create('cancel'),
279 $renderer->getUrl()->with($params),
280 null,
281 array('title' => $this->translate('Delete this node'))
282 ));
283 }
284 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Renderer;
3
4 use Icinga\Module\Businessprocess\Html\Container;
5 use Icinga\Module\Businessprocess\Html\Icon;
6 use Icinga\Module\Businessprocess\Html\Link;
7 use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile;
8
9 class TileRenderer extends Renderer
10 {
11 /**
12 * @inheritdoc
13 */
14 public function render()
15 {
16 $bp = $this->config;
17 $nodesDiv = Container::create(
18 array(
19 'class' => array(
20 'tiles',
21 $this->howMany()
22 ),
23 'data-base-target' => '_self',
24 )
25 );
26
27 $nodes = $this->getChildNodes();
28
29 if (! $this->isLocked() && count($nodes) > 8
30 && $this->config->getMetadata()->canModify()
31 ) {
32 $this->add($this->addNewNode());
33 }
34
35 $path = $this->getCurrentPath();
36 foreach ($nodes as $name => $node) {
37 $this->add(new NodeTile($this, $name, $node, $path));
38 }
39
40 if ($this->wantsRootNodes()) {
41 $unbound = $this->createUnboundParent($bp);
42 if ($unbound->hasChildren()) {
43 $name = $unbound->getName();
44 $this->add(new NodeTile($this, $name, $unbound));
45 }
46 }
47
48 if (! $this->isLocked() && $this->config->getMetadata()->canModify()) {
49 $this->add($this->addNewNode());
50 }
51
52 $nodesDiv->addContent($this->getContent());
53 $this->setContent($nodesDiv);
54
55 return parent::render();
56 }
57
58 /**
59 * A CSS class giving a rough indication of how many nodes we have
60 *
61 * This is used to show larger tiles when there are few and smaller
62 * ones if there are many.
63 *
64 * @return string
65 */
66 protected function howMany()
67 {
68 $count = $this->countChildNodes();
69 $howMany = 'normal';
70
71 if ($count <= 6) {
72 $howMany = 'few';
73 } elseif ($count > 12) {
74 $howMany = 'many';
75 }
76
77 return $howMany;
78 }
79
80 protected function addNewNode()
81 {
82 $div = Container::create(
83 array('class' => 'addnew')
84 );
85
86 $actions = Container::create(
87 array(
88 'class' => 'actions',
89 'data-base-target' => '_self'
90 )
91 );
92
93 $link = Link::create(
94 $this->translate('Add'),
95 $this->getUrl()->with('action', 'add'),
96 null,
97 array(
98 'title' => $this->translate('Add a new business process node')
99 )
100 );
101 $actions->add(
102 Link::create(
103 Icon::create('plus'),
104 $this->getUrl()->with('action', 'add'),
105 null,
106 array(
107 'title' => $this->translate('Add a new business process node')
108 )
109 )
110 );
111
112 return $div->add($actions)->add($link);
113 }
114 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Renderer;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Html\Container;
7 use Icinga\Module\Businessprocess\Html\Element;
8 use Icinga\Module\Businessprocess\Html\Icon;
9 use Icinga\Module\Businessprocess\Html\Link;
10 use Icinga\Module\Businessprocess\Node;
11 use Icinga\Module\Businessprocess\Web\Url;
12
13 class TreeRenderer extends Renderer
14 {
15 /**
16 * @inheritdoc
17 */
18 public function render()
19 {
20 $bp = $this->config;
21 $this->add(Container::create(
22 array(
23 'id' => $bp->getHtmlId(),
24 'class' => 'bp'
25 ),
26 $this->renderBp($bp)
27 ));
28
29 return parent::render();
30 }
31
32 /**
33 * @param BpConfig $bp
34 * @return string
35 */
36 public function renderBp(BpConfig $bp)
37 {
38 $html = array();
39 if ($this->wantsRootNodes()) {
40 $nodes = $bp->getChildren();
41 } else {
42 $nodes = $this->parent->getChildren();
43 }
44
45 foreach ($nodes as $name => $node) {
46 $html[] = $this->renderNode($bp, $node);
47 }
48
49 return $html;
50 }
51
52 /**
53 * @param Node $node
54 * @param $path
55 * @return string
56 */
57 protected function getId(Node $node, $path)
58 {
59 return md5(implode(';', $path) . (string) $node);
60 }
61
62 protected function getStateClassNames(Node $node)
63 {
64 $state = strtolower($node->getStateName());
65
66 if ($node->isMissing()) {
67 return array('missing');
68 } elseif ($state === 'ok') {
69 if ($node->hasMissingChildren()) {
70 return array('ok', 'missing-children');
71 } else {
72 return array('ok');
73 }
74 } else {
75 return array('problem', $state);
76 }
77 }
78
79 /**
80 * @param Node $node
81 * @return Icon[]
82 */
83 public function getNodeIcons(Node $node)
84 {
85 $icons = array();
86 if ($node->isInDowntime()) {
87 $icons[] = Icon::create('moon');
88 }
89 if ($node->isAcknowledged()) {
90 $icons[] = Icon::create('ok');
91 }
92 return $icons;
93 }
94
95 /**
96 * @param BpConfig $bp
97 * @param Node $node
98 * @param array $path
99 *
100 * @return string
101 */
102 public function renderNode(BpConfig $bp, Node $node, $path = array())
103 {
104 $table = Element::create(
105 'table',
106 array(
107 'id' => $this->getId($node, $path),
108 'class' => array(
109 'bp',
110 $node->getObjectClassName()
111 )
112 )
113 );
114 $attributes = $table->attributes();
115 $attributes->add('class', $this->getStateClassNames($node));
116 if ($node->isHandled()) {
117 $attributes->add('class', 'handled');
118 }
119 if ($node instanceof BpNode) {
120 $attributes->add('class', 'operator');
121 } else {
122 $attributes->add('class', 'node');
123 }
124
125 $tbody = $table->createElement('tbody');
126 $tr = $tbody->createElement('tr');
127
128 if ($node instanceof BpNode) {
129 $tr->createElement(
130 'th',
131 array(
132 'rowspan' => $node->countChildren() + 1 + ($this->isLocked() ? 0 : 1)
133 )
134 )->createElement(
135 'span',
136 array('class' => 'op')
137 )->setContent($node->operatorHtml());
138 }
139 $td = $tr->createElement('td');
140
141 if ($node instanceof BpNode && $node->hasInfoUrl()) {
142 $td->add($this->createInfoAction($node));
143 }
144
145 if (! $this->isLocked()) {
146 $td->addContent($this->getActionIcons($bp, $node));
147 }
148
149 $link = $node->getLink();
150 $link->attributes()->set('data-base-target', '_next');
151 $link->addContent($this->getNodeIcons($node));
152
153 if ($node->hasChildren()) {
154 $link->addContent($this->renderStateBadges($node->getStateSummary()));
155 }
156
157 if ($time = $node->getLastStateChange()) {
158 $since = $this->timeSince($time)->prependContent(
159 sprintf(' (%s ', $node->getStateName())
160 )->addContent(')');
161 $link->addContent($since);
162 }
163
164 $td->addContent($link);
165
166 foreach ($node->getChildren() as $name => $child) {
167 $tbody->createElement('tr')->createElement('td')->setContent(
168 $this->renderNode($bp, $child, $this->getCurrentPath())
169 );
170 }
171
172 if (! $this->isLocked() && $node instanceof BpNode && $bp->getMetadata()->canModify()) {
173 $tbody->createElement('tr')->createElement('td')->setContent(
174 $this->renderAddNewNode($node)
175 );
176 }
177
178 return $table;
179 }
180
181 protected function getActionIcons(BpConfig $bp, Node $node)
182 {
183 if ($node instanceof BpNode) {
184 if ($bp->getMetadata()->canModify()) {
185 return $this->createEditAction($bp, $node);
186 } else {
187 return '';
188 }
189 } else {
190 return $this->createSimulationAction($bp, $node);
191 }
192 }
193
194 protected function createEditAction(BpConfig $bp, BpNode $node)
195 {
196 return $this->actionIcon(
197 'wrench',
198 $this->getUrl()->with(array(
199 'action' => 'edit',
200 'editnode' => $node->getName()
201 )),
202 $this->translate('Modify this node')
203 );
204 }
205
206 protected function createSimulationAction(BpConfig $bp, Node $node)
207 {
208 return $this->actionIcon(
209 'magic',
210 $this->getUrl()->with(array(
211 //'config' => $bp->getName(),
212 'action' => 'simulation',
213 'simulationnode' => $node->getName()
214 )),
215 $this->translate('Simulate a specific state')
216 );
217 }
218
219 protected function createInfoAction(BpNode $node)
220 {
221 $url = $node->getInfoUrl();
222 return $this->actionIcon(
223 'help',
224 $url,
225 sprintf('%s: %s', $this->translate('More information'), $url)
226 );
227 }
228
229 protected function actionIcon($icon, $url, $title)
230 {
231 return Link::create(
232 Icon::create($icon),
233 $url,
234 null,
235 array(
236 'title' => $title,
237 'style' => 'float: right',
238 )
239 );
240 }
241
242 protected function renderAddNewNode($parent)
243 {
244 return Link::create(
245 $this->translate('Add'),
246 $this->getUrl()->with('action', 'add')->with('node', $parent->getName()),
247 null,
248 array(
249 'class' => 'addnew icon-plus',
250 'title' => $this->translate('Add a new business process node')
251 )
252 );
253 }
254 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\Web\Url;
5
6 class ServiceNode extends MonitoredNode
7 {
8 protected $hostname;
9
10 protected $service;
11
12 protected $className = 'service';
13
14 public function __construct(BpConfig $bp, $object)
15 {
16 $this->name = $object->hostname . ';' . $object->service;
17 $this->hostname = $object->hostname;
18 $this->service = $object->service;
19 $this->bp = $bp;
20 if (isset($object->state)) {
21 $this->setState($object->state);
22 } else {
23 $this->setState(0)->setMissing();
24 }
25 }
26
27 public function getHostname()
28 {
29 return $this->hostname;
30 }
31
32 public function getServiceDescription()
33 {
34 return $this->service;
35 }
36
37 public function getAlias()
38 {
39 return $this->hostname . ': ' . $this->service;
40 }
41
42 public function getUrl()
43 {
44 $params = array(
45 'host' => $this->getHostname(),
46 'service' => $this->getServiceDescription()
47 );
48
49 if ($this->bp->hasBackendName()) {
50 $params['backend'] = $this->bp->getBackendName();
51 }
52
53 return Url::fromPath('monitoring/service/show', $params);
54 }
55 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess;
3
4 use Icinga\Exception\ProgrammingError;
5 use Icinga\Web\Session\SessionNamespace;
6
7 class Simulation
8 {
9 const DEFAULT_SESSION_KEY = 'bp-simulations';
10
11 /**
12 * @var SessionNamespace
13 */
14 protected $session;
15
16 /**
17 * @var string
18 */
19 protected $sessionKey;
20
21 /**
22 * @var array
23 */
24 protected $simulations = array();
25
26 /**
27 * Simulation constructor.
28 * @param array $simulations
29 */
30 public function __construct(array $simulations = array())
31 {
32 $this->simulations = $simulations;
33 }
34
35 /**
36 * @param array $simulations
37 * @return static
38 */
39 public static function create(array $simulations = array())
40 {
41 return new static($simulations);
42 }
43
44 /**
45 * @param SessionNamespace $session
46 * @param null $sessionKey
47 * @return $this
48 */
49 public static function fromSession(SessionNamespace $session, $sessionKey = null)
50 {
51 return static::create()
52 ->setSessionKey($sessionKey)
53 ->persistToSession($session);
54 }
55
56 /**
57 * @param string $key
58 * @return $this
59 */
60 public function setSessionKey($key = null)
61 {
62 if ($key === null) {
63 $this->sessionKey = Simulation::DEFAULT_SESSION_KEY;
64 } else {
65 $this->sessionKey = $key;
66 }
67
68 return $this;
69 }
70
71 /**
72 * @param SessionNamespace $session
73 * @return $this
74 */
75 public function persistToSession(SessionNamespace $session)
76 {
77 $this->session = $session;
78 $this->simulations = $this->session->get($this->sessionKey, array());
79 return $this;
80 }
81
82 /**
83 * @return array
84 */
85 public function simulations()
86 {
87 return $this->simulations;
88 }
89
90 /**
91 * @param $simulations
92 * @return $this
93 */
94 protected function setSimulations($simulations)
95 {
96 $this->simulations = $simulations;
97 if ($this->session !== null) {
98 $this->session->set($this->sessionKey, $simulations);
99 }
100
101 return $this;
102 }
103
104 /**
105 * @return $this
106 */
107 public function clear()
108 {
109 $this->simulations = array();
110 if ($this->session !== null) {
111 $this->session->set($this->sessionKey, array());
112 }
113
114 return $this;
115 }
116
117 /**
118 * @return int
119 */
120 public function count()
121 {
122 return count($this->simulations());
123 }
124
125 /**
126 * @return bool
127 */
128 public function isEmpty()
129 {
130 return $this->count() === 0;
131 }
132
133 /**
134 * @param $node
135 * @param $properties
136 */
137 public function set($node, $properties)
138 {
139 $simulations = $this->simulations();
140 $simulations[$node] = $properties;
141 $this->setSimulations($simulations);
142 }
143
144 /**
145 * @param $name
146 * @return bool
147 */
148 public function hasNode($name)
149 {
150 $simulations = $this->simulations();
151 return array_key_exists($name, $simulations);
152 }
153
154 /**
155 * @param $name
156 * @return mixed
157 * @throws ProgrammingError
158 */
159 public function getNode($name)
160 {
161 $simulations = $this->simulations();
162 if (! array_key_exists($name, $simulations)) {
163 throw new ProgrammingError('Trying to access invalid node %s', $name);
164 }
165 return $simulations[$name];
166 }
167
168 /**
169 * @param $node
170 * @return bool
171 */
172 public function remove($node)
173 {
174 $simulations = $this->simulations();
175 if (array_key_exists($node, $simulations)) {
176 unset($simulations[$node]);
177 $this->setSimulations($simulations);
178
179 return true;
180 } else {
181 return false;
182 }
183 }
184 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\State;
3
4 use Exception;
5 use Icinga\Application\Benchmark;
6 use Icinga\Data\Filter\Filter;
7 use Icinga\Module\Businessprocess\BpConfig;
8 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
9
10 class MonitoringState
11 {
12 /** @var BpConfig */
13 protected $config;
14
15 /** @var MonitoringBackend */
16 protected $backend;
17
18 private function __construct(BpConfig $config)
19 {
20 $this->config = $config;
21 $this->backend = $config->getBackend();
22 }
23
24 public static function apply(BpConfig $config)
25 {
26 $self = new static($config);
27 $self->retrieveStatesFromBackend();
28 return $config;
29 }
30
31 public function retrieveStatesFromBackend()
32 {
33 $config = $this->config;
34
35 try {
36 $this->reallyRetrieveStatesFromBackend();
37 } catch (Exception $e) {
38 $config->addError(
39 $config->translate('Could not retrieve process state: %s'),
40 $e->getMessage()
41 );
42 }
43 }
44
45 public function reallyRetrieveStatesFromBackend()
46 {
47 $config = $this->config;
48
49 Benchmark::measure('Retrieving states for business process ' . $config->getName());
50 $backend = $this->backend;
51 $hostFilter = $config->listInvolvedHostNames();
52
53 if ($config->usesHardStates()) {
54 $hostStateColumn = 'host_hard_state';
55 $hostStateChangeColumn = 'host_last_hard_state_change';
56 $serviceStateColumn = 'service_hard_state';
57 $serviceStateChangeColumn = 'service_last_hard_state_change';
58 } else {
59 $hostStateColumn = 'host_state';
60 $hostStateChangeColumn = 'host_last_state_change';
61 $serviceStateColumn = 'service_state';
62 $serviceStateChangeColumn = 'service_last_state_change';
63 }
64 $filter = Filter::matchAny();
65 foreach ($hostFilter as $host) {
66 $filter->addFilter(Filter::where('host_name', $host));
67 }
68
69 if ($filter->isEmpty()) {
70 return $this;
71 }
72
73 $hostStatus = $backend->select()->from('hostStatus', array(
74 'hostname' => 'host_name',
75 'last_state_change' => $hostStateChangeColumn,
76 'in_downtime' => 'host_in_downtime',
77 'ack' => 'host_acknowledged',
78 'state' => $hostStateColumn
79 ))->applyFilter($filter)->getQuery()->fetchAll();
80
81 $serviceStatus = $backend->select()->from('serviceStatus', array(
82 'hostname' => 'host_name',
83 'service' => 'service_description',
84 'last_state_change' => $serviceStateChangeColumn,
85 'in_downtime' => 'service_in_downtime',
86 'ack' => 'service_acknowledged',
87 'state' => $serviceStateColumn
88 ))->applyFilter($filter)->getQuery()->fetchAll();
89
90 foreach ($serviceStatus as $row) {
91 $this->handleDbRow($row, $config);
92 }
93
94 foreach ($hostStatus as $row) {
95 $this->handleDbRow($row, $config);
96 }
97 // TODO: Union, single query?
98 Benchmark::measure('Got states for business process ' . $config->getName());
99
100 return $this;
101 }
102
103 protected function handleDbRow($row, BpConfig $config)
104 {
105 $key = $row->hostname;
106 if (property_exists($row, 'service')) {
107 $key .= ';' . $row->service;
108 } else {
109 $key .= ';Hoststatus';
110 }
111
112 // We fetch more states than we need, so skip unknown ones
113 if (! $config->hasNode($key)) {
114 return;
115 }
116
117 $node = $config->getNode($key);
118
119 if ($row->state !== null) {
120 $node->setState($row->state)->setMissing(false);
121 }
122 if ($row->last_state_change !== null) {
123 $node->setLastStateChange($row->last_state_change);
124 }
125 if ((int) $row->in_downtime === 1) {
126 $node->setDowntime(true);
127 }
128 if ((int) $row->ack === 1) {
129 $node->setAck(true);
130 }
131 }
132 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Storage;
3
4 use Diff;
5 use Diff_Renderer_Html_Inline;
6 use Diff_Renderer_Html_SideBySide;
7 use Diff_Renderer_Text_Context;
8 use Diff_Renderer_Text_Unified;
9 use Icinga\Module\Businessprocess\Html\Renderable;
10
11 class ConfigDiff implements Renderable
12 {
13 protected $a;
14
15 protected $b;
16
17 protected $diff;
18 protected $opcodes;
19
20 protected function __construct($a, $b)
21 {
22 $this->requireVendorLib('Diff.php');
23
24 if (empty($a)) {
25 $this->a = array();
26 } else {
27 $this->a = explode("\n", (string) $a);
28 }
29
30 if (empty($b)) {
31 $this->b = array();
32 } else {
33 $this->b = explode("\n", (string) $b);
34 }
35
36 $options = array(
37 'context' => 5,
38 // 'ignoreWhitespace' => true,
39 // 'ignoreCase' => true,
40 );
41 $this->diff = new Diff($this->a, $this->b, $options);
42 }
43
44 /**
45 * @return string
46 */
47 public function render()
48 {
49 return $this->renderHtmlSideBySide();
50 }
51
52 public function renderHtmlSideBySide()
53 {
54 $this->requireVendorLib('Diff/Renderer/Html/SideBySide.php');
55 $renderer = new Diff_Renderer_Html_SideBySide;
56 return $this->diff->render($renderer);
57 }
58
59 public function renderHtmlInline()
60 {
61 $this->requireVendorLib('Diff/Renderer/Html/Inline.php');
62 $renderer = new Diff_Renderer_Html_Inline;
63 return $this->diff->render($renderer);
64 }
65
66 public function renderTextContext()
67 {
68 $this->requireVendorLib('Diff/Renderer/Text/Context.php');
69 $renderer = new Diff_Renderer_Text_Context;
70 return $this->diff->render($renderer);
71 }
72
73 public function renderTextUnified()
74 {
75 $this->requireVendorLib('Diff/Renderer/Text/Unified.php');
76 $renderer = new Diff_Renderer_Text_Unified;
77 return $this->diff->render($renderer);
78 }
79
80 protected function requireVendorLib($file)
81 {
82 require_once dirname(dirname(__DIR__)) . '/vendor/php-diff/lib/' . $file;
83 }
84
85 public static function create($a, $b)
86 {
87 $diff = new static($a, $b);
88 return $diff;
89 }
90 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Storage;
3
4 use Icinga\Application\Benchmark;
5 use Icinga\Exception\ConfigurationError;
6 use Icinga\Exception\SystemPermissionException;
7 use Icinga\Module\Businessprocess\BpConfig;
8 use Icinga\Module\Businessprocess\BpNode;
9 use Icinga\Module\Businessprocess\Metadata;
10
11 class LegacyConfigParser
12 {
13 /** @var int */
14 protected $currentLineNumber;
15
16 /** @var string */
17 protected $currentFilename;
18
19 protected $name;
20
21 /** @var BpConfig */
22 protected $config;
23
24 /**
25 * LegacyConfigParser constructor
26 *
27 * @param $name
28 */
29 private function __construct($name)
30 {
31 $this->name = $name;
32 $this->config = new BpConfig();
33 $this->config->setName($name);
34 }
35
36 /**
37 * @return BpConfig
38 */
39 public function getParsedConfig()
40 {
41 return $this->config;
42 }
43
44 /**
45 * @param $name
46 * @param $filename
47 *
48 * @return BpConfig
49 */
50 public static function parseFile($name, $filename)
51 {
52 Benchmark::measure('Loading business process ' . $name);
53 $parser = new static($name);
54 $parser->reallyParseFile($filename);
55 Benchmark::measure('Business process ' . $name . ' loaded');
56 return $parser->getParsedConfig();
57 }
58
59 /**
60 * @param $name
61 * @param $string
62 *
63 * @return BpConfig
64 */
65 public static function parseString($name, $string)
66 {
67 Benchmark::measure('Loading BP config from file: ' . $name);
68 $parser = new static($name);
69
70 $config = $parser->getParsedConfig();
71 $config->setMetadata(
72 static::readMetadataFromString($name, $string)
73 );
74
75 foreach (preg_split('/\n/', $string) as $line) {
76 $parser->parseLine($line);
77 }
78
79 Benchmark::measure('Business process ' . $name . ' loaded');
80 return $config;
81 }
82
83 protected function reallyParseFile($filename)
84 {
85 $file = $this->currentFilename = $filename;
86 $fh = @fopen($file, 'r');
87 if (! $fh) {
88 throw new SystemPermissionException('Could not open "%s"', $filename);
89 }
90
91 $config = $this->config;
92 $config->setMetadata(
93 $this::readMetadataFromFileHeader($config->getName(), $filename)
94 );
95
96 $this->currentLineNumber = 0;
97 while ($line = fgets($fh)) {
98 $this->parseLine($line);
99 }
100
101 fclose($fh);
102 unset($this->currentLineNumber);
103 unset($this->currentFilename);
104 }
105
106 public static function readMetadataFromFileHeader($name, $filename)
107 {
108 $metadata = new Metadata($name);
109 $fh = fopen($filename, 'r');
110 $cnt = 0;
111 while ($cnt < 15 && false !== ($line = fgets($fh))) {
112 $cnt++;
113 static::parseHeaderLine($line, $metadata);
114 }
115
116 fclose($fh);
117 return $metadata;
118 }
119
120 public static function readMetadataFromString($name, & $string)
121 {
122 $metadata = new Metadata($name);
123
124 $lines = preg_split('/\r?\n/', substr($string, 0, 8092));
125 foreach ($lines as $line) {
126 static::parseHeaderLine($line, $metadata);
127 }
128
129 return $metadata;
130 }
131
132 protected function splitCommaSeparated($string)
133 {
134 return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY);
135 }
136
137 protected function readHeaderString($string, Metadata $metadata)
138 {
139 foreach (preg_split('/\n/', $string) as $line) {
140 $this->parseHeaderLine($line, $metadata);
141 }
142
143 return $metadata;
144 }
145
146 /**
147 * @return array
148 */
149 protected function emptyHeader()
150 {
151 return array(
152 'Title' => null,
153 'Description' => null,
154 'Owner' => null,
155 'AllowedUsers' => null,
156 'AllowedGroups' => null,
157 'AllowedRoles' => null,
158 'Backend' => null,
159 'Statetype' => 'soft',
160 'SLAHosts' => null
161 );
162 }
163
164 /**
165 * @param $line
166 * @param Metadata $metadata
167 */
168 protected static function parseHeaderLine($line, Metadata $metadata)
169 {
170 if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) {
171 if ($metadata->hasKey($m[1])) {
172 $metadata->set($m[1], $m[2]);
173 }
174 }
175 }
176
177 /**
178 * @param $line
179 * @param BpConfig $bp
180 */
181 protected function parseDisplay(& $line, BpConfig $bp)
182 {
183 list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3);
184 $bp->getBpNode($name)->setAlias($desc)->setDisplay($display);
185 if ($display > 0) {
186 $bp->addRootNode($name);
187 }
188 }
189
190 /**
191 * @param $line
192 * @param BpConfig $bp
193 */
194 protected function parseExternalInfo(& $line, BpConfig $bp)
195 {
196 list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2);
197 $bp->getBpNode($name)->setInfoCommand($script);
198 }
199
200 protected function parseExtraInfo(& $line, BpConfig $bp)
201 {
202 // TODO: Not yet
203 // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2);
204 // $this->getNode($name)->setExtraInfo($script);
205 }
206
207 protected function parseInfoUrl(& $line, BpConfig $bp)
208 {
209 list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2);
210 $bp->getBpNode($name)->setInfoUrl($url);
211 }
212
213 protected function parseExtraLine(& $line, $typeLength, BpConfig $bp)
214 {
215 $type = substr($line, 0, $typeLength);
216 if (substr($type, 0, 7) === 'display') {
217 $this->parseDisplay($line, $bp);
218 return true;
219 }
220
221 switch ($type) {
222 case 'external_info':
223 $this->parseExternalInfo($line, $bp);
224 break;
225 case 'extra_info':
226 $this->parseExtraInfo($line, $bp);
227 break;
228 case 'info_url':
229 $this->parseInfoUrl($line, $bp);
230 break;
231 case 'template':
232 // compat, ignoring for now
233 break;
234 default:
235 return false;
236 }
237
238 return true;
239 }
240
241 /**
242 * Parses a single line
243 *
244 * Adds eventual new knowledge to the given Business Process config
245 *
246 * @param $line
247 *
248 * @throws ConfigurationError
249 */
250 protected function parseLine(& $line)
251 {
252 $bp = $this->config;
253 $line = trim($line);
254
255 $this->currentLineNumber++;
256
257 // Skip empty or comment-only lines
258 if (empty($line) || $line[0] === '#') {
259 return;
260 }
261
262 // Space found in the first 14 cols? Might be a line with extra information
263 $pos = strpos($line, ' ');
264 if ($pos !== false && $pos < 14) {
265 if ($this->parseExtraLine($line, $pos, $bp)) {
266 return;
267 }
268 }
269
270 if (strpos($line, '=') === false) {
271 $this->parseError('Got invalid line');
272 }
273
274 list($name, $value) = preg_split('~\s*=\s*~', $line, 2);
275
276 if (strpos($name, ';') !== false) {
277 $this->parseError('No semicolon allowed in varname');
278 }
279
280 $op = '&';
281 if (preg_match_all('~([\|\+&\!])~', $value, $m)) {
282 $op = implode('', $m[1]);
283 for ($i = 1; $i < strlen($op); $i++) {
284 if ($op[$i] !== $op[$i - 1]) {
285 $this->parseError('Mixing operators is not allowed');
286 }
287 }
288 }
289 $op = $op[0];
290 $op_name = $op;
291
292 if ($op === '+') {
293 if (! preg_match('~^(\d+)(?::(\d+))?\s*of:\s*(.+?)$~', $value, $m)) {
294 $this->parseError('syntax: <var> = <num> of: <var1> + <var2> [+ <varn>]*');
295 }
296 $op_name = $m[1];
297 // New feature: $minWarn = $m[2];
298 $value = $m[3];
299 }
300 $cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY);
301 $childNames = array();
302
303 foreach ($cmps as $val) {
304 if (strpos($val, ';') !== false) {
305 if ($bp->hasNode($val)) {
306 $childNames[] = $val;
307 continue;
308 }
309
310 list($host, $service) = preg_split('~;~', $val, 2);
311 if ($service === 'Hoststatus') {
312 $bp->createHost($host);
313 } else {
314 $bp->createService($host, $service);
315 }
316 }
317 if ($val[0] === '@') {
318 if (strpos($val, ':') === false) {
319 throw new ConfigurationError(
320 "I'm unable to import full external configs, a node needs to be provided for '%s'",
321 $val
322 );
323 // TODO: this might work:
324 // $node = $bp->createImportedNode(substr($val, 1));
325 } else {
326 list($config, $nodeName) = preg_split('~:\s*~', substr($val, 1), 2);
327 $node = $bp->createImportedNode($config, $nodeName);
328 }
329 $val = $node->getName();
330 }
331
332 $childNames[] = $val;
333 }
334
335 $node = new BpNode($bp, (object) array(
336 'name' => $name,
337 'operator' => $op_name,
338 'child_names' => $childNames
339 ));
340
341 $bp->addNode($name, $node);
342 }
343
344 /**
345 * @return string
346 */
347 public function getFilename()
348 {
349 return $this->currentFilename ?: '[given string]';
350 }
351
352 /**
353 * @param $msg
354 * @throws ConfigurationError
355 */
356 protected function parseError($msg)
357 {
358 throw new ConfigurationError(
359 sprintf(
360 'Parse error on %s:%s: %s',
361 $this->getFilename(),
362 $this->currentLineNumber,
363 $msg
364 )
365 );
366 }
367 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Storage;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\BpConfig;
6
7 class LegacyConfigRenderer
8 {
9 /** @var array */
10 protected $renderedNodes;
11
12 /**
13 * LecagyConfigRenderer constructor
14 *
15 * @param BpConfig $config
16 */
17 public function __construct(BpConfig $config)
18 {
19 $this->config = $config;
20 }
21
22 /**
23 * @return string
24 */
25 public function render()
26 {
27 return $this->renderHeader() . $this->renderNodes();
28 }
29
30 /**
31 * @param BpConfig $config
32 * @return mixed
33 */
34 public static function renderConfig(BpConfig $config)
35 {
36 $renderer = new static($config);
37 return $renderer->render();
38 }
39
40 /**
41 * @return string
42 */
43 public function renderHeader()
44 {
45 $str = "### Business Process Config File ###\n#\n";
46
47 $meta = $this->config->getMetadata();
48 foreach ($meta->getProperties() as $key => $value) {
49 if ($value === null) {
50 continue;
51 }
52
53 $str .= sprintf("# %-15s : %s\n", $key, $value);
54 }
55
56 $str .= "#\n###################################\n\n";
57
58 return $str;
59 }
60
61 /**
62 * @return string
63 */
64 public function renderNodes()
65 {
66 $this->renderedNodes = array();
67
68 $config = $this->config;
69 $str = '';
70
71 foreach ($config->getRootNodes() as $node) {
72 $str .= $this->requireRenderedBpNode($node);
73 }
74
75 foreach ($config->getUnboundNodes() as $name => $node) {
76 $str .= $this->requireRenderedBpNode($node);
77 }
78
79 return $str . "\n";
80 }
81
82 /**
83 * Rendered node definition, empty string if already rendered
84 *
85 * @param BpNode $node
86 *
87 * @return string
88 */
89 protected function requireRenderedBpNode(BpNode $node)
90 {
91 $name = $node->getName();
92
93 if (array_key_exists($name, $this->renderedNodes)) {
94 return '';
95 } else {
96 $this->renderedNodes[$name] = true;
97 return $this->renderBpNode($node);
98 }
99 }
100
101 /**
102 * @param BpNode $node
103 * @return string
104 */
105 protected function renderBpNode(BpNode $node)
106 {
107 $name = $node->getName();
108 // Doing this before rendering children allows us to store loops
109 $cfg = '';
110
111 foreach ($node->getChildBpNodes() as $name => $child) {
112 $cfg .= $this->requireRenderedBpNode($child) . "\n";
113 }
114
115 $cfg .= static::renderSingleBpNode($node);
116
117 return $cfg;
118 }
119
120 /**
121 * @param BpNode $node
122 * @return string
123 */
124 public static function renderEqualSign(BpNode $node)
125 {
126 $op = $node->getOperator();
127 if (is_numeric($op)) {
128 return '= ' . $op . ' of:';
129 } else {
130 return '=';
131 }
132 }
133
134 /**
135 * @param BpNode $node
136 * @return string
137 */
138 public static function renderOperator(BpNode $node)
139 {
140 $op = $node->getOperator();
141 if (is_numeric($op)) {
142 return '+';
143 } else {
144 return $op;
145 }
146 }
147
148 /**
149 * @param BpNode $node
150 * @return string
151 */
152 public static function renderSingleBpNode(BpNode $node)
153 {
154 return static::renderExpression($node)
155 . static::renderDisplay($node)
156 . static::renderInfoUrl($node);
157 }
158
159 /**
160 * @param BpNode $node
161 * @return string
162 */
163 public static function renderExpression(BpNode $node)
164 {
165 return sprintf(
166 "%s %s %s\n",
167 $node->getName(),
168 static::renderEqualSign($node),
169 static::renderChildNames($node)
170 );
171 }
172
173 /**
174 * @param BpNode $node
175 * @return string
176 */
177 public static function renderChildNames(BpNode $node)
178 {
179 $op = static::renderOperator($node);
180 $children = $node->getChildNames();
181 $str = implode(' ' . $op . ' ', $children);
182
183 if ((count($children) < 2) && $op !== '&') {
184 return $op . ' ' . $str;
185 } else {
186 return $str;
187 }
188 }
189
190 /**
191 * @param BpNode $node
192 * @return string
193 */
194 public static function renderDisplay(BpNode $node)
195 {
196 if ($node->hasAlias() || $node->getDisplay() > 0) {
197 $prio = $node->getDisplay();
198 return sprintf(
199 "display %s;%s;%s\n",
200 $prio,
201 $node->getName(),
202 $node->getAlias()
203 );
204 } else {
205 return '';
206 }
207 }
208
209 /**
210 * @param BpNode $node
211 * @return string
212 */
213 public static function renderInfoUrl(BpNode $node)
214 {
215 if ($node->hasInfoUrl()) {
216 return sprintf(
217 "info_url %s;%s\n",
218 $node->getName(),
219 $node->getInfoUrl()
220 );
221 } else {
222 return '';
223 }
224 }
225 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Storage;
3
4 use DirectoryIterator;
5 use Icinga\Application\Icinga;
6 use Icinga\Module\Businessprocess\BpConfig;
7 use Icinga\Exception\SystemPermissionException;
8
9 class LegacyStorage extends Storage
10 {
11 /** @var string */
12 protected $configDir;
13
14 public function getConfigDir()
15 {
16 if ($this->configDir === null) {
17 $this->prepareDefaultConfigDir();
18 }
19
20 return $this->configDir;
21 }
22
23 protected function prepareDefaultConfigDir()
24 {
25 $dir = Icinga::app()
26 ->getModuleManager()
27 ->getModule('businessprocess')
28 ->getConfigDir();
29
30 // TODO: This is silly. We need Config::requireDirectory().
31 if (! is_dir($dir)) {
32 if (! is_dir(dirname($dir))) {
33 if (! @mkdir(dirname($dir))) {
34 throw new SystemPermissionException('Could not create config directory "%s"', dirname($dir));
35 }
36 }
37 if (! mkdir($dir)) {
38 throw new SystemPermissionException('Could not create config directory "%s"', $dir);
39 }
40 }
41 $dir = $dir . '/processes';
42 if (! is_dir($dir)) {
43 if (! mkdir($dir)) {
44 throw new SystemPermissionException('Could not create config directory "%s"', $dir);
45 }
46 }
47
48 $this->configDir = $dir;
49 }
50
51 /**
52 * @inheritdoc
53 */
54 public function listProcesses()
55 {
56 $files = array();
57
58 foreach ($this->listAllProcessNames() as $name) {
59 $meta = $this->loadMetadata($name);
60 if (! $meta->canRead()) {
61 continue;
62 }
63
64 $files[$name] = $meta->getExtendedTitle();
65 }
66
67 natcasesort($files);
68 return $files;
69 }
70
71 /**
72 * @inheritdoc
73 */
74 public function listProcessNames()
75 {
76 $files = array();
77
78 foreach ($this->listAllProcessNames() as $name) {
79 $meta = $this->loadMetadata($name);
80 if (! $meta->canRead()) {
81 continue;
82 }
83
84 $files[$name] = $name;
85 }
86
87 natcasesort($files);
88 return $files;
89 }
90
91 /**
92 * @inheritdoc
93 */
94 public function listAllProcessNames()
95 {
96 $files = array();
97
98 foreach (new DirectoryIterator($this->getConfigDir()) as $file) {
99 if ($file->isDot()) {
100 continue;
101 }
102
103 $filename = $file->getFilename();
104 if (substr($filename, -5) === '.conf') {
105 $files[] = substr($filename, 0, -5);
106 }
107 }
108
109 natcasesort($files);
110 return $files;
111 }
112
113 /**
114 * @inheritdoc
115 */
116 public function loadProcess($name)
117 {
118 return LegacyConfigParser::parseFile(
119 $name,
120 $this->getFilename($name)
121 );
122 }
123
124 /**
125 * @inheritdoc
126 */
127 public function storeProcess(BpConfig $process)
128 {
129 file_put_contents(
130 $this->getFilename($process->getName()),
131 LegacyConfigRenderer::renderConfig($process)
132 );
133 }
134
135 /**
136 * @inheritdoc
137 */
138 public function deleteProcess($name)
139 {
140 return @unlink($this->getFilename($name));
141 }
142
143 /**
144 * @inheritdoc
145 */
146 public function loadMetadata($name)
147 {
148 return LegacyConfigParser::readMetadataFromFileHeader(
149 $name,
150 $this->getFilename($name)
151 );
152 }
153
154 public function getSource($name)
155 {
156 return file_get_contents($this->getFilename($name));
157 }
158
159 public function getFilename($name)
160 {
161 return $this->getConfigDir() . '/' . $name . '.conf';
162 }
163
164 /**
165 * @param $name
166 * @param $string
167 *
168 * @return BpConfig
169 */
170 public function loadFromString($name, $string)
171 {
172 return LegacyConfigParser::parseString($name, $string);
173 }
174
175 /**
176 * @param $name
177 * @return bool
178 */
179 public function hasProcess($name)
180 {
181 $file = $this->getFilename($name);
182 if (! is_file($file)) {
183 return false;
184 }
185
186 return $this->loadMetadata($name)->canRead();
187 }
188 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Storage;
3
4 use Icinga\Data\ConfigObject;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Metadata;
7
8 abstract class Storage
9 {
10 /**
11 * @var ConfigObject
12 */
13 protected $config;
14
15 /**
16 * Storage constructor.
17 * @param ConfigObject $config
18 */
19 public function __construct(ConfigObject $config)
20 {
21 $this->config = $config;
22 $this->init();
23 }
24
25 protected function init()
26 {
27 }
28
29 /**
30 * All processes readable by the current user
31 *
32 * The returned array has the form <process name> => <nice title>, sorted
33 * by title
34 *
35 * @return array
36 */
37 abstract public function listProcesses();
38
39 /**
40 * All process names readable by the current user
41 *
42 * The returned array has the form <process name> => <process name> and is
43 * sorted
44 *
45 * @return array
46 */
47 abstract public function listProcessNames();
48
49 /**
50 * All available process names, regardless of eventual restrictions
51 *
52 * @return array
53 */
54 abstract public function listAllProcessNames();
55
56 /**
57 * Whether a configuration with the given name exists
58 *
59 * @param $name
60 *
61 * @return bool
62 */
63 abstract public function hasProcess($name);
64
65 /**
66 * @param $name
67 * @return BpConfig
68 */
69 abstract public function loadProcess($name);
70
71 /**
72 * Store eventual changes applied to the given configuration
73 *
74 * @param BpConfig $config
75 *
76 * @return mixed
77 */
78 abstract public function storeProcess(BpConfig $config);
79
80 /**
81 * @param $name
82 * @return bool Whether the process has been deleted
83 */
84 abstract public function deleteProcess($name);
85
86 /**
87 * @param string $name
88 * @return Metadata
89 */
90 abstract public function loadMetadata($name);
91 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Test;
3
4 use Icinga\Application\Config;
5 use Icinga\Application\ApplicationBootstrap;
6 use Icinga\Application\Icinga;
7 use Icinga\Module\Businessprocess\BpConfig;
8 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
9 use Icinga\Module\Businessprocess\Web\FakeRequest;
10 use PHPUnit_Framework_TestCase;
11
12 abstract class BaseTestCase extends PHPUnit_Framework_TestCase
13 {
14 /** @var ApplicationBootstrap */
15 private static $app;
16
17 /**
18 * @inheritdoc
19 */
20 public function setUp()
21 {
22 $this->app();
23 FakeRequest::setConfiguredBaseUrl('/icingaweb2/');
24 }
25
26 protected function emptyConfigSection()
27 {
28 return Config::module('businessprocess')->getSection('global');
29 }
30
31 /***
32 * @return BpConfig
33 */
34 protected function makeLoop()
35 {
36 return $this->makeInstance()->loadFromString(
37 'loop',
38 "a = b\nb = c\nc = a\nd = a"
39 );
40 }
41
42 /**
43 * @return LegacyStorage
44 */
45 protected function makeInstance()
46 {
47 return new LegacyStorage($this->emptyConfigSection());
48 }
49
50 /**
51 * @param null $subDir
52 * @return string
53 */
54 protected function getTestsBaseDir($subDir = null)
55 {
56 $dir = dirname(dirname(dirname(__DIR__))) . '/test';
57 if ($subDir === null) {
58 return $dir;
59 } else {
60 return $dir . '/' . ltrim($subDir, '/');
61 }
62 }
63
64 /**
65 * @return ApplicationBootstrap
66 */
67 protected function app()
68 {
69 if (self::$app === null) {
70 self::$app = Icinga::app();
71 }
72
73 return self::$app;
74 }
75 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Test;
3
4 use Icinga\Application\Cli;
5
6 class Bootstrap
7 {
8 public static function cli($basedir = null)
9 {
10 error_reporting(E_ALL | E_STRICT);
11 if ($basedir === null) {
12 $basedir = dirname(dirname(dirname(__DIR__)));
13 }
14 $testsDir = $basedir . '/test';
15 require_once 'Icinga/Application/Cli.php';
16
17 if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
18 $configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
19 } else {
20 $configDir = $testsDir . '/config';
21 }
22
23 Cli::start($testsDir, $configDir)
24 ->getModuleManager()
25 ->loadModule('businessprocess', $basedir);
26 }
27 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Html\BaseElement;
5
6 class ActionBar extends BaseElement
7 {
8 protected $contentSeparator = ' ';
9
10 /** @var string */
11 protected $tag = 'div';
12
13 protected $defaultAttributes = array('class' => 'action-bar');
14 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Html\Container;
5
6 class Content extends Container
7 {
8 protected $contentSeparator = "\n";
9
10 protected $defaultAttributes = array('class' => 'content');
11 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Html\Container;
5
6 class Controls extends Container
7 {
8 protected $contentSeparator = "\n";
9
10 protected $defaultAttributes = array('class' => 'controls');
11 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Authentication\Auth;
5 use Icinga\Module\Businessprocess\Html\BaseElement;
6 use Icinga\Module\Businessprocess\Html\Container;
7 use Icinga\Module\Businessprocess\Html\HtmlTag;
8 use Icinga\Module\Businessprocess\Storage\Storage;
9
10 class Dashboard extends BaseElement
11 {
12 /** @var string */
13 protected $contentSeparator = "\n";
14
15 /** @var string */
16 protected $tag = 'div';
17
18 protected $defaultAttributes = array(
19 'class' => 'overview-dashboard',
20 'data-base-target' => '_next'
21 );
22
23 /** @var Auth */
24 protected $auth;
25
26 /** @var Storage */
27 protected $storage;
28
29 /**
30 * Dashboard constructor.
31 * @param Auth $auth
32 * @param Storage $storage
33 */
34 protected function __construct(Auth $auth, Storage $storage)
35 {
36 $this->auth = $auth;
37 $this->storage = $storage;
38 // TODO: Auth?
39 $processes = $storage->listProcessNames();
40 $this->add(
41 HtmlTag::h1($this->translate('Welcome to your Business Process Overview'))
42 );
43 $this->add(
44 HtmlTag::p(
45 $this->translate(
46 'From here you can reach all your defined Business Process'
47 . ' configurations, create new or modify existing ones'
48 )
49 )
50 );
51 if ($auth->hasPermission('businessprocess/create')) {
52 $this->add(
53 new DashboardAction(
54 $this->translate('Create'),
55 $this->translate('Create a new Business Process configuration'),
56 'plus',
57 'businessprocess/process/create',
58 null,
59 array('class' => 'addnew')
60 )
61 )->add(
62 new DashboardAction(
63 $this->translate('Upload'),
64 $this->translate('Upload an existing Business Process configuration'),
65 'upload',
66 'businessprocess/process/upload',
67 null,
68 array('class' => 'addnew')
69 )
70 );
71 } elseif (empty($processes)) {
72 $this->addContent(
73 Container::create()
74 ->add(HtmlTag::h1($this->translate('Not available')))
75 ->add(HtmlTag::p($this->translate('No Business Process has been defined for you')))
76 );
77 }
78
79 foreach ($processes as $name) {
80 $meta = $storage->loadMetadata($name);
81 $title = $meta->get('Title');
82 if ($title) {
83 $title = sprintf('%s (%s)', $title, $name);
84 } else {
85 $title = $name;
86 }
87 $this->add(new DashboardAction(
88 $title,
89 $meta->get('Description'),
90 'sitemap',
91 'businessprocess/process/show',
92 array('config' => $name)
93 ));
94 }
95 }
96
97 /**
98 * @param Auth $auth
99 * @param Storage $storage
100 * @return static
101 */
102 public static function create(Auth $auth, Storage $storage)
103 {
104 return new static($auth, $storage);
105 }
106 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Html\BaseElement;
5 use Icinga\Module\Businessprocess\Html\Element;
6 use Icinga\Module\Businessprocess\Html\Icon;
7 use Icinga\Module\Businessprocess\Html\Link;
8
9 class DashboardAction extends BaseElement
10 {
11 protected $tag = 'div';
12
13 protected $defaultAttributes = array('class' => 'action');
14
15 public function __construct($title, $description, $icon, $url, $urlParams = null, $attributes = null)
16 {
17 $this->add(
18 Link::create(
19 Icon::create($icon),
20 $url,
21 $urlParams,
22 $attributes
23 )->add(
24 Element::create('span', array('class' => 'header'))->addContent($title)
25 )->addContent($description)
26 );
27 }
28 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Authentication\Auth;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Html\Link;
7 use Icinga\Module\Businessprocess\Renderer\Renderer;
8 use Icinga\Module\Businessprocess\Renderer\TreeRenderer;
9 use Icinga\Module\Businessprocess\Web\Url;
10
11 class RenderedProcessActionBar extends ActionBar
12 {
13 public function __construct(BpConfig $config, Renderer $renderer, Auth $auth, Url $url)
14 {
15 $meta = $config->getMetadata();
16
17 if ($renderer instanceof TreeRenderer) {
18 $this->add(
19 Link::create(
20 $this->translate('Tiles'),
21 $url->with('mode', 'tile'),
22 null,
23 array(
24 'class' => 'icon-dashboard',
25 'title' => $this->translate('Switch to Tile view'),
26 )
27 )
28 );
29 } else {
30 $this->add(
31 Link::create(
32 $this->translate('Tree'),
33 $url->with('mode', 'tree'),
34 null,
35 array(
36 'class' => 'icon-sitemap',
37 'title' => $this->translate('Switch to Tree view'),
38 )
39 )
40 );
41 }
42
43 $this->add(
44 Link::create(
45 $this->translate('Fullscreen'),
46 $url->with('showFullscreen', true),
47 null,
48 array(
49 'class' => 'icon-resize-full-alt',
50 'title' => $this->translate('Switch to fullscreen mode'),
51 'data-base-target' => '_main',
52 )
53 )
54 );
55
56 $hasChanges = $config->hasSimulations() || $config->hasBeenChanged();
57
58 if ($renderer->isLocked()) {
59 $this->add(
60 Link::create(
61 $this->translate('Unlock'),
62 $url->with('unlocked', true),
63 null,
64 array(
65 'class' => 'icon-lock-open',
66 'title' => $this->translate('Unlock this process'),
67 )
68 )
69 );
70 } elseif (! $hasChanges) {
71 $this->add(
72 Link::create(
73 $this->translate('Lock'),
74 $url->without('unlocked')->without('action'),
75 null,
76 array(
77 'class' => 'icon-lock',
78 'title' => $this->translate('Lock this process'),
79 )
80 )
81 );
82 }
83
84 if ($renderer->wantsRootNodes() && (
85 $hasChanges || (! $renderer->isLocked()) && $meta->canModify()
86 )) {
87 $this->add(
88 Link::create(
89 $this->translate('Config'),
90 'businessprocess/process/config',
91 $this->currentProcessParams($url),
92 array(
93 'class' => 'icon-wrench',
94 'title' => $this->translate('Modify this process'),
95 'data-base-target' => '_next',
96 )
97 )
98 );
99 }
100 }
101
102 protected function currentProcessParams(Url $url)
103 {
104 $urlParams = $url->getParams();
105 $params = array();
106 foreach (array('config', 'node') as $name) {
107 if ($value = $urlParams->get($name)) {
108 $params[$name] = $value;
109 }
110 }
111
112 return $params;
113 }
114 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Html\Renderable;
5
6 class Tabs extends WtfTabs implements Renderable
7 {
8 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Web\Widget\Tabs;
5
6 /**
7 * Class WtfTabs
8 *
9 * TODO: Please remove this as soon as we drop support for PHP 5.3.x
10 * This works around https://bugs.php.net/bug.php?id=43200 and fixes
11 * https://github.com/Icinga/icingaweb2-module-businessprocess/issues/81
12 *
13 * @package Icinga\Module\Businessprocess\Web\Component
14 */
15 class WtfTabs extends Tabs
16 {
17 public function render()
18 {
19 return parent::render();
20 }
21 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Module\Businessprocess\BpConfig;
6 use Icinga\Module\Businessprocess\Html\HtmlTag;
7 use Icinga\Module\Businessprocess\Modification\ProcessChanges;
8 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
9 use Icinga\Module\Businessprocess\Storage\Storage;
10 use Icinga\Module\Businessprocess\Web\Component\ActionBar;
11 use Icinga\Module\Businessprocess\Web\Component\Controls;
12 use Icinga\Module\Businessprocess\Web\Component\Content;
13 use Icinga\Module\Businessprocess\Web\Component\Tabs;
14 use Icinga\Module\Businessprocess\Web\Form\FormLoader;
15 use Icinga\Web\Controller as ModuleController;
16 use Icinga\Web\Notification;
17 use Icinga\Web\View;
18
19 class Controller extends ModuleController
20 {
21 /** @var View */
22 public $view;
23
24 /** @var BpConfig */
25 protected $bp;
26
27 /** @var Tabs */
28 protected $mytabs;
29
30 /** @var Storage */
31 private $storage;
32
33 /** @var bool */
34 protected $showFullscreen;
35
36 /** @var Url */
37 private $url;
38
39 public function init()
40 {
41 $m = Icinga::app()->getModuleManager();
42 if (! $m->hasLoaded('monitoring') && $m->hasInstalled('monitoring')) {
43 $m->loadModule('monitoring');
44 }
45 $this->controls();
46 $this->content();
47 $this->url();
48 $this->view->showFullscreen
49 = $this->showFullscreen
50 = (bool) $this->_helper->layout()->showFullscreen;
51
52 $this->view->compact = $this->params->get('view') === 'compact';
53 $this->setViewScript('default');
54 }
55
56 /**
57 * @return Url
58 */
59 protected function url()
60 {
61 if ($this->url === null) {
62 $this->url = Url::fromPath(
63 $this->getRequest()->getUrl()->getPath()
64 )->setParams($this->params);
65 }
66
67 return $this->url;
68 }
69
70 /**
71 * @return ActionBar
72 */
73 protected function actions()
74 {
75 if ($this->view->actions === null) {
76 $this->view->actions = new ActionBar();
77 }
78
79 return $this->view->actions;
80 }
81
82 /**
83 * @return Controls
84 */
85 protected function controls()
86 {
87 if ($this->view->controls === null) {
88 $controls = $this->view->controls = Controls::create();
89 if ($this->view->compact) {
90 $controls->attributes()->add('class', 'compact');
91 }
92 }
93
94 return $this->view->controls;
95 }
96
97 /**
98 * @return Content
99 */
100 protected function content()
101 {
102 if ($this->view->content === null) {
103 $content = $this->view->content = Content::create();
104 if ($this->view->compact) {
105 $content->attributes()->add('class', 'compact');
106 }
107 }
108
109 return $this->view->content;
110 }
111
112 /**
113 * @param $label
114 * @return Tabs
115 */
116 protected function singleTab($label)
117 {
118 return $this->tabs()->add(
119 'tab',
120 array(
121 'label' => $label,
122 'url' => $this->getRequest()->getUrl()
123 )
124 )->activate('tab');
125 }
126
127 /**
128 * @return Tabs
129 */
130 protected function defaultTab()
131 {
132 return $this->singleTab($this->translate('Business Process'));
133 }
134
135 /**
136 * @return Tabs
137 */
138 protected function overviewTab()
139 {
140 return $this->tabs()->add(
141 'overview',
142 array(
143 'label' => $this->translate('Business Process'),
144 'url' => 'businessprocess'
145 )
146 )->activate('overview');
147 }
148
149 /**
150 * @return Tabs
151 */
152 protected function tabs()
153 {
154 // Todo: do not add to view once all of them render controls()
155 if ($this->mytabs === null) {
156 $tabs = new Tabs();
157 //$this->controls()->add($tabs);
158 $this->mytabs = $tabs;
159 }
160
161 return $this->mytabs;
162 }
163
164 protected function session()
165 {
166 return $this->Window()->getSessionNamespace('businessprocess');
167 }
168
169 protected function setViewScript($name)
170 {
171 $this->_helper->viewRenderer->setNoController(true);
172 $this->_helper->viewRenderer->setScriptAction($name);
173 return $this;
174 }
175
176 protected function setTitle($title)
177 {
178 $args = func_get_args();
179 array_shift($args);
180 $this->view->title = vsprintf($title, $args);
181 return $this;
182 }
183
184 protected function addTitle($title)
185 {
186 $args = func_get_args();
187 array_shift($args);
188 $this->view->title = vsprintf($title, $args);
189 $this->controls()->add(HtmlTag::h1($this->view->title));
190 return $this;
191 }
192
193 protected function loadModifiedBpConfig()
194 {
195 $bp = $this->loadBpConfig();
196 $changes = ProcessChanges::construct($bp, $this->session());
197 if ($this->params->get('dismissChanges')) {
198 Notification::success(
199 sprintf(
200 $this->translate('%d pending change(s) have been dropped'),
201 $changes->count()
202 )
203 );
204 $changes->clear();
205 $this->redirectNow($this->url()->without('dismissChanges')->without('unlocked'));
206 }
207 $bp->applyChanges($changes);
208 return $bp;
209 }
210
211 protected function doNotRender()
212 {
213 $this->_helper->layout()->disableLayout();
214 $this->_helper->viewRenderer->setNoRender(true);
215 return $this;
216 }
217
218 protected function loadBpConfig()
219 {
220 $name = $this->params->get('config');
221 $storage = $this->storage();
222
223 if (! $storage->hasProcess($name)) {
224 $this->httpNotFound(
225 $this->translate('No such process config: "%s"'),
226 $name
227 );
228 }
229
230 $modifications = $this->session()->get('modifications', array());
231 if (array_key_exists($name, $modifications)) {
232 $bp = $storage->loadFromString($name, $modifications[$name]);
233 } else {
234 $bp = $storage->loadProcess($name);
235 }
236
237 // allow URL parameter to override configured state type
238 if (null !== ($stateType = $this->params->get('state_type'))) {
239 if ($stateType === 'soft') {
240 $bp->useSoftStates();
241 }
242 if ($stateType === 'hard') {
243 $bp->useHardStates();
244 }
245 }
246
247 $this->view->bpconfig = $this->bp = $bp;
248 $this->view->configName = $bp->getName();
249
250 return $bp;
251 }
252
253 public function loadForm($name)
254 {
255 return FormLoader::load($name, $this->Module());
256 }
257
258 /**
259 * @return LegacyStorage|Storage
260 */
261 protected function storage()
262 {
263 if ($this->storage === null) {
264 $this->storage = new LegacyStorage(
265 $this->Config()->getSection('global')
266 );
267 }
268
269 return $this->storage;
270 }
271 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web;
3
4 use Icinga\Exception\ProgrammingError;
5 use Icinga\Web\Request;
6
7 class FakeRequest extends Request
8 {
9 /** @var string */
10 private static $baseUrl;
11
12 public static function setConfiguredBaseUrl($url)
13 {
14 self::$baseUrl = $url;
15 }
16
17 public function getBaseUrl($raw = false)
18 {
19 if (self::$baseUrl === null) {
20 throw new ProgrammingError('Cannot determine base URL on CLI if not configured');
21 } else {
22 return self::$baseUrl;
23 }
24 }
25 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form;
3
4 use Icinga\Application\Config;
5 use Icinga\Authentication\Auth;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7 use Icinga\Module\Businessprocess\BpConfig;
8
9 abstract class BpConfigBaseForm extends QuickForm
10 {
11 /** @var LegacyStorage */
12 protected $storage;
13
14 /** @var BpConfig */
15 protected $config;
16
17 protected function listAvailableBackends()
18 {
19 $keys = array_keys(Config::module('monitoring', 'backends')->toArray());
20 return array_combine($keys, $keys);
21 }
22
23 public function setStorage(LegacyStorage $storage)
24 {
25 $this->storage = $storage;
26 return $this;
27 }
28
29 public function setProcessConfig(BpConfig $config)
30 {
31 $this->config = $config;
32 return $this;
33 }
34
35 protected function prepareMetadata(BpConfig $config)
36 {
37 $meta = $config->getMetadata();
38 $auth = Auth::getInstance();
39 $meta->set('Owner', $auth->getUser()->getUsername());
40 $prefixes = $auth->getRestrictions('businessprocess/prefix');
41 if (! empty($prefixes) && ! $meta->nameIsPrefixedWithOneOf($prefixes)) {
42 if (count($prefixes) === 1) {
43 $this->getElement('name')->addError(sprintf(
44 $this->translate('Please prefix the name with "%s"'),
45 current($prefixes)
46 ));
47 } else {
48 $this->getElement('name')->addError(sprintf(
49 $this->translate('Please prefix the name with one of "%s"'),
50 implode('", "', $prefixes)
51 ));
52 }
53
54 return false;
55 }
56
57 return true;
58 }
59 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form;
3
4 class CsrfToken
5 {
6 /**
7 * Check whether the given token is valid
8 *
9 * @param string $token Token
10 *
11 * @return bool
12 */
13 public static function isValid($token)
14 {
15 if (strpos($token, '|') === false) {
16 return false;
17 }
18
19 list($seed, $token) = explode('|', $elementValue);
20
21 if (!is_numeric($seed)) {
22 return false;
23 }
24
25 return $token === hash('sha256', self::getSessionId() . $seed);
26 }
27
28 /**
29 * Create a new token
30 *
31 * @return string
32 */
33 public static function generate()
34 {
35 $seed = mt_rand();
36 $token = hash('sha256', self::getSessionId() . $seed);
37
38 return sprintf('%s|%s', $seed, $token);
39 }
40
41 /**
42 * Get current session id
43 *
44 * TODO: we should do this through our App or Session object
45 *
46 * @return string
47 */
48 protected static function getSessionId()
49 {
50 return session_id();
51 }
52 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form\Element;
3
4 use Zend_Form_Element_Xhtml;
5
6 class FormElement extends Zend_Form_Element_Xhtml
7 {
8 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form\Element;
3
4 class SimpleNote extends FormElement
5 {
6 public $helper = 'formSimpleNote';
7
8 /**
9 * Always ignore this element
10 * @codingStandardsIgnoreStart
11 *
12 * @var boolean
13 */
14 protected $_ignore = true;
15 // @codingStandardsIgnoreEnd
16
17 public function isValid($value, $context = null)
18 {
19 return true;
20 }
21 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Application\Modules\Module;
6 use Icinga\Exception\ProgrammingError;
7
8 class FormLoader
9 {
10 public static function load($name, Module $module = null)
11 {
12 if ($module === null) {
13 $basedir = Icinga::app()->getApplicationDir('forms');
14 $ns = '\\Icinga\\Web\\Forms\\';
15 } else {
16 $basedir = $module->getFormDir();
17 $ns = '\\Icinga\\Module\\' . ucfirst($module->getName()) . '\\Forms\\';
18 }
19 if (preg_match('~^[a-z0-9/]+$~i', $name)) {
20 $parts = preg_split('~/~', $name);
21 $class = ucfirst(array_pop($parts)) . 'Form';
22 $file = sprintf('%s/%s/%s.php', rtrim($basedir, '/'), implode('/', $parts), $class);
23 if (file_exists($file)) {
24 require_once($file);
25 $class = $ns . $class;
26 $options = array();
27 if ($module !== null) {
28 $options['icingaModule'] = $module;
29 }
30
31 return new $class($options);
32 }
33 }
34 throw new ProgrammingError(sprintf('Cannot load %s (%s), no such form', $name, $file));
35 }
36 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Application\Modules\Module;
6 use Icinga\Module\Businessprocess\Html\Renderable;
7 use Zend_Form;
8
9 abstract class QuickBaseForm extends Zend_Form implements Renderable
10 {
11 /**
12 * The Icinga module this form belongs to. Usually only set if the
13 * form is initialized through the FormLoader
14 *
15 * @var Module
16 */
17 protected $icingaModule;
18
19 protected $icingaModuleName;
20
21 private $hintCount = 0;
22
23 public function __construct($options = null)
24 {
25 $this->callZfConstructor($this->handleOptions($options))
26 ->initializePrefixPaths();
27 }
28
29 protected function callZfConstructor($options = null)
30 {
31 parent::__construct($options);
32 return $this;
33 }
34
35 protected function initializePrefixPaths()
36 {
37 $this->addPrefixPathsForBusinessprocess();
38 if ($this->icingaModule && $this->icingaModuleName !== 'businessprocess') {
39 $this->addPrefixPathsForModule($this->icingaModule);
40 }
41 }
42
43 protected function addPrefixPathsForBusinessprocess()
44 {
45 $module = Icinga::app()
46 ->getModuleManager()
47 ->loadModule('businessprocess')
48 ->getModule('businessprocess');
49
50 $this->addPrefixPathsForModule($module);
51 }
52
53 public function addPrefixPathsForModule(Module $module)
54 {
55 $basedir = sprintf(
56 '%s/%s/Web/Form',
57 $module->getLibDir(),
58 ucfirst($module->getName())
59 );
60
61 $this->addPrefixPaths(array(
62 array(
63 'prefix' => __NAMESPACE__ . '\\Element\\',
64 'path' => $basedir . '/Element',
65 'type' => static::ELEMENT
66 )
67 ));
68
69 return $this;
70 }
71
72 public function addHidden($name, $value = null)
73 {
74 $this->addElement('hidden', $name);
75 $el = $this->getElement($name);
76 $el->setDecorators(array('ViewHelper'));
77 if ($value !== null) {
78 $this->setDefault($name, $value);
79 $el->setValue($value);
80 }
81
82 return $this;
83 }
84
85 // TODO: Should be an element
86 public function addHtmlHint($html, $options = array())
87 {
88 return $this->addHtml('<div class="hint">' . $html . '</div>', $options);
89 }
90
91 public function addHtml($html, $options = array())
92 {
93 if (array_key_exists('name', $options)) {
94 $name = $options['name'];
95 unset($options['name']);
96 } else {
97 $name = '_HINT' . ++$this->hintCount;
98 }
99
100 $this->addElement('simpleNote', $name, $options);
101 $this->getElement($name)
102 ->setValue($html)
103 ->setIgnore(true)
104 ->setDecorators(array('ViewHelper'));
105
106 return $this;
107 }
108
109 public function optionalEnum($enum, $nullLabel = null)
110 {
111 if ($nullLabel === null) {
112 $nullLabel = $this->translate('- please choose -');
113 }
114
115 return array(null => $nullLabel) + $enum;
116 }
117
118 protected function handleOptions($options = null)
119 {
120 if ($options === null) {
121 return $options;
122 }
123
124 if (array_key_exists('icingaModule', $options)) {
125 /** @var Module icingaModule */
126 $this->icingaModule = $options['icingaModule'];
127 $this->icingaModuleName = $this->icingaModule->getName();
128 unset($options['icingaModule']);
129 }
130
131 return $options;
132 }
133
134 public function setIcingaModule(Module $module)
135 {
136 $this->icingaModule = $module;
137 return $this;
138 }
139
140 protected function loadForm($name, Module $module = null)
141 {
142 if ($module === null) {
143 $module = $this->icingaModule;
144 }
145
146 return FormLoader::load($name, $module);
147 }
148
149 protected function valueIsEmpty($value)
150 {
151 if (is_array($value)) {
152 return empty($value);
153 }
154
155 return strlen($value) === 0;
156 }
157
158 public function translate($string)
159 {
160 if ($this->icingaModuleName === null) {
161 return t($string);
162 } else {
163 return mt($this->icingaModuleName, $string);
164 }
165 }
166 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web\Form;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Exception\ProgrammingError;
6 use Icinga\Web\Notification;
7 use Icinga\Web\Request;
8 use Icinga\Web\Response;
9 use Icinga\Web\Url;
10 use Exception;
11
12 /**
13 * QuickForm wants to be a base class for simple forms
14 */
15 abstract class QuickForm extends QuickBaseForm
16 {
17 const ID = '__FORM_NAME';
18
19 const CSRF = '__FORM_CSRF';
20
21 /**
22 * The name of this form
23 */
24 protected $formName;
25
26 /**
27 * Whether the form has been sent
28 */
29 protected $hasBeenSent;
30
31 /**
32 * Whether the form has been sent
33 */
34 protected $hasBeenSubmitted;
35
36 /**
37 * The submit caption, element - still tbd
38 */
39 // protected $submit;
40
41 /**
42 * Our request
43 */
44 protected $request;
45
46 protected $successUrl;
47
48 protected $successMessage;
49
50 protected $submitLabel;
51
52 protected $submitButtonName;
53
54 protected $deleteButtonName;
55
56 protected $fakeSubmitButtonName;
57
58 /**
59 * Whether form elements have already been created
60 */
61 protected $didSetup = false;
62
63 protected $isApiRequest = false;
64
65 public function __construct($options = null)
66 {
67 parent::__construct($options);
68
69 $this->setMethod('post');
70 $this->getActionFromRequest()
71 ->createIdElement()
72 ->regenerateCsrfToken()
73 ->setPreferredDecorators();
74 }
75
76 protected function getActionFromRequest()
77 {
78 $this->setAction(Url::fromRequest());
79 return $this;
80 }
81
82 protected function setPreferredDecorators()
83 {
84 $this->setAttrib('class', 'autofocus');
85 $this->setDecorators(
86 array(
87 'Description',
88 array('FormErrors', array('onlyCustomFormErrors' => true)),
89 'FormElements',
90 'Form'
91 )
92 );
93
94 return $this;
95 }
96
97 protected function addSubmitButtonIfSet()
98 {
99 if (false === ($label = $this->getSubmitLabel())) {
100 return;
101 }
102
103 if ($this->submitButtonName && $el = $this->getElement($this->submitButtonName)) {
104 return;
105 }
106
107 $el = $this->createElement('submit', $label)
108 ->setLabel($label)
109 ->setDecorators(array('ViewHelper'));
110 $this->submitButtonName = $el->getName();
111 $this->addElement($el);
112
113 $fakeEl = $this->createElement('submit', '_FAKE_SUBMIT')
114 ->setLabel($label)
115 ->setDecorators(array('ViewHelper'));
116 $this->fakeSubmitButtonName = $fakeEl->getName();
117 $this->addElement($fakeEl);
118
119 $this->addDisplayGroup(
120 array($this->fakeSubmitButtonName),
121 'fake_button',
122 array(
123 'decorators' => array('FormElements'),
124 'order' => 1,
125 )
126 );
127
128 $grp = array(
129 $this->submitButtonName,
130 $this->deleteButtonName
131 );
132 $this->addDisplayGroup($grp, 'buttons', array(
133 'decorators' => array(
134 'FormElements',
135 array('HtmlTag', array('tag' => 'dl')),
136 'DtDdWrapper',
137 ),
138 'order' => 1000,
139 ));
140 }
141
142 protected function addSimpleDisplayGroup($elements, $name, $options)
143 {
144 if (! array_key_exists('decorators', $options)) {
145 $options['decorators'] = array(
146 'FormElements',
147 array('HtmlTag', array('tag' => 'dl')),
148 'Fieldset',
149 );
150 }
151
152 return $this->addDisplayGroup($elements, $name, $options);
153 }
154
155 protected function createIdElement()
156 {
157 $this->detectName();
158 $this->addHidden(self::ID, $this->getName());
159 $this->getElement(self::ID)->setIgnore(true);
160 return $this;
161 }
162
163 public function getSentValue($name, $default = null)
164 {
165 $request = $this->getRequest();
166 if ($request->isPost() && $this->hasBeenSent()) {
167 return $request->getPost($name);
168 } else {
169 return $default;
170 }
171 }
172
173 public function getSubmitLabel()
174 {
175 if ($this->submitLabel === null) {
176 return $this->translate('Submit');
177 }
178
179 return $this->submitLabel;
180 }
181
182 public function setSubmitLabel($label)
183 {
184 $this->submitLabel = $label;
185 return $this;
186 }
187
188 public function setApiRequest($isApiRequest = true)
189 {
190 $this->isApiRequest = $isApiRequest;
191 return $this;
192 }
193
194 public function isApiRequest()
195 {
196 return $this->isApiRequest;
197 }
198
199 public function regenerateCsrfToken()
200 {
201 if (! $element = $this->getElement(self::CSRF)) {
202 $this->addHidden(self::CSRF, CsrfToken::generate());
203 $element = $this->getElement(self::CSRF);
204 }
205 $element->setIgnore(true);
206
207 return $this;
208 }
209
210 public function removeCsrfToken()
211 {
212 $this->removeElement(self::CSRF);
213 return $this;
214 }
215
216 public function setSuccessUrl($url, $params = null)
217 {
218 if (! $url instanceof Url) {
219 $url = Url::fromPath($url);
220 }
221 if ($params !== null) {
222 $url->setParams($params);
223 }
224 $this->successUrl = $url;
225 return $this;
226 }
227
228 public function getSuccessUrl()
229 {
230 $url = $this->successUrl ?: $this->getAction();
231 if (! $url instanceof Url) {
232 $url = Url::fromPath($url);
233 }
234
235 return $url;
236 }
237
238 protected function beforeSetup()
239 {
240 }
241
242 public function setup()
243 {
244 }
245
246 protected function onSetup()
247 {
248 }
249
250 public function setAction($action)
251 {
252 if ($action instanceof Url) {
253 $action = $action->getAbsoluteUrl('&');
254 }
255
256 return parent::setAction($action);
257 }
258
259 public function hasBeenSubmitted()
260 {
261 if ($this->hasBeenSubmitted === null) {
262 $req = $this->getRequest();
263 if ($req->isPost()) {
264 if (! $this->hasSubmitButton()) {
265 return $this->hasBeenSubmitted = $this->hasBeenSent();
266 }
267
268 $this->hasBeenSubmitted = $this->pressedButton(
269 $this->fakeSubmitButtonName,
270 $this->getSubmitLabel()
271 ) || $this->pressedButton(
272 $this->submitButtonName,
273 $this->getSubmitLabel()
274 );
275 } else {
276 $this->hasBeenSubmitted = false;
277 }
278 }
279
280 return $this->hasBeenSubmitted;
281 }
282
283 protected function hasSubmitButton()
284 {
285 return $this->submitButtonName !== null;
286 }
287
288 protected function pressedButton($name, $label)
289 {
290 $req = $this->getRequest();
291 if (! $req->isPost()) {
292 return false;
293 }
294
295 $req = $this->getRequest();
296 $post = $req->getPost();
297
298 return array_key_exists($name, $post)
299 && $post[$name] === $label;
300 }
301
302 protected function beforeValidation($data = array())
303 {
304 }
305
306 public function prepareElements()
307 {
308 if (! $this->didSetup) {
309 $this->beforeSetup();
310 $this->setup();
311 $this->addSubmitButtonIfSet();
312 $this->onSetup();
313 $this->didSetup = true;
314 }
315
316 return $this;
317 }
318
319 public function handleRequest(Request $request = null)
320 {
321 if ($request === null) {
322 $request = $this->getRequest();
323 } else {
324 $this->setRequest($request);
325 }
326
327 $this->prepareElements();
328
329 if ($this->hasBeenSent()) {
330 $post = $request->getPost();
331 if ($this->hasBeenSubmitted()) {
332 $this->beforeValidation($post);
333 if ($this->isValid($post)) {
334 try {
335 $this->onSuccess();
336 } catch (Exception $e) {
337 $this->addException($e);
338 $this->onFailure();
339 }
340 } else {
341 $this->onFailure();
342 }
343 } else {
344 $this->setDefaults($post);
345 }
346 } else {
347 // Well...
348 }
349
350 return $this;
351 }
352
353 public function addException(Exception $e, $elementName = null)
354 {
355 $file = preg_split('/[\/\\\]/', $e->getFile(), -1, PREG_SPLIT_NO_EMPTY);
356 $file = array_pop($file);
357 $msg = sprintf(
358 '%s (%s:%d)',
359 $e->getMessage(),
360 $file,
361 $e->getLine()
362 );
363
364 if ($el = $this->getElement($elementName)) {
365 $el->addError($msg);
366 } else {
367 $this->addError($msg);
368 }
369 }
370
371 public function onSuccess()
372 {
373 $this->redirectOnSuccess();
374 }
375
376 public function setSuccessMessage($message)
377 {
378 $this->successMessage = $message;
379 return $this;
380 }
381
382 public function getSuccessMessage($message = null)
383 {
384 if ($message !== null) {
385 return $message;
386 }
387 if ($this->successMessage === null) {
388 return t('Form has successfully been sent');
389 }
390 return $this->successMessage;
391 }
392
393 public function redirectOnSuccess($message = null)
394 {
395 if ($this->isApiRequest()) {
396 // TODO: Set the status line message?
397 $this->successMessage = $this->getSuccessMessage($message);
398 return;
399 }
400
401 $url = $this->getSuccessUrl();
402 $this->notifySuccess($this->getSuccessMessage($message));
403 $this->redirectAndExit($url);
404 }
405
406 public function onFailure()
407 {
408 }
409
410 public function notifySuccess($message = null)
411 {
412 if ($message === null) {
413 $message = t('Form has successfully been sent');
414 }
415 Notification::success($message);
416 return $this;
417 }
418
419 public function notifyError($message)
420 {
421 Notification::error($message);
422 return $this;
423 }
424
425 protected function redirectAndExit($url)
426 {
427 /** @var Response $response */
428 $response = Icinga::app()->getFrontController()->getResponse();
429 $response->redirectAndExit($url);
430 }
431
432 protected function setHttpResponseCode($code)
433 {
434 Icinga::app()->getFrontController()->getResponse()->setHttpResponseCode($code);
435 return $this;
436 }
437
438 protected function onRequest()
439 {
440 }
441
442 public function setRequest(Request $request)
443 {
444 if ($this->request !== null) {
445 throw new ProgrammingError('Unable to set request twice');
446 }
447
448 $this->request = $request;
449 $this->prepareElements();
450 $this->onRequest();
451 return $this;
452 }
453
454 /**
455 * @return Request
456 */
457 public function getRequest()
458 {
459 if ($this->request === null) {
460 /** @var Request $request */
461 $request = Icinga::app()->getFrontController()->getRequest();
462 $this->setRequest($request);
463 }
464 return $this->request;
465 }
466
467 public function hasBeenSent()
468 {
469 if ($this->hasBeenSent === null) {
470
471 /** @var Request $req */
472 if ($this->request === null) {
473 $req = Icinga::app()->getFrontController()->getRequest();
474 } else {
475 $req = $this->request;
476 }
477
478 if ($req->isPost()) {
479 $post = $req->getPost();
480 $this->hasBeenSent = array_key_exists(self::ID, $post) &&
481 $post[self::ID] === $this->getName();
482 } else {
483 $this->hasBeenSent = false;
484 }
485 }
486
487 return $this->hasBeenSent;
488 }
489
490 protected function detectName()
491 {
492 if ($this->formName !== null) {
493 $this->setName($this->formName);
494 } else {
495 $this->setName(get_class($this));
496 }
497 }
498 }
0 <?php
1
2 namespace Icinga\Module\Businessprocess\Web;
3
4 use Icinga\Application\Icinga;
5 use Icinga\Exception\ProgrammingError;
6 use Icinga\Web\Url as WebUrl;
7 use Icinga\Web\UrlParams;
8
9 /**
10 * Class Url
11 *
12 * The main purpose of this class is to get unit tests running on CLI
13 * Little code from Icinga\Web\Url has been duplicated, as neither fromPath()
14 * nor getRequest() can be extended in a meaningful way at the time of this
15 * writing
16 *
17 * @package Icinga\Module\Businessprocess\Web
18 */
19 class Url extends WebUrl
20 {
21 public static function fromPath($url, array $params = array(), $request = null)
22 {
23 if ($request === null) {
24 $request = static::getRequest();
25 }
26
27 if (! is_string($url)) {
28 throw new ProgrammingError(
29 'url "%s" is not a string',
30 $url
31 );
32 }
33
34 $self = new static;
35
36 if ($url === '#') {
37 return $self->setPath($url);
38 }
39
40 $parts = parse_url($url);
41
42 $self->setBasePath($request->getBaseUrl());
43 if (isset($parts['path'])) {
44 $self->setPath($parts['path']);
45 }
46
47 if (isset($urlParts['query'])) {
48 $params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params);
49 }
50
51 if (isset($parts['fragment'])) {
52 $self->setAnchor($parts['fragment']);
53 }
54
55 $self->setParams($params);
56 return $self;
57 }
58
59 public function setBasePath($basePath)
60 {
61 if (property_exists($this, 'basePath')) {
62 parent::setBasePath($basePath);
63 } else {
64 return $this->setBaseUrl($basePath);
65 }
66 }
67
68 protected static function getRequest()
69 {
70 $app = Icinga::app();
71 if ($app->isCli()) {
72 return new FakeRequest();
73 } else {
74 return $app->getRequest();
75 }
76 }
77 }
0 PHP Diff Class
1 --------------
2
3 Introduction
4 ------------
5 A comprehensive library for generating differences between
6 two hashable objects (strings or arrays). Generated differences can be
7 rendered in all of the standard formats including:
8 * Unified
9 * Context
10 * Inline HTML
11 * Side by Side HTML
12
13 The logic behind the core of the diff engine (ie, the sequence matcher)
14 is primarily based on the Python difflib package. The reason for doing
15 so is primarily because of its high degree of accuracy.
16
17 Example Use
18 -----------
19 A quick usage example can be found in the example/ directory and under
20 example.php.
21
22 More complete documentation will be available shortly.
23
24 Merge files using jQuery
25 ------------------------
26 Xiphe has build a jQuery plugin with that you can merge the compared
27 files. Have a look at [jQuery-Merge-for-php-diff](https://github.com/Xiphe/jQuery-Merge-for-php-diff).
28
29 Todo
30 ----
31 * Ability to ignore blank line changes
32 * 3 way diff support
33 * Performance optimizations
34
35 License (BSD License)
36 ---------------------
37 Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
38 All rights reserved.
39
40 Redistribution and use in source and binary forms, with or without
41 modification, are permitted provided that the following conditions are met:
42
43 - Redistributions of source code must retain the above copyright notice,
44 this list of conditions and the following disclaimer.
45 - Redistributions in binary form must reproduce the above copyright notice,
46 this list of conditions and the following disclaimer in the documentation
47 and/or other materials provided with the distribution.
48 - Neither the name of the Chris Boulton nor the names of its contributors
49 may be used to endorse or promote products derived from this software
50 without specific prior written permission.
51
52 ```
53 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
57 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
58 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
59 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
60 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
61 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
62 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
63 POSSIBILITY OF SUCH DAMAGE.
64 ```
0 git clone https://github.com/chrisboulton/php-diff.git
1 # Last used commit:
2 cd php-diff
3 git checkout f4db229d7ae8dffa0a4f90e1adbec9bf22c93d99
4 rm -rf .git
5 rm -rf .gitignore
6 rm -rf composer.json
7 rm -rf example
8 cd ..
0 <?php
1 /**
2 * Abstract class for diff renderers in PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 abstract class Diff_Renderer_Abstract
43 {
44 /**
45 * @var object Instance of the diff class that this renderer is generating the rendered diff for.
46 */
47 public $diff;
48
49 /**
50 * @var array Array of the default options that apply to this renderer.
51 */
52 protected $defaultOptions = array();
53
54 /**
55 * @var array Array containing the user applied and merged default options for the renderer.
56 */
57 protected $options = array();
58
59 /**
60 * The constructor. Instantiates the rendering engine and if options are passed,
61 * sets the options for the renderer.
62 *
63 * @param array $options Optionally, an array of the options for the renderer.
64 */
65 public function __construct(array $options = array())
66 {
67 $this->setOptions($options);
68 }
69
70 /**
71 * Set the options of the renderer to those supplied in the passed in array.
72 * Options are merged with the default to ensure that there aren't any missing
73 * options.
74 *
75 * @param array $options Array of options to set.
76 */
77 public function setOptions(array $options)
78 {
79 $this->options = array_merge($this->defaultOptions, $options);
80 }
81 }
0 <?php
1 /**
2 * Base renderer for rendering HTML based diffs for PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 require_once dirname(__FILE__).'/../Abstract.php';
43
44 class Diff_Renderer_Html_Array extends Diff_Renderer_Abstract
45 {
46 /**
47 * @var array Array of the default options that apply to this renderer.
48 */
49 protected $defaultOptions = array(
50 'tabSize' => 4
51 );
52
53 /**
54 * Render and return an array structure suitable for generating HTML
55 * based differences. Generally called by subclasses that generate a
56 * HTML based diff and return an array of the changes to show in the diff.
57 *
58 * @return array An array of the generated chances, suitable for presentation in HTML.
59 */
60 public function render()
61 {
62 // As we'll be modifying a & b to include our change markers,
63 // we need to get the contents and store them here. That way
64 // we're not going to destroy the original data
65 $a = $this->diff->getA();
66 $b = $this->diff->getB();
67
68 $changes = array();
69 $opCodes = $this->diff->getGroupedOpcodes();
70 foreach($opCodes as $group) {
71 $blocks = array();
72 $lastTag = null;
73 $lastBlock = 0;
74 foreach($group as $code) {
75 list($tag, $i1, $i2, $j1, $j2) = $code;
76
77 if($tag == 'replace' && $i2 - $i1 == $j2 - $j1) {
78 for($i = 0; $i < ($i2 - $i1); ++$i) {
79 $fromLine = $a[$i1 + $i];
80 $toLine = $b[$j1 + $i];
81
82 list($start, $end) = $this->getChangeExtent($fromLine, $toLine);
83 if($start != 0 || $end != 0) {
84 $last = $end + strlen($fromLine);
85 $fromLine = substr_replace($fromLine, "\0", $start, 0);
86 $fromLine = substr_replace($fromLine, "\1", $last + 1, 0);
87 $last = $end + strlen($toLine);
88 $toLine = substr_replace($toLine, "\0", $start, 0);
89 $toLine = substr_replace($toLine, "\1", $last + 1, 0);
90 $a[$i1 + $i] = $fromLine;
91 $b[$j1 + $i] = $toLine;
92 }
93 }
94 }
95
96 if($tag != $lastTag) {
97 $blocks[] = array(
98 'tag' => $tag,
99 'base' => array(
100 'offset' => $i1,
101 'lines' => array()
102 ),
103 'changed' => array(
104 'offset' => $j1,
105 'lines' => array()
106 )
107 );
108 $lastBlock = count($blocks)-1;
109 }
110
111 $lastTag = $tag;
112
113 if($tag == 'equal') {
114 $lines = array_slice($a, $i1, ($i2 - $i1));
115 $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines);
116 $lines = array_slice($b, $j1, ($j2 - $j1));
117 $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines);
118 }
119 else {
120 if($tag == 'replace' || $tag == 'delete') {
121 $lines = array_slice($a, $i1, ($i2 - $i1));
122 $lines = $this->formatLines($lines);
123 $lines = str_replace(array("\0", "\1"), array('<del>', '</del>'), $lines);
124 $blocks[$lastBlock]['base']['lines'] += $lines;
125 }
126
127 if($tag == 'replace' || $tag == 'insert') {
128 $lines = array_slice($b, $j1, ($j2 - $j1));
129 $lines = $this->formatLines($lines);
130 $lines = str_replace(array("\0", "\1"), array('<ins>', '</ins>'), $lines);
131 $blocks[$lastBlock]['changed']['lines'] += $lines;
132 }
133 }
134 }
135 $changes[] = $blocks;
136 }
137 return $changes;
138 }
139
140 /**
141 * Given two strings, determine where the changes in the two strings
142 * begin, and where the changes in the two strings end.
143 *
144 * @param string $fromLine The first string.
145 * @param string $toLine The second string.
146 * @return array Array containing the starting position (0 by default) and the ending position (-1 by default)
147 */
148 private function getChangeExtent($fromLine, $toLine)
149 {
150 $start = 0;
151 $limit = min(strlen($fromLine), strlen($toLine));
152 while($start < $limit && $fromLine{$start} == $toLine{$start}) {
153 ++$start;
154 }
155 $end = -1;
156 $limit = $limit - $start;
157 while(-$end <= $limit && substr($fromLine, $end, 1) == substr($toLine, $end, 1)) {
158 --$end;
159 }
160 return array(
161 $start,
162 $end + 1
163 );
164 }
165
166 /**
167 * Format a series of lines suitable for output in a HTML rendered diff.
168 * This involves replacing tab characters with spaces, making the HTML safe
169 * for output, ensuring that double spaces are replaced with &nbsp; etc.
170 *
171 * @param array $lines Array of lines to format.
172 * @return array Array of the formatted lines.
173 */
174 protected function formatLines($lines)
175 {
176 $lines = array_map(array($this, 'ExpandTabs'), $lines);
177 $lines = array_map(array($this, 'HtmlSafe'), $lines);
178 $callback = array($this, 'fixMatchedSpaces');
179 foreach($lines as &$line) {
180 // $line = preg_replace('# ( +)|^ #e', "\$this->fixSpaces('\\1')", $line);
181 $line = preg_replace_callback('# ( +)|^ #', $callback, $line);
182 }
183 return $lines;
184 }
185
186 protected function fixMatchedSpaces($m)
187 {
188 return $this->fixSpaces($m[1]);
189 }
190
191 /**
192 * Replace a string containing spaces with a HTML representation using &nbsp;.
193 *
194 * @param string $spaces The string of spaces.
195 * @return string The HTML representation of the string.
196 */
197 function fixSpaces($spaces='')
198 {
199 $count = strlen($spaces);
200 if($count == 0) {
201 return '';
202 }
203
204 $div = floor($count / 2);
205 $mod = $count % 2;
206 return str_repeat('&nbsp; ', $div).str_repeat('&nbsp;', $mod);
207 }
208
209 /**
210 * Replace tabs in a single line with a number of spaces as defined by the tabSize option.
211 *
212 * @param string $line The containing tabs to convert.
213 * @return string The line with the tabs converted to spaces.
214 */
215 private function expandTabs($line)
216 {
217 return str_replace("\t", str_repeat(' ', $this->options['tabSize']), $line);
218 }
219
220 /**
221 * Make a string containing HTML safe for output on a page.
222 *
223 * @param string $string The string.
224 * @return string The string with the HTML characters replaced by entities.
225 */
226 private function htmlSafe($string)
227 {
228 return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8');
229 }
230 }
0 <?php
1 /**
2 * Inline HTML diff generator for PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 require_once dirname(__FILE__).'/Array.php';
43
44 class Diff_Renderer_Html_Inline extends Diff_Renderer_Html_Array
45 {
46 /**
47 * Render a and return diff with changes between the two sequences
48 * displayed inline (under each other)
49 *
50 * @return string The generated inline diff.
51 */
52 public function render()
53 {
54 $changes = parent::render();
55 $html = '';
56 if(empty($changes)) {
57 return $html;
58 }
59
60 $html .= '<table class="Differences DifferencesInline">';
61 $html .= '<thead>';
62 $html .= '<tr>';
63 $html .= '<th>Old</th>';
64 $html .= '<th>New</th>';
65 $html .= '<th>Differences</th>';
66 $html .= '</tr>';
67 $html .= '</thead>';
68 foreach($changes as $i => $blocks) {
69 // If this is a separate block, we're condensing code so output ...,
70 // indicating a significant portion of the code has been collapsed as
71 // it is the same
72 if($i > 0) {
73 $html .= '<tbody class="Skipped">';
74 $html .= '<th>&hellip;</th>';
75 $html .= '<th>&hellip;</th>';
76 $html .= '<td>&nbsp;</td>';
77 $html .= '</tbody>';
78 }
79
80 foreach($blocks as $change) {
81 $html .= '<tbody class="Change'.ucfirst($change['tag']).'">';
82 // Equal changes should be shown on both sides of the diff
83 if($change['tag'] == 'equal') {
84 foreach($change['base']['lines'] as $no => $line) {
85 $fromLine = $change['base']['offset'] + $no + 1;
86 $toLine = $change['changed']['offset'] + $no + 1;
87 $html .= '<tr>';
88 $html .= '<th>'.$fromLine.'</th>';
89 $html .= '<th>'.$toLine.'</th>';
90 $html .= '<td class="Left">'.$line.'</td>';
91 $html .= '</tr>';
92 }
93 }
94 // Added lines only on the right side
95 else if($change['tag'] == 'insert') {
96 foreach($change['changed']['lines'] as $no => $line) {
97 $toLine = $change['changed']['offset'] + $no + 1;
98 $html .= '<tr>';
99 $html .= '<th>&nbsp;</th>';
100 $html .= '<th>'.$toLine.'</th>';
101 $html .= '<td class="Right"><ins>'.$line.'</ins>&nbsp;</td>';
102 $html .= '</tr>';
103 }
104 }
105 // Show deleted lines only on the left side
106 else if($change['tag'] == 'delete') {
107 foreach($change['base']['lines'] as $no => $line) {
108 $fromLine = $change['base']['offset'] + $no + 1;
109 $html .= '<tr>';
110 $html .= '<th>'.$fromLine.'</th>';
111 $html .= '<th>&nbsp;</th>';
112 $html .= '<td class="Left"><del>'.$line.'</del>&nbsp;</td>';
113 $html .= '</tr>';
114 }
115 }
116 // Show modified lines on both sides
117 else if($change['tag'] == 'replace') {
118 foreach($change['base']['lines'] as $no => $line) {
119 $fromLine = $change['base']['offset'] + $no + 1;
120 $html .= '<tr>';
121 $html .= '<th>'.$fromLine.'</th>';
122 $html .= '<th>&nbsp;</th>';
123 $html .= '<td class="Left"><span>'.$line.'</span></td>';
124 $html .= '</tr>';
125 }
126
127 foreach($change['changed']['lines'] as $no => $line) {
128 $toLine = $change['changed']['offset'] + $no + 1;
129 $html .= '<tr>';
130 $html .= '<th>'.$toLine.'</th>';
131 $html .= '<th>&nbsp;</th>';
132 $html .= '<td class="Right"><span>'.$line.'</span></td>';
133 $html .= '</tr>';
134 }
135 }
136 $html .= '</tbody>';
137 }
138 }
139 $html .= '</table>';
140 return $html;
141 }
142 }
0 <?php
1 /**
2 * Side by Side HTML diff generator for PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 require_once dirname(__FILE__).'/Array.php';
43
44 class Diff_Renderer_Html_SideBySide extends Diff_Renderer_Html_Array
45 {
46 /**
47 * Render a and return diff with changes between the two sequences
48 * displayed side by side.
49 *
50 * @return string The generated side by side diff.
51 */
52 public function render()
53 {
54 $changes = parent::render();
55
56 $html = '';
57 if(empty($changes)) {
58 return $html;
59 }
60
61 $html .= '<table class="Differences DifferencesSideBySide">';
62 $html .= '<thead>';
63 $html .= '<tr>';
64 $html .= '<th colspan="2">Old Version</th>';
65 $html .= '<th colspan="2">New Version</th>';
66 $html .= '</tr>';
67 $html .= '</thead>';
68 foreach($changes as $i => $blocks) {
69 if($i > 0) {
70 $html .= '<tbody class="Skipped">';
71 $html .= '<th>&hellip;</th><td>&nbsp;</td>';
72 $html .= '<th>&hellip;</th><td>&nbsp;</td>';
73 $html .= '</tbody>';
74 }
75
76 foreach($blocks as $change) {
77 $html .= '<tbody class="Change'.ucfirst($change['tag']).'">';
78 // Equal changes should be shown on both sides of the diff
79 if($change['tag'] == 'equal') {
80 foreach($change['base']['lines'] as $no => $line) {
81 $fromLine = $change['base']['offset'] + $no + 1;
82 $toLine = $change['changed']['offset'] + $no + 1;
83 $html .= '<tr>';
84 $html .= '<th>'.$fromLine.'</th>';
85 $html .= '<td class="Left"><span>'.$line.'</span>&nbsp;</span></td>';
86 $html .= '<th>'.$toLine.'</th>';
87 $html .= '<td class="Right"><span>'.$line.'</span>&nbsp;</span></td>';
88 $html .= '</tr>';
89 }
90 }
91 // Added lines only on the right side
92 else if($change['tag'] == 'insert') {
93 foreach($change['changed']['lines'] as $no => $line) {
94 $toLine = $change['changed']['offset'] + $no + 1;
95 $html .= '<tr>';
96 $html .= '<th>&nbsp;</th>';
97 $html .= '<td class="Left">&nbsp;</td>';
98 $html .= '<th>'.$toLine.'</th>';
99 $html .= '<td class="Right"><ins>'.$line.'</ins>&nbsp;</td>';
100 $html .= '</tr>';
101 }
102 }
103 // Show deleted lines only on the left side
104 else if($change['tag'] == 'delete') {
105 foreach($change['base']['lines'] as $no => $line) {
106 $fromLine = $change['base']['offset'] + $no + 1;
107 $html .= '<tr>';
108 $html .= '<th>'.$fromLine.'</th>';
109 $html .= '<td class="Left"><del>'.$line.'</del>&nbsp;</td>';
110 $html .= '<th>&nbsp;</th>';
111 $html .= '<td class="Right">&nbsp;</td>';
112 $html .= '</tr>';
113 }
114 }
115 // Show modified lines on both sides
116 else if($change['tag'] == 'replace') {
117 if(count($change['base']['lines']) >= count($change['changed']['lines'])) {
118 foreach($change['base']['lines'] as $no => $line) {
119 $fromLine = $change['base']['offset'] + $no + 1;
120 $html .= '<tr>';
121 $html .= '<th>'.$fromLine.'</th>';
122 $html .= '<td class="Left"><span>'.$line.'</span>&nbsp;</td>';
123 if(!isset($change['changed']['lines'][$no])) {
124 $toLine = '&nbsp;';
125 $changedLine = '&nbsp;';
126 }
127 else {
128 $toLine = $change['base']['offset'] + $no + 1;
129 $changedLine = '<span>'.$change['changed']['lines'][$no].'</span>';
130 }
131 $html .= '<th>'.$toLine.'</th>';
132 $html .= '<td class="Right">'.$changedLine.'</td>';
133 $html .= '</tr>';
134 }
135 }
136 else {
137 foreach($change['changed']['lines'] as $no => $changedLine) {
138 if(!isset($change['base']['lines'][$no])) {
139 $fromLine = '&nbsp;';
140 $line = '&nbsp;';
141 }
142 else {
143 $fromLine = $change['base']['offset'] + $no + 1;
144 $line = '<span>'.$change['base']['lines'][$no].'</span>';
145 }
146 $html .= '<tr>';
147 $html .= '<th>'.$fromLine.'</th>';
148 $html .= '<td class="Left"><span>'.$line.'</span>&nbsp;</td>';
149 $toLine = $change['changed']['offset'] + $no + 1;
150 $html .= '<th>'.$toLine.'</th>';
151 $html .= '<td class="Right">'.$changedLine.'</td>';
152 $html .= '</tr>';
153 }
154 }
155 }
156 $html .= '</tbody>';
157 }
158 }
159 $html .= '</table>';
160 return $html;
161 }
162 }
0 <?php
1 /**
2 * Context diff generator for PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 require_once dirname(__FILE__).'/../Abstract.php';
43
44 class Diff_Renderer_Text_Context extends Diff_Renderer_Abstract
45 {
46 /**
47 * @var array Array of the different opcode tags and how they map to the context diff equivalent.
48 */
49 private $tagMap = array(
50 'insert' => '+',
51 'delete' => '-',
52 'replace' => '!',
53 'equal' => ' '
54 );
55
56 /**
57 * Render and return a context formatted (old school!) diff file.
58 *
59 * @return string The generated context diff.
60 */
61 public function render()
62 {
63 $diff = '';
64 $opCodes = $this->diff->getGroupedOpcodes();
65 foreach($opCodes as $group) {
66 $diff .= "***************\n";
67 $lastItem = count($group)-1;
68 $i1 = $group[0][1];
69 $i2 = $group[$lastItem][2];
70 $j1 = $group[0][3];
71 $j2 = $group[$lastItem][4];
72
73 if($i2 - $i1 >= 2) {
74 $diff .= '*** '.($group[0][1] + 1).','.$i2." ****\n";
75 }
76 else {
77 $diff .= '*** '.$i2." ****\n";
78 }
79
80 if($j2 - $j1 >= 2) {
81 $separator = '--- '.($j1 + 1).','.$j2." ----\n";
82 }
83 else {
84 $separator = '--- '.$j2." ----\n";
85 }
86
87 $hasVisible = false;
88 foreach($group as $code) {
89 if($code[0] == 'replace' || $code[0] == 'delete') {
90 $hasVisible = true;
91 break;
92 }
93 }
94
95 if($hasVisible) {
96 foreach($group as $code) {
97 list($tag, $i1, $i2, $j1, $j2) = $code;
98 if($tag == 'insert') {
99 continue;
100 }
101 $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetA($i1, $i2))."\n";
102 }
103 }
104
105 $hasVisible = false;
106 foreach($group as $code) {
107 if($code[0] == 'replace' || $code[0] == 'insert') {
108 $hasVisible = true;
109 break;
110 }
111 }
112
113 $diff .= $separator;
114
115 if($hasVisible) {
116 foreach($group as $code) {
117 list($tag, $i1, $i2, $j1, $j2) = $code;
118 if($tag == 'delete') {
119 continue;
120 }
121 $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetB($j1, $j2))."\n";
122 }
123 }
124 }
125 return $diff;
126 }
127 }
0 <?php
1 /**
2 * Unified diff generator for PHP DiffLib.
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package DiffLib
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 require_once dirname(__FILE__).'/../Abstract.php';
43
44 class Diff_Renderer_Text_Unified extends Diff_Renderer_Abstract
45 {
46 /**
47 * Render and return a unified diff.
48 *
49 * @return string The unified diff.
50 */
51 public function render()
52 {
53 $diff = '';
54 $opCodes = $this->diff->getGroupedOpcodes();
55 foreach($opCodes as $group) {
56 $lastItem = count($group)-1;
57 $i1 = $group[0][1];
58 $i2 = $group[$lastItem][2];
59 $j1 = $group[0][3];
60 $j2 = $group[$lastItem][4];
61
62 if($i1 == 0 && $i2 == 0) {
63 $i1 = -1;
64 $i2 = -1;
65 }
66
67 $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n";
68 foreach($group as $code) {
69 list($tag, $i1, $i2, $j1, $j2) = $code;
70 if($tag == 'equal') {
71 $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n";
72 }
73 else {
74 if($tag == 'replace' || $tag == 'delete') {
75 $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n";
76 }
77
78 if($tag == 'replace' || $tag == 'insert') {
79 $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n";
80 }
81 }
82 }
83 }
84 return $diff;
85 }
86 }
0 <?php
1 /**
2 * Sequence matcher for Diff
3 *
4 * PHP version 5
5 *
6 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Chris Boulton nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @package Diff
35 * @author Chris Boulton <chris.boulton@interspire.com>
36 * @copyright (c) 2009 Chris Boulton
37 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
38 * @version 1.1
39 * @link http://github.com/chrisboulton/php-diff
40 */
41
42 class Diff_SequenceMatcher
43 {
44 /**
45 * @var string|array Either a string or an array containing a callback function to determine if a line is "junk" or not.
46 */
47 private $junkCallback = null;
48
49 /**
50 * @var array The first sequence to compare against.
51 */
52 private $a = null;
53
54 /**
55 * @var array The second sequence.
56 */
57 private $b = null;
58
59 /**
60 * @var array Array of characters that are considered junk from the second sequence. Characters are the array key.
61 */
62 private $junkDict = array();
63
64 /**
65 * @var array Array of indices that do not contain junk elements.
66 */
67 private $b2j = array();
68
69 private $options = array();
70
71 private $defaultOptions = array(
72 'ignoreNewLines' => false,
73 'ignoreWhitespace' => false,
74 'ignoreCase' => false
75 );
76
77 /**
78 * The constructor. With the sequences being passed, they'll be set for the
79 * sequence matcher and it will perform a basic cleanup & calculate junk
80 * elements.
81 *
82 * @param string|array $a A string or array containing the lines to compare against.
83 * @param string|array $b A string or array containing the lines to compare.
84 * @param string|array $junkCallback Either an array or string that references a callback function (if there is one) to determine 'junk' characters.
85 */
86 public function __construct($a, $b, $junkCallback=null, $options)
87 {
88 $this->a = null;
89 $this->b = null;
90 $this->junkCallback = $junkCallback;
91 $this->setOptions($options);
92 $this->setSequences($a, $b);
93 }
94
95 public function setOptions($options)
96 {
97 $this->options = array_merge($this->defaultOptions, $options);
98 }
99
100 /**
101 * Set the first and second sequences to use with the sequence matcher.
102 *
103 * @param string|array $a A string or array containing the lines to compare against.
104 * @param string|array $b A string or array containing the lines to compare.
105 */
106 public function setSequences($a, $b)
107 {
108 $this->setSeq1($a);
109 $this->setSeq2($b);
110 }
111
112 /**
113 * Set the first sequence ($a) and reset any internal caches to indicate that
114 * when calling the calculation methods, we need to recalculate them.
115 *
116 * @param string|array $a The sequence to set as the first sequence.
117 */
118 public function setSeq1($a)
119 {
120 if(!is_array($a)) {
121 $a = str_split($a);
122 }
123 if($a == $this->a) {
124 return;
125 }
126
127 $this->a= $a;
128 $this->matchingBlocks = null;
129 $this->opCodes = null;
130 }
131
132 /**
133 * Set the second sequence ($b) and reset any internal caches to indicate that
134 * when calling the calculation methods, we need to recalculate them.
135 *
136 * @param string|array $b The sequence to set as the second sequence.
137 */
138 public function setSeq2($b)
139 {
140 if(!is_array($b)) {
141 $b = str_split($b);
142 }
143 if($b == $this->b) {
144 return;
145 }
146
147 $this->b = $b;
148 $this->matchingBlocks = null;
149 $this->opCodes = null;
150 $this->fullBCount = null;
151 $this->chainB();
152 }
153
154 /**
155 * Generate the internal arrays containing the list of junk and non-junk
156 * characters for the second ($b) sequence.
157 */
158 private function chainB()
159 {
160 $length = count ($this->b);
161 $this->b2j = array();
162 $popularDict = array();
163
164 for($i = 0; $i < $length; ++$i) {
165 $char = $this->b[$i];
166 if(isset($this->b2j[$char])) {
167 if($length >= 200 && count($this->b2j[$char]) * 100 > $length) {
168 $popularDict[$char] = 1;
169 unset($this->b2j[$char]);
170 }
171 else {
172 $this->b2j[$char][] = $i;
173 }
174 }
175 else {
176 $this->b2j[$char] = array(
177 $i
178 );
179 }
180 }
181
182 // Remove leftovers
183 foreach(array_keys($popularDict) as $char) {
184 unset($this->b2j[$char]);
185 }
186
187 $this->junkDict = array();
188 if(is_callable($this->junkCallback)) {
189 foreach(array_keys($popularDict) as $char) {
190 if(call_user_func($this->junkCallback, $char)) {
191 $this->junkDict[$char] = 1;
192 unset($popularDict[$char]);
193 }
194 }
195
196 foreach(array_keys($this->b2j) as $char) {
197 if(call_user_func($this->junkCallback, $char)) {
198 $this->junkDict[$char] = 1;
199 unset($this->b2j[$char]);
200 }
201 }
202 }
203 }
204
205 /**
206 * Checks if a particular character is in the junk dictionary
207 * for the list of junk characters.
208 *
209 * @return boolean $b True if the character is considered junk. False if not.
210 */
211 private function isBJunk($b)
212 {
213 if(isset($this->juncDict[$b])) {
214 return true;
215 }
216
217 return false;
218 }
219
220 /**
221 * Find the longest matching block in the two sequences, as defined by the
222 * lower and upper constraints for each sequence. (for the first sequence,
223 * $alo - $ahi and for the second sequence, $blo - $bhi)
224 *
225 * Essentially, of all of the maximal matching blocks, return the one that
226 * startest earliest in $a, and all of those maximal matching blocks that
227 * start earliest in $a, return the one that starts earliest in $b.
228 *
229 * If the junk callback is defined, do the above but with the restriction
230 * that the junk element appears in the block. Extend it as far as possible
231 * by matching only junk elements in both $a and $b.
232 *
233 * @param int $alo The lower constraint for the first sequence.
234 * @param int $ahi The upper constraint for the first sequence.
235 * @param int $blo The lower constraint for the second sequence.
236 * @param int $bhi The upper constraint for the second sequence.
237 * @return array Array containing the longest match that includes the starting position in $a, start in $b and the length/size.
238 */
239 public function findLongestMatch($alo, $ahi, $blo, $bhi)
240 {
241 $a = $this->a;
242 $b = $this->b;
243
244 $bestI = $alo;
245 $bestJ = $blo;
246 $bestSize = 0;
247
248 $j2Len = array();
249 $nothing = array();
250
251 for($i = $alo; $i < $ahi; ++$i) {
252 $newJ2Len = array();
253 $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing);
254 foreach($jDict as $jKey => $j) {
255 if($j < $blo) {
256 continue;
257 }
258 else if($j >= $bhi) {
259 break;
260 }
261
262 $k = $this->arrayGetDefault($j2Len, $j -1, 0) + 1;
263 $newJ2Len[$j] = $k;
264 if($k > $bestSize) {
265 $bestI = $i - $k + 1;
266 $bestJ = $j - $k + 1;
267 $bestSize = $k;
268 }
269 }
270
271 $j2Len = $newJ2Len;
272 }
273
274 while($bestI > $alo && $bestJ > $blo && !$this->isBJunk($b[$bestJ - 1]) &&
275 !$this->linesAreDifferent($bestI - 1, $bestJ - 1)) {
276 --$bestI;
277 --$bestJ;
278 ++$bestSize;
279 }
280
281 while($bestI + $bestSize < $ahi && ($bestJ + $bestSize) < $bhi &&
282 !$this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) {
283 ++$bestSize;
284 }
285
286 while($bestI > $alo && $bestJ > $blo && $this->isBJunk($b[$bestJ - 1]) &&
287 !$this->isLineDifferent($bestI - 1, $bestJ - 1)) {
288 --$bestI;
289 --$bestJ;
290 ++$bestSize;
291 }
292
293 while($bestI + $bestSize < $ahi && $bestJ + $bestSize < $bhi &&
294 $this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) {
295 ++$bestSize;
296 }
297
298 return array(
299 $bestI,
300 $bestJ,
301 $bestSize
302 );
303 }
304
305 /**
306 * Check if the two lines at the given indexes are different or not.
307 *
308 * @param int $aIndex Line number to check against in a.
309 * @param int $bIndex Line number to check against in b.
310 * @return boolean True if the lines are different and false if not.
311 */
312 public function linesAreDifferent($aIndex, $bIndex)
313 {
314 $lineA = $this->a[$aIndex];
315 $lineB = $this->b[$bIndex];
316
317 if($this->options['ignoreWhitespace']) {
318 $replace = array("\t", ' ');
319 $lineA = str_replace($replace, '', $lineA);
320 $lineB = str_replace($replace, '', $lineB);
321 }
322
323 if($this->options['ignoreCase']) {
324 $lineA = strtolower($lineA);
325 $lineB = strtolower($lineB);
326 }
327
328 if($lineA != $lineB) {
329 return true;
330 }
331
332 return false;
333 }
334
335 /**
336 * Return a nested set of arrays for all of the matching sub-sequences
337 * in the strings $a and $b.
338 *
339 * Each block contains the lower constraint of the block in $a, the lower
340 * constraint of the block in $b and finally the number of lines that the
341 * block continues for.
342 *
343 * @return array Nested array of the matching blocks, as described by the function.
344 */
345 public function getMatchingBlocks()
346 {
347 if(!empty($this->matchingBlocks)) {
348 return $this->matchingBlocks;
349 }
350
351 $aLength = count($this->a);
352 $bLength = count($this->b);
353
354 $queue = array(
355 array(
356 0,
357 $aLength,
358 0,
359 $bLength
360 )
361 );
362
363 $matchingBlocks = array();
364 while(!empty($queue)) {
365 list($alo, $ahi, $blo, $bhi) = array_pop($queue);
366 $x = $this->findLongestMatch($alo, $ahi, $blo, $bhi);
367 list($i, $j, $k) = $x;
368 if($k) {
369 $matchingBlocks[] = $x;
370 if($alo < $i && $blo < $j) {
371 $queue[] = array(
372 $alo,
373 $i,
374 $blo,
375 $j
376 );
377 }
378
379 if($i + $k < $ahi && $j + $k < $bhi) {
380 $queue[] = array(
381 $i + $k,
382 $ahi,
383 $j + $k,
384 $bhi
385 );
386 }
387 }
388 }
389
390 usort($matchingBlocks, array($this, 'tupleSort'));
391
392 $i1 = 0;
393 $j1 = 0;
394 $k1 = 0;
395 $nonAdjacent = array();
396 foreach($matchingBlocks as $block) {
397 list($i2, $j2, $k2) = $block;
398 if($i1 + $k1 == $i2 && $j1 + $k1 == $j2) {
399 $k1 += $k2;
400 }
401 else {
402 if($k1) {
403 $nonAdjacent[] = array(
404 $i1,
405 $j1,
406 $k1
407 );
408 }
409
410 $i1 = $i2;
411 $j1 = $j2;
412 $k1 = $k2;
413 }
414 }
415
416 if($k1) {
417 $nonAdjacent[] = array(
418 $i1,
419 $j1,
420 $k1
421 );
422 }
423
424 $nonAdjacent[] = array(
425 $aLength,
426 $bLength,
427 0
428 );
429
430 $this->matchingBlocks = $nonAdjacent;
431 return $this->matchingBlocks;
432 }
433
434 /**
435 * Return a list of all of the opcodes for the differences between the
436 * two strings.
437 *
438 * The nested array returned contains an array describing the opcode
439 * which includes:
440 * 0 - The type of tag (as described below) for the opcode.
441 * 1 - The beginning line in the first sequence.
442 * 2 - The end line in the first sequence.
443 * 3 - The beginning line in the second sequence.
444 * 4 - The end line in the second sequence.
445 *
446 * The different types of tags include:
447 * replace - The string from $i1 to $i2 in $a should be replaced by
448 * the string in $b from $j1 to $j2.
449 * delete - The string in $a from $i1 to $j2 should be deleted.
450 * insert - The string in $b from $j1 to $j2 should be inserted at
451 * $i1 in $a.
452 * equal - The two strings with the specified ranges are equal.
453 *
454 * @return array Array of the opcodes describing the differences between the strings.
455 */
456 public function getOpCodes()
457 {
458 if(!empty($this->opCodes)) {
459 return $this->opCodes;
460 }
461
462 $i = 0;
463 $j = 0;
464 $this->opCodes = array();
465
466 $blocks = $this->getMatchingBlocks();
467 foreach($blocks as $block) {
468 list($ai, $bj, $size) = $block;
469 $tag = '';
470 if($i < $ai && $j < $bj) {
471 $tag = 'replace';
472 }
473 else if($i < $ai) {
474 $tag = 'delete';
475 }
476 else if($j < $bj) {
477 $tag = 'insert';
478 }
479
480 if($tag) {
481 $this->opCodes[] = array(
482 $tag,
483 $i,
484 $ai,
485 $j,
486 $bj
487 );
488 }
489
490 $i = $ai + $size;
491 $j = $bj + $size;
492
493 if($size) {
494 $this->opCodes[] = array(
495 'equal',
496 $ai,
497 $i,
498 $bj,
499 $j
500 );
501 }
502 }
503 return $this->opCodes;
504 }
505
506 /**
507 * Return a series of nested arrays containing different groups of generated
508 * opcodes for the differences between the strings with up to $context lines
509 * of surrounding content.
510 *
511 * Essentially what happens here is any big equal blocks of strings are stripped
512 * out, the smaller subsets of changes are then arranged in to their groups.
513 * This means that the sequence matcher and diffs do not need to include the full
514 * content of the different files but can still provide context as to where the
515 * changes are.
516 *
517 * @param int $context The number of lines of context to provide around the groups.
518 * @return array Nested array of all of the grouped opcodes.
519 */
520 public function getGroupedOpcodes($context=3)
521 {
522 $opCodes = $this->getOpCodes();
523 if(empty($opCodes)) {
524 $opCodes = array(
525 array(
526 'equal',
527 0,
528 1,
529 0,
530 1
531 )
532 );
533 }
534
535 if($opCodes[0][0] == 'equal') {
536 $opCodes[0] = array(
537 $opCodes[0][0],
538 max($opCodes[0][1], $opCodes[0][2] - $context),
539 $opCodes[0][2],
540 max($opCodes[0][3], $opCodes[0][4] - $context),
541 $opCodes[0][4]
542 );
543 }
544
545 $lastItem = count($opCodes) - 1;
546 if($opCodes[$lastItem][0] == 'equal') {
547 list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem];
548 $opCodes[$lastItem] = array(
549 $tag,
550 $i1,
551 min($i2, $i1 + $context),
552 $j1,
553 min($j2, $j1 + $context)
554 );
555 }
556
557 $maxRange = $context * 2;
558 $groups = array();
559 $group = array();
560 foreach($opCodes as $code) {
561 list($tag, $i1, $i2, $j1, $j2) = $code;
562 if($tag == 'equal' && $i2 - $i1 > $maxRange) {
563 $group[] = array(
564 $tag,
565 $i1,
566 min($i2, $i1 + $context),
567 $j1,
568 min($j2, $j1 + $context)
569 );
570 $groups[] = $group;
571 $group = array();
572 $i1 = max($i1, $i2 - $context);
573 $j1 = max($j1, $j2 - $context);
574 }
575 $group[] = array(
576 $tag,
577 $i1,
578 $i2,
579 $j1,
580 $j2
581 );
582 }
583
584 if(!empty($group) && !(count($group) == 1 && $group[0][0] == 'equal')) {
585 $groups[] = $group;
586 }
587
588 return $groups;
589 }
590
591 /**
592 * Return a measure of the similarity between the two sequences.
593 * This will be a float value between 0 and 1.
594 *
595 * Out of all of the ratio calculation functions, this is the most
596 * expensive to call if getMatchingBlocks or getOpCodes is yet to be
597 * called. The other calculation methods (quickRatio and realquickRatio)
598 * can be used to perform quicker calculations but may be less accurate.
599 *
600 * The ratio is calculated as (2 * number of matches) / total number of
601 * elements in both sequences.
602 *
603 * @return float The calculated ratio.
604 */
605 public function Ratio()
606 {
607 $matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0);
608 return $this->calculateRatio($matches, count ($this->a) + count ($this->b));
609 }
610
611 /**
612 * Helper function to calculate the number of matches for Ratio().
613 *
614 * @param int $sum The running total for the number of matches.
615 * @param array $triple Array containing the matching block triple to add to the running total.
616 * @return int The new running total for the number of matches.
617 */
618 private function ratioReduce($sum, $triple)
619 {
620 return $sum + ($triple[count($triple) - 1]);
621 }
622
623 /**
624 * Quickly return an upper bound ratio for the similarity of the strings.
625 * This is quicker to compute than Ratio().
626 *
627 * @return float The calculated ratio.
628 */
629 private function quickRatio()
630 {
631 if($this->fullBCount === null) {
632 $this->fullBCount = array();
633 $bLength = count ($b);
634 for($i = 0; $i < $bLength; ++$i) {
635 $char = $this->b[$i];
636 $this->fullBCount[$char] = $this->arrayGetDefault($this->fullBCount, $char, 0) + 1;
637 }
638 }
639
640 $avail = array();
641 $matches = 0;
642 $aLength = count ($this->a);
643 for($i = 0; $i < $aLength; ++$i) {
644 $char = $this->a[$i];
645 if(isset($avail[$char])) {
646 $numb = $avail[$char];
647 }
648 else {
649 $numb = $this->arrayGetDefault($this->fullBCount, $char, 0);
650 }
651 $avail[$char] = $numb - 1;
652 if($numb > 0) {
653 ++$matches;
654 }
655 }
656
657 $this->calculateRatio($matches, count ($this->a) + count ($this->b));
658 }
659
660 /**
661 * Return an upper bound ratio really quickly for the similarity of the strings.
662 * This is quicker to compute than Ratio() and quickRatio().
663 *
664 * @return float The calculated ratio.
665 */
666 private function realquickRatio()
667 {
668 $aLength = count ($this->a);
669 $bLength = count ($this->b);
670
671 return $this->calculateRatio(min($aLength, $bLength), $aLength + $bLength);
672 }
673
674 /**
675 * Helper function for calculating the ratio to measure similarity for the strings.
676 * The ratio is defined as being 2 * (number of matches / total length)
677 *
678 * @param int $matches The number of matches in the two strings.
679 * @param int $length The length of the two strings.
680 * @return float The calculated ratio.
681 */
682 private function calculateRatio($matches, $length=0)
683 {
684 if($length) {
685 return 2 * ($matches / $length);
686 }
687 else {
688 return 1;
689 }
690 }
691
692 /**
693 * Helper function that provides the ability to return the value for a key
694 * in an array of it exists, or if it doesn't then return a default value.
695 * Essentially cleaner than doing a series of if(isset()) {} else {} calls.
696 *
697 * @param array $array The array to search.
698 * @param string $key The key to check that exists.
699 * @param mixed $default The value to return as the default value if the key doesn't exist.
700 * @return mixed The value from the array if the key exists or otherwise the default.
701 */
702 private function arrayGetDefault($array, $key, $default)
703 {
704 if(isset($array[$key])) {
705 return $array[$key];
706 }
707 else {
708 return $default;
709 }
710 }
711
712 /**
713 * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks
714 *
715 * @param array $a First array to compare.
716 * @param array $b Second array to compare.
717 * @return int -1, 0 or 1, as expected by the usort function.
718 */
719 private function tupleSort($a, $b)
720 {
721 $max = max(count($a), count($b));
722 for($i = 0; $i < $max; ++$i) {
723 if($a[$i] < $b[$i]) {
724 return -1;
725 }
726 else if($a[$i] > $b[$i]) {
727 return 1;
728 }
729 }
730
731 if(count($a) == $count($b)) {
732 return 0;
733 }
734 else if(count($a) < count($b)) {
735 return -1;
736 }
737 else {
738 return 1;
739 }
740 }
741 }
0 <?php
1 /**
2 * Diff
3 *
4 * A comprehensive library for generating differences between two strings
5 * in multiple formats (unified, side by side HTML etc)
6 *
7 * PHP version 5
8 *
9 * Copyright (c) 2009 Chris Boulton <chris.boulton@interspire.com>
10 *
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *
16 * - Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 * - Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 * - Neither the name of the Chris Boulton nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 * @package Diff
38 * @author Chris Boulton <chris.boulton@interspire.com>
39 * @copyright (c) 2009 Chris Boulton
40 * @license New BSD License http://www.opensource.org/licenses/bsd-license.php
41 * @version 1.1
42 * @link http://github.com/chrisboulton/php-diff
43 */
44
45 class Diff
46 {
47 /**
48 * @var array The "old" sequence to use as the basis for the comparison.
49 */
50 private $a = null;
51
52 /**
53 * @var array The "new" sequence to generate the changes for.
54 */
55 private $b = null;
56
57 /**
58 * @var array Array containing the generated opcodes for the differences between the two items.
59 */
60 private $groupedCodes = null;
61
62 /**
63 * @var array Associative array of the default options available for the diff class and their default value.
64 */
65 private $defaultOptions = array(
66 'context' => 3,
67 'ignoreNewLines' => false,
68 'ignoreWhitespace' => false,
69 'ignoreCase' => false
70 );
71
72 /**
73 * @var array Array of the options that have been applied for generating the diff.
74 */
75 private $options = array();
76
77 /**
78 * The constructor.
79 *
80 * @param array $a Array containing the lines of the first string to compare.
81 * @param array $b Array containing the lines for the second string to compare.
82 */
83 public function __construct($a, $b, $options=array())
84 {
85 $this->a = $a;
86 $this->b = $b;
87
88 if (is_array($options))
89 $this->options = array_merge($this->defaultOptions, $options);
90 else
91 $this->options = $this->defaultOptions;
92 }
93
94 /**
95 * Render a diff using the supplied rendering class and return it.
96 *
97 * @param object $renderer An instance of the rendering object to use for generating the diff.
98 * @return mixed The generated diff. Exact return value depends on the rendered.
99 */
100 public function render(Diff_Renderer_Abstract $renderer)
101 {
102 $renderer->diff = $this;
103 return $renderer->render();
104 }
105
106 /**
107 * Get a range of lines from $start to $end from the first comparison string
108 * and return them as an array. If no values are supplied, the entire string
109 * is returned. It's also possible to specify just one line to return only
110 * that line.
111 *
112 * @param int $start The starting number.
113 * @param int $end The ending number. If not supplied, only the item in $start will be returned.
114 * @return array Array of all of the lines between the specified range.
115 */
116 public function getA($start=0, $end=null)
117 {
118 if($start == 0 && $end === null) {
119 return $this->a;
120 }
121
122 if($end === null) {
123 $length = 1;
124 }
125 else {
126 $length = $end - $start;
127 }
128
129 return array_slice($this->a, $start, $length);
130
131 }
132
133 /**
134 * Get a range of lines from $start to $end from the second comparison string
135 * and return them as an array. If no values are supplied, the entire string
136 * is returned. It's also possible to specify just one line to return only
137 * that line.
138 *
139 * @param int $start The starting number.
140 * @param int $end The ending number. If not supplied, only the item in $start will be returned.
141 * @return array Array of all of the lines between the specified range.
142 */
143 public function getB($start=0, $end=null)
144 {
145 if($start == 0 && $end === null) {
146 return $this->b;
147 }
148
149 if($end === null) {
150 $length = 1;
151 }
152 else {
153 $length = $end - $start;
154 }
155
156 return array_slice($this->b, $start, $length);
157 }
158
159 /**
160 * Generate a list of the compiled and grouped opcodes for the differences between the
161 * two strings. Generally called by the renderer, this class instantiates the sequence
162 * matcher and performs the actual diff generation and return an array of the opcodes
163 * for it. Once generated, the results are cached in the diff class instance.
164 *
165 * @return array Array of the grouped opcodes for the generated diff.
166 */
167 public function getGroupedOpcodes()
168 {
169 if(!is_null($this->groupedCodes)) {
170 return $this->groupedCodes;
171 }
172
173 require_once dirname(__FILE__).'/Diff/SequenceMatcher.php';
174 $sequenceMatcher = new Diff_SequenceMatcher($this->a, $this->b, null, $this->options);
175 $this->groupedCodes = $sequenceMatcher->getGroupedOpcodes($this->options['context']);
176 return $this->groupedCodes;
177 }
178 }
0 Name: Businessprocess
1 Version: 2.1.0
2 Depends: monitoring>=2.4.1
3 Description: A Business Process viewer and modeler
4 Provides a web-based process modeler for. It integrates as a module into
5 Icinga Web 2 and provides a plugin check command for Icinga. Tile and tree
6 views can be shown inline, as dashlets or fullscreen. All of those for whole
7 processes or just parts of them.
8
9 Hooks into the monitoring module to show Business Impact for a specific host
10 or service and provides a Business Impact Simulation mode to visualize the
11 influence of a potential outage.
12
13 Supports legacy BPaddon config files.
0 Building Debian packages
1 ========================
2
3 This is work in progress, please expect build instructions to change any time
4 soon. Currently, to build custom Debian or Ubuntu packages, please proceed as
5 follows:
6
7 ```sh
8 apt-get install --no-install-recommends \
9 debhelper devscripts build-essential fakeroot libparse-debcontrol-perl
10 # Eventually adjust debian/changelog
11 cp -a packaging/debian debian
12 dpkg-buildpackage -us -uc
13 rm -rf debian
14 ```
15
16 Please move to your parent directory (`cd ..`) to find your new Debian packages.
17
0 icingaweb2-module-businessprocesss (2.0.0-rc1) stable; urgency=low
1
2 * First packaged release
3
4 -- Thomas Gelf <thomas@gelf.net> Fri, 09 Jan 2016 10:37:31 +0100
5
0 Source: icingaweb2-module-businessprocesss
1 Section: admin
2 Maintainer: Icinga Development Team <info@icinga.com>
3 Priority: optional
4 Build-Depends: debhelper (>=9)
5 Standards-Version: 3.9.4
6 Homepage: https://www.icinga.com
7
8 Package: icingaweb2-module-businessprocess
9 Architecture: all
10 Depends: icingaweb2-common (>= 2.2.0), php-curl|php5-curl, ${misc:Depends}
11 Suggests: icingaweb2
12 Description: A businessprocess viewer and modeler
13 Supports legacy BPaddon config files
14
0 application usr/share/icingaweb2/modules/businessprocess
1 doc usr/share/icingaweb2/modules/businessprocess
2 library usr/share/icingaweb2/modules/businessprocess
3 public usr/share/icingaweb2/modules/businessprocess
4 test usr/share/icingaweb2/modules/businessprocess
5 run.php usr/share/icingaweb2/modules/businessprocess
6 configuration.php usr/share/icingaweb2/modules/businessprocess
7 module.info usr/share/icingaweb2/modules/businessprocess
8 phpunit.xml usr/share/icingaweb2/modules/businessprocess
9 README.md usr/share/icingaweb2/modules/businessprocess
0 #!/usr/bin/make -f
1 #export DH_VERBOSE=1
2
3 %:
4 dh $@
5
6 clean:
7 dh_testdir
8 dh_clean
9
10 build:
11 dh_testdir
12
13 binary:
14 dh_testroot
15 dh_prep
16 dh_installdirs
17 dh_install
18 dh_installchangelogs
19 dh_installinfo
20 dh_installinit
21 dh_fixperms
22 dh_installdeb
23 dh_gencontrol
24 dh_md5sums
25 dh_builddeb
0 <?xml version="1.0" encoding="UTF-8"?>
1 <phpunit backupGlobals="false"
2 backupStaticAttributes="false"
3 colors="true"
4 convertErrorsToExceptions="true"
5 convertNoticesToExceptions="true"
6 convertWarningsToExceptions="true"
7 processIsolation="false"
8 stopOnFailure="false"
9 syntaxCheck="false"
10 bootstrap="test/bootstrap.php"
11 >
12 <testsuites>
13 <testsuite name="Businessprocess PHP Unit tests">
14 <directory suffix=".php">test/php</directory>
15 </testsuite>
16 </testsuites>
17 <filter>
18 <whitelist processUncoveredFilesFromWhitelist="true">
19 <directory suffix=".php">library/Businessprocess</directory>
20 <exclude>
21 <directory suffix=".php">library/Businessprocess/Director</directory>
22 </exclude>
23 <exclude>
24 <directory suffix=".php">library/Businessprocess/ProvidedHook</directory>
25 </exclude>
26 </whitelist>
27 </filter>
28 </phpunit>
0 a:focus {
1 outline: none;
2 text-decoration: underline;
3 &::before {
4 text-decoration: none;
5 }
6 }
7
8 .action-bar a {
9 color: @icinga-blue;
10 &:hover::before {
11 text-decoration: none;
12 }
13 margin-right: 1em;
14 }
15
16 form a {
17 color: @icinga-blue;
18 }
19
20 div.bp {
21 margin-bottom: 4px;
22 }
23
24 .simulation div.bp {
25 border-right: 1em solid @colorCriticalHandled;
26 padding-right: 1em;
27 background: white;
28 }
29
30 table.bp {
31 /* Business process table styling starts here */
32 width: 100%;
33 margin: 0;
34 padding: 0;
35 color: @text-color;
36 border-collapse: collapse;
37 border-spacing: 0;
38 box-sizing: border-box;
39 font-size: 1em;
40 font-weight: normal;
41
42 /* Reset all paddings and margins, just to be on the safe side */
43 th, td {
44 padding: 0;
45 margin: 0;
46 }
47
48 /* Left outer margin on nested BPs */
49 table.bp {
50
51 width: 99.6%;
52 margin-left: .4%;
53 margin-top: 4px;
54
55 }
56
57 .time-since {
58 display: none;
59 }
60 }
61
62 table.bp th {
63 font-weight: bold;
64 }
65
66 /* END of font settings */
67
68 /* No focus outline on our links, look ugly */
69 table.bp a:focus {
70 outline: none;
71 }
72
73 /* No link underlining */
74 table.bp a, table.bp a:hover {
75 text-decoration: none;
76 }
77
78 /* White font for all hovered objects */
79 table.bp.hovered {
80 color: white;
81
82 > tbody > tr > td > a > .time-since {
83 display: inline;
84 }
85 }
86
87 table.bp.handled.hovered {
88 color: #0a0a0a;
89 }
90
91 table.bp a {
92 color: inherit;
93 }
94
95 /* Show a pointer when hovering th, highlighting is JS-triggered */
96 table.bp tr th {
97 cursor: pointer;
98 }
99
100 /* Expand / collapse styling */
101 table.bp.process {
102
103 position: relative;
104
105 > tbody > tr:first-child > td:before {
106 content: '\e81d';
107 font-family: ifont;
108 position: absolute;
109 font-size: 1.5em;
110 margin-left: -0.8em;
111 -webkit-transition: -webkit-transform 0.3s;
112 -moz-transition: -moz-transform 0.3s;
113 -o-transition: -o-transform 0.3s;
114 transition: transform 0.3s;
115 }
116
117 &.collapsed {
118
119 > tbody > tr:first-child > td:before {
120 -moz-transform:rotate(-90deg);
121 -ms-transform:rotate(-90deg);
122 -o-transform:rotate(-90deg);
123 -webkit-transform:rotate(-90deg);
124 transform:rotate(-90deg);
125 }
126
127 table.bp, th span {
128 display: none;
129 }
130 }
131 }
132
133 table.bp th > a, table.bp td > a, table.bp td > span {
134 display: block;
135 text-decoration: none;
136 }
137
138 table.bp span.op {
139 width: 1.5em;
140 min-height: 1.5em;
141 margin-top: 1em;
142 display: block;
143 line-height: 2em;
144 -moz-transform: rotate(-90deg);
145 -ms-transform: rotate(-90deg);
146 -o-transform: rotate(-90deg);
147 -webkit-transform: rotate(-90deg);
148 transform: rotate(-90deg);
149 }
150
151 table.bp .icon {
152 float: left;
153 margin-top: 0.4em;
154 margin-right: 0.4em;
155 }
156
157 table.bp.node {
158 td:before {
159 font-family: ifont;
160 z-index: 1;
161 font-size: 1.25em;
162 position: absolute;
163 margin-left: 1.25em;
164 margin-top: 0.25em;
165 }
166 }
167
168 table.bp.node.subtree td:before {
169 content: '\e80e';
170 }
171
172 table.bp.node.service td:before {
173 content: '\e840';
174 }
175
176 table.bp.node.host td:before {
177 content: '\e866';
178 }
179
180 /* Border defaults */
181 table.bp {
182 border-width: 0;
183 border-style: solid;
184 border-color: transparent;
185 }
186
187 table.bp tr, table.bp tbody, table.bp th, table.bp td, table.bp.node td > a, table.node.missing td > span {
188 border-width: 0;
189 border-style: inherit;
190 border-color: inherit;
191 }
192
193 table.bp td > a, table.node.missing td > span {
194 height: 2.5em;
195 line-height: 2.5em;
196 padding-left: 0.5em;
197 display: block;
198 }
199
200 table.bp.node td > a:last-child, table.node.missing td > span {
201 padding-left: 2.5em;
202 background-repeat: no-repeat;
203 background-position: 0.5em 0.5em;
204 border-left-width: 0.8em;
205 }
206
207 table.bp.node.handled td > a:last-child, table.bp.node.ok td > a:last-child,
208 table.node.missing td > span, table.bp.node.up td > a:last-child
209 {
210 border-left-width: 0.3em;
211 background-position: 1em 0.5em;
212 padding-left: 3em;
213 }
214
215 table.bp th {
216 border-left-width: 0.8em;
217 width: 1.8em;
218 }
219
220 table.process.missing th span {
221 display: none;
222 }
223
224 table.bp.handled > tbody > tr > th, table.bp.ok > tbody > tr > th {
225 border-left-width: 0.3em;
226 width: 2em;
227 }
228
229 /* Operator: upper line */
230 table.bp.operator > tbody > tr:first-child > * {
231 border-top-width: 1px;
232 border-top-style: solid;
233 }
234
235 table.bp.operator.hovered > tbody > tr:first-child > * {
236 border-top-style: solid;
237 }
238
239 /* Set colors based on element state */
240 table.bp {
241 &.ok { border-color: @colorOk; }
242 &.up { border-color: @colorOk; }
243 &.warning { border-color: @colorWarning; }
244 &.warning.handled { border-color: @colorWarningHandled; }
245 &.critical { border-color: @colorCritical; }
246 &.critical.handled { border-color: @colorCriticalHandled; }
247 &.down { border-color: @colorCritical; }
248 &.down.handled { border-color: @colorCriticalHandled; }
249 &.unknown { border-color: @colorUnknown; }
250 &.unknown.handled { border-color: @colorUnknownHandled; }
251 &.unreachable { border-color: @colorUnknown; }
252 &.unreachable.handled { border-color: @colorUnknownHandled; }
253 &.pending { border-color: @colorPending; }
254 &.missing { border-color: #ccc; }
255 &.hovered {
256 &.ok > tbody > tr > {
257 th, td > a { background-color: @colorOk; }
258 }
259 &.up > tbody > tr > {
260 th, td > a { background-color: @colorOk; }
261 }
262 &.warning > tbody > tr > {
263 th, td > a { background-color: @colorWarning; }
264 }
265 &.warning.handled > tbody > tr {
266 > th, > td > a { background-color: @colorWarningHandled; }
267 }
268 &.critical > tbody > tr > {
269 th, td > a { background-color: @colorCritical; }
270 }
271 &.critical.handled > tbody > tr > {
272 th, td > a { background-color: @colorCriticalHandled; }
273 }
274 &.down > tbody > tr > {
275 th, td > a { background-color: @colorCritical; }
276 }
277 &.down.handled > tbody > tr > {
278 th, td > a { background-color: @colorCriticalHandled; }
279 }
280 &.unknown > tbody > tr > {
281 th, td > a { background-color: @colorUnknown; }
282 }
283 &.unknown.handled > tbody > tr > {
284 th, td > a { background-color: @colorUnknownHandled; }
285 }
286 &.unreachable > tbody > tr > {
287 th, td > a { background-color: @colorUnknown; }
288 }
289 &.unreachable.handled > tbody > tr > {
290 th, td > a { background-color: @colorUnreachableHandled; }
291 }
292 &.pending > tbody > tr > {
293 th, td > a { background-color: @colorPending; }
294 }
295 &.missing > tbody > tr > {
296 th, td > a, td > span { background-color: #ccc; }
297 }
298 }
299 }
300
301 /* Reduce font size after the 3rd level... */
302 table.bp table.bp table.bp table.bp {
303 font-size: 0.95em;
304 }
305
306 /* ...and keep it constant afterwards */
307 table.bp table.bp table.bp table.bp table.bp {
308 font-size: 1em;
309 }
310 /* Transitions */
311 div.knightrider table.bp {
312
313 // That's ugly, I know
314 .transition(@val1, @val2) {
315 transition: @val1, @val2;
316 -moz-transition: @val1, @val2;
317 -o-transition: @val1, @val2;
318 -webkit-transition: @val1, @val2;
319 }
320 > tbody > tr > td > a:last-child, > tbody > tr > td > span, > tbody > tr > th {
321 // Fade out
322 .transition(color 0.5s 0.1s step-start,
323 background-color 0.5s 0.1s ease-out
324 );
325 }
326
327 &.hovered > tbody > tr {
328 > td > a:last-child, > td > span, > th {
329 // Fade in
330 .transition(color 0.0s 0.0s step-start,
331 background-color 0.0s 0.0s ease-out
332 );
333 }
334 }
335 }
336
337 /** BEGIN Dashboard **/
338 .overview-dashboard {
339 > div.action {
340 width: 20em;
341 display: inline-block;
342 vertical-align: top;
343
344 > a {
345 text-decoration: none;
346 color: inherit;
347 vertical-align: middle;
348 display: block;
349 padding: 1em;
350 word-wrap: break-word;
351 width: 100%;
352 height: 12em;
353 overflow: hidden;
354 box-sizing: border-box;
355
356 > span.header {
357 font-weight: bold;
358 display: block;
359 font-size: 1.25em;
360 }
361
362 &:hover {
363 background-color: @text-color;
364 color: white;
365 }
366
367 &.addnew:hover {
368 background-color: @icinga-blue;
369 }
370
371 i {
372 float: left;
373 font-size: 2.5em;
374 margin-top: -0.1em;
375 margin-bottom: 2em;
376 color: inherit;
377 }
378 }
379 }
380 }
381 /** END Dashboard **/
382
383 /** BEGIN Badges **/
384 .badges {
385 display: block;
386 padding: 0.5em;
387
388 .badge {
389 border: 1px solid white;
390 margin: 0;
391 margin-right: 1px;
392 }
393 }
394
395 div.bp .badges {
396 display: inline-block;
397 padding-top: 0;
398 }
399
400 .badge-critical, .badge-down { background: @colorCritical; }
401 .badge-unknown, .badge-unreachable { background: @colorUnknown; }
402 .badge-warning { background: @colorWarning; }
403 .badge-pending { background: @colorPending; }
404 .badge-missing { background: #ccc; }
405 /** END Badges **/
406
407 /** BEGIN Tiles **/
408 .tiles:after {
409 content:'';
410 display:block;
411 clear: both;
412 }
413
414 .tiles > div {
415 color: white;
416 width: 12em;
417 display: inline-block;
418 float: left;
419 vertical-align: top;
420 margin-right: 0.2em;
421 margin-bottom: 0.2em;
422 height: 6em;
423 cursor: pointer;
424
425 .badges {
426 text-align: center;
427 font-size: 0.5em;
428 display: block;
429 }
430
431 > a {
432 display: block;
433 }
434
435 &:hover {
436 box-shadow: 0px 0px 5px #666;
437 }
438
439 .actions {
440 font-size: 0.75em;
441 margin-left: 0.5em;
442 padding-top: 0.2em;
443 height: 1.8em;
444
445 i {
446 float: none;
447 display: block;
448 width: 100%;
449 font-size: 1em;
450 line-height: normal;
451 margin: 0;
452 padding: 0 0 0 0.25em;
453 }
454 a {
455 margin: 0;
456 padding: 0;
457 display: inline-block;
458 width: 1.5em;
459 height: 1.5em;
460 border-radius: 0.3em;
461 }
462
463 a:hover {
464 background-color: white;
465 color: @text-color;
466 }
467 }
468 }
469
470
471 .tiles > div.parent::before {
472 content: '&';
473 position: absolute;
474 font-size: 1.2em;
475 }
476
477 .tiles > div.parent {
478 width: 100%;
479 height: 2em;
480 }
481
482 .tiles > div > a {
483 text-decoration: none;
484 font-size: 0.5em;
485 color: inherit;
486 vertical-align: middle;
487 text-align: center;
488 padding: 1em;
489 font-weight: bold;
490 word-wrap: break-word;
491 width: 100%;
492 box-sizing: border-box;
493 }
494
495 .tiles {
496 > .critical { background-color: @colorCritical; background-color: @colorCritical; color: white; }
497 > .critical.handled { background-color: @colorCriticalHandled; }
498 > .down { background-color: @colorCritical; }
499 > .down.handled { background-color: @colorCriticalHandled; }
500 > .unknown { background-color: @colorUnknown; }
501 > .unknown.handled { background-color: @colorUnknownHandled; }
502 > .unreachable { background-color: @colorUnknown; }
503 > .unreachable.handled { background-color: @colorUnknownHandled; }
504 > .warning { background-color: @colorWarning; }
505 > .warning.handled { background-color: @colorWarningHandled; }
506 > .ok { background-color: @colorOk; }
507 > .up { background-color: @colorOk; }
508 > .pending { background-color: @colorPending; }
509 > .missing { background-color: #ccc; }
510 > .addnew { background-color: @icinga-blue; }
511 }
512
513 /*
514 .tiles > div:hover {
515 > .critical { background: white; color: @text-color; }
516 > .critical.handled { background: @colorCriticalHandled; }
517 > .down { background: @colorCritical; }
518 > .down.handled { background: @colorCriticalHandled; }
519 > .unknown { background: @colorUnknown; }
520 > .unknown.handled { background: @colorUnknownHandled; }
521 > .unreachable { background: @colorUnknown; }
522 > .unreachable.handled { background: @colorUnknownHandled; }
523 > .warning { background: @colorWarning; }
524 > .warning.handled { background: @colorWarningHandled; }
525 > .ok { background: @colorOk; }
526 > .pending { background: @colorPending; }
527 > .missing { background: #ccc; }
528 }
529 */
530 /*
531 .tiles .missing a {
532 pointer-events: none;
533 cursor: default;
534 }
535 */
536
537 .tiles.few { font-size: 2.5em; }
538 .tiles.normal { font-size: 2.1em; }
539 .tiles.many { font-size: 1.8em; }
540
541 #layout.twocols, #layout.layout-minimal, div.compact {
542 .tiles.few { font-size: 1.8em; }
543 .tiles.normal { font-size: 1.8em; }
544 .tiles.many { font-size: 1.8em; }
545 }
546
547 #layout.fullscreen-layout .controls {
548 padding: 0 1em;
549 }
550
551 /** END of tiles **/
552
553 /** BEGIN breadcrumb **/
554
555 .breadcrumb {
556 list-style: none;
557 overflow: hidden;
558 padding: 0;
559
560 .badges {
561 display: inline-block;
562 padding: 0 0 0 0.5em;
563 .badge {
564 line-height: 1.25em;
565 font-size: 0.8em;
566 border: 1px solid white;
567 margin: -0.25em 1px 0 0;
568 }
569 }
570 }
571
572 .breadcrumb {
573 > .critical a { background: @colorCritical; }
574 > .critical.handled a { background: @colorCriticalHandled; }
575 > .unknown a { background: @colorUnknown; }
576 > .unknown.handled a { background: @colorUnknownHandled; }
577 > .warning a { background: @colorWarning; }
578 > .warning.handled a { background: @colorWarningHandled; }
579 > .ok a { background: @colorOk; }
580 }
581
582 .breadcrumb {
583 > .critical a:after { border-left-color: @colorCritical; }
584 > .critical.handled a:after { border-left-color: @colorCriticalHandled; }
585 > .unknown a:after { border-left-color: @colorUnknown; }
586 > .unknown.handled a:after { border-left-color: @colorUnknownHandled; }
587 > .warning a:after { border-left-color: @colorWarning; }
588 > .warning.handled a:after { border-left-color: @colorWarningHandled; }
589 > .ok a:after { border-left-color: @colorOk; }
590 }
591
592 .breadcrumb:after {
593 content:'';
594 display:block;
595 clear: both;
596 }
597 .breadcrumb li {
598 float: left;
599 cursor: pointer;
600 user-select: none;
601 -webkit-user-select: none;
602 -moz-user-select: none;
603 -ms-user-select: none;
604
605 }
606 .breadcrumb li a {
607 color: white;
608 margin: 0;
609 font-size: 1.2em;
610 text-decoration: none;
611 padding-left: 2em;
612 line-height: 2.5em;
613 background: @icinga-blue;
614 position: relative;
615 display: block;
616 float: left;
617 &:focus {
618 outline: none;
619 }
620 }
621
622 .breadcrumb li a:before, .breadcrumb li a:after {
623 content: " ";
624 display: block;
625 width: 0;
626 height: 0;
627 border-top: 1.3em solid transparent;
628 border-bottom: 1.2em solid transparent;
629 position: absolute;
630 margin-top: -1.2em;
631 top: 50%;
632 left: 100%;
633 }
634
635 .breadcrumb li a:before {
636 border-left: 1.2em solid white;
637 margin-left: 1px;
638 z-index: 1;
639 }
640
641 .breadcrumb li a:after {
642 border-left: 1.2em solid @icinga-blue;
643 z-index: 2;
644 }
645
646 .breadcrumb li:first-child a {
647 padding-left: 1em;
648 padding-right: 0.5em;
649 }
650 .breadcrumb li:last-child a {
651 cursor: default;
652 }
653 .breadcrumb li:last-child a:hover {
654
655 }
656
657 .breadcrumb li:not(:last-child) a:hover { background: @text-color; color: white; }
658 .breadcrumb li:not(:last-child) a:hover:after { border-left-color: @text-color; }
659
660 .breadcrumb li a:focus {
661 text-decoration: underline;
662 }
663
664 #layout.twocols, #layout.layout-minimal, div.compact {
665 .breadcrumb {
666 font-size: 0.833em;
667 }
668 }
669
670 /** END of breadcrumb **/
671
672
673 ul.error, ul.warning {
674 padding: 0;
675 list-style-type: none;
676 background-color: @colorCritical;
677
678 li {
679 font-weight: bold;
680 color: white;
681 padding: 0.3em 0.8em;
682 }
683
684 li a {
685 color: inherit;
686 text-decoration: underline;
687
688 &:hover {
689 text-decoration: none;
690 }
691 }
692 }
693
694
695 ul.warning {
696 background-color: @colorWarning;
697 }
698
699 table.sourcecode {
700 font-family: monospace;
701 white-space: pre-wrap;
702
703 th {
704 vertical-align: top;
705 padding-right: 0.5em;
706 user-select: none;
707 -moz-user-select: none;
708 -o-user-select: none;
709 -ms-user-select: none;
710 -webkit-user-select: none;
711 font-weight: bold;
712 }
713 td {
714 vertical-align: top;
715 }
716 }
717
718 .diff {
719 font-family: monospace;
720 white-space: pre-wrap;
721
722 del, ins {
723 text-decoration: none;
724 }
725
726 del {
727 color: @colorCritical;
728 background-color: #fdd;
729 }
730
731 ins {
732 color: @colorOk;
733 background-color: #dfd;
734 }
735 }
736
737 /** Forms stolen from director **/
738 .content form {
739 margin-bottom: 2em;
740 }
741
742 .content form.inline {
743 margin: 0;
744 }
745
746 .invisible {
747 position: absolute;
748 left: -100%;
749 }
750
751 form input[type=file] {
752 background-color: white;
753 padding-right: 1em;
754 }
755
756
757 form input[type=submit] {
758 .button();
759 border-width: 1px;
760 margin-top: 0.5em;
761
762 &:disabled {
763 border-color: @gray-light;
764 background-color: @gray-light;
765 color: #fff;
766 }
767 }
768
769 form input[type=submit]:first-of-type {
770 border-width: 2px;
771 }
772
773 form input[type=submit].link-button {
774 color: @icinga-blue;
775 background: none;
776 border: none;
777 padding: 0;
778
779 text-align: left;
780
781 &:hover {
782 text-decoration: underline;
783 }
784 }
785
786 form p.description {
787 padding: 1em 1em;
788 margin: 0;
789 font-style: italic;
790 width: 100%;
791 }
792
793 input[type=text], input[type=button], select, select option, textarea {
794 -webkit-appearance: none;
795 -moz-appearance: none;
796 appearance: none;
797 }
798
799 form ul.form-errors {
800 margin-bottom: 0.5em;
801
802 ul.errors li {
803 background: @color-critical;
804 font-weight: bold;
805 padding: 0.5em 1em;
806 color: white;
807 }
808 }
809
810 select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */
811 display: none;
812 }
813
814 select {
815 border: 1px solid #ddd;
816 cursor: pointer;
817 background: none;
818 }
819
820 input[type=text], input[type=password], textarea, select {
821 max-width: 36em;
822 min-width: 20em;
823 width: 100%;
824 line-height: 2em;
825 height: 2.4em;
826 padding-left: 0.5em;
827 border-style: solid;
828 border-color: transparent;
829 border-bottom-color: @gray-lighter;
830 border-width: 1px 1px 1px 3px;
831 background-color: white;
832
833 &.search {
834 background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em;
835 padding-left: 2em;
836 }
837 }
838
839 select[multiple] {
840 height: auto;
841 }
842
843 select option {
844 height: 2em;
845 padding-top: 0.3em;
846 }
847
848 select[multiple=multiple] {
849 height: auto;
850 }
851
852 label {
853 line-height: 2em;
854 }
855
856 form dl {
857 margin: 0;
858 padding: 0;
859 }
860
861 select::-moz-focus-inner { border: 0; }
862
863 select:-moz-focusring {
864 color: transparent;
865 text-shadow: 0 0 0 #000;
866 }
867
868 select, input[type=text], textarea {
869 &:hover {
870 border-style: dotted solid dotted solid;
871 border-color: @gray-light;
872 }
873
874 &:focus, &:focus:hover {
875 border-style: solid;
876 border-color: @icinga-blue;
877 outline: none;
878 }
879 }
880
881 select[value=""] {
882 color: blue;
883 border: 1px solid #666;
884 background-color: white;
885 }
886
887 select option {
888 color: inherit;
889 padding-left: 0.5em;
890 background-color: white;
891 }
892
893 select option[value=""] {
894 color: #aaa;
895 background-color: white;
896 }
897
898 form dt label {
899 width: auto;
900 font-weight: normal;
901 font-size: inherit;
902
903 &.required {
904 &::after {
905 content: '*'
906 }
907 }
908
909 &:hover {
910 text-decoration: underline;
911 cursor: pointer;
912 }
913 }
914
915 form fieldset {
916 min-width: 36em;
917 }
918
919 form dd input.related-action[type='submit'] {
920 display: none;
921 }
922
923 form dd.active li.active input.related-action[type='submit'] {
924 display: inline-block;
925 }
926
927 form dd.active {
928 p.description {
929 color: inherit;
930 font-style: normal;
931 }
932 }
933
934 form dd {
935 padding: 0.3em 0.5em;
936 margin: 0;
937 }
938
939 form dt {
940 padding: 0.5em 0.5em;
941 margin: 0;
942 }
943
944 form dt.active, form dd.active {
945 background-color: @tr-active-color;
946 }
947
948 form dt {
949 display: inline-block;
950 vertical-align: top;
951 min-width: 12em;
952 min-height: 2.5em;
953 width: 30%;
954 &.errors label {
955 color: @color-critical;
956 }
957 }
958
959 form .errors label {
960 color: @color-critical;
961 }
962
963 form dd {
964 display: inline-block;
965 width: 63%;
966 min-height: 2.5em;
967 vertical-align: top;
968 margin: 0;
969 &.errors {
970 input[type=text], select {
971 border-color: @color-critical;
972 }
973 }
974
975 &.full-width {
976 padding: 0.5em;
977 width: 100%;
978 }
979 }
980
981 form dd:after {
982 display: block;
983 content: '';
984 }
985
986 form textarea {
987 height: auto;
988 }
989
990 form dd ul.errors {
991 list-style-type: none;
992 padding-left: 0.3em;
993
994 li {
995 color: @colorCritical;
996 padding: 0.3em;
997 }
998 }
999
1000 form div.hint {
1001 padding: 1em;
1002 background-color: @tr-hover-color;
1003 margin: 1em 0;
1004 max-width: 65em;
1005 font-size: 1em;
1006
1007 pre {
1008 font-style: normal;
1009 background-color: white;
1010 margin: 0;
1011 padding: 1em;
1012 }
1013 }
1014
1015
1016 form {
1017 #_FAKE_SUBMIT {
1018 position: absolute;
1019 left: -100%;
1020 }
1021 }
1022
1023 /** END of forms **/
1024
1025 /** php-diff **/
1026 .Differences {
1027 width: 100%;
1028 table-layout: fixed;
1029 empty-cells: show;
1030 }
1031
1032 .Differences thead {
1033 display: none;
1034 }
1035
1036 .Differences thead th {
1037 text-align: left;
1038 padding-left: 4 / 14 * 16em;
1039 }
1040 .Differences tbody th {
1041 text-align: right;
1042 width: 4em;
1043 padding: 1px 2px;
1044 border-right: 1px solid @gray-light;
1045 background: @gray-lightest;
1046 font-weight: normal;
1047 vertical-align: top;
1048 }
1049
1050 .Differences tbody td {
1051 width: 50%;
1052 .preformatted();
1053 word-break: break-all;
1054 }
1055
1056 @color-diff-ins: #bfb;
1057 @color-diff-del: #faa;
1058 @color-diff-changed-old: #fdd;
1059 @color-diff-changed-new: #efe;
1060
1061 .DifferencesSideBySide {
1062 ins, del {
1063 text-decoration: none;
1064 }
1065
1066 .ChangeInsert {
1067 td.Left {
1068 background: @gray-lighter;
1069 }
1070 td.Right {
1071 background: @color-diff-ins;
1072 }
1073 }
1074
1075 .ChangeDelete {
1076 td.Left {
1077 background: @color-diff-del;
1078 }
1079 td.Right {
1080 background: @gray-lighter;
1081 }
1082 }
1083
1084 .ChangeReplace {
1085 td.Left {
1086 background: @color-diff-changed-old;
1087 del {
1088 background: @color-diff-del;
1089 }
1090 }
1091
1092 td.Right {
1093 background: @color-diff-changed-new;
1094 ins {
1095 background: @color-diff-ins;
1096 }
1097 }
1098
1099 }
1100 }
1101
1102 .Differences .Skipped {
1103 background: @gray-lightest;
1104 }
1105
1106 .DifferencesInline .ChangeReplace .Left,
1107 .DifferencesInline .ChangeDelete .Left {
1108 background: #fdd;
1109 }
1110
1111 .DifferencesInline .ChangeReplace .Right,
1112 .DifferencesInline .ChangeInsert .Right {
1113 background: #dfd;
1114 }
1115
1116 .DifferencesInline .ChangeReplace ins {
1117 background: #9e9;
1118 }
1119
1120 .DifferencesInline .ChangeReplace del {
1121 background: #e99;
1122 }
1123 /** END of php-diff **/
1124
1125 /** Custom font styling **/
1126 textarea.smaller {
1127 font-size: 0.833em;
1128 max-width: 60em;
1129 }
1130 /** END of custom font styling **/
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0
1 (function(Icinga) {
2
3 var Bp = function(module) {
4 /**
5 * YES, we need Icinga
6 */
7 this.module = module;
8
9 this.idCache = {};
10
11 this.initialize();
12
13 this.module.icinga.logger.debug('BP module loaded');
14 };
15
16 Bp.prototype = {
17
18 initialize: function()
19 {
20 /**
21 * Tell Icinga about our event handlers
22 */
23 this.module.on('beforerender', this.rememberOpenedBps);
24 this.module.on('rendered', this.onRendered);
25
26 this.module.on('click', 'table.bp.process > tbody > tr:first-child > td > a:last-child', this.processTitleClick);
27 this.module.on('click', 'table.bp > tbody > tr:first-child > th', this.processOperatorClick);
28 this.module.on('focus', 'form input, form textarea, form select', this.formElementFocus);
29
30 this.module.on('mouseenter', 'table.bp > tbody > tr > td > a', this.procMouseOver);
31 this.module.on('mouseenter', 'table.bp > tbody > tr > th', this.procMouseOver);
32 this.module.on('mouseenter', 'table.node.missing > tbody > tr > td > span', this.procMouseOver);
33 this.module.on('mouseleave', 'div.bp', this.procMouseOut);
34
35 this.module.on('click', 'div.tiles > div', this.tileClick);
36
37 this.module.icinga.logger.debug('BP module initialized');
38 },
39
40 onRendered: function (event) {
41 var $container = $(event.currentTarget);
42 this.fixFullscreen($container);
43 this.fixOpenedBps($container);
44 this.highlightFormErrors($container);
45 this.hideInactiveFormDescriptions($container);
46 this.fixTileLinksOnDashboard($container);
47 },
48
49 processTitleClick: function (event) {
50 event.stopPropagation();
51 var $el = $(event.currentTarget).closest('table.bp');
52 $el.toggleClass('collapsed');
53 },
54
55 processOperatorClick: function (event) {
56 event.stopPropagation();
57 var $el = $(event.currentTarget).closest('table.bp');
58
59 // Click on arrow
60 $el.removeClass('collapsed');
61
62 var children = $el.find('> tbody > tr > td > table.bp.process');
63 if (children.length === 0) {
64 $el.toggleClass('collapsed');
65 return;
66 }
67 if (children.filter('.collapsed').length) {
68 children.removeClass('collapsed');
69 } else {
70 children.each(function(idx, el) {
71 var $el = $(el);
72 $el.addClass('collapsed');
73 $el.find('table.bp.process').addClass('collapsed');
74 });
75 }
76 },
77
78 hideInactiveFormDescriptions: function($container) {
79 $container.find('dd').not('.active').find('p.description').hide();
80 },
81
82 tileClick: function(event) {
83 $(event.currentTarget).find('> a').first().trigger('click');
84 },
85
86 /**
87 * Add 'hovered' class to hovered title elements
88 *
89 * TODO: Skip on tablets
90 */
91 procMouseOver: function (event) {
92 event.stopPropagation();
93 var $hovered = $(event.currentTarget);
94 var $el = $hovered.closest('table.bp');
95
96 if ($el.is('.operator')) {
97 if (!$hovered.closest('tr').is('tr:first-child')) {
98 // Skip hovered space between cols
99 return;
100 }
101 } else {
102 // return;
103 }
104
105 $('table.bp.hovered').not($el.parents('table.bp')).removeClass('hovered'); // not self & parents
106 $el.addClass('hovered');
107 $el.parents('table.bp').addClass('hovered');
108 },
109
110 /**
111 * Remove 'hovered' class from hovered title elements
112 *
113 * TODO: Skip on tablets
114 */
115 procMouseOut: function (event) {
116 $('table.bp.hovered').removeClass('hovered');
117 },
118
119 /**
120 * Handle clicks on operator or title element
121 *
122 * Title shows subelement, operator unfolds all subelements
123 */
124 titleClicked: function (event) {
125 var self = this;
126 event.stopPropagation();
127 event.preventDefault();
128 var $el = $(event.currentTarget),
129 affected = []
130 $container = $el.closest('.container');
131 if ($el.hasClass('operator')) {
132 $affected = $el.closest('table').children('tbody')
133 .children('tr.children').children('td').children('table');
134
135 // Only if there are child BPs
136 if ($affected.find('th.operator').length < 1) {
137 $affected = $el.closest('table');
138 }
139 } else {
140 $affected = $el.closest('table');
141 }
142 $affected.each(function (key, el) {
143 var $bptable = $(el).closest('table');
144 $bptable.toggleClass('collapsed');
145 if ($bptable.hasClass('collapsed')) {
146 $bptable.find('table').addClass('collapsed');
147 }
148 });
149
150 /*$container.data('refreshParams', {
151 opened: self.listOpenedBps($container)
152 });*/
153 },
154
155 fixTileLinksOnDashboard: function($container) {
156 if ($container.closest('div.dashboard').length) {
157 $container.find('div.tiles').data('baseTarget', '_next');
158 }
159 },
160
161 fixFullscreen: function($container) {
162 var $controls = $container.find('div.controls');
163 var $layout = $('#layout');
164 var icinga = this.module.icinga;
165 if ($controls.hasClass('want-fullscreen')) {
166 if (!$layout.hasClass('fullscreen-layout')) {
167
168 $layout.addClass('fullscreen-layout');
169 $controls.removeAttr('style');
170 $container.find('.fake-controls').remove();
171 icinga.ui.currentLayout = 'fullscreen';
172 }
173 } else {
174 if ($layout.hasClass('fullscreen-layout')) {
175 $layout.removeClass('fullscreen-layout');
176 icinga.ui.layoutHasBeenChanged();
177 icinga.ui.initializeControls($container);
178 }
179 }
180 },
181
182 fixOpenedBps: function($container) {
183 var $bpDiv = $container.find('div.bp');
184 var bpName = $bpDiv.attr('id');
185
186 if (typeof this.idCache[bpName] === 'undefined') {
187 return;
188 }
189 var $procs = $bpDiv.find('table.process');
190
191 $.each(this.idCache[bpName], function(idx, id) {
192 var $el = $('#' + id);
193 $procs = $procs.not($el);
194
195 $el.parents('table.process').each(function (idx, el) {
196 $procs = $procs.not($(el));
197 });
198 });
199
200 $procs.addClass('collapsed');
201 },
202
203 /**
204 * Get a list of all currently opened BPs.
205 *
206 * Only get the deepest nodes to keep requests as small as possible
207 */
208 rememberOpenedBps: function (event) {
209 var ids = [];
210 var $bpDiv = $(event.currentTarget).find('div.bp');
211 var $bpName = $bpDiv.attr('id');
212
213 $bpDiv.find('table.process')
214 .not('table.process.collapsed')
215 .not('table.process.collapsed table.process')
216 .each(function (key, el) {
217 ids.push($(el).attr('id'));
218 });
219 if (ids.length === 0) {
220 return;
221 }
222
223 this.idCache[$bpName] = ids;
224 },
225
226 /** BEGIN Form handling, borrowed from Director **/
227 formElementFocus: function(ev)
228 {
229 var $input = $(ev.currentTarget);
230 var $dd = $input.closest('dd');
231 $dd.find('p.description').show();
232 if ($dd.attr('id') && $dd.attr('id').match(/button/)) {
233 return;
234 }
235 var $li = $input.closest('li');
236 var $dt = $dd.prev();
237 var $form = $dd.closest('form');
238
239 $form.find('dt, dd, li').removeClass('active');
240 $li.addClass('active');
241 $dt.addClass('active');
242 $dd.addClass('active');
243 $dd.find('p.description.fading-out')
244 .stop(true)
245 .removeClass('fading-out')
246 .fadeIn('fast');
247
248 $form.find('dd').not($dd)
249 .find('p.description')
250 .not('.fading-out')
251 .addClass('fading-out')
252 .delay(2000)
253 .fadeOut('slow', function() {
254 $(this).removeClass('fading-out').hide()
255 });
256 },
257
258 highlightFormErrors: function($container)
259 {
260 $container.find('dd ul.errors').each(function(idx, ul) {
261 var $ul = $(ul);
262 var $dd = $ul.closest('dd');
263 var $dt = $dd.prev();
264
265 $dt.addClass('errors');
266 $dd.addClass('errors');
267 });
268 }
269 /** END Form handling **/
270 };
271
272 Icinga.availableModules.businessprocess = Bp;
273
274 }(Icinga));
275
0 <?php
1
2 $this->provideHook('monitoring/HostActions');
3 $this->provideHook('monitoring/ServiceActions');
4 //$this->provideHook('director/shipConfigFiles');
0 <?php
1
2 use Icinga\Module\Businessprocess\Test\Bootstrap;
3
4 call_user_func(function () {
5 $basedir = dirname(__DIR__);
6 if (! class_exists('PHPUnit_Framework_TestCase')) {
7 require_once __DIR__ . '/phpunit-compat.php';
8 }
9 require_once $basedir . '/library/Businessprocess/Test/Bootstrap.php';
10 Bootstrap::cli($basedir);
11 });
(New empty file)
0 hostsAnd = host1;Hoststatus + host2;Hoststatus
0 all = @simple_with-header:top & @simple_without-header:minTwo & @simple_with-header:minTwo
0 ############################################
1 #
2 # Title: Simple with header
3 #
4 ############################################
5
6 hostsAnd = host1;Hoststatus & host2;Hoststatus
7 servicesOr = host1;ping | host2;ping | host3;ping
8 singleHost = host1;Hoststatus
9 minTwo = 2 of: hostsAnd + servicesOr + singleHost
10 top = minTwo & hostsAnd & servicesOr
11 display 1;top;Top Node
12 info_url top;https://top.example.com/
0 hostsAnd = host1;Hoststatus & host2;Hoststatus
1 servicesOr = host1;ping | host2;ping | host3;ping
2 singleHost = host1;Hoststatus
3 minTwo = 2 of: hostsAnd + servicesOr + singleHost
4 top = minTwo & hostsAnd & servicesOr
5 display 1;top;Top Node
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\BpNode;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class BpNodeTest extends BaseTestCase
8 {
9 /**
10 * @expectedException \Icinga\Module\Businessprocess\Exception\NestingError
11 */
12 public function testThrowsNestingErrorWhenCheckedForLoops()
13 {
14 /** @var BpNode $bpNode */
15 $bpNode = $this->makeLoop()->getNode('d');
16 $bpNode->checkForLoops();
17 }
18
19 /**
20 * @expectedExceptionMessage d -> a -> b -> c -> a
21 * @expectedException \Icinga\Module\Businessprocess\Exception\NestingError
22 */
23 public function testNestingErrorReportsFullLoop()
24 {
25 /** @var BpNode $bpNode */
26 $bpNode = $this->makeLoop()->getNode('d');
27 $bpNode->checkForLoops();
28 }
29
30 public function testStateForALoopGivesUnknown()
31 {
32 $loop = $this->makeLoop();
33 /** @var BpNode $bpNode */
34 $bpNode = $loop->getNode('d');
35 $this->assertEquals(
36 'UNKNOWN',
37 $bpNode->getStateName()
38 );
39 }
40 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\HostNode;
6 use Icinga\Module\Businessprocess\Test\BaseTestCase;
7
8 class HostNodeTest extends BaseTestCase
9 {
10 public function testReturnsCorrectHostName()
11 {
12 $this->assertEquals(
13 'localhost',
14 $this->localhost()->getHostname()
15 );
16 }
17
18 public function testReturnsCorrectIdentifierWhenCastedToString()
19 {
20 $this->assertEquals(
21 'localhost;Hoststatus',
22 (string) $this->localhost()
23 );
24 }
25
26 public function testReturnsCorrectAlias()
27 {
28 $this->assertEquals(
29 'localhost',
30 $this->localhost()->getAlias()
31 );
32 }
33
34 public function testRendersCorrectLink()
35 {
36 $this->assertEquals(
37 '<a href="/icingaweb2/monitoring/host/show?host=localhost">'
38 . 'localhost</a>',
39 $this->localhost()->getLink()->render()
40 );
41 }
42
43 /**
44 * @expectedException \Icinga\Exception\ProgrammingError
45 */
46 public function testSettingAnInvalidStateFails()
47 {
48 $bp = new BpConfig();
49 $host = $bp->createHost('localhost')->setState(98);
50 $bp->createBp('p')->addChild($host)->getState();
51 }
52
53 /**
54 * @return HostNode
55 */
56 protected function localhost()
57 {
58 $bp = new BpConfig();
59 return new HostNode($bp, (object) array(
60 'hostname' => 'localhost',
61 'state' => 0,
62 ));
63 }
64 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\Attribute;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class AttributeTest extends BaseTestCase
8 {
9 public function testCreatesFactoryCreatesAttribute()
10 {
11 $this->assertInstanceOf(
12 get_class(new Attribute('a', 'b')),
13 Attribute::create('a', 'b')
14 );
15 }
16
17 public function testKnowsItsName()
18 {
19 $a = new Attribute('target', '_blank');
20 $this->assertEquals(
21 'target',
22 $a->getName()
23 );
24 }
25
26 public function testKnowsItsValue()
27 {
28 $a = new Attribute('target', '_blank');
29 $this->assertEquals(
30 '_blank',
31 $a->getValue()
32 );
33 }
34
35 public function testItsValueCanBeModified()
36 {
37 $a = new Attribute('target', '_blank');
38 $a->setValue('_self');
39 $this->assertEquals(
40 '_self',
41 $a->getValue()
42 );
43 }
44
45 public function testPreservesComplexValues()
46 {
47 $a = new Attribute('special', 'süß "\'&');
48 $this->assertEquals(
49 'süß "\'&',
50 $a->getValue()
51 );
52 }
53
54 public function testAllowsToExtendItsValue()
55 {
56 $a = new Attribute('class', 'first');
57 $a->addValue('second');
58
59 $this->assertEquals(
60 array('first', 'second'),
61 $a->getValue()
62 );
63
64 $a->addValue('third');
65
66 $this->assertEquals(
67 array('first', 'second', 'third'),
68 $a->getValue()
69 );
70
71 $a->addValue(array('some', 'more'));
72
73 $this->assertEquals(
74 array('first', 'second', 'third', 'some', 'more'),
75 $a->getValue()
76 );
77 }
78
79 public function testAcceptsAndReturnsArrayValues()
80 {
81 $value = array('first', 'second', 'third');
82 $a = new Attribute('class', $value);
83
84 $this->assertEquals(
85 $value,
86 $a->getValue()
87 );
88
89 $value[] = 'forth';
90
91 $a->setValue($value);
92 $this->assertEquals(
93 $value,
94 $a->getValue()
95 );
96 }
97
98 public function testCorrectlyRendersItsName()
99 {
100 $a = new Attribute('class', 'test');
101 $this->assertEquals(
102 'class',
103 $a->renderName()
104 );
105 }
106
107 public function testCorrectlyRendersItsValue()
108 {
109 $a = new Attribute('href', '/some/url?a=b&c=d');
110 $this->assertEquals(
111 '/some/url?a=b&amp;c=d',
112 $a->renderValue()
113 );
114 }
115
116 public function testCorrectlyRendersArrayValues()
117 {
118 $a = new Attribute('weird', array('"sü?ß', '/some/url?a=b&c=d'));
119 $this->assertEquals(
120 '&quot;sü?ß /some/url?a=b&amp;c=d',
121 $a->renderValue()
122 );
123 }
124
125 public function testCorrectlyEscapesAName()
126 {
127 $this->assertEquals(
128 'class',
129 Attribute::escapeName('class')
130 );
131 }
132
133 public function testCorrectlyEscapesAValue()
134 {
135 $this->assertEquals(
136 "&quot;sü?ß' /some/url?a=b&amp;c=d",
137 Attribute::escapeValue('"sü?ß\' /some/url?a=b&c=d')
138 );
139 }
140
141 public function testRendersCorrectly()
142 {
143 $a = new Attribute('weird', array('"sü?ß', '/some/url?a=b&c=d'));
144 $this->assertEquals(
145 'weird="&quot;sü?ß /some/url?a=b&amp;c=d"',
146 $a->render()
147 );
148 }
149 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\Attribute;
5 use Icinga\Module\Businessprocess\Html\Attributes;
6 use Icinga\Module\Businessprocess\Test\BaseTestCase;
7
8 class AttributesTest extends BaseTestCase
9 {
10 public function testCanBeConstructedFromANormalArray()
11 {
12 $a = new Attributes(
13 array(
14 'class' => array('small', 'nice'),
15 'target' => '_blank'
16 )
17 );
18
19 $this->assertEquals(
20 ' class="small nice" target="_blank"',
21 $a->render()
22 );
23 }
24
25 public function testCanBeInstantiatedThroughCreate()
26 {
27 $class = get_class(new Attributes());
28
29 $this->assertInstanceOf(
30 $class,
31 Attributes::create()
32 );
33
34 $this->assertInstanceOf(
35 $class,
36 Attributes::create(array('some' => 'attr'))
37 );
38 }
39
40 public function testCanBeCreatedForArrayOrNullOrAttributes()
41 {
42 $empty = Attributes::wantAttributes(null);
43 $this->assertEquals('', $empty->render());
44
45 $array = Attributes::wantAttributes(array('a' => 'b'));
46 $this->assertEquals(' a="b"', $array->render());
47
48 $attributes = Attributes::wantAttributes(
49 Attributes::create(array('a' => 'b'))
50 );
51 $this->assertEquals(' a="b"', $attributes->render());
52 }
53
54 public function testCanBeExtendedWithAnAttribute()
55 {
56 $a = Attributes::create();
57 $a->add(Attribute::create('a', 'b'));
58 $this->assertEquals(' a="b"', $a->render());
59
60 $a->add(Attribute::create('c', 'd'));
61 $this->assertEquals(' a="b" c="d"', $a->render());
62
63 $a->add(Attribute::create('a', 'c'));
64 $this->assertEquals(' a="b c" c="d"', $a->render());
65 }
66
67 public function testCanBeExtendedWithAttributes()
68 {
69 $a = Attributes::create();
70 $a->add(Attributes::create(array('a' => 'b')));
71 $this->assertEquals(' a="b"', $a->render());
72
73 $a->add(Attributes::create(
74 array(
75 'a' => 'c',
76 'c' => 'd'
77 )
78 ));
79 $this->assertEquals(' a="b c" c="d"', $a->render());
80 }
81
82 public function testCanBeExtendedWithANameValuePair()
83 {
84 $a = Attributes::create();
85 $a->add('a', 'b');
86 $this->assertEquals(' a="b"', $a->render());
87 }
88
89 public function testAttributesCanBeReplacedWithAnAttribute()
90 {
91 $a = Attributes::create();
92 $a->add(Attribute::create('a', 'b'));
93 $a->set(Attribute::create('a', 'c'));
94 $this->assertEquals(' a="c"', $a->render());
95 }
96
97 public function testAttributesCanBeReplacedWithANameValuePair()
98 {
99 $a = Attributes::create();
100 $a->add(Attribute::create('a', 'b'));
101 $a->set('a', 'c');
102 $this->assertEquals(' a="c"', $a->render());
103 }
104
105 public function testCanBeConstructedAndRenderedEmpty()
106 {
107 $a = new Attributes();
108 $this->assertEquals('', $a->render());
109 }
110 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\HtmlTag;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class HtmlTagTest extends BaseTestCase
8 {
9 public function testHeaderIsRendered()
10 {
11 $h1 = HtmlTag::h1('Hea & der');
12 $this->assertEquals(
13 $h1->render(),
14 '<h1>Hea &amp; der</h1>'
15 );
16 }
17 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\Html;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class HtmlTest extends BaseTestCase
8 {
9 public function testContentIsRendered()
10 {
11 $h = new Html();
12 $h->setContent('Some content');
13 $this->assertEquals(
14 'Some content',
15 $h->render()
16 );
17 }
18
19 public function testContentCanBeExtended()
20 {
21 $h = new Html();
22 $h->setContent('Some content');
23 $h->addContent('More content');
24 $this->assertEquals(
25 'Some contentMore content',
26 $h->render()
27 );
28 }
29
30 public function testSeparatorsAreRespected()
31 {
32 $h = new Html();
33 $h->setContent('Some content');
34 $h->setSeparator(', and ');
35 $h->addContent('More content');
36 $this->assertEquals(
37 'Some content, and More content',
38 $h->render()
39 );
40 }
41 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\Link;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class LinkTest extends BaseTestCase
8 {
9 public function testContentFromFactoryIsRendered()
10 {
11 $l = Link::create('Click here', 'go/some/where');
12 $this->assertEquals(
13 'Click here',
14 $l->renderContent()
15 );
16 }
17
18 public function testSimpleLinkRendersCorrectly()
19 {
20 $l = Link::create('Click here', 'go/some/where');
21 $this->assertEquals(
22 '<a href="/icingaweb2/go/some/where">Click here</a>',
23 $l->render()
24 );
25 }
26
27 public function testLinkWithParamsRendersCorrectly()
28 {
29 $l = Link::create(
30 'Click here',
31 'go/some/where',
32 array(
33 'with' => 'me',
34 'and' => 'aDog'
35 )
36 );
37 $this->assertEquals(
38 '<a href="/icingaweb2/go/some/where?with=me&amp;and=aDog">Click here</a>',
39 $l->render()
40 );
41 }
42 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Html;
3
4 use Icinga\Module\Businessprocess\Html\Text;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class TextTest extends BaseTestCase
8 {
9 public function testTextIsReturnedAsGiven()
10 {
11 $this->assertEquals(
12 'A & O',
13 Text::create('A & O')->getText()
14 );
15 }
16
17 public function testTextIsRenderedAsGivenWhenDeclaredBeingEscaped()
18 {
19 $this->assertEquals(
20 'A & O',
21 Text::create('A & O')->setEscaped()->render()
22 );
23
24 $this->assertEquals(
25 'A & O',
26 Text::create('A & O')->setEscaped(true)->render()
27 );
28
29 $this->assertEquals(
30 'A &amp; O',
31 Text::create('A & O')->setEscaped(false)->render()
32 );
33 }
34
35 public function testTextIsEscapedWhenRendered()
36 {
37 $this->assertEquals(
38 'A &amp; O',
39 Text::create('A & O')->render()
40 );
41 }
42 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\Metadata;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class MetadataTest extends BaseTestCase
8 {
9 public function testDetectsMatchingPrefixes()
10 {
11 $meta = new Metadata('matchme');
12 $this->assertFalse(
13 $meta->nameIsPrefixedWithOneOf(array())
14 );
15 $this->assertFalse(
16 $meta->nameIsPrefixedWithOneOf(array('matchr', 'atchme'))
17 );
18 $this->assertTrue(
19 $meta->nameIsPrefixedWithOneOf(array('not', 'mat', 'yes'))
20 );
21 $this->assertTrue(
22 $meta->nameIsPrefixedWithOneOf(array('m'))
23 );
24 $this->assertTrue(
25 $meta->nameIsPrefixedWithOneOf(array('matchme'))
26 );
27 $this->assertFalse(
28 $meta->nameIsPrefixedWithOneOf(array('matchmenot'))
29 );
30 }
31 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Operator;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7
8 class AndOperatorTest extends BaseTestCase
9 {
10 public function testTheOperatorCanBeParsed()
11 {
12 $storage = new LegacyStorage($this->emptyConfigSection());
13 $expressions = array(
14 'a = b',
15 'a = b & c & d',
16 );
17
18 foreach ($expressions as $expression) {
19 $this->assertInstanceOf(
20 'Icinga\\Module\\Businessprocess\\BpConfig',
21 $storage->loadFromString('dummy', $expression)
22 );
23 }
24 }
25
26 public function testThreeTimesCriticalIsCritical()
27 {
28 $bp = $this->getBp();
29 $bp->setNodeState('b', 2);
30 $bp->setNodeState('c', 2);
31 $bp->setNodeState('d', 2);
32
33 $this->assertEquals(
34 'CRITICAL',
35 $bp->getNode('a')->getStateName()
36 );
37 }
38
39 public function testTwoTimesCriticalAndOkIsCritical()
40 {
41 $bp = $this->getBp();
42 $bp->setNodeState('b', 2);
43 $bp->setNodeState('c', 0);
44 $bp->setNodeState('d', 2);
45
46 $this->assertEquals(
47 'CRITICAL',
48 $bp->getNode('a')->getStateName()
49 );
50 }
51
52 public function testCriticalAndWarningAndOkIsCritical()
53 {
54 $bp = $this->getBp();
55 $bp->setNodeState('b', 2);
56 $bp->setNodeState('c', 1);
57 $bp->setNodeState('d', 0);
58
59 $this->assertEquals(
60 'CRITICAL',
61 $bp->getNode('a')->getStateName()
62 );
63 }
64
65 public function testUnknownAndWarningAndOkIsUnknown()
66 {
67 $bp = $this->getBp();
68 $bp->setNodeState('b', 0);
69 $bp->setNodeState('c', 1);
70 $bp->setNodeState('d', 3);
71
72 $this->assertEquals(
73 'UNKNOWN',
74 $bp->getNode('a')->getStateName()
75 );
76 }
77
78 public function testTwoTimesWarningAndOkIsWarning()
79 {
80 $bp = $this->getBp();
81 $bp->setNodeState('b', 0);
82 $bp->setNodeState('c', 1);
83 $bp->setNodeState('d', 1);
84
85 $this->assertEquals(
86 'WARNING',
87 $bp->getNode('a')->getStateName()
88 );
89 }
90
91 public function testThreeTimesOkIsOk()
92 {
93 $bp = $this->getBp();
94 $bp->setNodeState('b', 0);
95 $bp->setNodeState('c', 0);
96 $bp->setNodeState('d', 0);
97
98 $this->assertEquals(
99 'OK',
100 $bp->getNode('a')->getStateName()
101 );
102 }
103
104 public function testSimpleAndOperationWorksCorrectly()
105 {
106 $bp = new BpConfig();
107 $bp->throwErrors();
108 $host = $bp->createHost('localhost')->setState(1);
109 $service = $bp->createService('localhost', 'ping')->setState(1);
110 $p = $bp->createBp('p');
111 $p->addChild($host);
112 $p->addChild($service);
113
114 $this->assertEquals(
115 'DOWN',
116 $host->getStateName()
117 );
118
119 $this->assertEquals(
120 'WARNING',
121 $service->getStateName()
122 );
123
124 $this->assertEquals(
125 'CRITICAL',
126 $p->getStateName()
127 );
128 }
129
130 public function testSimpleOrOperationWorksCorrectly()
131 {
132 $bp = new BpConfig();
133 $bp->throwErrors();
134 $host = $bp->createHost('localhost')->setState(1);
135 $service = $bp->createService('localhost', 'ping')->setState(1);
136 $p = $bp->createBp('p', '|');
137 $p->addChild($host);
138 $p->addChild($service);
139
140 $this->assertEquals('DOWN', $host->getStateName());
141 $this->assertEquals('WARNING', $service->getStateName());
142 $this->assertEquals('WARNING', $p->getStateName());
143 }
144
145 public function testPendingIsAccepted()
146 {
147 $bp = new BpConfig();
148 $host = $bp->createHost('localhost')->setState(99);
149 $service = $bp->createService('localhost', 'ping')->setState(99);
150 $p = $bp->createBp('p')
151 ->addChild($host)
152 ->addChild($service);
153
154 $this->assertEquals(
155 'PENDING',
156 $p->getStateName()
157 );
158 }
159
160 public function testWhetherWarningIsWorseThanPending()
161 {
162 $bp = new BpConfig();
163 $host = $bp->createHost('localhost')->setState(99);
164 $service = $bp->createService('localhost', 'ping')->setState(1);
165 $p = $bp->createBp('p')
166 ->addChild($host)
167 ->addChild($service);
168
169 $this->assertEquals(
170 'WARNING',
171 $p->getStateName()
172 );
173 }
174
175 public function testPendingIsWorseThanUpOrOk()
176 {
177 $bp = new BpConfig();
178 $host = $bp->createHost('localhost')->setState(99);
179 $service = $bp->createService('localhost', 'ping')->setState(0);
180 $p = $bp->createBp('p')
181 ->addChild($host)
182 ->addChild($service);
183
184 $this->assertEquals(
185 'PENDING',
186 $p->getStateName()
187 );
188
189 $p->clearState();
190 $host->setState(0);
191 $service->setState(99);
192
193 $this->assertEquals(
194 'PENDING',
195 $p->getStateName()
196 );
197 }
198
199 /**
200 * @return BpConfig
201 */
202 protected function getBp()
203 {
204 $storage = new LegacyStorage($this->emptyConfigSection());
205 $expression = 'a = b & c & d';
206 $bp = $storage->loadFromString('dummy', $expression);
207 $bp->createBp('b');
208 $bp->createBp('c');
209 $bp->createBp('d');
210
211 return $bp;
212 }
213 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Operator;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7
8 class MinOperatorTest extends BaseTestCase
9 {
10 public function testTheOperatorCanBeParsed()
11 {
12 $storage = new LegacyStorage($this->emptyConfigSection());
13 $expressions = array(
14 'a = 1 of: b',
15 'a = 2 of: b + c + d',
16 );
17 $this->getName();
18 foreach ($expressions as $expression) {
19 $this->assertInstanceOf(
20 'Icinga\\Module\\Businessprocess\\BpConfig',
21 $storage->loadFromString('dummy', $expression)
22 );
23 }
24 }
25 public function testTwoOfThreeTimesCriticalAreAtLeastCritical()
26 {
27 $bp = $this->getBp();
28 $bp->setNodeState('b', 2);
29 $bp->setNodeState('c', 2);
30 $bp->setNodeState('d', 2);
31
32 $this->assertEquals(
33 'CRITICAL',
34 $bp->getNode('a')->getStateName()
35 );
36 }
37
38 public function testTwoOfTwoTimesCriticalAndUnknownAreAtLeastCritical()
39 {
40 $bp = $this->getBp();
41 $bp->setNodeState('b', 2);
42 $bp->setNodeState('c', 3);
43 $bp->setNodeState('d', 2);
44
45 $this->assertEquals(
46 'CRITICAL',
47 $bp->getNode('a')->getStateName()
48 );
49 }
50
51 public function testTwoOfCriticalAndWarningAndOkAreAtLeastWarning()
52 {
53 $bp = $this->getBp();
54 $bp->setNodeState('b', 2);
55 $bp->setNodeState('c', 1);
56 $bp->setNodeState('d', 0);
57
58 $this->assertEquals(
59 'WARNING',
60 $bp->getNode('a')->getStateName()
61 );
62 }
63
64 public function testTwoOfUnknownAndWarningAndCriticalAreAtLeastUnknown()
65 {
66 $bp = $this->getBp();
67 $bp->setNodeState('b', 2);
68 $bp->setNodeState('c', 1);
69 $bp->setNodeState('d', 3);
70
71 $this->assertEquals(
72 'UNKNOWN',
73 $bp->getNode('a')->getStateName()
74 );
75 }
76
77 public function testTwoOfTwoTimesWarningAndUnknownAreAtLeastWarning()
78 {
79 $bp = $this->getBp();
80 $bp->setNodeState('b', 0);
81 $bp->setNodeState('c', 1);
82 $bp->setNodeState('d', 1);
83
84 $this->assertEquals(
85 'WARNING',
86 $bp->getNode('a')->getStateName()
87 );
88 }
89
90 public function testTwoOfThreeTimesOkAreAtLeastOk()
91 {
92 $bp = $this->getBp();
93 $bp->setNodeState('b', 0);
94 $bp->setNodeState('c', 0);
95 $bp->setNodeState('d', 0);
96
97 $this->assertEquals(
98 'OK',
99 $bp->getNode('a')->getStateName()
100 );
101 }
102
103 /**
104 * @return BpConfig
105 */
106 protected function getBp()
107 {
108 $storage = new LegacyStorage($this->emptyConfigSection());
109 $expression = 'a = 2 of: b + c + d';
110 $bp = $storage->loadFromString('dummy', $expression);
111 $bp->createBp('b');
112 $bp->createBp('c');
113 $bp->createBp('d');
114
115 return $bp;
116 }
117 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Operator;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7
8 class NotOperatorTest extends BaseTestCase
9 {
10 public function testNegationOperatorsCanBeParsed()
11 {
12 $storage = new LegacyStorage($this->emptyConfigSection());
13 $expressions = array(
14 'a = !b',
15 'a = ! b',
16 'a = b ! c ! d',
17 'a = ! b ! c ! d !',
18 );
19
20 foreach ($expressions as $expression) {
21 $this->assertInstanceOf(
22 'Icinga\\Module\\Businessprocess\\BpConfig',
23 $storage->loadFromString('dummy', $expression)
24 );
25 }
26 }
27
28 public function testASimpleNegationGivesTheCorrectResult()
29 {
30 $storage = new LegacyStorage($this->emptyConfigSection());
31 $expression = 'a = !b';
32 $bp = $storage->loadFromString('dummy', $expression);
33 $a = $bp->getNode('a');
34 $b = $bp->createBp('b')->setState(3);
35 $this->assertEquals(
36 'OK',
37 $a->getStateName()
38 );
39
40 $a->clearState();
41 $b->setState(0);
42 $this->assertEquals(
43 'CRITICAL',
44 $a->getStateName()
45 );
46 }
47
48 public function testThreeTimesCriticalIsOk()
49 {
50 $bp = $this->getBp();
51 $bp->setNodeState('b', 2);
52 $bp->setNodeState('c', 2);
53 $bp->setNodeState('d', 2);
54
55 $this->assertEquals(
56 'OK',
57 $bp->getNode('a')->getStateName()
58 );
59 }
60
61 public function testThreeTimesUnknownIsOk()
62 {
63 $bp = $this->getBp();
64 $bp->setNodeState('b', 3);
65 $bp->setNodeState('c', 3);
66 $bp->setNodeState('d', 3);
67
68 $this->assertEquals(
69 'OK',
70 $bp->getNode('a')->getStateName()
71 );
72 }
73
74 public function testThreeTimesWarningIsWarning()
75 {
76 $bp = $this->getBp();
77 $bp->setNodeState('b', 1);
78 $bp->setNodeState('c', 1);
79 $bp->setNodeState('d', 1);
80
81 $this->assertEquals(
82 'WARNING',
83 $bp->getNode('a')->getStateName()
84 );
85 }
86
87 public function testThreeTimesOkIsCritical()
88 {
89 $bp = $this->getBp();
90 $bp->setNodeState('b', 0);
91 $bp->setNodeState('c', 0);
92 $bp->setNodeState('d', 0);
93
94 $this->assertEquals(
95 'CRITICAL',
96 $bp->getNode('a')->getStateName()
97 );
98 }
99
100 public function testNotOkAndWarningAndCriticalIsOk()
101 {
102 $bp = $this->getBp();
103 $bp->setNodeState('b', 0);
104 $bp->setNodeState('c', 1);
105 $bp->setNodeState('d', 2);
106
107 $this->assertEquals(
108 'OK',
109 $bp->getNode('a')->getStateName()
110 );
111 }
112
113 public function testNotWarningAndUnknownAndCriticalIsOk()
114 {
115 $bp = $this->getBp();
116 $bp->setNodeState('b', 3);
117 $bp->setNodeState('c', 2);
118 $bp->setNodeState('d', 1);
119
120 $this->assertEquals(
121 'OK',
122 $bp->getNode('a')->getStateName()
123 );
124 }
125
126 public function testNotTwoTimesWarningAndOkIsWarning()
127 {
128 $bp = $this->getBp();
129 $bp->setNodeState('b', 0);
130 $bp->setNodeState('c', 1);
131 $bp->setNodeState('d', 1);
132
133 $this->assertEquals(
134 'WARNING',
135 $bp->getNode('a')->getStateName()
136 );
137 }
138
139 /**
140 * @return BpConfig
141 */
142 protected function getBp()
143 {
144 $storage = new LegacyStorage($this->emptyConfigSection());
145 $expression = 'a = ! b ! c ! d';
146 $bp = $storage->loadFromString('dummy', $expression);
147 $bp->createBp('b');
148 $bp->createBp('c');
149 $bp->createBp('d');
150
151 return $bp;
152 }
153 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Operator;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
7
8 class OrOperatorTest extends BaseTestCase
9 {
10 public function testTheOperatorCanBeParsed()
11 {
12 $storage = new LegacyStorage($this->emptyConfigSection());
13 $expressions = array(
14 'a = b',
15 'a = b | c | d',
16 );
17
18 foreach ($expressions as $expression) {
19 $this->assertInstanceOf(
20 'Icinga\\Module\\Businessprocess\\BpConfig',
21 $storage->loadFromString('dummy', $expression)
22 );
23 }
24 }
25
26 public function testThreeTimesCriticalIsCritical()
27 {
28 $bp = $this->getBp();
29 $bp->setNodeState('b', 2);
30 $bp->setNodeState('c', 2);
31 $bp->setNodeState('d', 2);
32
33 $this->assertEquals(
34 'CRITICAL',
35 $bp->getNode('a')->getStateName()
36 );
37 }
38
39 public function testTwoTimesCriticalOrUnknownIsUnknown()
40 {
41 $bp = $this->getBp();
42 $bp->setNodeState('b', 2);
43 $bp->setNodeState('c', 3);
44 $bp->setNodeState('d', 2);
45
46 $this->assertEquals(
47 'UNKNOWN',
48 $bp->getNode('a')->getStateName()
49 );
50 }
51
52 public function testCriticalOrWarningOrOkIsOk()
53 {
54 $bp = $this->getBp();
55 $bp->setNodeState('b', 2);
56 $bp->setNodeState('c', 1);
57 $bp->setNodeState('d', 0);
58
59 $this->assertEquals(
60 'OK',
61 $bp->getNode('a')->getStateName()
62 );
63 }
64
65 public function testUnknownOrWarningOrCriticalIsWarning()
66 {
67 $bp = $this->getBp();
68 $bp->setNodeState('b', 2);
69 $bp->setNodeState('c', 1);
70 $bp->setNodeState('d', 3);
71
72 $this->assertEquals(
73 'WARNING',
74 $bp->getNode('a')->getStateName()
75 );
76 }
77
78 public function testTwoTimesWarningAndOkIsOk()
79 {
80 $bp = $this->getBp();
81 $bp->setNodeState('b', 0);
82 $bp->setNodeState('c', 1);
83 $bp->setNodeState('d', 1);
84
85 $this->assertEquals(
86 'OK',
87 $bp->getNode('a')->getStateName()
88 );
89 }
90
91 public function testThreeTimesWarningIsWarning()
92 {
93 $bp = $this->getBp();
94 $bp->setNodeState('b', 1);
95 $bp->setNodeState('c', 1);
96 $bp->setNodeState('d', 1);
97
98 $this->assertEquals(
99 'WARNING',
100 $bp->getNode('a')->getStateName()
101 );
102 }
103
104 /**
105 * @return BpConfig
106 */
107 protected function getBp()
108 {
109 $storage = new LegacyStorage($this->emptyConfigSection());
110 $expression = 'a = b | c | d';
111 $bp = $storage->loadFromString('dummy', $expression);
112 $bp->createBp('b');
113 $bp->createBp('c');
114 $bp->createBp('d');
115
116 return $bp;
117 }
118 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\BpConfig;
5 use Icinga\Module\Businessprocess\ServiceNode;
6 use Icinga\Module\Businessprocess\Test\BaseTestCase;
7
8 class ServiceNodeTest extends BaseTestCase
9 {
10 public function testReturnsCorrectHostName()
11 {
12 $this->assertEquals(
13 'localhost',
14 $this->pingOnLocalhost()->getHostname()
15 );
16 }
17
18 public function testReturnsCorrectServiceDescription()
19 {
20 $this->assertEquals(
21 'ping <> pong',
22 $this->pingOnLocalhost()->getServiceDescription()
23 );
24 }
25
26 public function testReturnsCorrectAlias()
27 {
28 $this->assertEquals(
29 'localhost: ping <> pong',
30 $this->pingOnLocalhost()->getAlias()
31 );
32 }
33
34 public function testRendersCorrectLink()
35 {
36 $this->assertEquals(
37 '<a href="/icingaweb2/monitoring/service/show?host=localhost&amp;service=ping%20%3C%3E%20pong">'
38 . 'localhost: ping &lt;&gt; pong</a>',
39 $this->pingOnLocalhost()->getLink()->render()
40 );
41 }
42
43 /**
44 * @return ServiceNode
45 */
46 protected function pingOnLocalhost()
47 {
48 $bp = new BpConfig();
49 return new ServiceNode($bp, (object) array(
50 'hostname' => 'localhost',
51 'service' => 'ping <> pong',
52 'state' => 0,
53 ));
54 }
55 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess;
3
4 use Icinga\Module\Businessprocess\Simulation;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class SimulationTest extends BaseTestCase
8 {
9 public function testSimulationInstantiation()
10 {
11 $class = 'Icinga\\Module\\Businessprocess\\Simulation';
12 $this->assertInstanceOf(
13 $class,
14 Simulation::create()
15 );
16 }
17
18 public function testAppliedSimulation()
19 {
20 $data = (object) array(
21 'state' => 0,
22 'acknowledged' => false,
23 'in_downtime' => false
24 );
25 $config = $this->makeInstance()->loadProcess('simple_with-header');
26 $simulation = Simulation::create(array(
27 'host1;Hoststatus' => $data
28 ));
29 $parent = $config->getBpNode('singleHost');
30
31 $config->applySimulation($simulation);
32 $this->assertEquals(
33 'OK',
34 $parent->getStateName()
35 );
36
37 $parent->clearState();
38 $data->state = 1;
39 $simulation->set('host1;Hoststatus', $data);
40 $config->applySimulation($simulation);
41 $this->assertEquals(
42 'CRITICAL',
43 $parent->getStateName()
44 );
45 }
46 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Storage;
3
4 use Icinga\Module\Businessprocess\Test\BaseTestCase;
5 use Icinga\Module\Businessprocess\Storage\LegacyStorage;
6
7 class LegacyStorageTest extends BaseTestCase
8 {
9 private $processClass = 'Icinga\\Module\\Businessprocess\\BpConfig';
10
11 public function testCanBeInstantiatedWithAnEmptyConfigSection()
12 {
13 $baseClass = 'Icinga\\Module\\Businessprocess\\Storage\\LegacyStorage';
14 $this->assertInstanceOf(
15 $baseClass,
16 new LegacyStorage($this->emptyConfigSection())
17 );
18 }
19
20 public function testDefaultConfigDirIsDiscoveredCorrectly()
21 {
22 $this->assertEquals(
23 $this->getTestsBaseDir('config/modules/businessprocess/processes'),
24 $this->makeInstance()->getConfigDir()
25 );
26 }
27
28 public function testAllAvailableProcessesAreListed()
29 {
30 $keys = array_keys($this->makeInstance()->listProcesses());
31 $this->assertEquals(
32 array(
33 'broken_wrong-operator',
34 'combined',
35 'simple_with-header',
36 'simple_without-header',
37 ),
38 $keys
39 );
40 }
41
42 public function testHeaderTitlesAreRespectedInProcessList()
43 {
44 $keys = array_values($this->makeInstance()->listProcesses());
45 $this->assertEquals(
46 array(
47 'broken_wrong-operator',
48 'combined',
49 'Simple with header (simple_with-header)',
50 'simple_without-header',
51 ),
52 $keys
53 );
54 }
55
56 public function testProcessFilenameIsReturned()
57 {
58 $this->assertEquals(
59 $this->getTestsBaseDir('config/modules/businessprocess/processes/simple_with-header.conf'),
60 $this->makeInstance()->getFilename('simple_with-header')
61 );
62 }
63
64 public function testAnExistingProcessExists()
65 {
66 $this->assertTrue(
67 $this->makeInstance()->hasProcess('simple_with-header')
68 );
69 }
70
71 public function testAMissingProcessIsMissing()
72 {
73 $this->assertFalse(
74 $this->makeInstance()->hasProcess('simple_with-headerx')
75 );
76 }
77
78 public function testAValidProcessCanBeLoaded()
79 {
80 $this->assertInstanceOf(
81 $this->processClass,
82 $this->makeInstance()->loadProcess('simple_with-header')
83 );
84 }
85
86 public function testProcessConfigCanBeLoadedFromAString()
87 {
88 $this->assertInstanceOf(
89 $this->processClass,
90 $this->makeInstance()->loadFromString('dummy', 'a = Host1;ping & Host2;ping')
91 );
92 }
93
94 public function testFullProcessSourceCanBeFetched()
95 {
96 $this->assertEquals(
97 file_get_contents(
98 $this->getTestsBaseDir(
99 'config/modules/businessprocess/processes/simple_with-header.conf'
100 )
101 ),
102 $this->makeInstance()->getSource('simple_with-header')
103 );
104 }
105
106 public function testTitleCanBeReadFromConfig()
107 {
108 $this->assertEquals(
109 'Simple with header',
110 $this->makeInstance()->loadProcess('simple_with-header')->getMetadata()->get('Title')
111 );
112 }
113
114 public function testInfoUrlBeReadFromConfig()
115 {
116 $this->assertEquals(
117 'https://top.example.com/',
118 $this->makeInstance()->loadProcess('simple_with-header')->getBpNode('top')->getInfoUrl()
119 );
120 }
121
122 public function testAConfiguredLoopCanBeParsed()
123 {
124 $this->assertInstanceOf(
125 $this->processClass,
126 $this->makeLoop()
127 );
128 }
129
130 public function testImportedNodesCanBeParsed()
131 {
132 $this->assertInstanceOf(
133 $this->processClass,
134 $this->makeInstance()->loadProcess('combined')
135 );
136 }
137 }
0 <?php
1
2 namespace Tests\Icinga\Module\Businessprocess\Web\Component;
3
4 use Icinga\Module\Businessprocess\Web\Component\Tabs;
5 use Icinga\Module\Businessprocess\Test\BaseTestCase;
6
7 class TabsTest extends BaseTestCase
8 {
9 public function testEmptyTabsCanBeInstantiated()
10 {
11 $this->assertInstanceOf(
12 'Icinga\Module\Businessprocess\Web\Component\Tabs',
13 new Tabs()
14 );
15 }
16 }
0 <?php
1
2 use PHPUnit\Framework\TestCase;
3
4 /**
5 * @codingStandardsIgnoreStart
6 */
7 class PHPUnit_Framework_TestCase extends TestCase
8 {
9 }