westferry_ui_PYTHON = \
src/westferry/ui/__init__.py \
- src/westferry/ui/base.py
+ src/westferry/ui/base.py \
+ src/westferry/ui/menu.py \
+ src/westferry/ui/utils.py
westferry_uidir = $(pythondir)/westferry/ui
+# - templates ------------------------------------------------------------------
+
+templatesdir = $(datadir)/westferry/templates
+
+dist_templates_DATA = \
+ src/templates/base.html
+
+templates_modulesdir = $(templatesdir)/modules
+
+templates_modules_menudir = $(templates_modulesdir)/menu
+
+dist_templates_modules_menu_DATA = \
+ src/templates/modules/menu/sidebar.html
+
+ui_modulesdir = $(datadir)/westferry/templates/modules
+
+ui_modules_DATA =
+
+dist_webroot_css_DATA = \
+ src/styles/westferry.css
+
# - third party ----------------------------------------------------------------
# We currently ship a copy of Bootstrap which eventually has to go away
-dist_webroot_css_DATA = \
+dist_webroot_css_DATA += \
src/third-party/bootstrap/css/bootstrap.min.css
EXTRA_DIST += \
# Also shipping jQuery
-dist_webroot_scripts_DATA = \
+dist_webroot_scripts_DATA += \
src/third-party/jquery.min.js
# ------------------------------------------------------------------------------
'|configsdir=$(configsdir)|' \
'|bindir=$(bindir)|' \
'|datadir=$(datadir)|' \
+ '|templatesdir=$(templatesdir)|' \
'|webrootdir=$(webrootdir)|'
SED_PROCESS = \
--- /dev/null
+/*
+ * Base structure
+ */
+
+/* Move down content because we have a fixed navbar that is 50px tall */
+body {
+ padding-top: 50px;
+}
+
+/*
+ * Global add-ons
+ */
+
+.sub-header {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+/*
+ * Top navigation
+ * Hide default border to remove 1px line.
+ */
+.navbar-fixed-top {
+ border: 0;
+}
+
+/*
+ * Main content
+ */
+
+.main {
+ padding: 20px;
+}
+
+@media (min-width: 768px) {
+ .main {
+ padding-right: 40px;
+ padding-left: 40px;
+ }
+}
+
+.main .page-header {
+ margin-top: 0;
+}
+
+/* Make sidebar nav vertical */
+@media (min-width: 768px) {
+ .sidebar-nav .navbar .navbar-collapse {
+ padding: 0;
+ max-height: none;
+ }
+
+ .sidebar-nav .navbar ul {
+ float: none;
+ }
+
+ .sidebar-nav .navbar ul:not {
+ display: block;
+ }
+
+ .sidebar-nav .navbar li {
+ float: none;
+ display: block;
+ }
+
+ .sidebar-nav .navbar li a {
+ padding-top: 12px;
+ padding-bottom: 12px;
+ }
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+
+ <meta name="description" content="">
+ <meta name="author" content="">
+ <link rel="icon" href="/favicon.ico">
+
+ <title>Dashboard Template for Bootstrap</title>
+
+ <link href="{{ static_url("css/bootstrap.min.css") }}" rel="stylesheet">
+ <link href="{{ static_url("css/westferry.css") }}" rel="stylesheet">
+ </head>
+
+ <body>
+ <nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+ <span class="sr-only">{{ _("Toggle navigation") }}</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+
+ <a class="navbar-brand" href="/">{{ hostname() }}</a>
+ </div>
+ <div id="navbar" class="navbar-collapse collapse">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="#">Dashboard</a></li>
+ <li><a href="#">Settings</a></li>
+ <li><a href="#">Profile</a></li>
+ <li><a href="#">Help</a></li>
+ </ul>
+ </div>
+ </div>
+ </nav>
+
+ <div class="container">
+ <div class="row">
+ <div class="col-sm-3 col-md-2">
+ {% module SidebarMenu() %}
+ </div>
+
+ <div class="col-sm-9 col-md-10 main">
+ {% block main %}
+ <h1 class="page-header">Dashboard</h1>
+ {% end %}
+ </div>
+ </div>
+ </div>
+
+ <script src="{{ static_url("scripts/jquery.min.js") }}"></script>
+ <script src="{{ static_url("scripts/bootstrap.min.js") }}"></script>
+ </body>
+</html>
--- /dev/null
+<div class="sidebar-nav">
+ <div class="navbar navbar-default" role="navigation">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".sidebar-navbar-collapse">
+ <span class="sr-only">{{ _("Toggle navigation") }}</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <span class="visible-xs navbar-brand">{{ _("Sidebar menu") }}</span>
+ </div>
+
+ <div class="navbar-collapse collapse sidebar-navbar-collapse">
+ <ul class="nav navbar-nav">
+ <li class="active"><a href="#">Menu Item 1</a></li>
+ <li><a href="#">Menu Item 2</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="#">Action</a></li>
+ <li><a href="#">Another action</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li class="divider"></li>
+ <li class="dropdown-header">Nav header</li>
+ <li><a href="#">Separated link</a></li>
+ <li><a href="#">One more separated link</a></li>
+ </ul>
+ </li>
+ <li><a href="#">Menu Item 4</a></li>
+ <li><a href="#">Reviews <span class="badge">1,118</span></a></li>
+ </ul>
+ </div>
+ </div>
+</div>
# Serve static files from our webroot
"static_path" : WEBROOTDIR,
+ # Templates
+ "template_path" : TEMPLATESDIR,
+
# Use Cross-Site-Request-Forgery protection
"xsrf_cookies" : True,
}
# #
###############################################################################
+TEMPLATESDIR = "@templatesdir@"
WEBROOTDIR = "@webrootdir@"
url = r"/"
def get(self):
- self.finish("Hello World")
+ self.render("base.html")
###############################################################################
from . import base
+from . import menu
+from . import utils
def get_ui_methods():
"""
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# Westferry - The IPFire web user interface #
+# Copyright (C) 2015 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from . import base
+
+class SidebarMenuModule(base.BaseUIModule):
+ def render(self):
+ return self.render_string("modules/menu/sidebar.html")
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# Westferry - The IPFire web user interface #
+# Copyright (C) 2015 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+import socket
+
+from . import base
+
+class HostnameMethod(base.BaseUIMethod):
+ """
+ This method returns the hostname of this system.
+ """
+
+ handle = "hostname"
+
+ def __call__(self, handler):
+ return socket.gethostname()