You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by ke...@apache.org on 2018/03/23 19:12:34 UTC

allura git commit: fixup! [#8196] Remember text in large form inputs before submitting

Repository: allura
Updated Branches:
  refs/heads/kt/8196 9c27942b1 -> e5aad89e3


fixup! [#8196] Remember text in large form inputs before submitting


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/e5aad89e
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/e5aad89e
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/e5aad89e

Branch: refs/heads/kt/8196
Commit: e5aad89e30bb75d662a058a85761a7c2b4e78faf
Parents: 9c27942
Author: Kenton Taylor <kt...@slashdotmedia.com>
Authored: Fri Mar 23 15:12:18 2018 -0400
Committer: Kenton Taylor <kt...@slashdotmedia.com>
Committed: Fri Mar 23 15:12:18 2018 -0400

----------------------------------------------------------------------
 Allura/allura/lib/decorators.py                 |   4 +-
 Allura/allura/lib/plugin.py                     |   1 +
 Allura/allura/lib/widgets/form_fields.py        |   1 -
 .../lib/widgets/resources/js/memorable.js       | 236 -----------------
 .../lib/widgets/resources/js/sf_markitup.js     |   4 +-
 Allura/allura/public/nf/js/memorable.js         | 253 +++++++++++++++++++
 .../allura/templates/jinja_master/master.html   |   3 +-
 ForgeBlog/forgeblog/main.py                     |   1 +
 8 files changed, 260 insertions(+), 243 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/lib/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index 214491f..4e1135c 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -33,7 +33,7 @@ from tg.render import render
 from webob import exc
 from pylons import tmpl_context as c
 from pylons import response
-from webob.exc import HTTPFound
+from webob.exc import HTTPFound, WSGIHTTPException
 
 from allura.lib import helpers as h
 from allura.lib import utils
@@ -291,7 +291,7 @@ def memorable_forget():
             res = func(*args, **kwargs)
             forget(res)
             return res
-        except Exception as ex:
+        except WSGIHTTPException as ex:
             forget(None, ex)
             raise ex
 

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index f7b6fed..ee2cfe7 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -215,6 +215,7 @@ class AuthenticationProvider(object):
         self.session.invalidate()
         self.session.save()
         response.delete_cookie('allura-loggedin')
+        response.set_cookie('memorable_forget', '/')
 
     def validate_password(self, user, password):
         '''Check that provided password matches actual user password

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/lib/widgets/form_fields.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/form_fields.py b/Allura/allura/lib/widgets/form_fields.py
index 04bd7c5..900e46e 100644
--- a/Allura/allura/lib/widgets/form_fields.py
+++ b/Allura/allura/lib/widgets/form_fields.py
@@ -280,7 +280,6 @@ class MarkdownEdit(ew.TextArea):
         yield ew.CSSLink('css/markitup_sf.css')
         yield ew.JSLink('js/simplemde.min.js')
         yield ew.JSLink('js/sf_markitup.js')
-        yield ew.JSLink('js/memorable.js')
 
 class PageList(ew_core.Widget):
     template = 'jinja:allura:templates/widgets/page_list.html'

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/lib/widgets/resources/js/memorable.js
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/resources/js/memorable.js b/Allura/allura/lib/widgets/resources/js/memorable.js
deleted file mode 100644
index 3ddf892..0000000
--- a/Allura/allura/lib/widgets/resources/js/memorable.js
+++ /dev/null
@@ -1,236 +0,0 @@
-/*global $, SF, console, jQuery, localStorage */
-window.SF = window.SF || {};
-SF.Memorable = {};
-
-/**
- * Class that describes the management of a memorable input - identifying, watching, saving, restoring, and forgetting.
- */
-SF.Memorable.InputManager = (function(){
-
-    /**
-     * @param inputObj - the InputBasic or InputMDE object representing the input to be tracked
-     * @constructor
-     */
-    function InputManager(inputObj){
-        this.inputObj = inputObj;
-        this.$form = this.inputObj.getForm();
-
-        this.inputObj.watchObj.on(this.inputObj.watchEvent, this.handleSave.bind(this));
-
-        this.forget();
-        this.restore();
-    }
-
-    /**
-     * Builds a unique key to use when persisting the input's value
-     * @returns {string}
-     */
-    InputManager.prototype.getStorageKey = function(){
-        function isUsableName($el){
-            var name = $el.attr('name');
-            if (name && name.length != 28){
-                return true;
-            }
-        }
-        function getRelativeAction($f){
-            var action = $f[0].action;
-            var list = action.split('/');
-            var relativeAction = "";
-            for (i = 3; i < list.length; i++) {
-              relativeAction += "/";
-              relativeAction += list[i];
-            }
-            return relativeAction;
-        }
-
-        var key = '';
-        var $f = this.$form;
-        var keySeparator = '__';
-        if ($f.attr('action')){
-            var relativeAction = getRelativeAction($f);
-            key += relativeAction;
-        }
-        if (isUsableName(this.inputObj.$el)) {
-            key += keySeparator + this.inputObj.$el.attr('name');
-        } else if (this.inputObj.$el.attr('class')) {
-            // id can't be relied upon, because of EW.  We can key off class, if it's the only one in the form.
-            var klass = this.inputObj.$el.attr('class');
-            if ($('.' + klass, $f).length == 1) {
-                key += keySeparator + klass;
-            } else {
-                throw "Element isn't memorable, it has no unique class";
-            }
-        } else {
-            throw "Element isn't memorable, it has no identifiable traits";
-        }
-        return key;
-    };
-
-    /**
-     * Gets the value of the tracked input field
-     */
-    InputManager.prototype.getValue = function(){
-        return this.inputObj.getValue();
-    };
-
-    /**
-     * Saves the input's value to local storage, and registers it as part of the form for later removal
-     */
-    InputManager.prototype.save = function(){
-        localStorage[this.getStorageKey()] = this.getValue();
-    };
-
-    /**
-     * Event handler for invoke the safe
-     * @param e
-     * @returns {boolean}
-     */
-    InputManager.prototype.handleSave = function(e){
-        if (e.preventDefault){
-            e.preventDefault();
-        }
-        this.save();
-        return false;
-    };
-
-    /**
-     * Fetches the tracked input's persisted value from storage
-     * @returns {string}
-     */
-    InputManager.prototype.storedValue = function(){
-        return localStorage[this.getStorageKey()];
-    };
-
-    /**
-     * Fetches the input's remembered value and restores it to the target field
-     */
-    InputManager.prototype.restore = function(){
-        if (!this.storedValue()){
-            return;
-        }
-        this.inputObj.setValue(this.storedValue());
-    };
-
-    /**
-     * Forgets any successfully processed inputs from user
-     */
-    InputManager.prototype.forget = function(){
-        var key_prefix = $.cookie('memorable_forget');
-        if (key_prefix) {
-            for (var i = localStorage.length -1; i >=0; i--) {
-                if(localStorage.key(i).indexOf(key_prefix) == 0){
-                    localStorage.removeItem(localStorage.key(i));
-                }
-            }
-            $.removeCookie('memorable_forget', { path: '/' });
-        }
-    };
-
-    return InputManager;
-})();
-
-
-/**
- * Class describing a simple input field, as identified by a selector or DOM element, with specific methods for
- * getting & setting the value, and finding it's parent form
- *
- * @property obj: the raw object representing the field to be tracked; a standard jquery object
- * @property watchEvent: the name of the event to watch to detect when changes have been made
- * @property watchObj: the object instance to watch for events on. same as this.obj
- * @property $el: the jquery object representing the actual input field on the page. same as this.obj
- */
-SF.Memorable.InputBasic = (function() {
-    /**
-     * @param obj: a selector or DOM Element identifying the basic input field to be remembered
-     * @constructor
-     */
-    function InputBasic(obj) {
-        this.obj = $(obj);
-        this.watchEvent = 'change';
-        this.watchObj = this.obj;
-        this.$el = this.obj;
-    }
-    InputBasic.prototype.getValue = function () {
-        return this.obj.val();
-    };
-    InputBasic.prototype.setValue = function (val) {
-        this.$el.val(val);
-    };
-    InputBasic.prototype.getForm = function () {
-        return this.$el.parents('form').eq(0);
-    };
-    return InputBasic;
-})();
-
-
-/**
- * Class describing a field backed by SimpleMDE, as identified by the passed instance of `SimpleMDE` provided, with specific methods for
- * getting & setting the value, and finding it's parent form
- *
- * @property obj: the SimpleMDE object describing the field to be tracked
- * @property watchEvent: the name of the event to watch to detect when changes have been made
- * @property watchObj: the object instance to watch for events on; editor.codemirror per their docs
- * @property $el: the jquery object representing the actual input field on the page
- */
-SF.Memorable.InputMDE = (function() {
-    /**
-     * @param obj: A SimpleMDE object representing the input field
-     * @constructor
-     */
-    function InputMDE(obj) {
-        this.obj = obj;
-        this.watchEvent = 'change';
-        this.watchObj = this.obj.codemirror;
-        this.$el= $(this.obj.element);
-    }
-    InputMDE.prototype.getValue = function () {
-        return this.obj.value();
-    };
-    InputMDE.prototype.setValue = function (val) {
-        this.obj.value(val);
-    };
-    InputMDE.prototype.getForm = function () {
-        return this.$el.parents('form').eq(0);
-    };
-    return InputMDE;
-})();
-
-
-/**
- * Takes an arbitrary object, and determines the best Input class to represent it
- */
-SF.Memorable.inputFactory = function(obj) {
-    if (obj.codemirror){
-        return SF.Memorable.InputMDE;
-    } else {
-        return SF.Memorable.InputBasic;
-    }
-};
-
-
-/**
- * Convenience method to find any classes decorated with `.memorable` and create a related Input object for it
- * @param selector - use to override the selector used to find all fields to be remembered
- */
-SF.Memorable.initialize = function(selector){
-    var s = selector || '.memorable';
-    SF.Memorable.items = [];
-    $(s).each(function(){
-        var cls = SF.Memorable.inputFactory(this);
-        SF.Memorable.items.push(new SF.Memorable.InputManager(new cls(this)));
-    });
-};
-
-
-/**
- * Creates a new Input object to remember changes to an individual field
- * @param obj - the raw object representing the field to be tracked
- */
-SF.Memorable.add = function(obj){
-    var cls = SF.Memorable.inputFactory(obj);
-    SF.Memorable.items.push(new SF.Memorable.InputManager(new cls(obj)));
-};
-
-
-// Initialize
-$(function(){SF.Memorable.initialize();});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/lib/widgets/resources/js/sf_markitup.js
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/resources/js/sf_markitup.js b/Allura/allura/lib/widgets/resources/js/sf_markitup.js
index 09fe07b..3c99caa 100644
--- a/Allura/allura/lib/widgets/resources/js/sf_markitup.js
+++ b/Allura/allura/lib/widgets/resources/js/sf_markitup.js
@@ -17,7 +17,7 @@
        under the License.
 */
 
-/*global SimpleMDE, _replaceSelection */
+/*global SimpleMDE, _replaceSelection, Memorable */
 $(window).load(function() {
     if(!window.markdown_init){
         window.markdown_init = true;
@@ -75,7 +75,7 @@ $(window).load(function() {
                   "toggleUnorderedList": null, // default is cmd-l, used to activate location bar
               }
             });
-            SF.Memorable.add(editor);
+            Memorable.add(editor);
             // https://github.com/codemirror/CodeMirror/issues/1576#issuecomment-19146595
             // can't use simplemde's shortcuts settings, since those only hook into bindings set up for each button
             editor.codemirror.options.extraKeys.Home = "goLineLeft";

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/public/nf/js/memorable.js
----------------------------------------------------------------------
diff --git a/Allura/allura/public/nf/js/memorable.js b/Allura/allura/public/nf/js/memorable.js
new file mode 100644
index 0000000..a9000a0
--- /dev/null
+++ b/Allura/allura/public/nf/js/memorable.js
@@ -0,0 +1,253 @@
+/*global $, console, jQuery, localStorage */
+window.Memorable = {};
+
+/**
+ * Class that describes the management of a memorable input - identifying, watching, saving, and restoring
+ */
+Memorable.InputManager = (function(){
+
+    var defaults = {
+        invalidInputName: /([A-Za-z0-9\-_]{28})/,
+        cancelSelectors: '.cancel_edit_post, .cancel_form'
+    };
+
+    /**
+     * @param inputObj - the InputBasic or InputMDE object representing the input to be tracked
+     * @constructor
+     */
+    function InputManager(inputObj, options){
+        this.options = $.extend({}, defaults, options);
+
+        this.inputObj = inputObj;
+        this.$form = this.inputObj.getForm();
+
+        //watch the Input object for change
+        this.inputObj.watchObj.on(this.inputObj.watchEvent, this.handleSave.bind(this));
+
+        //watch "cancel"-style links, to forget immediately
+        $(this.options.cancelSelectors, this.$form).on('click', this.handleCancel.bind(this));
+        this.restore();
+    }
+
+    /**
+     * Builds a unique key to use when persisting the input's value
+     * @returns {string}
+     */
+    InputManager.prototype.getStorageKey = function(){
+        var self = this;
+        function isUsableName($el){
+            var name = $el.attr('name');
+            if (name && !name.match(self.options.invalidInputName)){
+                return true;
+            }
+        }
+        function getRelativeAction($f){
+            var action = $f[0].action;
+            var list = action.split('/');
+            var relativeAction = "/" + list.slice(3).join('/');
+            return relativeAction;
+        }
+
+        var key = '';
+        var $f = this.$form;
+        var keySeparator = '__';
+        if ($f.attr('action')){
+            var relativeAction = getRelativeAction($f);
+            key += relativeAction;
+        }
+        if (isUsableName(this.inputObj.$el)) {
+            key += keySeparator + this.inputObj.$el.attr('name');
+        } else if (this.inputObj.$el.attr('class')) {
+            // id can't be relied upon, because of EW.  We can key off class, if it's the only one in the form.
+            var klass = this.inputObj.$el.attr('class');
+            if ($('.' + klass, $f).length == 1) {
+                key += keySeparator + klass;
+            } else {
+                throw "Element isn't memorable, it has no unique class";
+            }
+        } else {
+            throw "Element isn't memorable, it has no identifiable traits";
+        }
+        return key;
+    };
+
+    /**
+     * Gets the value of the tracked input field
+     */
+    InputManager.prototype.getValue = function(){
+        return this.inputObj.getValue();
+    };
+
+    /**
+     * Saves the input's value to local storage, and registers it as part of the form for later removal
+     */
+    InputManager.prototype.save = function(){
+        localStorage[this.getStorageKey()] = this.getValue();
+    };
+
+    /**
+     * Event handler for invoking the save
+     * @param e
+     * @returns {boolean}
+     */
+    InputManager.prototype.handleSave = function(e){
+        if (e.preventDefault){
+            e.preventDefault();
+        }
+        this.save();
+        return false;
+    };
+
+    /**
+     * Event handler for clicking "cancel"
+     * @param e
+     * @returns {boolean}
+     */
+    InputManager.prototype.handleCancel = function(e){
+        Memorable.forget(this.getStorageKey());
+        return true;
+    };
+    /**
+     * Fetches the tracked input's persisted value from storage
+     * @returns {string}
+     */
+    InputManager.prototype.storedValue = function(){
+        return localStorage[this.getStorageKey()];
+    };
+
+    /**
+     * Fetches the input's remembered value and restores it to the target field
+     */
+    InputManager.prototype.restore = function(){
+        if (!this.storedValue()){
+            return;
+        }
+        this.inputObj.setValue(this.storedValue());
+    };
+
+
+    return InputManager;
+})();
+
+
+/**
+ * Class describing a simple input field, as identified by a selector or DOM element, with specific methods for
+ * getting & setting the value, and finding it's parent form
+ *
+ * @property obj: the raw object representing the field to be tracked; a standard jquery object
+ * @property watchEvent: the name of the event to watch to detect when changes have been made
+ * @property watchObj: the object instance to watch for events on. same as this.obj
+ * @property $el: the jquery object representing the actual input field on the page. same as this.obj
+ */
+Memorable.InputBasic = (function() {
+    /**
+     * @param obj: a selector or DOM Element identifying the basic input field to be remembered
+     * @constructor
+     */
+    function InputBasic(obj) {
+        this.obj = $(obj);
+        this.watchEvent = 'change';
+        this.watchObj = this.obj;
+        this.$el = this.obj;
+    }
+    InputBasic.prototype.getValue = function () {
+        return this.obj.val();
+    };
+    InputBasic.prototype.setValue = function (val) {
+        this.$el.val(val);
+    };
+    InputBasic.prototype.getForm = function () {
+        return this.$el.parents('form').eq(0);
+    };
+    return InputBasic;
+})();
+
+
+/**
+ * Class describing a field backed by SimpleMDE, as identified by the passed instance of `SimpleMDE` provided, with specific methods for
+ * getting & setting the value, and finding it's parent form
+ *
+ * @property obj: the SimpleMDE object describing the field to be tracked
+ * @property watchEvent: the name of the event to watch to detect when changes have been made
+ * @property watchObj: the object instance to watch for events on; editor.codemirror per their docs
+ * @property $el: the jquery object representing the actual input field on the page
+ */
+Memorable.InputMDE = (function() {
+    /**
+     * @param obj: A SimpleMDE object representing the input field
+     * @constructor
+     */
+    function InputMDE(obj) {
+        this.obj = obj;
+        this.watchEvent = 'change';
+        this.watchObj = this.obj.codemirror;
+        this.$el= $(this.obj.element);
+    }
+    InputMDE.prototype.getValue = function () {
+        return this.obj.value();
+    };
+    InputMDE.prototype.setValue = function (val) {
+        this.obj.value(val);
+    };
+    InputMDE.prototype.getForm = function () {
+        return this.$el.parents('form').eq(0);
+    };
+    return InputMDE;
+})();
+
+
+/**
+ * Takes an arbitrary object, and determines the best Input class to represent it
+ */
+Memorable.inputFactory = function(obj) {
+    if (obj.codemirror){
+        return Memorable.InputMDE;
+    } else {
+        return Memorable.InputBasic;
+    }
+};
+
+
+/**
+ * Convenience method to find any classes decorated with `.memorable` and create a related Input object for it
+ * @param selector - use to override the selector used to find all fields to be remembered
+ */
+Memorable.initialize = function(selector){
+    Memorable.forget();
+    var s = selector || '.memorable';
+    Memorable.items = [];
+    $(s).each(function(){
+        Memorable.add(this);
+    });
+};
+
+
+/**
+ * Forgets any successfully processed inputs from user
+ */
+Memorable.forget = function(key_prefix){
+    key_prefix = key_prefix || $.cookie('memorable_forget');
+    if (key_prefix) {
+        for (var i = localStorage.length -1; i >=0; i--) {
+            if(localStorage.key(i).indexOf(key_prefix) == 0){
+                localStorage.removeItem(localStorage.key(i));
+            }
+        }
+        $.removeCookie('memorable_forget', { path: '/' });
+    }
+};
+
+
+
+/**
+ * Creates a new Input object to remember changes to an individual field
+ * @param obj - the raw object representing the field to be tracked
+ */
+Memorable.add = function(obj){
+    var cls = Memorable.inputFactory(obj);
+    Memorable.items.push(new Memorable.InputManager(new cls(obj)));
+};
+
+
+// Initialize
+$(function(){Memorable.initialize();});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/Allura/allura/templates/jinja_master/master.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/master.html b/Allura/allura/templates/jinja_master/master.html
index 9fb165e..9c6962e 100644
--- a/Allura/allura/templates/jinja_master/master.html
+++ b/Allura/allura/templates/jinja_master/master.html
@@ -29,7 +29,6 @@
 {% do g.register_forge_js('js/twemoji.min.js') %}
 {% do g.register_forge_js('js/pb.transformie.min.js') %}
 {% do g.register_forge_js('js/allura-base.js') %}
-{#{% do g.register_forge_js('js/sisyphus.js') %}#}
 {% do g.register_forge_css('css/forge/hilite.css') %}
 {% do g.register_forge_css('css/forge/tooltipster.css') %}
 {% do g.register_forge_css('css/font-awesome.min.css', compress=False) %}
@@ -41,6 +40,7 @@
        any css.  (Unlike other html files which are the top-level file and extend this master.html) #}
     {% do g.register_forge_css('css/navbar.css') %}
 {% endif %}
+{% do g.register_forge_js('js/memorable.js') %}
 {% do g.resource_manager.register_widgets(c) %}
 {# paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ #}
 <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
@@ -188,7 +188,6 @@
             $(this).tooltipster('hide');
         });
 
-        {#$('form').sisyphus();#}
     });
 </script>
 </body>

http://git-wip-us.apache.org/repos/asf/allura/blob/e5aad89e/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index d20658d..657ba17 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -291,6 +291,7 @@ class RootController(BaseController, FeedController):
         c.form = W.new_post_form
         return dict(post=post)
 
+    @memorable_forget()
     @expose()
     @require_post()
     @validate(form=W.edit_post_form, error_handler=new)