Commit 461cc752 authored by Pietro Albini's avatar Pietro Albini

Rewrite the navbar code

parent 10f5ad48
Pipeline #63 passed with stage
in 0 seconds
recursive-include uitwww/templates *
recursive-include uitwww/static *
recursive-include uitwww/data *.toml
include uitwww/navbar.json
recursive-include uitwww/data *
......@@ -37,6 +37,7 @@ setuptools.setup(
"requests",
"itsdangerous",
"toml",
"pyyaml",
],
packages = [
......
......@@ -22,6 +22,7 @@ from . import pages
from . import cache
from . import utils
from . import download
from . import navbar
def create_app(data_path):
......@@ -51,7 +52,10 @@ def create_app(data_path):
)
app.register_blueprint(pages.prepare_blueprint(app))
pages.prepare_navbar(app)
nav = navbar.Navbar()
nav.add_generator("download-distros", app.download.generate_navbar)
nav.install(app)
@app.errorhandler(404)
def not_found(error):
......
......@@ -50,7 +50,7 @@ def run(data, gunicorn_config, port, public, workers, debug):
# In debug mode, run the flask builtin webserver
if debug:
extra_files = [
os.path.join(src_directory, "navbar.json"),
os.path.join(src_directory, "data/navbar.yml"),
]
app.run(debug=True, port=port, host=host, extra_files=extra_files)
......
- name: Download
endpoint: download.index
# Evidenzia anche la pagina di ringraziamento sotto questo menu
also-match:
download.thanks: null
# Le sottopagine sono generate automaticamente dalla lista di distro
generate-subs: download-distros
- name: Scopri Ubuntu
endpoint: pages.scopri-ubuntu_index
- name: Supporto
endpoint: pages.supporto
subs:
- name: Documentazione
external: https://wiki.ubuntu-it.org
- name: Forum
external: https://forum.ubuntu-it.org
- name: Domande e risposte
external: https://chat.ubuntu-it.org
- name: Chat
external: https://chat.ubuntu-it.org
- name: Comunità
endpoint: pages.comunita_index
- name: Eventi
endpoint: pages.eventi_index
- name: Contribuisci
endpoint: pages.contribuisci_index
- name: Progetto
endpoint: pages.progetto_index
- name: Contatti
endpoint: pages.contatti_index
......@@ -279,3 +279,15 @@ class Downloads:
return flask.render_template("download/thanks.html", **content)
return bp
def generate_navbar(self):
"""Generate the sub-navbar for /download"""
result = []
for name, distro in self.config["distros"].items():
result.append({
"name": distro["name"],
"endpoint": "download.landing",
"endpoint-args": {"distro": name},
})
return result
[
{
"name": "Download",
"endpoint": "download.index",
"subs": []
},
{
"name": "Scopri Ubuntu",
"endpoint": "pages.scopri-ubuntu_index",
"subs": []
},
{
"name": "Supporto",
"endpoint": "pages.supporto",
"subs": [
{
"name": "Documentazione",
"external": "http://wiki.ubuntu-it.org"
},
{
"name": "Forum",
"external": "http://forum.ubuntu-it.org"
},
{
"name": "Domande e risposte",
"external": "http://chiedi.ubuntu-it.org"
},
{
"name": "Chat",
"external": "http://chat.ubuntu-it.org"
}
]
},
{
"name": "Comunità",
"endpoint": "pages.comunita_index",
"subs": []
},
{
"name": "Eventi",
"endpoint": "pages.eventi_index",
"subs": []
},
{
"name": "Contribuisci",
"endpoint": "pages.contribuisci_index",
"subs": []
},
{
"name": "Progetto",
"endpoint": "pages.progetto_index",
"subs": []
},
{
"name": "Contatti",
"endpoint": "pages.contatti_index",
"subs": []
}
]
# Source code of the Ubuntu-it website
# Copyright (C) 2018 Pietro Albini <pietroalbini@ubuntu.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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; witout even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import flask
import json
import pkg_resources
import yaml
class Navbar:
def __init__(self):
self.navbars = []
self.match = {}
self.generators = {}
def add_generator(self, name, generator):
"""Add a subpages generator"""
self.generators[name] = generator
def current_navbars(self):
"""Get the list of current navbars"""
matcher = (flask.request.endpoint, tuple(flask.request.view_args.items()))
if matcher in self.match:
return self._return_navbars(self.match[matcher])
elif flask.request.endpoint in self.match:
return self._return_navbars(self.match[flask.request.endpoint])
else:
return self._return_navbars([(0, -1)])
def _return_navbars(self, navbars):
"""Return a list of navbars"""
result = []
for bar_id, active_id in navbars:
bar = []
for i, item in enumerate(self.navbars[bar_id]):
bar.append((item[0], item[1](), i == active_id))
result.append(bar)
return result
def install(self, app):
"""Install this navbar on the app"""
raw = pkg_resources.resource_string("uitwww", "data/navbar.yml")
config = yaml.load(raw.decode("utf-8"))
self._prepare_navbar_cache(config, [])
# Add the _navbars variable to the templates
@app.context_processor
def inject_navbar():
return {
"_navbars": self.current_navbars(),
}
def _prepare_navbar_cache(self, bar, parents):
"""Add the bar to the cache"""
bar_id = len(self.navbars)
self.navbars.append([])
for item in bar:
item.setdefault("endpoint-args", None)
# Load the list of subs dynamically if requested
if "generate-subs" in item:
item["subs"] = self.generators[item["generate-subs"]]()
# Add hte item to the navbar
item_id = len(self.navbars[bar_id])
self.navbars[bar_id].append((item["name"], self._url_of(item)))
# Load sub-navbars recursively
sub_id = None
if "subs" in item:
sub_id = self._prepare_navbar_cache(item["subs"], parents + [(bar_id, item_id)])
if "endpoint" in item:
# Generate the list of navbars to show for this match
# If this item has childs the childs are shown
navbars = parents + [(bar_id, item_id)]
if sub_id is not None:
navbars.append((sub_id, -1))
# Add a match for each endpoint pointing to the list of navbars
match = {item["endpoint"]: item["endpoint-args"]}
if "also-match" in item:
match.update(item["also-match"])
for endpoint, args in match.items():
if args is None:
self.match[endpoint] = navbars
else:
self.match[(endpoint, tuple(args.items()))] = navbars
return bar_id
def _url_of(self, item):
"""Lazily the URL of an item"""
if "external" in item:
return lambda: item["external"]
# This function return lambdas because flask.url_for is only available
# during requests
if item["endpoint-args"] is not None:
return lambda: flask.url_for(item["endpoint"], **item["endpoint-args"])
else:
return lambda: flask.url_for(item["endpoint"])
......@@ -87,65 +87,3 @@ def prepare_blueprint(app):
return flask.render_template(_tmpl)
return bp
def prepare_navbar(app):
"""Prepare the context processor which will provide navbar informations"""
# This will load the navbar.json file, and parse it as json
navbar_raw = pkg_resources.resource_string("uitwww", "navbar.json")
navbar = json.loads(navbar_raw.decode("utf-8"))
def calculate_menu(current, recursive=True, _final=True):
# This will process only a level of the result
active = False
active_i = None
# Make all items non-active by default
for item in current:
item["active"] = False
for i, item in enumerate(current):
active = False
# An item should be considered active if either is the current
# one, or one of its childs is active
endpoint = flask.request.endpoint
if "endpoint" in item and item["endpoint"] == endpoint:
active = True
elif "subs" in item and recursive:
active = calculate_menu(item["subs"], _final=False) is not None
# Break the loop if this item is active
if active:
item["active"] = True
active_i = i
break
if active or _final:
return current, active_i
# else None is returned
@app.context_processor
def add_menu():
result = []
result_active = []
# Get all the possible navbar levels
next_to_calculate = navbar
while True:
calculated, next_i = calculate_menu(next_to_calculate)
result.append(calculated)
if next_i is not None:
result_active.append(calculated[next_i])
if next_i is not None and "subs" in calculated[next_i]:
next_to_calculate = calculated[next_i]["subs"]
else:
break
# Force something in the second navbar
if len(result) == 1:
result.append(result[0][0]["subs"])
return {"_navbar": result, "_navbar_active": result_active}
......@@ -56,37 +56,32 @@
<li><a href="https://planet.ubuntu-it.org">Planet</a></li>
</ul>
<ul class="right">
<li><a href="https://telegram.me/ubuntuit">Telegram</a></li>
<li><a href="https://www.youtube.com/ubuntuitpromozione">YouTube</a></li>
<li><a href="https://plus.google.com/+ubuntuit">Google+</a></li>
<li<a href="https://twitter.com/ubuntuit">Twitter</a></li>
<li><a href="https://www.facebook.com/ubuntu.it">Facebook</a></li>
<li><a href="https://twitter.com/ubuntuit">Twitter</a></li>
<li><a href="https://plus.google.com/+ubuntuit">Google+</a></li>
<li><a href="https://www.youtube.com/ubuntuitpromozione">YouTube</a></li>
<li><a href="https://telegram.me/ubuntuit">Telegram</a></li>
</ul>
</div>
</nav>
<nav class="main-navbar">
<div class="container">
<ul>
<li class="site-name"><a href="{{ url_for("pages.index") }}">ubuntu-it</a></li>
{% for item in _navbar[0] %}
{{ nav_item(item) }}
{% endfor %}
</ul>
</div>
</nav>
{% for navbar in _navbars %}
<nav {%- if loop.index == 1 %} class="main-navbar"{% endif %}>
<div class="container">
<ul>
{% if loop.index == 1 %}
<li class="site-name"><a href="{{ url_for("pages.index") }}">ubuntu-it</a></li>
{% endif -%}
{% if _navbar[1] %}
<nav>
<div class="container">
<ul>
{% for item in _navbar[1] %}
{{ nav_item(item) }}
{% endfor %}
</ul>
</div>
</nav>
{% endif %}
{%- for name, url, active in navbar %}
<li>
<a {%- if active %} class="active"{% endif %} href="{{ url }}">{{ name }}</a>
</li>
{% endfor %}
</ul>
</div>
</nav>
{% endfor %}
<div class="container">
{% block content %}{% endblock %}
......
......@@ -14,15 +14,3 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% macro nav_item(item) %}
<li>
{% if "external" in item %}
<a href="{{ item.external }}">{{ item.name }}</a>
{% elif item.active %}
<a href="{{ url_for(item.endpoint) }}" class="active">{{ item.name }}</a>
{% else %}
<a href="{{ url_for(item.endpoint) }}">{{ item.name }}</a>
{% endif %}
</li>
{% endmacro %}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment