diff --git a/CHANGES.rst b/CHANGES.rst
index 816536a1ff..a02b05bb2a 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -24,6 +24,7 @@ Changelog
**Changed**
+- #1513 Better Ajax Loader for Sample Add Form
- #1508 Do not try to render InstrumentQCFailuresViewlet to non-lab personnel
- #1495 Better Remarks handling and display
- #1502 Improved DateTime Widget
diff --git a/bika/lims/browser/bootstrap.py b/bika/lims/browser/bootstrap.py
new file mode 100644
index 0000000000..1380cef9d4
--- /dev/null
+++ b/bika/lims/browser/bootstrap.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of SENAITE.CORE.
+#
+# SENAITE.LIMS is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright 2018-2020 by it's authors.
+# Some rights reserved, see README and LICENSE.
+
+from bika.lims import api
+from plone.memoize.ram import cache
+from Products.Five import BrowserView
+from senaite.lims import logger
+from zope.component import getMultiAdapter
+from zope.interface import Interface
+from zope.interface import implements
+
+
+class IBootstrapView(Interface):
+ """Twitter Bootstrap View
+ """
+
+ def getColumnsClasses(view=None):
+ """A helper method to return the clases for the columns of the site
+ it should return a dict with three elements:'one', 'two', 'content'
+ Each of them should contain the classnames for the first (leftmost)
+ second (rightmost) and middle column
+ """
+
+ def getViewportValues(view=None):
+ """Determine the value of the viewport meta-tag
+ """
+
+
+def icon_cache_key(method, self, brain_or_object):
+ """Generates a cache key for the icon lookup
+
+ Includes the virtual URL to handle multiple HTTP/HTTPS domains
+ Example: http://senaite.local/clients?modified=1512033263370
+ """
+ url = api.get_url(brain_or_object)
+ modified = api.get_modification_date(brain_or_object).millis()
+ key = "{}?modified={}".format(url, modified)
+ logger.debug("Generated Cache Key: {}".format(key))
+ return key
+
+
+class BootstrapView(BrowserView):
+ """Twitter Bootstrap helper view for SENAITE LIMS
+ """
+ implements(IBootstrapView)
+
+ def __init__(self, context, request):
+ super(BrowserView, self).__init__(context, request)
+
+ @cache(icon_cache_key)
+ def get_icon_for(self, brain_or_object):
+ """Get the navigation portlet icon for the brain or object
+
+ The cache key ensures that the lookup is done only once per domain name
+ """
+ portal_types = api.get_tool("portal_types")
+ fti = portal_types.getTypeInfo(api.get_portal_type(brain_or_object))
+ icon = fti.getIcon()
+ if not icon:
+ return ""
+ # Always try to get the big icon for high-res displays
+ icon_big = icon.replace(".png", "_big.png")
+ # fall back to a default icon if the looked up icon does not exist
+ if self.context.restrictedTraverse(icon_big, None) is None:
+ icon_big = None
+ portal_url = api.get_url(api.get_portal())
+ title = api.get_title(brain_or_object)
+ html_tag = "".format(
+ title, portal_url, icon_big or icon)
+ logger.debug("Generated Icon Tag for {}: {}".format(
+ api.get_path(brain_or_object), html_tag))
+ return html_tag
+
+ def getViewportValues(self, view=None):
+ """Determine the value of the viewport meta-tag
+ """
+ values = {
+ 'width': 'device-width',
+ 'initial-scale': '1.0',
+ }
+
+ return ','.join('%s=%s' % (k, v) for k, v in values.items())
+
+ def getColumnsClasses(self, view=None):
+ """Determine whether a column should be shown. The left column is
+ called plone.leftcolumn; the right column is called
+ plone.rightcolumn.
+ """
+
+ plone_view = getMultiAdapter(
+ (self.context, self.request), name=u'plone')
+ portal_state = getMultiAdapter(
+ (self.context, self.request), name=u'plone_portal_state')
+
+ sl = plone_view.have_portlets('plone.leftcolumn', view=view)
+ sr = plone_view.have_portlets('plone.rightcolumn', view=view)
+
+ isRTL = portal_state.is_rtl()
+
+ # pre-fill dictionary
+ columns = dict(one="", content="", two="")
+
+ if not sl and not sr:
+ # we don't have columns, thus conten takes the whole width
+ columns['content'] = "col-md-12"
+
+ elif sl and sr:
+ # In case we have both columns, content takes 50% of the whole
+ # width and the rest 50% is spread between the columns
+ columns['one'] = "col-xs-12 col-md-2"
+ columns['content'] = "col-xs-12 col-md-8"
+ columns['two'] = "col-xs-12 col-md-2"
+
+ elif (sr and not sl) and not isRTL:
+ # We have right column and we are NOT in RTL language
+ columns['content'] = "col-xs-12 col-md-10"
+ columns['two'] = "col-xs-12 col-md-2"
+
+ elif (sl and not sr) and isRTL:
+ # We have left column and we are in RTL language
+ columns['one'] = "col-xs-12 col-md-2"
+ columns['content'] = "col-xs-12 col-md-10"
+
+ elif (sl and not sr) and not isRTL:
+ # We have left column and we are in NOT RTL language
+ columns['one'] = "col-xs-12 col-md-2"
+ columns['content'] = "col-xs-12 col-md-10"
+
+ # # append cell to each css-string
+ # for key, value in columns.items():
+ # columns[key] = "cell " + value
+
+ return columns
diff --git a/bika/lims/browser/bootstrap.zcml b/bika/lims/browser/bootstrap.zcml
new file mode 100644
index 0000000000..bcd044a41d
--- /dev/null
+++ b/bika/lims/browser/bootstrap.zcml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/bika/lims/browser/configure.zcml b/bika/lims/browser/configure.zcml
index a7eb065517..49ffcdd5d6 100644
--- a/bika/lims/browser/configure.zcml
+++ b/bika/lims/browser/configure.zcml
@@ -16,9 +16,11 @@
+
+
diff --git a/bika/lims/browser/controlpanel.py b/bika/lims/browser/controlpanel.py
new file mode 100644
index 0000000000..418e44bf60
--- /dev/null
+++ b/bika/lims/browser/controlpanel.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of SENAITE.CORE.
+#
+# SENAITE.LIMS is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright 2018-2020 by it's authors.
+# Some rights reserved, see README and LICENSE.
+
+from plone.app.controlpanel.overview import OverviewControlPanel
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+
+
+class SenaiteOverviewControlPanel(OverviewControlPanel):
+ template = ViewPageTemplateFile(
+ "templates/plone.app.controlpanel.overview.pt")
diff --git a/bika/lims/browser/controlpanel.zcml b/bika/lims/browser/controlpanel.zcml
new file mode 100644
index 0000000000..13814ac408
--- /dev/null
+++ b/bika/lims/browser/controlpanel.zcml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/bika/lims/browser/js/bika.lims.analysisrequest.add.js b/bika/lims/browser/js/bika.lims.analysisrequest.add.js
index f7881ff8f7..fb4cead80a 100644
--- a/bika/lims/browser/js/bika.lims.analysisrequest.add.js
+++ b/bika/lims/browser/js/bika.lims.analysisrequest.add.js
@@ -1250,9 +1250,10 @@
var button;
console.debug("°°° on_ajax_start °°°");
button = $("input[name=save_button]");
- return button.prop({
+ button.prop({
"disabled": true
});
+ return button[0].value = _("Loading ...");
};
AnalysisRequestAdd.prototype.on_ajax_end = function() {
@@ -1263,9 +1264,10 @@
var button;
console.debug("°°° on_ajax_end °°°");
button = $("input[name=save_button]");
- return button.prop({
+ button.prop({
"disabled": false
});
+ return button[0].value = _("Save");
};
AnalysisRequestAdd.prototype.on_form_submit = function(event, callback) {
diff --git a/bika/lims/browser/js/bika.lims.site.js b/bika/lims/browser/js/bika.lims.site.js
index de056db9d3..2e7e28f9d1 100644
--- a/bika/lims/browser/js/bika.lims.site.js
+++ b/bika/lims/browser/js/bika.lims.site.js
@@ -8,9 +8,6 @@
window.SiteView = (function() {
function SiteView() {
- this.on_ajax_error = bind(this.on_ajax_error, this);
- this.on_ajax_end = bind(this.on_ajax_end, this);
- this.on_ajax_start = bind(this.on_ajax_start, this);
this.on_service_info_click = bind(this.on_service_info_click, this);
this.on_reference_definition_list_change = bind(this.on_reference_definition_list_change, this);
this.on_numeric_field_keypress = bind(this.on_numeric_field_keypress, this);
@@ -20,8 +17,6 @@
this.on_autocomplete_keydown = bind(this.on_autocomplete_keydown, this);
this.on_date_range_end_change = bind(this.on_date_range_end_change, this);
this.on_date_range_start_change = bind(this.on_date_range_start_change, this);
- this.stop_spinner = bind(this.stop_spinner, this);
- this.start_spinner = bind(this.start_spinner, this);
this.notify_in_panel = bind(this.notify_in_panel, this);
this.notificationPanel = bind(this.notificationPanel, this);
this.set_cookie = bind(this.set_cookie, this);
@@ -35,7 +30,6 @@
this.get_portal_url = bind(this.get_portal_url, this);
this.init_referencedefinition = bind(this.init_referencedefinition, this);
this.init_datepickers = bind(this.init_datepickers, this);
- this.init_spinner = bind(this.init_spinner, this);
this.init_client_add_overlay = bind(this.init_client_add_overlay, this);
this.bind_eventhandler = bind(this.bind_eventhandler, this);
this.load = bind(this.load, this);
@@ -45,7 +39,6 @@
console.debug("SiteView::load");
jarn.i18n.loadCatalog('senaite.core');
this._ = window.jarn.i18n.MessageFactory("senaite.core");
- this.init_spinner();
this.init_client_add_overlay();
this.init_datepickers();
this.init_referencedefinition();
@@ -74,9 +67,17 @@
$("body").on("change", ".date_range_start", this.on_date_range_start_change);
$("body").on("change", ".date_range_end", this.on_date_range_end_change);
$("body").on("click", "a.service_info", this.on_service_info_click);
- $(document).on("ajaxStart", this.on_ajax_start);
- $(document).on("ajaxStop", this.on_ajax_end);
- return $(document).on("ajaxError", this.on_ajax_error);
+ return $(document).on({
+ ajaxStart: function() {
+ $("body").addClass("loading");
+ },
+ ajaxStop: function() {
+ $("body").removeClass("loading");
+ },
+ ajaxError: function() {
+ $("body").removeClass("loading");
+ }
+ });
};
SiteView.prototype.init_client_add_overlay = function() {
@@ -102,20 +103,6 @@
});
};
- SiteView.prototype.init_spinner = function() {
-
- /*
- * Initialize Spinner Overlay
- */
- console.debug("SiteView::init_spinner");
- $(document).unbind('ajaxStart');
- $(document).unbind('ajaxStop');
- $('#ajax-spinner').remove();
- this.counter = 0;
- this.spinner = $("
+ You have not configured a mail host or a site 'From'
+ address, various features including contact forms, email
+ notification and password reset will not work. Go to the
+
+ Mail control panel
+
+ to fix this.
+
+
+
+
+
+ Warning
+
+
+ Warning: PIL is not installed properly, image scaling will not work.
+
+ You are running in "production mode". This is the preferred mode of
+ operation for a live Plone site, but means that some
+ configuration changes will not take effect until your server is
+ restarted or a product refreshed. If this is a development instance,
+ and you want to enable debug mode, stop the server, set 'debug-mode=on'
+ in your buildout.cfg, re-run bin/buildout and then restart the server
+ process.
+
+
+
+
+ You are running in "debug mode". This mode is intended for sites that
+ are under development. This allows many configuration changes to be
+ immediately visible, but will make your site run more slowly. To turn
+ off debug mode, stop the server, set 'debug-mode=off' in your
+ buildout.cfg, re-run bin/buildout and then restart the server
+ process.
+
+
+
+
+
+
+
diff --git a/bika/lims/skins/bika/base_properties.props b/bika/lims/skins/bika/base_properties.props
index bd0829b0da..3aab062243 100644
--- a/bika/lims/skins/bika/base_properties.props
+++ b/bika/lims/skins/bika/base_properties.props
@@ -1,4 +1,4 @@
-title:string=Plone's color, font, logo and border defaults
+title:string=SENAITE Theme
plone_skin:string=Plone Default
logoName:string=logo.png
diff --git a/bika/lims/skins/bika/main_template.pt b/bika/lims/skins/bika/main_template.pt
new file mode 100644
index 0000000000..5b07cefa2c
--- /dev/null
+++ b/bika/lims/skins/bika/main_template.pt
@@ -0,0 +1,206 @@
+
+
+
+
+ Get the global cache headers located in global_cache_settings.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Various slots where you can insert elements in the header from a template.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Status message
+
+
+
+
+
+
+
+
+
+
+ Visual Header
+
+
+
+
+
+
+
+ If you write a custom title always use
+ for it
+
+
+ Generic KSS Title. Is rendered with class="documentFirstHeading".
+
+
+
+
+
+
+ If you write a custom description always use
+ for it
+
+
+ Generic KSS Description. Is rendered with class="documentDescription".
+
+
+
+
+
+
+ Page body text
+
+
+
+
+
+
+
+
+
+ This slot is here for backwards compatibility only.
+ Don't use it in your custom templates.
+
+