source: tailbone/tailbone/templates/themes/falafel/base.mako @ 839f6af

Last change on this file since 839f6af was 839f6af, checked in by Lance Edgar <ledgar@…>, 7 months ago

Add basic "DB picker" support, for views which allow multiple engines

i.e. whichever engine is "current" will determine where data comes from

  • Property mode set to 100644
File size: 15.6 KB
Line 
1## -*- coding: utf-8; -*-
2<%namespace file="/grids/nav.mako" import="grid_index_nav" />
3<%namespace file="/feedback_dialog_buefy.mako" import="feedback_dialog" />
4<%namespace file="/autocomplete.mako" import="tailbone_autocomplete_template" />
5<%namespace name="base_meta" file="/base_meta.mako" />
6<!DOCTYPE html>
7<html lang="en">
8  <head>
9    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
10    <title>${base_meta.global_title()} &raquo; ${capture(self.title)|n}</title>
11    ${base_meta.favicon()}
12    ${self.header_core()}
13
14    % if background_color:
15        <style type="text/css">
16          body, .navbar, .footer {
17              background-color: ${background_color};
18          }
19        </style>
20    % endif
21
22    % if not request.rattail_config.production():
23        <style type="text/css">
24          body, .navbar, .footer {
25            background-image: url(${request.static_url('tailbone:static/img/testing.png')});
26          }
27        </style>
28    % endif
29
30    ${self.head_tags()}
31  </head>
32
33  <body>
34
35    ## TODO: should move template to JS, then can postpone the JS
36    ${tailbone_autocomplete_template()}
37    ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.autocomplete.js') + '?ver={}'.format(tailbone.__version__))}
38
39    <header>
40
41      <nav class="navbar" role="navigation" aria-label="main navigation">
42
43        <div class="navbar-brand">
44          <a class="navbar-item" href="${url('home')}">
45            ${base_meta.header_logo()}
46            ${base_meta.global_title()}
47          </a>
48          <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
49            <span aria-hidden="true"></span>
50            <span aria-hidden="true"></span>
51            <span aria-hidden="true"></span>
52          </a>
53        </div>
54
55        <div class="navbar-menu">
56          <div class="navbar-start">
57
58            % for topitem in menus:
59                % if topitem.is_link:
60                    ${h.link_to(topitem.title, topitem.url, target=topitem.target, class_='navbar-item')}
61                % else:
62                    <div class="navbar-item has-dropdown is-hoverable">
63                      <a class="navbar-link">${topitem.title}</a>
64                      <div class="navbar-dropdown">
65                        % for subitem in topitem.items:
66                            % if subitem.is_sep:
67                                <hr class="navbar-divider">
68                            % else:
69                                ${h.link_to(subitem.title, subitem.url, class_='navbar-item', target=subitem.target)}
70                            % endif
71                        % endfor
72                      </div>
73                    </div>
74                % endif
75            % endfor
76
77          </div><!-- navbar-start -->
78          <div class="navbar-end">
79
80            ## User Menu
81            % if request.user:
82                <div class="navbar-item has-dropdown is-hoverable">
83                  % if messaging_enabled:
84                      <a class="navbar-link ${'root-user' if request.is_root else ''}">${request.user}${" ({})".format(inbox_count) if inbox_count else ''}</a>
85                  % else:
86                      <a class="navbar-link ${'root-user' if request.is_root else ''}">${request.user}</a>
87                  % endif
88                  <div class="navbar-dropdown">
89                    % if request.is_root:
90                        ${h.link_to("Stop being root", url('stop_root'), class_='navbar-item root-user')}
91                    % elif request.is_admin:
92                        ${h.link_to("Become root", url('become_root'), class_='navbar-item root-user')}
93                    % endif
94                    % if messaging_enabled:
95                        ${h.link_to("Messages{}".format(" ({})".format(inbox_count) if inbox_count else ''), url('messages.inbox'), class_='navbar-item')}
96                    % endif
97                    ${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
98                    ${h.link_to("Logout", url('logout'), class_='navbar-item')}
99                  </div>
100                </div>
101            % else:
102                ${h.link_to("Login", url('login'), class_='navbar-item')}
103            % endif
104
105          </div><!-- navbar-end -->
106        </div>
107      </nav>
108
109      <nav class="level" style="margin: 0.5rem auto;">
110        <div class="level-left">
111
112          ## Current Context
113          <div id="current-context" class="level-item">
114            % if master:
115                % if master.listing:
116                    <span>${index_title}</span>
117                % else:
118                    ${h.link_to(index_title, index_url)}
119                    % if parent_url is not Undefined:
120                        <span>&raquo;</span>
121                        ${h.link_to(parent_title, parent_url)}
122                    % elif instance_url is not Undefined:
123                        <span>&raquo;</span>
124                        ${h.link_to(instance_title, instance_url)}
125                    % endif
126                    % if master.viewing and grid_index:
127                        ${grid_index_nav()}
128                    % endif
129                % endif
130            % elif index_title:
131                <span>${index_title}</span>
132            % endif
133          </div>
134
135          % if expose_db_picker is not Undefined and expose_db_picker:
136              <div class="level-item">
137                <p>DB:</p>
138              </div>
139              <div class="level-item">
140                ${h.form(url('change_db_engine'))}
141                ${h.csrf_token(request)}
142                ${h.hidden('engine_type', value=master.engine_type_key)}
143                <div class="select">
144                  ${h.select('dbkey', db_picker_selected, db_picker_options, id='db-picker')}
145                </div>
146                ${h.end_form()}
147              </div>
148          % endif
149
150        </div><!-- level-left -->
151        <div class="level-right">
152
153          ## Quickie Lookup
154          % if quickie is not Undefined and quickie and request.has_perm(quickie.perm):
155              <div class="level-item">
156                ${h.form(quickie.url, method="get")}
157                <div class="level">
158                  <div class="level-right">
159                    <div class="level-item">
160                      ${h.text('entry', placeholder=quickie.placeholder, autocomplete='off')}
161                    </div>
162                    <div class="level-item">
163                      <button type="submit" class="button is-primary">
164                        <span class="icon is-small">
165                          <i class="fas fa-search"></i>
166                        </span>
167                        <span>Lookup</span>
168                      </button>
169                    </div>
170                  </div>
171                </div>
172                ${h.end_form()}
173              </div>
174          % endif
175
176          ## Theme Picker
177          % if expose_theme_picker and request.has_perm('common.change_app_theme'):
178              <div class="level-item">
179                ${h.form(url('change_theme'), method="post")}
180                ${h.csrf_token(request)}
181                Theme:
182                <div class="theme-picker">
183                  <div class="select">
184                    ${h.select('theme', theme, options=theme_picker_options, id='theme-picker')}
185                  </div>
186                </div>
187                ${h.end_form()}
188              </div>
189          % endif
190
191          ## Help Button
192          % if help_url is not Undefined and help_url:
193              <div class="level-item">
194                ${h.link_to("Help", help_url, target='_blank', class_='button')}
195              </div>
196          % endif
197
198          ## Feedback Button / Dialog
199          ${h.javascript_link(request.static_url('tailbone:static/themes/falafel/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
200          ${feedback_dialog()}
201          <div id="feedback-app">
202            <feedback-form action="${url('feedback')}">
203            </feedback-form>
204          </div>
205          <script type="text/javascript">
206            new Vue({el: '#feedback-app'})
207          </script>
208
209        </div><!-- level-right -->
210      </nav><!-- level -->
211    </header>
212
213    ## Page Title
214    <section id="content-title" class="hero is-primary">
215      <div class="container">
216        % if capture(self.content_title):
217
218            % if show_prev_next is not Undefined and show_prev_next:
219                <div style="float: right;">
220                  % if prev_url:
221                      ${h.link_to(u"« Older", prev_url, class_='button autodisable')}
222                  % else:
223                      ${h.link_to(u"« Older", '#', class_='button', disabled='disabled')}
224                  % endif
225                  % if next_url:
226                      ${h.link_to(u"Newer »", next_url, class_='button autodisable')}
227                  % else:
228                      ${h.link_to(u"Newer »", '#', class_='button', disabled='disabled')}
229                  % endif
230                </div>
231            % endif
232
233            <h1 class="title">${self.content_title()}</h1>
234        % endif
235      </div>
236    </section>
237
238    <div class="content-wrapper">
239
240    ## Page Body
241    <section id="page-body">
242
243      % if request.session.peek_flash('error'):
244          % for error in request.session.pop_flash('error'):
245              <div class="notification is-warning">
246                <!-- <button class="delete"></button> -->
247                ${error}
248              </div>
249          % endfor
250      % endif
251
252      % if request.session.peek_flash():
253          % for msg in request.session.pop_flash():
254              <div class="notification is-info">
255                <!-- <button class="delete"></button> -->
256                ${msg}
257              </div>
258          % endfor
259      % endif
260
261      ${self.body()}
262    </section>
263
264    ## Footer
265    <footer class="footer">
266      <div class="content">
267        ${base_meta.footer()}
268      </div>
269    </footer>
270
271    </div><!-- content-wrapper -->
272
273  </body>
274</html>
275
276<%def name="title()"></%def>
277
278<%def name="content_title()">
279  ${self.title()}
280</%def>
281
282<%def name="header_core()">
283
284  ${self.core_javascript()}
285  ${self.extra_javascript()}
286  ${self.core_styles()}
287  ${self.extra_styles()}
288
289  ## TODO: should this be elsewhere / more customizable?
290  % if dform is not Undefined:
291      <% resources = dform.get_widget_resources() %>
292      % for path in resources['js']:
293          ${h.javascript_link(request.static_url(path))}
294      % endfor
295      % for path in resources['css']:
296          ${h.stylesheet_link(request.static_url(path))}
297      % endfor
298  % endif
299</%def>
300
301<%def name="core_javascript()">
302  ${self.jquery()}
303  ${self.vuejs()}
304  ${self.buefy()}
305  ${self.fontawesome()}
306
307  ## some commonly-useful logic for detecting (non-)numeric input
308  ${h.javascript_link(request.static_url('tailbone:static/js/numeric.js') + '?ver={}'.format(tailbone.__version__))}
309
310  ## Tailbone / Buefy stuff
311  ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.datepicker.js') + '?ver={}'.format(tailbone.__version__))}
312  ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.numericinput.js') + '?ver={}'.format(tailbone.__version__))}
313  ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.oncebutton.js') + '?ver={}'.format(tailbone.__version__))}
314  ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.timepicker.js') + '?ver={}'.format(tailbone.__version__))}
315  ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.grid.js') + '?ver={}'.format(tailbone.__version__))}
316
317  <script type="text/javascript">
318    var session_timeout = ${request.get_session_timeout() or 'null'};
319    var logout_url = '${request.route_url('logout')}';
320    var noop_url = '${request.route_url('noop')}';
321    % if expose_db_picker is not Undefined and expose_db_picker:
322        $(function() {
323            $('#db-picker').change(function() {
324                $(this).parents('form:first').submit();
325            });
326        });
327    % endif
328    % if expose_theme_picker and request.has_perm('common.change_app_theme'):
329        $(function() {
330            $('#theme-picker').change(function() {
331                $(this).parents('form:first').submit();
332            });
333        });
334    % endif
335    $(function() {
336        ## NOTE: this code was copied from
337        ## https://bulma.io/documentation/components/navbar/#navbar-menu
338        $('.navbar-burger').click(function() {
339            $('.navbar-burger').toggleClass('is-active');
340            $('.navbar-menu').toggleClass('is-active');
341        });
342    });
343  </script>
344</%def>
345
346<%def name="jquery()">
347  ## jQuery 1.12.4
348  ${h.javascript_link('https://code.jquery.com/jquery-1.12.4.min.js')}
349</%def>
350
351<%def name="vuejs()">
352  ## Vue.js (latest)
353  ${h.javascript_link('https://unpkg.com/vue')}
354
355  ## vue-resource
356  ## (needed for e.g. this.$http.get() calls, used by grid at least)
357  ${h.javascript_link('https://cdn.jsdelivr.net/npm/vue-resource@1.5.1')}
358</%def>
359
360<%def name="buefy()">
361  ${h.javascript_link('https://unpkg.com/buefy@0.7.4/dist/buefy.min.js')}
362</%def>
363
364<%def name="fontawesome()">
365  <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
366</%def>
367
368<%def name="extra_javascript()"></%def>
369
370<%def name="core_styles()">
371
372  ${self.buefy_styles()}
373
374  ${h.stylesheet_link(request.static_url('tailbone:static/themes/bobcat/css/base.css') + '?ver={}'.format(tailbone.__version__))}
375  ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/layout.css') + '?ver={}'.format(tailbone.__version__))}
376  ${h.stylesheet_link(request.static_url('tailbone:static/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
377  ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
378  ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.rowstatus.css') + '?ver={}'.format(tailbone.__version__))}
379##   ${h.stylesheet_link(request.static_url('tailbone:static/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
380  ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
381  ${h.stylesheet_link(request.static_url('tailbone:static/themes/bobcat/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
382  ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
383  ${h.stylesheet_link(request.static_url('tailbone:static/css/diffs.css') + '?ver={}'.format(tailbone.__version__))}
384
385  <style type="text/css">
386    .filters .filter-fieldname {
387        min-width: ${filter_fieldname_width};
388        justify-content: left;
389    }
390    .filters .filter-verb {
391        min-width: ${filter_verb_width};
392    }
393  </style>
394</%def>
395
396<%def name="buefy_styles()">
397  ## Buefy 0.7.4
398  ${h.stylesheet_link('https://unpkg.com/buefy@0.7.4/dist/buefy.min.css')}
399</%def>
400
401## TODO: this is only being referenced by the progress template i think?
402## (so, should make a Buefy progress page at least)
403<%def name="jquery_theme()">
404  ${h.stylesheet_link('https://code.jquery.com/ui/1.11.4/themes/dark-hive/jquery-ui.css')}
405</%def>
406
407<%def name="extra_styles()"></%def>
408
409<%def name="head_tags()"></%def>
410
411<%def name="wtfield(form, name, **kwargs)">
412  <div class="field-wrapper${' error' if form[name].errors else ''}">
413    <label for="${name}">${form[name].label}</label>
414    <div class="field">
415      ${form[name](**kwargs)}
416    </div>
417  </div>
418</%def>
419
420<%def name="simple_field(label, value)">
421  ## TODO: keep this? only used by personal profile view currently
422  ## (although could be useful for any readonly scenario)
423  <div class="field-wrapper">
424    <div class="field-row">
425      <label>${label}</label>
426      <div class="field">
427        ${'' if value is None else value}
428      </div>
429    </div>
430  </div>
431</%def>
Note: See TracBrowser for help on using the repository browser.