improve CodeMirror integration
[racktables] / wwwroot / inc / interface-config.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 // Find direct sub-pages and dump as a list.
8 function renderConfigMainpage ()
9 {
10 global $pageno, $page;
11 echo '<ul>';
12 foreach ($page as $cpageno => $cpage)
13 if (isset ($cpage['parent']) && $cpage['parent'] == $pageno && permitted ($cpageno))
14 echo '<li>' . mkA (getTitle ($cpageno), $cpageno) . "</li>\n";
15 echo '</ul>';
16 }
17
18 function renderUserList ()
19 {
20 renderCellList ('user', 'User accounts');
21 }
22
23 function renderUserListEditor ()
24 {
25 function printNewItemTR ()
26 {
27 startPortlet ('Add new');
28 printOpFormIntro ('createUser');
29 echo '<table cellspacing=0 cellpadding=5 align=center>';
30 echo '<tr><th>&nbsp;</th><th>&nbsp;</th><th>Tags</th></tr>';
31 echo '<tr><th class=tdright>Username</th><td class=tdleft><input type=text size=64 name=username></td>';
32 echo '<tr><th class=tdright>Real name</th><td class=tdleft><input type=text size=64 name=realname></td></tr>';
33 echo '<tr><th class=tdright>Password</th><td class=tdleft><input type=password size=64 name=password></td></tr>';
34 echo '<tr><th class=tdright>Tags</th><td class=tdleft>';
35 printTagsPicker ();
36 echo '</td></tr>';
37 echo '<tr><td colspan=2>';
38 printImageHREF ('CREATE', 'Add new account', TRUE);
39 echo '</td></tr>';
40 echo '</table></form>';
41 finishPortlet();
42 }
43 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
44 printNewItemTR();
45 $accounts = listCells ('user');
46 startPortlet ('Manage existing (' . count ($accounts) . ')');
47 echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
48 echo '<tr><th>Username</th><th>Real name</th><th>New password (use old if blank)</th><th>&nbsp;</th></tr>';
49 foreach ($accounts as $account)
50 {
51 printOpFormIntro ('updateUser', array ('user_id' => $account['user_id']));
52 echo "<tr><td><input type=text name=username value='${account['user_name']}' size=16></td>";
53 echo "<td><input type=text name=realname value='${account['user_realname']}' size=24></td>";
54 echo "<td><input type=password name=password size=40></td><td>";
55 printImageHREF ('save', 'Save changes', TRUE);
56 echo '</td></form></tr>';
57 }
58 echo '</table><br>';
59 finishPortlet();
60 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
61 printNewItemTR();
62 }
63
64 function renderUser ($user_id)
65 {
66 $userinfo = spotEntity ('user', $user_id);
67
68 $summary = array();
69 $summary['Account name'] = $userinfo['user_name'];
70 $summary['Real name'] = $userinfo['user_realname'];
71 $summary['tags'] = '';
72 renderEntitySummary ($userinfo, 'summary', $summary);
73
74 renderFilesPortlet ('user', $user_id);
75 }
76
77 function renderUserProperties ($user_id)
78 {
79 printOpFormIntro ('edit');
80 echo '<table border=0 align=center>';
81 echo "<tr><th class=tdright>Tags:</th><td class=tdleft>";
82 printTagsPicker ();
83 echo "</td></tr>\n";
84 echo "<tr><th class=submit colspan=2>";
85 printImageHREF ('SAVE', 'Save changes', TRUE);
86 echo '</th></tr></table></form>';
87 }
88
89 function renderRackCodeViewer ()
90 {
91 echo '<table width="100%" border=0>';
92 addJS ('js/codemirror/codemirror.js');
93 addJS ('js/codemirror/rackcode.js');
94 addCSS ('css/codemirror/codemirror.css');
95 addCSS ('css/codemirror/rackcode.css');
96 if (! array_key_exists ('line', $_REQUEST))
97 $scrollcode = '';
98 else
99 {
100 // Line numbers start from 0 in CodeMirror API and from 1 elsewhere.
101 $lineno = genericAssertion ('line', 'uint') - 1;
102 $scrollcode = "rackCodeMirror.addLineClass (${lineno}, 'wrap', 'border_highlight');\n" .
103 "rackCodeMirror.scrollIntoView ({line: ${lineno}, ch: 0}, 50);\n";
104 }
105 addJS (<<<ENDJAVASCRIPT
106 $(document).ready(function() {
107 var rackCodeMirror = CodeMirror.fromTextArea(document.getElementById("RCTA"),{
108 mode:'rackcode',
109 theme:'rackcode',
110 readOnly:'nocursor',
111 lineNumbers:true });
112 ${scrollcode}
113 });
114 ENDJAVASCRIPT
115 , TRUE);
116 echo "<tr><td><textarea rows=40 cols=100 id=RCTA>";
117 echo loadScript ('RackCode') . "</textarea></td></tr>\n";
118 echo '</table>';
119 }
120
121 function renderRackCodeEditor ()
122 {
123 addJS ('js/codemirror/codemirror.js');
124 addJS ('js/codemirror/rackcode.js');
125 addCSS ('css/codemirror/codemirror.css');
126 addCSS ('css/codemirror/rackcode.css');
127 addJS (<<<ENDJAVASCRIPT
128 function verify()
129 {
130 $.ajax({
131 type: "POST",
132 url: "index.php",
133 data: {'module': 'ajax', 'ac': 'verifyCode', 'code': $("#RCTA").text()},
134 success: function (data)
135 {
136 arr = data.split("\\n");
137 if (arr[0] == "ACK")
138 {
139 $("#SaveChanges")[0].disabled = "";
140 $("#ShowMessage")[0].innerHTML = "Code verification OK, don't forget to save the code";
141 $("#ShowMessage")[0].className = "msg_success";
142 }
143 else
144 {
145 $("#SaveChanges")[0].disabled = "disabled";
146 $("#ShowMessage")[0].innerHTML = arr[1];
147 $("#ShowMessage")[0].className = "msg_warning";
148 }
149 }
150 });
151 }
152
153 $(document).ready(function() {
154 $("#SaveChanges")[0].disabled = "disabled";
155 $("#ShowMessage")[0].innerHTML = "";
156 $("#ShowMessage")[0].className = "";
157
158 var rackCodeMirror = CodeMirror.fromTextArea(document.getElementById("RCTA"),{
159 mode:'rackcode',
160 theme:'rackcode',
161 autofocus:true,
162 lineNumbers:true });
163 rackCodeMirror.on("change",function(cm,cmChangeObject){
164 $("#RCTA").text(cm.getValue());
165 });
166 });
167 ENDJAVASCRIPT
168 , TRUE);
169
170 printOpFormIntro ('saveRackCode');
171 echo '<table width="100%" border=0>';
172 echo "<tr><td><textarea rows=40 cols=100 name=rackcode id=RCTA>";
173 echo loadScript ('RackCode') . "</textarea></td></tr>\n";
174 echo "<tr><td class=submit>";
175 echo '<div id="ShowMessage"></div>';
176 echo "<input type='button' value='Verify' onclick='verify();'>";
177 echo "<input type='submit' value='Save' disabled='disabled' id='SaveChanges'>";
178 echo "</td></tr>";
179 echo '</table>';
180 echo "</form>";
181 }
182
183 function renderObjectParentCompatViewer()
184 {
185 global $nextorder;
186 $order = 'odd';
187 $last_left_parent_id = NULL;
188 echo '<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>';
189 echo '<tr><th>Parent</th><th>Child</th></tr>';
190 foreach (getObjectParentCompat() as $pair)
191 {
192 if ($last_left_parent_id != $pair['parent_objtype_id'])
193 {
194 $order = $nextorder[$order];
195 $last_left_parent_id = $pair['parent_objtype_id'];
196 }
197 echo "<tr class=row_${order}><td>${pair['parent_name']}</td><td>${pair['child_name']}</td></tr>\n";
198 }
199 echo '</table>';
200 }
201
202 function renderObjectParentCompatEditor()
203 {
204 function printNewitemTR()
205 {
206 printOpFormIntro ('add');
207 echo '<tr><th class=tdleft>';
208 printImageHREF ('add', 'add pair', TRUE);
209 echo '</th><th class=tdleft>';
210 $chapter = readChapter (CHAP_OBJTYPE);
211 // remove rack, row, location
212 unset ($chapter['1560'], $chapter['1561'], $chapter['1562']);
213 printSelect ($chapter, array ('name' => 'parent_objtype_id'));
214 echo '</th><th class=tdleft>';
215 printSelect ($chapter, array ('name' => 'child_objtype_id'));
216 echo "</th></tr></form>\n";
217 }
218
219 global $nextorder;
220 $last_left_parent_id = NULL;
221 $order = 'odd';
222 echo '<br><table class=cooltable align=center border=0 cellpadding=5 cellspacing=0>';
223 echo '<tr><th>&nbsp;</th><th>Parent</th><th>Child</th></tr>';
224 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
225 printNewitemTR();
226 foreach (getObjectParentCompat() as $pair)
227 {
228 if ($last_left_parent_id != $pair['parent_objtype_id'])
229 {
230 $order = $nextorder[$order];
231 $last_left_parent_id = $pair['parent_objtype_id'];
232 }
233 echo "<tr class=row_${order}><td>";
234 if ($pair['count'] > 0)
235 printImageHREF ('nodelete', $pair['count'] . ' relationship(s) stored');
236 else
237 echo getOpLink (array ('op' => 'del', 'parent_objtype_id' => $pair['parent_objtype_id'], 'child_objtype_id' => $pair['child_objtype_id']), '', 'delete', 'remove pair');
238 echo "</td><td class=tdleft>${pair['parent_name']}</td><td class=tdleft>${pair['child_name']}</td></tr>\n";
239 }
240 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
241 printNewitemTR();
242 echo '</table>';
243 }
244
245 function renderOIFCompatViewer()
246 {
247 global $nextorder;
248 $order = 'odd';
249 $last_left_oif_id = NULL;
250 echo '<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>';
251 echo '<tr><th>From interface</th><th>To interface</th></tr>';
252 foreach (getPortOIFCompat() as $pair)
253 {
254 if ($last_left_oif_id != $pair['type1'])
255 {
256 $order = $nextorder[$order];
257 $last_left_oif_id = $pair['type1'];
258 }
259 echo "<tr class=row_${order}><td>${pair['type1name']}</td><td>${pair['type2name']}</td></tr>";
260 }
261 echo '</table>';
262 }
263
264 function renderOIFCompatEditor()
265 {
266 function printNewitemTR()
267 {
268 printOpFormIntro ('add');
269 echo '<tr><th class=tdleft>';
270 printImageHREF ('add', 'add pair', TRUE);
271 echo '</th><th class=tdleft>';
272 printSelect (getPortOIFOptions(), array ('name' => 'type1'));
273 echo '</th><th class=tdleft>';
274 printSelect (getPortOIFOptions(), array ('name' => 'type2'));
275 echo '</th></tr></form>';
276 }
277
278 global $nextorder, $wdm_packs;
279
280 startPortlet ('WDM wideband receivers');
281 echo '<table border=0 align=center cellspacing=0 cellpadding=5 class=zebra>';
282 echo '<tr><th>&nbsp;</th><th>enable</th><th>disable</th></tr>';
283 foreach ($wdm_packs as $codename => $packinfo)
284 {
285 echo '<tr><td class=tdleft>' . $packinfo['title'] . '</td><td>';
286 echo getOpLink (array ('op' => 'addPack', 'standard' => $codename), '', 'add');
287 echo '</td><td>';
288 echo getOpLink (array ('op' => 'delPack', 'standard' => $codename), '', 'delete');
289 echo '</td></tr>';
290 }
291 echo '</table>';
292 finishPortlet();
293
294 startPortlet ('interface by interface');
295 $last_left_oif_id = NULL;
296 echo '<br><table class=cooltable align=center border=0 cellpadding=5 cellspacing=0>';
297 echo '<tr><th>&nbsp;</th><th>From Interface</th><th>To Interface</th></tr>';
298 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
299 printNewitemTR();
300 $order = 'odd';
301 foreach (getPortOIFCompat() as $pair)
302 {
303 if ($last_left_oif_id != $pair['type1'])
304 {
305 $order = $nextorder[$order];
306 $last_left_oif_id = $pair['type1'];
307 }
308 echo "<tr class=row_${order}><td>";
309 echo getOpLink (array ('op' => 'del', 'type1' => $pair['type1'], 'type2' => $pair['type2']), '', 'delete', 'remove pair');
310 echo "</td><td class=tdleft>${pair['type1name']}</td><td class=tdleft>${pair['type2name']}</td></tr>";
311 }
312 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
313 printNewitemTR();
314 echo '</table>';
315 finishPortlet();
316 }
317
318 function renderIIFOIFCompat()
319 {
320 echo '<br>';
321 renderTwoColumnCompatTableViewer
322 (
323 getPortInterfaceCompat(),
324 array
325 (
326 'header' => 'Inner interface',
327 'key' => 'iif_id',
328 'value' => 'iif_name',
329 'width' => 16,
330 ),
331 array
332 (
333 'header' => 'Outer interface',
334 'key' => 'oif_id',
335 'value' => 'oif_name',
336 'width' => 48,
337 )
338 );
339 echo '<br>';
340 }
341
342 function renderIIFOIFCompatEditor()
343 {
344 startPortlet ('WDM standard by interface');
345 $iif = getPortIIFOptions();
346 global $nextorder, $wdm_packs;
347 $order = 'odd';
348 echo '<table border=0 align=center cellspacing=0 cellpadding=5>';
349 foreach ($wdm_packs as $codename => $packinfo)
350 {
351 echo "<tr><th>&nbsp;</th><th colspan=2>${packinfo['title']}</th></tr>";
352 foreach ($packinfo['iif_ids'] as $iif_id)
353 {
354 echo "<tr class=row_${order}><th class=tdleft>" . $iif[$iif_id] . '</th><td>';
355 echo getOpLink (array ('op' => 'addPack', 'standard' => $codename, 'iif_id' => $iif_id), '', 'add');
356 echo '</td><td>';
357 echo getOpLink (array ('op' => 'delPack', 'standard' => $codename, 'iif_id' => $iif_id), '', 'delete');
358 echo '</td></tr>';
359 $order = $nextorder[$order];
360 }
361 }
362 echo '</table>';
363 finishPortlet();
364
365 startPortlet ('interface by interface');
366 renderTwoColumnCompatTableEditor
367 (
368 getPortInterfaceCompat(),
369 array
370 (
371 'header' => 'inner interface',
372 'key' => 'iif_id',
373 'value' => 'iif_name',
374 'width' => 16,
375 'options' => getPortIIFOptions(),
376 ),
377 array
378 (
379 'header' => 'outer interface',
380 'key' => 'oif_id',
381 'value' => 'oif_name',
382 'width' => 48,
383 'options' => getPortOIFOptions()
384 )
385 );
386 finishPortlet();
387 }
388
389 function renderPortOIFViewer()
390 {
391 echo '<br><table class="cooltable zebra" align=center border=0 cellpadding=5 cellspacing=0>';
392 echo '<tr><th>Origin</th><th>Key</th><th>Refcnt</th><th>Outer Interface</th></tr>';
393 $refcnt = getPortOIFRefc();
394 foreach (getPortOIFOptions() as $oif_id => $oif_name)
395 {
396 echo '<tr>';
397 echo '<td class=tdleft>' . getImageHREF ($oif_id < 2000 ? 'computer' : 'favorite') . '</td>';
398 echo "<td class=tdright>${oif_id}</td>";
399 echo '<td class=tdright>' . ($refcnt[$oif_id] ? $refcnt[$oif_id] : '&nbsp;') . '</td>';
400 echo '<td class=tdleft>' . stringForTD ($oif_name, 48) . '</td>';
401 echo '</tr>';
402 }
403 echo '</table>';
404 }
405
406 function renderPortOIFEditor()
407 {
408 function printNewitemTR()
409 {
410 printOpFormIntro ('add');
411 echo '<tr>';
412 echo '<td>&nbsp;</td>';
413 echo '<td>&nbsp;</td>';
414 echo '<td>&nbsp;</td>';
415 echo '<td class=tdleft>' . getImageHREF ('create', 'create new', TRUE) . '</td>';
416 echo '<td class=tdleft><input type=text size=48 name=oif_name></td>';
417 echo '<td class=tdleft>' . getImageHREF ('create', 'create new', TRUE) . '</td>';
418 echo '</tr></form>';
419 }
420 echo '<table class=widetable border=0 cellpadding=5 cellspacing=0 align=center>';
421 echo '<tr><th class=tdleft>Origin</th><th>Key</th><th>Refcnt</th><th>&nbsp;</th><th>Outer Interface</th><th>&nbsp;</th></tr>';
422 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
423 printNewitemTR();
424 $refcnt = getPortOIFRefc();
425 foreach (getPortOIFOptions() as $oif_id => $oif_name)
426 {
427 echo '<tr>';
428 if ($oif_id < 2000)
429 {
430 echo '<td class=tdleft>' . getImageHREF ('computer') . '</td>';
431 echo "<td class=tdleft>${oif_id}</td>";
432 echo '<td class=tdright>' . ($refcnt[$oif_id] ? $refcnt[$oif_id] : '&nbsp;') . '</td>';
433 echo '<td>&nbsp;</td>';
434 echo '<td class=tdleft>' . stringForTD ($oif_name, 48) . '</td>';
435 echo '<td>&nbsp;</td>';
436 }
437 else
438 {
439 printOpFormIntro ('upd', array ('id' => $oif_id));
440 echo '<td class=tdleft>' . getImageHREF ('favorite') . '</td>';
441 echo "<td class=tdleft>${oif_id}</td>";
442 if ($refcnt[$oif_id])
443 {
444 echo "<td class=tdright>${refcnt[$oif_id]}</td>";
445 echo '<td class=tdleft>' . getImageHREF ('nodestroy', 'cannot remove') . '</td>';
446 }
447 else
448 {
449 echo '<td>&nbsp;</td>';
450 echo '<td class=tdleft>';
451 echo getOpLink (array ('op' => 'del', 'id' => $oif_id), '', 'destroy', 'remove');
452 echo '</td>';
453 }
454 echo '<td class=tdleft><input type=text size=48 name=oif_name value="' . stringForTextInputValue ($oif_name, 48) . '"></td>';
455 echo '<td>' . getImageHREF ('save', 'Save changes', TRUE) . '</td>';
456 echo '</form>';
457 }
458 echo '</tr>';
459 }
460 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
461 printNewitemTR();
462 echo '</table>';
463 }
464
465 function renderAttributes ()
466 {
467 global $attrtypes;
468 startPortlet ('Optional attributes');
469 echo '<table class="cooltable zebra" border=0 cellpadding=5 cellspacing=0 align=center>';
470 echo "<tr><th class=tdleft>Attribute name</th><th class=tdleft>Attribute type</th><th class=tdleft>Applies to</th></tr>";
471 foreach (getAttrMap() as $attr)
472 {
473 echo '<tr>';
474 echo "<td class=tdleft>${attr['name']}</td>";
475 echo "<td class=tdleft>" . $attrtypes[$attr['type']] . "</td>";
476 echo '<td class=tdleft>';
477 if (count ($attr['application']) == 0)
478 echo '&nbsp;';
479 else
480 foreach ($attr['application'] as $app)
481 if ($attr['type'] == 'dict')
482 echo decodeObjectType ($app['objtype_id']) . " (values from '${app['chapter_name']}')<br>";
483 else
484 echo decodeObjectType ($app['objtype_id']) . '<br>';
485 echo '</td></tr>';
486 }
487 echo "</table><br>\n";
488 finishPortlet();
489 }
490
491 function renderEditAttributesForm ()
492 {
493 function printNewItemTR ()
494 {
495 printOpFormIntro ('add');
496 echo '<tr><td>';
497 printImageHREF ('create', 'Create attribute', TRUE);
498 echo "</td><td><input type=text name=attr_name></td><td>";
499 global $attrtypes;
500 printSelect ($attrtypes, array ('name' => 'attr_type'));
501 echo '</td><td>';
502 printImageHREF ('create', 'Create attribute', TRUE);
503 echo '</td></tr></form>';
504 }
505 startPortlet ('Optional attributes');
506 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
507 echo '<tr><th>&nbsp;</th><th>Name</th><th>Type</th><th>&nbsp;</th></tr>';
508 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
509 printNewItemTR();
510 foreach (getAttrMap() as $attr)
511 {
512 printOpFormIntro ('upd', array ('attr_id' => $attr['id']));
513 echo '<tr><td>';
514 if ($attr['id'] < 10000)
515 printImageHREF ('nodestroy', 'system attribute');
516 elseif (count ($attr['application']))
517 printImageHREF ('nodestroy', count ($attr['application']) . ' reference(s) in attribute map');
518 else
519 echo getOpLink (array('op'=>'del', 'attr_id'=>$attr['id']), '', 'destroy', 'Remove attribute');
520 echo "</td><td><input type=text name=attr_name value='${attr['name']}'></td>";
521 echo "<td class=tdleft>${attr['type']}</td><td>";
522 printImageHREF ('save', 'Save changes', TRUE);
523 echo '</td></tr>';
524 echo '</form>';
525 }
526 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
527 printNewItemTR();
528 echo "</table>\n";
529 finishPortlet();
530 }
531
532 function getAttributeOptions ($attrMap)
533 {
534 $ret = array();
535 $shortType = array
536 (
537 'uint' => 'U',
538 'float' => 'F',
539 'string' => 'S',
540 'dict' => 'D',
541 'date' => 'T',
542 );
543 foreach ($attrMap as $attr)
544 $ret[$attr['id']] = sprintf ('[%s] %s', $shortType[$attr['type']], $attr['name']);
545 return $ret;
546 }
547
548 function renderEditAttrMapForm ()
549 {
550 function printNewItemTR ($aselect)
551 {
552 printOpFormIntro ('add');
553 echo '<tr>';
554 echo "<td colspan=2 class=tdleft>${aselect}</td>";
555 echo '<td class=tdleft>';
556 printImageHREF ('add', '', TRUE);
557 echo ' ';
558 $objtypes = readChapter (CHAP_OBJTYPE, 'o');
559 printNiftySelect (cookOptgroups ($objtypes), array ('name' => 'objtype_id'));
560 $choptions = array (0 => '-- dictionary chapter for [D] attributes --');
561 foreach (getChapterList() as $chapter)
562 if ($chapter['sticky'] != 'yes')
563 $choptions[$chapter['id']] = $chapter['name'];
564 echo ' ' . getSelect ($choptions, array ('name' => 'chapter_no'));
565 echo '</td></tr></form>';
566 }
567 global $attrtypes, $nextorder;
568 $order = 'odd';
569 $attrMap = getAttrMap();
570 $aselect = getSelect (getAttributeOptions ($attrMap), array ('name' => 'attr_id'));
571 startPortlet ('Attribute map');
572 echo "<table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>";
573 echo '<tr><th class=tdleft>Attribute name</th><th class=tdleft>Attribute type</th><th class=tdleft>Applies to</th></tr>';
574 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
575 printNewItemTR ($aselect);
576 foreach ($attrMap as $attr)
577 {
578 if (!count ($attr['application']))
579 continue;
580 echo "<tr class=row_${order}><td class=tdleft>${attr['name']}</td>";
581 echo "<td class=tdleft>" . $attrtypes[$attr['type']] . "</td><td colspan=2 class=tdleft>";
582 foreach ($attr['application'] as $app)
583 {
584 if ($app['sticky'] == 'yes')
585 printImageHREF ('nodelete', 'system mapping');
586 elseif ($app['refcnt'])
587 printImageHREF ('nodelete', $app['refcnt'] . ' value(s) stored for objects');
588 else
589 echo getOpLink (array('op'=>'del', 'attr_id'=>$attr['id'], 'objtype_id'=>$app['objtype_id']), '', 'delete', 'Remove mapping');
590 echo ' ';
591 if ($attr['type'] == 'dict')
592 echo decodeObjectType ($app['objtype_id']) . " (values from '${app['chapter_name']}')<br>";
593 else
594 echo decodeObjectType ($app['objtype_id']) . '<br>';
595 }
596 echo "</td></tr>";
597 $order = $nextorder[$order];
598 }
599 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
600 printNewItemTR ($aselect);
601 echo "</table>\n";
602 finishPortlet();
603 }
604
605 function renderDictionary ()
606 {
607 echo '<ul>';
608 foreach (getChapterList() as $chapter_no => $chapter)
609 echo '<li>' . mkA ($chapter['name'], 'chapter', $chapter_no) . " (${chapter['wordc']} records)</li>";
610 echo '</ul>';
611 }
612
613 // We don't allow to rename/delete a sticky chapter and we don't allow
614 // to delete a non-empty chapter.
615 function renderChaptersEditor ()
616 {
617 function printNewItemTR ()
618 {
619 printOpFormIntro ('add');
620 echo '<tr><td>';
621 printImageHREF ('create', 'Add new', TRUE);
622 echo "</td><td><input type=text name=chapter_name></td><td>&nbsp;</td><td>";
623 printImageHREF ('create', 'Add new', TRUE);
624 echo '</td></tr></form>';
625 }
626 $dict = getChapterList();
627 foreach (array_keys ($dict) as $chapter_no)
628 $dict[$chapter_no]['mapped'] = FALSE;
629 foreach (getAttrMap() as $attrinfo)
630 if ($attrinfo['type'] == 'dict')
631 foreach ($attrinfo['application'] as $app)
632 $dict[$app['chapter_no']]['mapped'] = TRUE;
633 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
634 echo '<tr><th>&nbsp;</th><th>Chapter name</th><th>Words</th><th>&nbsp;</th></tr>';
635 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
636 printNewItemTR();
637 foreach ($dict as $chapter_id => $chapter)
638 {
639 $wordcount = $chapter['wordc'];
640 $sticky = $chapter['sticky'] == 'yes';
641 printOpFormIntro ('upd', array ('chapter_no' => $chapter_id));
642 echo '<tr>';
643 echo '<td>';
644 if ($sticky)
645 printImageHREF ('nodestroy', 'system chapter');
646 elseif ($wordcount > 0)
647 printImageHREF ('nodestroy', 'contains ' . $wordcount . ' word(s)');
648 elseif ($chapter['mapped'])
649 printImageHREF ('nodestroy', 'used in attribute map');
650 else
651 echo getOpLink (array('op'=>'del', 'chapter_no'=>$chapter_id), '', 'destroy', 'Remove chapter');
652 echo '</td>';
653 echo "<td><input type=text name=chapter_name value='${chapter['name']}'" . ($sticky ? ' disabled' : '') . "></td>";
654 echo "<td class=tdleft>${wordcount}</td><td>";
655 if ($sticky)
656 echo '&nbsp;';
657 else
658 printImageHREF ('save', 'Save changes', TRUE);
659 echo '</td></tr>';
660 echo '</form>';
661 }
662 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
663 printNewItemTR();
664 echo "</table>\n";
665 }
666
667 function renderChapter ($tgt_chapter_no)
668 {
669 global $nextorder;
670 $words = readChapter ($tgt_chapter_no, 'a');
671 $wc = count ($words);
672 if (!$wc)
673 {
674 echo "<center><h2>(no records)</h2></center>";
675 return;
676 }
677 $refcnt = getChapterRefc ($tgt_chapter_no, array_keys ($words));
678 $attrs = getChapterAttributes($tgt_chapter_no);
679 echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
680 echo "<tr><th colspan=4>${wc} record(s)</th></tr>\n";
681 echo "<tr><th>Origin</th><th>Key</th><th>Refcnt</th><th>Word</th></tr>\n";
682 $order = 'odd';
683 foreach ($words as $key => $value)
684 {
685 echo "<tr class=row_${order}><td>";
686 printImageHREF ($key < 50000 ? 'computer' : 'favorite');
687 echo "</td><td class=tdright>${key}</td><td class=tdright>";
688 if ($refcnt[$key])
689 {
690 // For the ObjectType chapter the extra filter is as simple as "{\$typeid_${key}}" but
691 // the reference counter also includes the relations with AttributeMap.objtype_id hence
692 // it often is not the same as the amount of objects that match the expression. With
693 // this in mind don't display the counter as a link for this specific chapter.
694 if ($tgt_chapter_no == CHAP_OBJTYPE)
695 $cfe = '';
696 else
697 {
698 $tmp = array();
699 foreach ($attrs as $attr_id)
700 $tmp[] = "{\$attr_${attr_id}_${key}}";
701 $cfe = implode (' or ', $tmp);
702 }
703
704 if (! empty($cfe))
705 {
706 $href = makeHref
707 (
708 array
709 (
710 'page'=>'depot',
711 'tab'=>'default',
712 'andor' => 'and',
713 'cfe' => $cfe
714 )
715 );
716 echo '<a href="' . $href . '">' . $refcnt[$key] . '</a>';
717 }
718 else
719 echo $refcnt[$key];
720 }
721 echo "</td><td>${value}</td></tr>\n";
722 $order = $nextorder[$order];
723 }
724 echo "</table>\n<br>";
725 }
726
727 function renderChapterEditor ($tgt_chapter_no)
728 {
729 global $nextorder;
730 function printNewItemTR ()
731 {
732 printOpFormIntro ('add');
733 echo '<tr><td>&nbsp;</td><td>&nbsp;</td><td>';
734 printImageHREF ('add', 'Add new', TRUE);
735 echo "</td>";
736 echo "<td class=tdleft><input type=text name=dict_value size=64></td><td>";
737 printImageHREF ('add', 'Add new', TRUE);
738 echo '</td></tr></form>';
739 }
740 echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
741 $words = readChapter ($tgt_chapter_no, 'r');
742 $refcnt = getChapterRefc ($tgt_chapter_no, array_keys ($words));
743 $order = 'odd';
744 echo "<tr><th>Origin</th><th>Key</th><th>&nbsp;</th><th>Word</th><th>&nbsp;</th></tr>\n";
745 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
746 printNewItemTR();
747 foreach ($words as $key => $value)
748 {
749 echo "<tr class=row_${order}><td>";
750 $order = $nextorder[$order];
751 // Show plain row for stock records, render a form for user's ones.
752 if ($key < 50000)
753 {
754 printImageHREF ('computer');
755 echo "</td><td class=tdright>${key}</td><td>&nbsp;</td><td>${value}</td><td>&nbsp;</td></tr>";
756 continue;
757 }
758 printOpFormIntro ('upd', array ('dict_key' => $key));
759 printImageHREF ('favorite');
760 echo "</td><td class=tdright>${key}</td><td>";
761 // Prevent deleting words currently used somewhere.
762 if ($refcnt[$key])
763 printImageHREF ('nodelete', 'referenced ' . $refcnt[$key] . ' time(s)');
764 else
765 echo getOpLink (array('op'=>'del', 'dict_key'=>$key), '', 'delete', 'Delete word');
766 echo '</td>';
767 echo "<td class=tdleft><input type=text name=dict_value size=64 value='${value}'></td><td>";
768 printImageHREF ('save', 'Save changes', TRUE);
769 echo "</td></tr></form>";
770 }
771 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
772 printNewItemTR();
773 echo "</table>\n<br>";
774 }
775
776 // $v is a $configCache item
777 // prints HTML-formatted varname and description
778 function renderConfigVarName ($v)
779 {
780 echo '<span class="varname">' . $v['varname'] . '</span>';
781 echo '<p class="vardescr">' . $v['description'] . ($v['is_userdefined'] == 'yes' ? '' : ' (system-wide)') . '</p>';
782 }
783
784 function renderUIConfig ()
785 {
786 startPortlet ('Current configuration');
787 echo '<table class="cooltable zebra" border=0 cellpadding=5 cellspacing=0 align=center width="70%">';
788 echo '<tr><th class=tdleft>Option</th><th class=tdleft>Value</th></tr>';
789 foreach (loadConfigCache() as $v)
790 {
791 if ($v['is_hidden'] != 'no')
792 continue;
793 echo '<tr>';
794 echo "<td nowrap valign=top class=tdright>";
795 renderConfigVarName ($v);
796 echo '</td>';
797 echo "<td valign=top class=tdleft>${v['varvalue']}</td></tr>";
798 }
799 echo "</table>\n";
800 finishPortlet();
801 }
802
803 function renderConfigEditor ()
804 {
805 global $pageno;
806 $per_user = ($pageno == 'myaccount');
807 global $configCache;
808 startPortlet ('Current configuration');
809 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='50%'>\n";
810 echo "<tr><th class=tdleft>Option</th>";
811 echo "<th class=tdleft>Value</th></tr>";
812 printOpFormIntro ('upd');
813
814 $i = 0;
815 foreach ($per_user ? $configCache : loadConfigCache() as $v)
816 {
817 if ($v['is_hidden'] != 'no')
818 continue;
819 if ($per_user && $v['is_userdefined'] != 'yes')
820 continue;
821 echo "<input type=hidden name=${i}_varname value='${v['varname']}'>";
822 echo '<tr><td class="tdright">';
823 renderConfigVarName ($v);
824 echo '</td>';
825 echo "<td class=\"tdleft\"><input type=text name=${i}_varvalue value='" . htmlspecialchars ($v['varvalue'], ENT_QUOTES) . "' size=24></td>";
826 echo '<td class="tdleft">';
827 if ($per_user && $v['is_altered'] == 'yes')
828 echo getOpLink (array('op'=>'reset', 'varname'=>$v['varname']), 'reset');
829 echo '</td>';
830 echo "</tr>\n";
831 $i++;
832 }
833 echo "<input type=hidden name=num_vars value=${i}>\n";
834 echo "<tr><td colspan=3>";
835 printImageHREF ('SAVE', 'Save changes', TRUE);
836 echo "</td></tr>";
837 echo "</form>";
838 finishPortlet();
839 }
840
841 function renderUIResetForm()
842 {
843 printOpFormIntro ('go');
844 echo "This button will reset user interface configuration to its defaults (except organization name): ";
845 echo "<input type=submit value='proceed'>";
846 echo "</form>";
847 }
848
849 function serializeTagStats ($taginfo)
850 {
851 $statsdecoder = array
852 (
853 'total' => ' total record(s) linked',
854 'object' => ' object(s)',
855 'rack' => ' rack(s)',
856 'file' => ' file(s)',
857 'user' => ' user account(s)',
858 'ipv6net' => ' IPv6 network(s)',
859 'ipv4net' => ' IPv4 network(s)',
860 'ipv4vs' => ' IPv4 virtual service(s)',
861 'ipv4rspool' => ' IPv4 real server pool(s)',
862 'vst' => ' VLAN switch template(s)',
863 'ipvs' => ' VS group(s)',
864 );
865 $stats = array ("tag ID = ${taginfo['id']}");
866 if ($taginfo['kidc'])
867 $stats[] = "${taginfo['kidc']} sub-tag(s)";
868 if ($taginfo['refcnt']['total'])
869 foreach ($taginfo['refcnt'] as $article => $count)
870 if (array_key_exists ($article, $statsdecoder))
871 $stats[] = $count . $statsdecoder[$article];
872 return implode (', ', $stats);
873 }
874
875 function renderTagRowForViewer ($taginfo, $level = 0)
876 {
877 $self = __FUNCTION__;
878 $trclass = '';
879 if ($level == 0)
880 $trclass .= ' separator';
881 $trclass .= $taginfo['is_assignable'] == 'yes' ? '' : ($taginfo['kidc'] ? ' trnull' : ' trwarning');
882 if (!count ($taginfo['kids']))
883 $level++; // Shift instead of placing a spacer. This won't impact any nested nodes.
884 $refc = $taginfo['refcnt']['total'];
885 echo "<tr class='${trclass}'><td align=left style='padding-left: " . ($level * 16) . "px;'>";
886 if (count ($taginfo['kids']))
887 printImageHREF ('node-expanded-static');
888 echo '<span title="' . serializeTagStats ($taginfo) . '" class="' . getTagClassName ($taginfo['id']) . '">' . $taginfo['tag'];
889 echo ($refc ? " <i>(${refc})</i>" : '') . '</span></td></tr>';
890 foreach ($taginfo['kids'] as $kid)
891 $self ($kid, $level + 1);
892 }
893
894 function renderTagTree ()
895 {
896 echo '<center><table class=tagtree>';
897 foreach (getTagTree() as $taginfo)
898 renderTagRowForViewer ($taginfo);
899 echo '</table></center>';
900 }
901
902 function renderTagRowForEditor ($taginfo, $parent_name = NULL, $level = 0)
903 {
904 $self = __FUNCTION__;
905 if (!count ($taginfo['kids']))
906 $level++; // Idem
907 $trclass = $taginfo['is_assignable'] == 'yes' ? '' : ($taginfo['kidc'] ? ' class=trnull' : ' class=trwarning');
908 echo "<tr${trclass}><td align=left style='padding-left: " . ($level * 16) . "px;'>";
909 if ($taginfo['kidc'])
910 printImageHREF ('node-expanded-static');
911 if ($taginfo['refcnt']['total'] > 0 || $taginfo['kidc'])
912 printImageHREF ('nodestroy', serializeTagStats ($taginfo));
913 else
914 echo getOpLink (array ('op' => 'destroyTag', 'tag_id' => $taginfo['id']), '', 'destroy', 'Delete tag');
915 echo '</td><td>';
916 printOpFormIntro ('updateTag', array ('tag_id' => $taginfo['id']));
917 echo "<input type=text size=48 name=tag_name ";
918 echo "value='${taginfo['tag']}'></td><td class=tdleft>";
919 if ($taginfo['refcnt']['total'])
920 printSelect (array ('yes' => 'yes'), array ('name' => 'is_assignable')); # locked
921 else
922 printSelect (array ('yes' => 'yes', 'no' => 'no'), array ('name' => 'is_assignable'), $taginfo['is_assignable']);
923 echo '</td><td class=tdleft>';
924
925 $poptions = $parent_name === NULL ?
926 array (0 => '-- NONE --') :
927 array ($taginfo['parent_id'] => $parent_name);
928 $sparams = array ('name' => 'parent_id', 'id' => 'nodeid_' . $taginfo['id'], 'class' => 'nodelist-popup');
929 echo getSelect ($poptions, $sparams, $taginfo['parent_id'], FALSE);
930
931 echo '</td><td>' . getImageHREF ('save', 'Save changes', TRUE) . '</form></td></tr>';
932 foreach ($taginfo['kids'] as $kid)
933 $self ($kid, $taginfo['tag'], $level + 1);
934 }
935
936 function addParentNodeOptionsJS ($prefix, $nodetype)
937 {
938 addJS
939 (
940 <<<END
941 function ${prefix}_showselectbox(e) {
942 $(this).load('index.php', {module: 'ajax', ac: 'get-parent-node-options', node_type: '${nodetype}', node_id: this.id});
943 $(this).unbind('mousedown', ${prefix}_showselectbox);
944 }
945 $(document).ready(function () {
946 $('select.nodelist-popup').bind('mousedown', ${prefix}_showselectbox);
947 });
948 END
949 , TRUE
950 );
951 }
952
953 function renderTagTreeEditor ()
954 {
955 function printNewItemTR ($options)
956 {
957 printOpFormIntro ('createTag');
958 echo '<tr>';
959 echo '<td align=left style="padding-left: 16px;">' . getImageHREF ('create', 'Create tag', TRUE) . '</td>';
960 echo '<td><input type=text size=48 name=tag_name></td>';
961 echo '<td class=tdleft>' . getSelect (array ('yes' => 'yes', 'no' => 'no'), array ('name' => 'is_assignable'), 'yes') . '</td>';
962 echo '<td>' . getSelect ($options, array ('name' => 'parent_id')) . '</td>';
963 echo '<td>' . getImageHREF ('create', 'Create tag', TRUE, 120) . '</td>';
964 echo '</tr></form>';
965 }
966 global $taglist;
967 addParentNodeOptionsJS ('tageditor', 'existing tag');
968 $options = getParentNodeOptionsNew ($taglist, 'tag');
969 echo '<br><table cellspacing=0 cellpadding=5 align=center class=widetable>';
970 echo '<tr><th>&nbsp;</th><th>tag name</th><th>assignable</th><th>parent tag</th><th>&nbsp;</th></tr>';
971 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
972 printNewItemTR ($options);
973 foreach (getTagTree() as $taginfo)
974 renderTagRowForEditor ($taginfo);
975 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
976 printNewItemTR ($options);
977 echo '</table>';
978 }
979
980 function renderGraphCycleResolver()
981 {
982 global $pageno;
983 // $fieldmap below does not contain 'parent_id' as it is done by the SELECT.
984 switch ($pageno)
985 {
986 case 'tagtree';
987 global $taglist;
988 $nodelist = $taglist;
989 $textfield = 'tag';
990 $opcode = 'updateTag';
991 $fieldmap = array
992 (
993 'tag_id' => 'id',
994 'tag_name' => 'tag',
995 'is_assignable' => 'is_assignable',
996 );
997 break;
998 default:
999 throw new RackTablesError ('unexpected call to tabhandler function', RackTablesError::INTERNAL);
1000 }
1001 $invalids = getInvalidNodes ($nodelist);
1002 $options = getParentNodeOptionsNew ($nodelist, $textfield);
1003 echo '<br><table cellspacing=0 cellpadding=5 align=center class=widetable>';
1004 echo '<tr><th>node</th><th>current parent node</th><th>new parent node</th><th>&nbsp;</th></tr>';
1005 foreach ($invalids as $node)
1006 {
1007 $formvalues = array();
1008 foreach ($fieldmap as $form_param => $nodefield)
1009 $formvalues[$form_param] = $node[$nodefield];
1010 printOpFormIntro ($opcode, $formvalues);
1011 echo '<tr>';
1012 echo '<td class=tdleft>' . stringForLabel ($node[$textfield]) . '</td>';
1013 echo '<td class="tdleft trerror">' . stringForLabel ($invalids[$node['parent_id']][$textfield]) . '</td>';
1014 echo '<td>' . getSelect ($options, array ('name' => 'parent_id'), $node['parent_id']) . '</td>';
1015 echo '<td>' . getImageHREF ('save', 'Save changes', TRUE) . '</td>';
1016 echo '</tr></form>';
1017 }
1018 echo '</table>';
1019 }
1020
1021 function renderMyAccount ()
1022 {
1023 global $remote_username, $remote_displayname, $expl_tags, $impl_tags, $auto_tags;
1024
1025 startPortlet ('Current user info');
1026 echo '<div style="text-align: left; display: inline-block;">';
1027 echo "<table>";
1028 echo "<tr><th>Login:</th><td>${remote_username}</td></tr>\n";
1029 echo "<tr><th>Name:</th><td>${remote_displayname}</td></tr>\n";
1030 echo "<tr><th>Explicit tags:</th><td>" . serializeTags (getExplicitTagsOnly ($expl_tags)) . "</td></tr>\n";
1031 echo "<tr><th>Implicit tags:</th><td>" . serializeTags ($impl_tags) . "</td></tr>\n";
1032 echo "<tr><th>Automatic tags:</th><td>" . serializeTags ($auto_tags) . "</td></tr>\n";
1033 echo '</table></div>';
1034 }
1035
1036 function renderMyPasswordEditor ()
1037 {
1038 printOpFormIntro ('changeMyPassword');
1039 echo '<table border=0 align=center>';
1040 echo "<tr><th class=tdright>Current password (*):</th><td><input type=password name=oldpassword></td></tr>";
1041 echo "<tr><th class=tdright>New password (*):</th><td><input type=password name=newpassword1></td></tr>";
1042 echo "<tr><th class=tdright>New password again (*):</th><td><input type=password name=newpassword2></td></tr>";
1043 echo "<tr><td colspan=2 align=center><input type=submit value='Change'></td></tr>";
1044 echo '</table></form>';
1045 }
1046
1047 function renderMyQuickLinks ()
1048 {
1049 global $indexlayout, $page;
1050 startPortlet ('Items to display in page header');
1051 echo '<div style="text-align: left; display: inline-block;">';
1052 printOpFormIntro ('save');
1053 echo '<ul class="qlinks-form">';
1054 $active_items = explode (',', getConfigVar ('QUICK_LINK_PAGES'));
1055 $items = array();
1056 foreach ($indexlayout as $row)
1057 foreach ($row as $ypageno)
1058 {
1059 $items[$ypageno] = getPageName ($ypageno);
1060 if ($ypageno == 'config') // expand
1061 foreach ($page as $subpageno => $subpage)
1062 if (array_fetch ($subpage, 'parent', NULL) == $ypageno)
1063 $items[$subpageno] = $items[$ypageno] . ': ' . getPageName ($subpageno);
1064 }
1065 foreach ($items as $ypageno => $pagename)
1066 {
1067 $checked_state = in_array ($ypageno, $active_items) ? 'checked' : '';
1068 echo "<li><label><input type='checkbox' name='page_list[]' value='$ypageno' $checked_state>" . $pagename . "</label></li>\n";
1069 }
1070 echo '</ul>';
1071 printImageHREF ('SAVE', 'Save changes', TRUE);
1072 echo '</form></div>';
1073 finishPortlet();
1074 }
1075
1076 function renderCactiConfig()
1077 {
1078 $columns = array
1079 (
1080 array ('th_text' => 'base URL', 'row_key' => 'base_url'),
1081 array ('th_text' => 'username', 'row_key' => 'username'),
1082 array ('th_text' => 'graph(s)', 'row_key' => 'num_graphs', 'td_class' => 'tdright'),
1083 );
1084 $servers = getCactiServers();
1085 startPortlet ('Cacti servers (' . count ($servers) . ')');
1086 renderTableViewer ($columns, $servers);
1087 finishPortlet();
1088 }
1089
1090 function renderCactiServersEditor()
1091 {
1092 function printNewItemTR ()
1093 {
1094 printOpFormIntro ('add');
1095 echo '<tr>' .
1096 '<td>' . getImageHREF ('create', 'add a new server', TRUE) . '</td>' .
1097 '<td><input type=text size=48 name=base_url></td>' .
1098 '<td><input type=text size=24 name=username></td>' .
1099 '<td><input type=password size=24 name=password></td>' .
1100 '<td>&nbsp;</td>' .
1101 '<td>' . getImageHREF ('create', 'add a new server', TRUE) . '</td>' .
1102 '</tr></form>';
1103 }
1104 echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
1105 echo '<tr>' .
1106 '<th>&nbsp;</th>' .
1107 '<th>base URL</th>' .
1108 '<th>username</th>' .
1109 '<th>password</th>' .
1110 '<th>graph(s)</th>' .
1111 '<th>&nbsp;</th>' .
1112 '</tr>';
1113 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
1114 printNewItemTR();
1115 foreach (getCactiServers() as $server)
1116 {
1117 printOpFormIntro ('upd', array ('id' => $server['id']));
1118 echo '<tr><td>';
1119 if ($server['num_graphs'])
1120 printImageHREF ('nodestroy', 'cannot delete, graphs exist');
1121 else
1122 echo getOpLink (array ('op' => 'del', 'id' => $server['id']), '', 'destroy', 'delete this server');
1123 echo '</td>';
1124 echo '<td><input type=text size=48 name=base_url value="' . htmlspecialchars ($server['base_url'], ENT_QUOTES, 'UTF-8') . '"></td>';
1125 echo '<td><input type=text size=24 name=username value="' . htmlspecialchars ($server['username'], ENT_QUOTES, 'UTF-8') . '"></td>';
1126 echo '<td><input type=password size=24 name=password value="' . htmlspecialchars ($server['password'], ENT_QUOTES, 'UTF-8') . '"></td>';
1127 echo "<td class=tdright>${server['num_graphs']}</td>";
1128 echo '<td>' . getImageHREF ('save', 'update this server', TRUE) . '</td>';
1129 echo '</tr></form>';
1130 }
1131 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
1132 printNewItemTR();
1133 echo '</table>';
1134 }
1135
1136 function renderMuninConfig()
1137 {
1138 $columns = array
1139 (
1140 array ('th_text' => 'base URL', 'row_key' => 'base_url', 'td_maxlen' => 150),
1141 array ('th_text' => 'graph(s)', 'row_key' => 'num_graphs', 'td_class' => 'tdright'),
1142 );
1143 $servers = getMuninServers();
1144 startPortlet ('Munin servers (' . count ($servers) . ')');
1145 renderTableViewer ($columns, $servers);
1146 finishPortlet();
1147 }
1148
1149 function renderMuninServersEditor()
1150 {
1151 function printNewItemTR()
1152 {
1153 printOpFormIntro ('add');
1154 echo '<tr>' .
1155 '<td>' . getImageHREF ('create', 'add a new server', TRUE) . '</td>' .
1156 '<td><input type=text size=48 name=base_url></td>' .
1157 '<td>&nbsp;</td>' .
1158 '<td>' . getImageHREF ('create', 'add a new server', TRUE) . '</td>' .
1159 '</tr></form>';
1160 }
1161 echo '<table cellspacing=0 cellpadding=5 align=center class=widetable>';
1162 echo '<tr>' .
1163 '<th>&nbsp;</th>' .
1164 '<th>base URL</th>' .
1165 '<th>graph(s)</th>' .
1166 '<th>&nbsp;</th>' .
1167 '</tr>';
1168 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
1169 printNewItemTR();
1170 foreach (getMuninServers() as $server)
1171 {
1172 printOpFormIntro ('upd', array ('id' => $server['id']));
1173 echo '<tr><td>';
1174 if ($server['num_graphs'])
1175 printImageHREF ('nodestroy', 'cannot delete, graphs exist');
1176 else
1177 echo getOpLink (array ('op' => 'del', 'id' => $server['id']), '', 'destroy', 'delete this server');
1178 echo '</td>';
1179 echo '<td><input type=text size=48 name=base_url value="' . htmlspecialchars ($server['base_url'], ENT_QUOTES, 'UTF-8') . '"></td>';
1180 echo "<td class=tdright>${server['num_graphs']}</td>";
1181 echo '<td>' . getImageHREF ('save', 'update this server', TRUE) . '</td>';
1182 echo '</tr></form>';
1183 }
1184 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
1185 printNewItemTR();
1186 echo '</table>';
1187 }
1188
1189 ?>