Perf dashboard should support authentication via a slave password
[WebKit-https.git] / Websites / perf.webkit.org / public / include / admin-header.php
1 <?php
2
3 require_once('db.php');
4 require_once('manifest.php');
5
6 ?><!DOCTYPE html>
7 <html>
8 <head>
9 <title>WebKit Perf Monitor</title>
10 <link rel="stylesheet" href="/common.css">
11 <link rel="stylesheet" href="/admin/admin.css">
12 </head>
13 <body>
14 <header id="title">
15 <h1><a href="/">WebKit Perf Monitor</a></h1>
16 <ul>
17     <li><a href="/admin/platforms">Platforms</a></li>
18     <li><a href="/admin/tests">Tests</a></li>
19     <li><a href="/admin/aggregators">Aggregators</a></li>
20     <li><a href="/admin/builders">Builders</a></li>
21     <li><a href="/admin/build-slaves">Slaves</a></li>
22     <li><a href="/admin/repositories">Repositories</a></li>
23     <li><a href="/admin/bug-trackers">Bug Trackers</a></li>
24 </ul>
25 </header>
26
27 <div id="mainContents">
28 <?php
29
30 function notice($message) {
31     echo "<p class='notice'>$message</p>";
32 }
33
34 $db = new Database;
35 if (!$db->connect()) {
36     notice('Failed to connect to the database');
37     $db = NULL;
38 } else
39     $action = array_key_exists('action', $_POST) ? $_POST['action'] : NULL;
40
41 function execute_query_and_expect_one_row_to_be_affected($query, $params, $success_message, $failure_message) {
42     global $db;
43
44     foreach ($params as &$param) {
45         if ($param == '')
46             $param = NULL;
47     }
48
49     $affected_rows = $db->query_and_get_affected_rows($query, $params);
50     if ($affected_rows) {
51         assert('$affected_rows == 1');
52         notice($success_message);
53         return true;
54     }
55
56     notice($failure_message);
57     return false;
58 }
59
60 function update_field($table, $prefix, $field_name, $new_value = NULL) {
61     global $db;
62
63     if (!array_key_exists('id', $_POST))
64         return FALSE;
65
66     $id = intval($_POST['id']);
67     $prefixed_field_name = $prefix . '_' . $field_name;
68     $id_field_name = $prefix . '_id';
69
70     if ($new_value == NULL) {
71         if (array_get($_POST, 'updated-column') != $field_name && !array_key_exists($field_name, $_POST))
72             return FALSE;
73         $new_value = array_get($_POST, $field_name);
74     }
75
76     execute_query_and_expect_one_row_to_be_affected("UPDATE $table SET $prefixed_field_name = \$2 WHERE $id_field_name = \$1",
77         array($id, $new_value),
78         "Updated the $prefix $id",
79         "Could not update $prefix $id");
80
81     return TRUE;
82 }
83
84 function regenerate_manifest() {
85     global $db;
86
87     $generator = new ManifestGenerator($db);
88     if (!$generator->generate()) {
89         notice("Failed to generate the manifest (before trying to write into the filesystem).");
90         return FALSE;
91     }
92
93     if (!$generator->store()) {
94         notice("Failed to save the generated manifest into the filesystem");
95         return FALSE;
96     }
97
98     return TRUE;
99 }
100
101 class AdministrativePage {
102     private $table;
103     private $prefix;
104     private $column_to_be_ordered_by;
105     private $column_info;
106
107     function __construct($db, $table, $prefix, $column_info) {
108         $this->db = $db;
109         $this->table = $table;
110         $this->prefix = $prefix;
111         $this->column_info = $column_info;
112     }
113
114     private function name_to_titlecase($name) {
115         return ucwords(str_replace('_', ' ', $name));
116     }
117
118     private function column_label($name) {
119         return array_get($this->column_info[$name], 'label', $this->name_to_titlecase($name));
120     }
121
122     private function render_form_control_for_column($editing_mode, $name, $value = '', $show_update_button_if_needed = FALSE) {
123         $show_update_button = FALSE;
124         switch ($editing_mode) {
125         case 'text':
126             echo <<< END
127 <textarea name="$name" rows="7" cols="50">$value</textarea><br>
128 END;
129             $show_update_button = $show_update_button_if_needed;
130             break;
131         case 'boolean':
132             $checkedness = $this->db->is_true($value) ? ' checked' : '';
133             echo <<< END
134 <input type="checkbox" name="$name"$checkedness>
135 END;
136             $show_update_button = $show_update_button_if_needed;
137             break;
138         case 'url':
139             echo <<< END
140 <input type="text" name="$name" value="$value" size="70">
141 END;
142             break;
143         default:
144             assert($editing_mode == 'string');
145             echo <<< END
146 <input type="text" name="$name" value="$value">
147 END;
148         }
149
150         if ($show_update_button) {
151             echo <<< END
152
153 <button type="submit" name="action" value="update">Update</button>
154 END;
155         }
156     }
157
158     function render_table($column_to_be_ordered_by) {
159         $column_names = array_keys($this->column_info);
160         $column_to_subcolumn_names = array();
161         foreach ($column_names as $name) {
162             if (array_get($this->column_info[$name], 'pre_insertion'))
163                 continue;
164             $subcolumns = array_get($this->column_info[$name], 'subcolumns', array());
165             if (!$subcolumns || !array_get($this->column_info[$name], 'custom'))
166                 continue;
167             $column_to_subcolumn_names[$name] = $subcolumns;
168         }
169
170         $rowspan_if_needed = $column_to_subcolumn_names ? ' rowspan="2"' : '';
171
172         $headers = "<tr><td$rowspan_if_needed>ID</td>";
173         foreach ($column_names as $name) {
174             if (array_get($this->column_info[$name], 'pre_insertion'))
175                 continue;
176             $label = htmlspecialchars($this->column_label($name));
177             if (array_get($column_to_subcolumn_names, $name)) {
178                 $count = count($column_to_subcolumn_names[$name]);
179                 $headers .= "<td colspan=\"$count\">$label</td>";
180             } else
181                 $headers .= "<td$rowspan_if_needed>$label</td>";
182         }
183         $headers .= "</tr>\n";
184
185         if ($column_to_subcolumn_names) {
186             $headers .= '<tr>';
187             foreach ($column_names as $name) {
188                 $subcolumn_names = array_get($column_to_subcolumn_names, $name);
189                 if (!$subcolumn_names)
190                     continue;
191                 foreach ($subcolumn_names as $label)
192                     $headers .= '<td>' . htmlspecialchars($label) . '</td>';
193             }
194             $headers .= "</tr>\n";
195         }
196
197         echo <<< END
198 <table>
199 <thead>
200 $headers
201 </thead>
202 <tbody>
203
204 END;
205
206         assert(ctype_alnum_underscore($column_to_be_ordered_by));
207         $rows = $this->db->fetch_table($this->table, $this->prefix . '_' . $column_to_be_ordered_by);
208         if ($rows) {
209             foreach ($rows as $row) {
210                 $id = intval($row[$this->prefix . '_id']);
211
212                 $custom_cells_list = array();
213                 $maximum_rows = 1;
214                 foreach ($column_names as $name) {
215                     if (array_get($this->column_info[$name], 'pre_insertion'))
216                         continue;
217
218                     if ($custom = array_get($this->column_info[$name], 'custom')) {
219                         $custom_cells_list[$name] = $custom($row);
220                         $maximum_rows = max($maximum_rows, count($custom_cells_list[$name]));
221                     }
222                 }
223
224                 $rowspan_if_needed = $maximum_rows > 1 ? ' rowspan="' . $maximum_rows . '"' : '';
225
226                 echo "<tr>\n<td$rowspan_if_needed>$id</td>\n";
227                 foreach ($column_names as $name) {
228                     if (array_get($this->column_info[$name], 'pre_insertion'))
229                         continue;
230
231                     if (array_key_exists($name, $custom_cells_list)) {
232                         $this->render_custom_cells(array_get($column_to_subcolumn_names, $name), $custom_cells_list[$name], 0);
233                         continue;
234                     }
235
236                     $value = htmlspecialchars(array_get($row, $this->prefix . '_' . $name), ENT_QUOTES);
237                     $editing_mode = array_get($this->column_info[$name], 'editing_mode');
238                     if (!$editing_mode) {
239                         echo "<td$rowspan_if_needed>$value</td>\n";
240                         continue;
241                     }
242
243                     echo <<< END
244 <td$rowspan_if_needed>
245 <form method="POST">
246 <input type="hidden" name="id" value="$id">
247 <input type="hidden" name="action" value="update">
248 <input type="hidden" name="updated-column" value="$name">
249
250 END;
251                     $this->render_form_control_for_column($editing_mode, $name, $value, TRUE);
252                     echo "</form></td>\n";
253
254                 }
255                 echo "</tr>\n";
256
257                 for ($row = 1; $row < $maximum_rows; $row++) {
258                     echo "<tr>\n";
259                     foreach ($column_names as $name) {
260                         if (array_key_exists($name, $custom_cells_list))
261                             $this->render_custom_cells(array_get($column_to_subcolumn_names, $name), $custom_cells_list[$name], $row);
262                     }
263                     echo "</tr>\n";
264                 }
265             }
266         }
267         echo <<< END
268 </tbody>
269 </table>
270 END;
271     }
272
273     function render_custom_cells($subcolum_names, $rows, $row_index) {
274         $cells = array_get($rows, $row_index, array());
275         if (!is_array($cells))
276             $cells = array($cells);
277
278         if ($subcolum_names && count($cells) <= 1) {
279             $colspan = count($subcolum_names);
280             $content = $cells ? $cells[0] : '';
281             echo "<td colspan=\"$colspan\">$content</td>\n";
282             return;
283         }
284
285         for ($i = 0; $i < ($subcolum_names ? count($subcolum_names) : 1); $i++)
286             echo '<td>' . array_get($cells, $i, '') . '</td>';
287         echo "\n";
288     }
289
290     function render_form_to_add($title = NULL) {
291
292         if (!$title) # Can't use the table name since it needs to be singular.
293             $title = 'New ' . $this->name_to_titlecase($this->prefix);
294
295 echo <<< END
296 <section class="action-field">
297 <h2>$title</h2>
298 <form method="POST">
299
300 END;
301         foreach (array_keys($this->column_info) as $name) {
302             $editing_mode = array_get($this->column_info[$name], 'editing_mode');
303             if (array_get($this->column_info[$name], 'custom') || !$editing_mode)
304                 continue;
305             if (array_get($this->column_info[$name], 'post_insertion'))
306                 continue;
307
308             $label = htmlspecialchars($this->column_label($name));
309             echo "<label>$label<br>\n";
310             $this->render_form_control_for_column($editing_mode, $name);
311             echo "</label><br>\n";
312         }
313
314 echo <<< END
315
316 <button type="submit" name="action" value="add">Add</button>
317 </form>
318 </section>
319 END;
320
321     }
322
323 }
324
325 ?>