Generalize the Javascript menu rendering into an API for templates that only refresh the menu when there are changes. Closes GitLab issue #511. Thanks to Daniel Carrico @daniel1113 for the report.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2018-09-16 21:48:37 -04:00
parent 654f2a1ad2
commit fafdb538b3
10 changed files with 98 additions and 29 deletions

View File

@@ -147,6 +147,10 @@
- Improve the speed of the document indexing. - Improve the speed of the document indexing.
- Move the matchHeight call from lazy loading to image loading. - Move the matchHeight call from lazy loading to image loading.
Reduces the chance of wrongly sized cards. Reduces the chance of wrongly sized cards.
- Generalize the Javascript menu rendering into an API for
templates that only refresh the menu when there are changes.
Closes GitLab issue #511. Thanks to Daniel Carrico
@daniel1113 for the report.
3.0.3 (2018-08-17) 3.0.3 (2018-08-17)
================== ==================

View File

@@ -363,6 +363,10 @@ classes beyond the provide line chart.
- Improve the speed of the document indexing. - Improve the speed of the document indexing.
- Move the matchHeight call from lazy loading to image loading. - Move the matchHeight call from lazy loading to image loading.
Reduces the chance of wrongly sized cards. Reduces the chance of wrongly sized cards.
- Generalize the Javascript menu rendering into an API for
templates that only refresh the menu when there are changes.
Closes GitLab issue #511. Thanks to Daniel Carrico
@daniel1113 for the report.
Removals Removals
-------- --------
@@ -446,5 +450,6 @@ Bugs fixed or issues closed
* `GitLab issue #7 <https://gitlab.com/mayan-edms/mayan-edms/issues/7>`_ Feature: other compressors than zip for compressed documents * `GitLab issue #7 <https://gitlab.com/mayan-edms/mayan-edms/issues/7>`_ Feature: other compressors than zip for compressed documents
* `GitLab issue #259 <https://gitlab.com/mayan-edms/mayan-edms/issues/259>`_ Thumbnails: why are they created on the fly (therefore: not cached) * `GitLab issue #259 <https://gitlab.com/mayan-edms/mayan-edms/issues/259>`_ Thumbnails: why are they created on the fly (therefore: not cached)
* `GitLab issue #360 <https://gitlab.com/mayan-edms/mayan-edms/issues/360>`_ Should quick rename (optionally) retain original file type extension? * `GitLab issue #360 <https://gitlab.com/mayan-edms/mayan-edms/issues/360>`_ Should quick rename (optionally) retain original file type extension?
* `GitLab issue #511 <https://gitlab.com/mayan-edms/mayan-edms/issues/511>`_ Menu bar flickering in 3.1b1
.. _PyPI: https://pypi.python.org/pypi/mayan-edms/ .. _PyPI: https://pypi.python.org/pypi/mayan-edms/

View File

@@ -8,6 +8,15 @@ class MayanApp {
this.ajaxSpinnerSeletor = '#ajax-spinner'; this.ajaxSpinnerSeletor = '#ajax-spinner';
this.ajaxExecuting = false; this.ajaxExecuting = false;
this.ajaxMenusOptions = [
{
app: this,
interval: 5000,
menuSelector: '#main-menu',
url: apiTemplateMainMenuURL,
}
];
this.ajaxMenuHashes = {};
this.window = $(window); this.window = $(window);
} }
@@ -113,25 +122,22 @@ class MayanApp {
} }
} }
doRefreshMainMenu (options) { doRefreshAJAXMenu (options) {
var self = this;
var $mainMenu = $('#main-menu');
var $mainMenuBuffer = $('#main-menu-buffer');
$.ajax({ $.ajax({
complete: function() { complete: function() {
setTimeout(app.doRefreshMainMenu, options.interval, options); setTimeout(app.doRefreshAJAXMenu, options.interval, options);
}, },
success: function(data) { success: function(data) {
var $elements = $('.dropdown.open'); console.log(data);
if ($elements.length === 0) { var menuHash = options.app.ajaxMenuHashes[data.name];
// Don't refresh the HTML if there are open dropdowns console.log('menuHash' + menuHash);
$mainMenuBuffer.html(data);
$mainMenuBuffer.show(); if ((menuHash === undefined) || (menuHash !== data.hex_hash)) {
$mainMenu.hide(); $(options.menuSelector).html(data.html);
$mainMenu.html($mainMenuBuffer.html()); if (options.callback !== undefined) {
$mainMenu.show(); options.callback();
$mainMenuBuffer.hide(); }
options.app.ajaxMenuHashes[data.name] = data.hex_hash;
} }
}, },
url: options.url, url: options.url,
@@ -217,9 +223,8 @@ class MayanApp {
this.setupItemsSelector(); this.setupItemsSelector();
this.setupNavbarCollapse(); this.setupNavbarCollapse();
this.setupNewWindowAnchor(); this.setupNewWindowAnchor();
this.doRefreshMainMenu({ $.each(this.ajaxMenusOptions, function(index, value) {
interval: 5000, app.doRefreshAJAXMenu(value);
url: '/main_menu'
}); });
partialNavigation.initialize(); partialNavigation.initialize();
} }

View File

@@ -36,7 +36,6 @@
<div id="main-menu"> <div id="main-menu">
{% include 'appearance/main_menu.html' %} {% include 'appearance/main_menu.html' %}
</div> </div>
<div id="main-menu-buffer" style="display: none;"></div>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
@@ -114,6 +113,7 @@
{# Transfer variable from Django to javascript #} {# Transfer variable from Django to javascript #}
var initialURL = '{% url home_view %}'; var initialURL = '{% url home_view %}';
var djangoDEBUG = {% if debug %}true{% else %}false{% endif %}; var djangoDEBUG = {% if debug %}true{% else %}false{% endif %};
var apiTemplateMainMenuURL = '{% url "rest_api:template-detail" "main_menu" %}';
</script> </script>
<script src="{% static 'appearance/js/base.js' %}" type="text/javascript"></script> <script src="{% static 'appearance/js/base.js' %}" type="text/javascript"></script>
<script> <script>

View File

@@ -4,7 +4,8 @@ from django.contrib.contenttypes.models import ContentType
from rest_framework import generics from rest_framework import generics
from .serializers import ContentTypeSerializer from .classes import Template
from .serializers import ContentTypeSerializer, TemplateSerializer
class APIContentTypeList(generics.ListAPIView): class APIContentTypeList(generics.ListAPIView):
@@ -13,3 +14,14 @@ class APIContentTypeList(generics.ListAPIView):
""" """
serializer_class = ContentTypeSerializer serializer_class = ContentTypeSerializer
queryset = ContentType.objects.order_by('app_label', 'model') queryset = ContentType.objects.order_by('app_label', 'model')
class APITemplateView(generics.RetrieveAPIView):
"""
Returns the selected partial template details.
get: Retrieve the details of the partial template.
"""
serializer_class = TemplateSerializer
def get_object(self):
return Template.get(self.kwargs['name']).render(request=self.request)

View File

@@ -17,6 +17,7 @@ from django.utils.translation import ugettext_lazy as _
from mayan.celery import app from mayan.celery import app
from .classes import Template
from .handlers import ( from .handlers import (
handler_pre_initial_setup, handler_pre_upgrade, handler_pre_initial_setup, handler_pre_upgrade,
user_locale_profile_session_config, user_locale_profile_create user_locale_profile_session_config, user_locale_profile_create
@@ -89,6 +90,10 @@ class CommonApp(MayanAppConfig):
if check_for_sqlite(): if check_for_sqlite():
warnings.warn(force_text(MESSAGE_SQLITE_WARNING)) warnings.warn(force_text(MESSAGE_SQLITE_WARNING))
Template(
name='main_menu', template_name='appearance/main_menu.html'
)
app.conf.CELERYBEAT_SCHEDULE.update( app.conf.CELERYBEAT_SCHEDULE.update(
{ {
'task_delete_stale_uploads': { 'task_delete_stale_uploads': {

View File

@@ -1,8 +1,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import hashlib
from django.apps import apps from django.apps import apps
from django.conf import settings
from django.db import models from django.db import models
from django.template import loader from django.template import loader
from django.template.response import TemplateResponse
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext from django.utils.translation import ugettext
@@ -287,3 +291,33 @@ class PropertyHelper(object):
by each subclass. by each subclass.
""" """
raise NotImplementedError raise NotImplementedError
class Template(object):
_registry = {}
@classmethod
def get(cls, name):
return cls._registry[name]
def __init__(self, name, template_name):
self.name = name
self.template_name = template_name
self.__class__._registry[name] = self
def get_absolute_url(self):
return reverse('rest_api:template-detail', args=(self.name,))
def render(self, request):
context = {
'home_view': settings.HOME_VIEW,
}
result = TemplateResponse(
request=request,
template=self.template_name,
context=context,
).render()
self.html = result.content
self.hex_hash = hashlib.sha256(result.content).hexdigest()
return self

View File

@@ -9,3 +9,9 @@ class ContentTypeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
fields = ('app_label', 'id', 'model') fields = ('app_label', 'id', 'model')
model = ContentType model = ContentType
class TemplateSerializer(serializers.Serializer):
hex_hash = serializers.CharField(read_only=True)
name = serializers.CharField(read_only=True)
html = serializers.CharField(read_only=True)

View File

@@ -3,11 +3,11 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from django.views.i18n import javascript_catalog, set_language from django.views.i18n import javascript_catalog, set_language
from .api_views import APIContentTypeList from .api_views import APIContentTypeList, APITemplateView
from .views import ( from .views import (
AboutView, CheckVersionView, CurrentUserDetailsView, CurrentUserEditView, AboutView, CheckVersionView, CurrentUserDetailsView, CurrentUserEditView,
CurrentUserLocaleProfileDetailsView, CurrentUserLocaleProfileEditView, CurrentUserLocaleProfileDetailsView, CurrentUserLocaleProfileEditView,
FaviconRedirectView, HomeView, LicenseView, MainMenuView, FaviconRedirectView, HomeView, LicenseView,
ObjectErrorLogEntryListClearView, ObjectErrorLogEntryListView, ObjectErrorLogEntryListClearView, ObjectErrorLogEntryListView,
PackagesLicensesView, RootView, SetupListView, ToolsListView, PackagesLicensesView, RootView, SetupListView, ToolsListView,
multi_object_action_view multi_object_action_view
@@ -22,7 +22,6 @@ urlpatterns = [
name='check_version_view' name='check_version_view'
), ),
url(r'^license/$', LicenseView.as_view(), name='license_view'), url(r'^license/$', LicenseView.as_view(), name='license_view'),
url(r'^main_menu/$', MainMenuView.as_view(), name='main_menu_view'),
url( url(
r'^packages/licenses/$', PackagesLicensesView.as_view(), r'^packages/licenses/$', PackagesLicensesView.as_view(),
name='packages_licenses_view' name='packages_licenses_view'
@@ -78,4 +77,8 @@ api_urls = [
r'^content_types/$', APIContentTypeList.as_view(), r'^content_types/$', APIContentTypeList.as_view(),
name='content-type-list' name='content-type-list'
), ),
url(
r'^templates/(?P<name>[-\w]+)/$', APITemplateView.as_view(),
name='template-detail'
),
] ]

View File

@@ -168,11 +168,6 @@ class LicenseView(SimpleView):
template_name = 'appearance/generic_form.html' template_name = 'appearance/generic_form.html'
class MainMenuView(SimpleView):
extra_context = {'home_view': settings.HOME_VIEW}
template_name = 'appearance/main_menu.html'
class ObjectErrorLogEntryListClearView(ConfirmView): class ObjectErrorLogEntryListClearView(ConfirmView):
def get_extra_context(self): def get_extra_context(self):
return { return {