From 052aa55d34a8d9ee9a58969c3a20587cac9adc04 Mon Sep 17 00:00:00 2001 From: Gabriel Hurley Date: Tue, 28 Feb 2012 23:27:46 -0800 Subject: Unifies the project packaging into one set of modules. There are no longer two separate projects living inside the horizon repository. There is a single project now with a single setup.py, single README, etc. The openstack-dashboard/dashboard django project is now named "openstack_dashboard" and lives as an example project in the topmost horizon directory. The "horizon/horizon" directory has been bumped up a level and now is directly on the path when the root horizon directory is on your python path. Javascript media which the horizon module directly relies upon now ships in the horizon/static dir rather than openstack-dashboard/dashboard/static. All the corresponding setup, installation, build, and env scripts have been updated accordingly. Implements blueprint unified-packaging. Change-Id: Ieed8e3c777432cd046c3e0298869a9428756ab62 --- horizon/templatetags/__init__.py | 0 horizon/templatetags/branding.py | 62 +++++++++++++++ horizon/templatetags/horizon.py | 136 ++++++++++++++++++++++++++++++++ horizon/templatetags/parse_date.py | 77 ++++++++++++++++++ horizon/templatetags/sizeformat.py | 77 ++++++++++++++++++ horizon/templatetags/truncate_filter.py | 35 ++++++++ 6 files changed, 387 insertions(+) create mode 100644 horizon/templatetags/__init__.py create mode 100644 horizon/templatetags/branding.py create mode 100644 horizon/templatetags/horizon.py create mode 100644 horizon/templatetags/parse_date.py create mode 100644 horizon/templatetags/sizeformat.py create mode 100644 horizon/templatetags/truncate_filter.py (limited to 'horizon/templatetags') diff --git a/horizon/templatetags/__init__.py b/horizon/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/horizon/templatetags/branding.py b/horizon/templatetags/branding.py new file mode 100644 index 000000000..a7681a5a2 --- /dev/null +++ b/horizon/templatetags/branding.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Template tags for customizing Horizon. +""" + +from django import template +from django.conf import settings + + +register = template.Library() + + +class SiteBrandingNode(template.Node): + def render(self, context): + return settings.SITE_BRANDING + + +@register.tag +def site_branding(parser, token): + return SiteBrandingNode() + + +@register.tag +def site_title(parser, token): + return settings.SITE_BRANDING + + +# TODO(jeffjapan): This is just an assignment tag version of the above, replace +# when the dashboard is upgraded to a django version that +# supports the @assignment_tag decorator syntax instead. +class SaveBrandingNode(template.Node): + def __init__(self, var_name): + self.var_name = var_name + + def render(self, context): + context[self.var_name] = settings.SITE_BRANDING + return "" + + +@register.tag +def save_site_branding(parser, token): + tagname = token.contents.split() + return SaveBrandingNode(tagname[-1]) diff --git a/horizon/templatetags/horizon.py b/horizon/templatetags/horizon.py new file mode 100644 index 000000000..696385593 --- /dev/null +++ b/horizon/templatetags/horizon.py @@ -0,0 +1,136 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from __future__ import absolute_import + +import copy + +from django import template + +from horizon.base import Horizon + + +register = template.Library() + + +@register.filter +def can_haz(user, component): + """ + Checks if the given user meets the requirements for the component. This + includes both user roles and services in the service catalog. + """ + if hasattr(user, 'roles'): + user_roles = set([role['name'].lower() for role in user.roles]) + else: + user_roles = set([]) + roles_statisfied = set(getattr(component, 'roles', [])) <= user_roles + + if hasattr(user, 'roles'): + services = set([service['type'] for service in user.service_catalog]) + else: + services = set([]) + services_statisfied = set(getattr(component, 'services', [])) <= services + + if roles_statisfied and services_statisfied: + return True + return False + + +@register.filter +def can_haz_list(components, user): + return [component for component in components if can_haz(user, component)] + + +@register.inclusion_tag('horizon/_nav_list.html', takes_context=True) +def horizon_main_nav(context): + """ Generates top-level dashboard navigation entries. """ + if 'request' not in context: + return {} + current_dashboard = context['request'].horizon.get('dashboard', None) + dashboards = [] + for dash in Horizon.get_dashboards(): + if callable(dash.nav) and dash.nav(context): + dashboards.append(dash) + elif dash.nav: + dashboards.append(dash) + return {'components': dashboards, + 'user': context['request'].user, + 'current': getattr(current_dashboard, 'slug', None)} + + +@register.inclusion_tag('horizon/_subnav_list.html', takes_context=True) +def horizon_dashboard_nav(context): + """ Generates sub-navigation entries for the current dashboard. """ + if 'request' not in context: + return {} + dashboard = context['request'].horizon['dashboard'] + if isinstance(dashboard.panels, dict): + panels = copy.copy(dashboard.get_panels()) + else: + panels = {dashboard.name: dashboard.get_panels()} + + for heading, items in panels.iteritems(): + temp_panels = [] + for panel in items: + if callable(panel.nav) and panel.nav(context): + temp_panels.append(panel) + elif not callable(panel.nav) and panel.nav: + temp_panels.append(panel) + panels[heading] = temp_panels + non_empty_panels = dict([(k, v) for k, v in panels.items() if len(v) > 0]) + return {'components': non_empty_panels, + 'user': context['request'].user, + 'current': context['request'].horizon['panel'].slug} + + +@register.inclusion_tag('horizon/common/_progress_bar.html') +def horizon_progress_bar(current_val, max_val): + """ Renders a progress bar based on parameters passed to the tag. The first + parameter is the current value and the second is the max value. + + Example: ``{% progress_bar 25 50 %}`` + + This will generate a half-full progress bar. + + The rendered progress bar will fill the area of its container. To constrain + the rendered size of the bar provide a container with appropriate width and + height styles. + + """ + return {'current_val': current_val, + 'max_val': max_val} + + +class JSTemplateNode(template.Node): + """ Helper node for the ``jstemplate`` template tag. """ + def __init__(self, nodelist): + self.nodelist = nodelist + + def render(self, context, ): + output = self.nodelist.render(context) + return output.replace('[[', '{{').replace(']]', '}}') + + +@register.tag +def jstemplate(parser, token): + """ + Replaces ``[[`` and ``]]`` with ``{{`` and ``}}`` to avoid conflicts + with Django's template engine when using any of the Mustache-based + templating libraries. + """ + nodelist = parser.parse(('endjstemplate',)) + parser.delete_first_token() + return JSTemplateNode(nodelist) diff --git a/horizon/templatetags/parse_date.py b/horizon/templatetags/parse_date.py new file mode 100644 index 000000000..523e6f2a9 --- /dev/null +++ b/horizon/templatetags/parse_date.py @@ -0,0 +1,77 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Template tags for parsing date strings. +""" + +import datetime +from django import template +from dateutil import tz + + +register = template.Library() + + +def _parse_datetime(dtstr): + if not dtstr: + return "None" + fmts = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f", + "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + for fmt in fmts: + try: + return datetime.datetime.strptime(dtstr, fmt) + except: + pass + + +class ParseDateNode(template.Node): + def render(self, context): + """Turn an iso formatted time back into a datetime.""" + if not context: + return "None" + date_obj = _parse_datetime(context) + return date_obj.strftime("%m/%d/%y at %H:%M:%S") + + +@register.filter(name='parse_date') +def parse_date(value): + return ParseDateNode().render(value) + + +@register.filter(name='parse_datetime') +def parse_datetime(value): + return _parse_datetime(value) + + +@register.filter(name='parse_local_datetime') +def parse_local_datetime(value): + dt = _parse_datetime(value) + local_tz = tz.tzlocal() + utc = tz.gettz('UTC') + local_dt = dt.replace(tzinfo=utc) + return local_dt.astimezone(local_tz) + + +@register.filter(name='pretty_date') +def pretty_date(value): + if not value: + return "None" + return value.strftime("%d/%m/%y at %H:%M:%S") diff --git a/horizon/templatetags/sizeformat.py b/horizon/templatetags/sizeformat.py new file mode 100644 index 000000000..68d7c1835 --- /dev/null +++ b/horizon/templatetags/sizeformat.py @@ -0,0 +1,77 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Template tags for displaying sizes +""" + +from django import template +from django.utils import translation +from django.utils import formats + + +register = template.Library() + + +def int_format(value): + return int(value) + + +def float_format(value): + return formats.number_format(round(value, 1), 0) + + +def filesizeformat(bytes, filesize_number_format): + try: + bytes = float(bytes) + except (TypeError, ValueError, UnicodeDecodeError): + return translation.ungettext("%(size)d byte", + "%(size)d bytes", 0) % {'size': 0} + + if bytes < 1024: + return translation.ungettext("%(size)d", + "%(size)d", bytes) % {'size': bytes} + if bytes < 1024 * 1024: + return translation.ugettext("%s KB") % \ + filesize_number_format(bytes / 1024) + if bytes < 1024 * 1024 * 1024: + return translation.ugettext("%s MB") % \ + filesize_number_format(bytes / (1024 * 1024)) + if bytes < 1024 * 1024 * 1024 * 1024: + return translation.ugettext("%s GB") % \ + filesize_number_format(bytes / (1024 * 1024 * 1024)) + if bytes < 1024 * 1024 * 1024 * 1024 * 1024: + return translation.ugettext("%s TB") % \ + filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024)) + return translation.ugettext("%s PB") % \ + filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024)) + + +@register.filter(name='mbformat') +def mbformat(mb): + if not mb: + return 0 + return filesizeformat(mb * 1024 * 1024, int_format).replace(' ', '') + + +@register.filter(name='diskgbformat') +def diskgbformat(gb): + return filesizeformat(gb * 1024 * 1024 * 1024, + float_format).replace(' ', '') diff --git a/horizon/templatetags/truncate_filter.py b/horizon/templatetags/truncate_filter.py new file mode 100644 index 000000000..aca8468a1 --- /dev/null +++ b/horizon/templatetags/truncate_filter.py @@ -0,0 +1,35 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Template tags for truncating strings. +""" + +from django import template + +register = template.Library() + + +@register.filter("truncate") +def truncate(value, size): + if len(value) > size and size > 3: + return value[0:(size - 3)] + '...' + else: + return value[0:size] -- cgit v1.2.1