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:
@@ -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)
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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': {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user