[yocto] [layerindex-web][PATCH 7/7] Add site-wide notice support
Paul Eggleton
paul.eggleton at linux.intel.com
Mon Jul 2 15:58:51 PDT 2018
Add the ability to show a notice at the top of every page; this provides
the ability for admins to display a message to visitors in the case of
infrastructure or index data issues. Notices can have an expiry date and
can be disabled and re-enabled if needed. A subset of HTML can be used
for formatting the text, URLs will be made into clickable links, and
four "levels" are supported (info, success, warning and error).
Signed-off-by: Paul Eggleton <paul.eggleton at linux.intel.com>
---
layerindex/admin.py | 1 +
layerindex/context_processors.py | 7 +++++--
layerindex/migrations/0014_sitenotice.py | 24 +++++++++++++++++++++++
layerindex/models.py | 25 +++++++++++++++++++++++-
layerindex/utils.py | 14 +++++++++++++
requirements.txt | 1 +
templates/base.html | 5 +++++
7 files changed, 74 insertions(+), 3 deletions(-)
create mode 100644 layerindex/migrations/0014_sitenotice.py
diff --git a/layerindex/admin.py b/layerindex/admin.py
index 3cb59691..312f7a30 100644
--- a/layerindex/admin.py
+++ b/layerindex/admin.py
@@ -198,3 +198,4 @@ admin.site.register(Patch)
admin.site.register(RecipeChangeset, RecipeChangesetAdmin)
admin.site.register(ClassicRecipe, ClassicRecipeAdmin)
admin.site.register(PythonEnvironment)
+admin.site.register(SiteNotice)
diff --git a/layerindex/context_processors.py b/layerindex/context_processors.py
index db8e3fa3..7cf20ede 100644
--- a/layerindex/context_processors.py
+++ b/layerindex/context_processors.py
@@ -1,11 +1,13 @@
# layerindex-web - custom context processor
#
-# Copyright (C) 2013 Intel Corporation
+# Copyright (C) 2013, 2018 Intel Corporation
#
# Licensed under the MIT license, see COPYING.MIT for details
-from layerindex.models import Branch, LayerItem
+from layerindex.models import Branch, LayerItem, SiteNotice
from django.contrib.sites.models import Site
+from django.db.models import Q
+from datetime import datetime
def layerindex_context(request):
import settings
@@ -20,4 +22,5 @@ def layerindex_context(request):
'oe_classic': Branch.objects.filter(name='oe-classic'),
'site_name': site_name,
'rrs_enabled': 'rrs' in settings.INSTALLED_APPS,
+ 'notices': SiteNotice.objects.filter(disabled=False).filter(Q(expires__isnull=True) | Q(expires__gte=datetime.now())),
}
diff --git a/layerindex/migrations/0014_sitenotice.py b/layerindex/migrations/0014_sitenotice.py
new file mode 100644
index 00000000..630700cf
--- /dev/null
+++ b/layerindex/migrations/0014_sitenotice.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('layerindex', '0013_patch'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SiteNotice',
+ fields=[
+ ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
+ ('text', models.TextField(help_text='Text to show in the notice. A limited subset of HTML is supported for formatting.')),
+ ('level', models.CharField(choices=[('I', 'Info'), ('S', 'Success'), ('W', 'Warning'), ('E', 'Error')], help_text='Level of notice to display', default='I', max_length=1)),
+ ('disabled', models.BooleanField(verbose_name='Disabled', help_text='Use to temporarily disable this notice', default=False)),
+ ('expires', models.DateTimeField(blank=True, help_text='Optional date/time when this notice will stop showing', null=True)),
+ ],
+ ),
+ ]
diff --git a/layerindex/models.py b/layerindex/models.py
index ff164baa..891f5dfb 100644
--- a/layerindex/models.py
+++ b/layerindex/models.py
@@ -1,6 +1,6 @@
# layerindex-web - model definitions
#
-# Copyright (C) 2013-2016 Intel Corporation
+# Copyright (C) 2013-2018 Intel Corporation
#
# Licensed under the MIT license, see COPYING.MIT for details
@@ -658,3 +658,26 @@ class RecipeChange(models.Model):
def reset_fields(self):
for fieldname in self.RECIPE_VARIABLE_MAP:
setattr(self, fieldname, getattr(self.recipe, fieldname))
+
+class SiteNotice(models.Model):
+ NOTICE_LEVEL_CHOICES = [
+ ('I', 'Info'),
+ ('S', 'Success'),
+ ('W', 'Warning'),
+ ('E', 'Error'),
+ ]
+ text = models.TextField(help_text='Text to show in the notice. A limited subset of HTML is supported for formatting.')
+ level = models.CharField(max_length=1, choices=NOTICE_LEVEL_CHOICES, default='I', help_text='Level of notice to display')
+ disabled = models.BooleanField('Disabled', default=False, help_text='Use to temporarily disable this notice')
+ expires = models.DateTimeField(blank=True, null=True, help_text='Optional date/time when this notice will stop showing')
+
+ def __str__(self):
+ prefix = ''
+ if self.expires and datetime.now() >= self.expires:
+ prefix = '[expired] '
+ elif self.disabled:
+ prefix = '[disabled] '
+ return '%s%s' % (prefix, self.text)
+
+ def text_sanitised(self):
+ return utils.sanitise_html(self.text)
diff --git a/layerindex/utils.py b/layerindex/utils.py
index 8f652da7..e86eff0a 100644
--- a/layerindex/utils.py
+++ b/layerindex/utils.py
@@ -14,6 +14,7 @@ import time
import fcntl
import signal
import codecs
+from bs4 import BeautifulSoup
def get_branch(branchname):
from layerindex.models import Branch
@@ -379,6 +380,7 @@ def setup_core_layer_sys_path(settings, branchname):
core_layerdir = os.path.join(core_repodir, core_layerbranch.vcs_subdir)
sys.path.insert(0, os.path.join(core_layerdir, 'lib'))
+
def run_command_interruptible(cmd):
"""
Run a command with output displayed on the console, but ensure any Ctrl+C is
@@ -407,3 +409,15 @@ def run_command_interruptible(cmd):
finally:
signal.signal(signal.SIGINT, signal.SIG_DFL)
return process.returncode, buf
+
+
+def sanitise_html(html):
+ soup = BeautifulSoup(html, "html.parser")
+ for tag in soup.findAll(True):
+ if tag.name not in ['strong', 'em', 'b', 'i', 'p', 'ul', 'ol', 'li', 'br', 'p']:
+ tag.hidden = True
+ elif tag.attrs:
+ tag.attrs = []
+
+ return soup.renderContents()
+
diff --git a/requirements.txt b/requirements.txt
index 5ca7cf6e..7facd2b6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
amqp==2.2.2
anyjson==0.3.3
+beautifulsoup4==4.6.0
billiard==3.5.0.3
celery==4.1.0
confusable-homoglyphs==3.0.0
diff --git a/templates/base.html b/templates/base.html
index 38373ab8..a5e97cd5 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -89,6 +89,11 @@
{% endblock %}
{% block contenttag %}<div id="content" class="container top-padded">{% endblock %}
+ {% if notices %}
+ {% for notice in notices %}
+ <div class="alert {% if notice.level == 'I' %}alert-info{% elif notice.level == 'S' %}alert-success{% elif notice.level == 'W' %}{% elif notice.level == 'E' %}alert-error{% endif %}">{{ notice.text_sanitised|safe|urlize }}</div>
+ {% endfor %}
+ {% endif %}
{% if messages %}
{% for message in messages %}
<div{% if message.tags %} class="alert {{ message.tags }}"{% endif %}>{{ message }}</div>
--
2.17.1
More information about the yocto
mailing list