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