Adding upstream version 2.1.0.
Signed-off-by: David Kunz <david.kunz@dknet.ch>
David Kunz
5 years ago
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 | } |
Binary diff not shown
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" |
Binary diff not shown
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 | ||
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 (>= 2.4.1) | |
7 | * PHP (>= 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` -> `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). |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
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 
 | |
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 ' '; | |
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 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 . | |
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(' ', $div).str_repeat(' ', $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>…</th>'; | |
75 | $html .= '<th>…</th>'; | |
76 | $html .= '<td> </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> </th>'; | |
100 | $html .= '<th>'.$toLine.'</th>'; | |
101 | $html .= '<td class="Right"><ins>'.$line.'</ins> </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> </th>'; | |
112 | $html .= '<td class="Left"><del>'.$line.'</del> </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> </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> </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>…</th><td> </td>'; | |
72 | $html .= '<th>…</th><td> </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> </span></td>'; | |
86 | $html .= '<th>'.$toLine.'</th>'; | |
87 | $html .= '<td class="Right"><span>'.$line.'</span> </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> </th>'; | |
97 | $html .= '<td class="Left"> </td>'; | |
98 | $html .= '<th>'.$toLine.'</th>'; | |
99 | $html .= '<td class="Right"><ins>'.$line.'</ins> </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> </td>'; | |
110 | $html .= '<th> </th>'; | |
111 | $html .= '<td class="Right"> </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> </td>'; | |
123 | if(!isset($change['changed']['lines'][$no])) { | |
124 | $toLine = ' '; | |
125 | $changedLine = ' '; | |
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 = ' '; | |
140 | $line = ' '; | |
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> </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 | 9 |
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 | REAMDE.md |
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 | 3.0 (git) |
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
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 | }); |
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&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 | '"sü?ß /some/url?a=b&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 | ""sü?ß' /some/url?a=b&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=""sü?ß /some/url?a=b&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 & 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&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 & O', | |
31 | Text::create('A & O')->setEscaped(false)->render() | |
32 | ); | |
33 | } | |
34 | ||
35 | public function testTextIsEscapedWhenRendered() | |
36 | { | |
37 | $this->assertEquals( | |
38 | 'A & 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&service=ping%20%3C%3E%20pong">' | |
38 | . 'localhost: ping <> 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 | } |