changeset 110:827e236aa7c6

Various front-end fixes: - Handle absolute URLs correctly. - Show the state warning only after the page has rendered.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 14 Nov 2013 15:21:48 -0800
parents f0172c5d8e2c
children e5dea315583b
files static/js/wikked/models.js static/js/wikked/views.js static/tpl/read-page.html static/tpl/search-results.html static/tpl/state-warning.html wikked/fs.py wikked/page.py wikked/resolver.py wikked/utils.py wikked/views.py
diffstat 10 files changed, 68 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/static/js/wikked/models.js	Tue Nov 12 13:54:29 2013 -0800
+++ b/static/js/wikked/models.js	Thu Nov 14 15:21:48 2013 -0800
@@ -226,17 +226,22 @@
             this.footer.addExtraUrl('JSON', function() { return '/api/read/' + model.id; });
         },
         _onChange: function() {
-            if (this.getMeta('redirect') &&
-                !this.get('no_redirect') &&
-                !this.get('redirected_from')) {
+            if (this.getMeta('redirect')) {
                 // Handle redirects.
-                var oldPath = this.get('path');
-                this.set({
-                    'path': this.getMeta('redirect'),
-                    'redirected_from': oldPath
-                });
-                this.fetch();
-                this.navigate('/read/' + this.getMeta('redirect'), { replace: true, trigger: false });
+                var newPath = this.getMeta('redirect').replace(/^\//, "");
+                if (this.get('no_redirect')) {
+                    this.set({ 'redirects_to': newPath }, { 'silent': true });
+                } else {
+                    var oldPath = this.get('path');
+                    this.set({
+                        'path': newPath,
+                        'redirected_from': oldPath
+                    }, {
+                        'silent': true
+                    });
+                    this.fetch();
+                    this.navigate('/read/' + newPath, { replace: true, trigger: false });
+                }
             }
         }
     });
--- a/static/js/wikked/views.js	Tue Nov 12 13:54:29 2013 -0800
+++ b/static/js/wikked/views.js	Thu Nov 14 15:21:48 2013 -0800
@@ -247,14 +247,19 @@
             this.$('a.wiki-link[data-wiki-url]').each(function(i) {
                 var jel = $(this);
                 if (jel.hasClass('missing') || jel.attr('data-action') == 'edit')
-                    jel.attr('href', '/#/edit/' + jel.attr('data-wiki-url'));
+                    jel.attr('href', '/#/edit' + jel.attr('data-wiki-url'));
                 else
-                    jel.attr('href', '/#/read/' + jel.attr('data-wiki-url'));
+                    jel.attr('href', '/#/read' + jel.attr('data-wiki-url'));
             });
-            // If we've already rendered the content, and we need to display
-            // a warning, do so now.
+            // If we've already rendered the content, see if need to display a
+            // warning about the page's state.
             if (this.model.get('content')) {
-                this._showPageStateWarning();
+                if (this._pageState === undefined) {
+                    if (!this._isCheckingPageState)
+                        this._checkPageState();
+                } else {
+                    this._showPageStateWarning();
+                }
             }
         },
         _showPageStateWarning: function() {
@@ -263,10 +268,11 @@
 
             var state = this._pageState.get('state');
             if (state == 'new' || state == 'modified') {
+                var article = $('.wrapper>article');
                 var warning = $(this.warningTemplate(this._pageState.toJSON()));
                 $('[rel="tooltip"]', warning).tooltip({container:'body'});
                 //warning.css('display', 'none');
-                warning.prependTo($('.wrapper>article'));
+                warning.prependTo(article);
                 //warning.slideDown();
                 $('.dismiss', warning).click(function() {
                     //warning.slideUp();
@@ -275,29 +281,20 @@
                 });
             }
         },
+        _isCheckingPageState: false,
         _checkPageState: function() {
+            this._isCheckingPageState = true;
             var $view = this;
             var stateModel = new Models.PageStateModel({ path: this.model.get('path') });
             stateModel.fetch({
                 success: function(model, response, options) {
                     $view._pageState = model;
-                    // If we've already rendered the content, display
-                    // the warning, if any, now.
+                    $view._isCheckingPageState = false;
                     if ($view.model && $view.model.get('content')) {
                         $view._showPageStateWarning();
                     }
                 }
             });
-        },
-        _firstRender: true,
-        _onModelChange: function() {
-            PageReadView.__super__._onModelChange.apply(this, arguments);
-
-            // Fetch the state if the current page changed.
-            if (!this.isError && (this.model.hasChanged('path') || this._firstRender)) {
-                this._checkPageState();
-                this._firstRender = false;
-            }
         }
     });
 
--- a/static/tpl/read-page.html	Tue Nov 12 13:54:29 2013 -0800
+++ b/static/tpl/read-page.html	Thu Nov 14 15:21:48 2013 -0800
@@ -6,7 +6,7 @@
         <small>Redirected from <a href="/#/read/{{redirected_from}}?no_redirect">{{redirected_from}}</a></small>
         {{/if}}
         {{#if meta.redirect}}
-        <small>Redirects to <a href="/#/read/{{meta.redirect}}">{{meta.redirect}}</a></small>
+        <small>Redirects to <a href="/#/read/{{redirects_to}}">{{redirects_to}}</a></small>
         {{/if}}
     </header>
     {{/ifnot}}
--- a/static/tpl/search-results.html	Tue Nov 12 13:54:29 2013 -0800
+++ b/static/tpl/search-results.html	Thu Nov 14 15:21:48 2013 -0800
@@ -10,7 +10,7 @@
         <ul class="search-results">
             {{#each hits}}
             <li>
-            <h3><a href="/#/read/{{url}}">{{title}}</a></h3>
+            <h3><a href="/#/read{{url}}">{{title}}</a></h3>
             <div class="highlighted"><pre><code>{{{content_highlights}}}</code></pre></div>
             </li>
             {{/each}}
--- a/static/tpl/state-warning.html	Tue Nov 12 13:54:29 2013 -0800
+++ b/static/tpl/state-warning.html	Thu Nov 14 15:21:48 2013 -0800
@@ -1,4 +1,4 @@
-<div class="ribbon">
+<div class="ribbon state-warning">
     <div class="ribbon-inner">
         <span><a href="{{url_edit}}" rel="tooltip" title="This page is {{state}} in the repository. You can edit it now to commit." data-toggle="tooltip" data-placement="bottom">modified <i class="icon-info-sign icon-white"></i></a></span>
     </div>
--- a/wikked/fs.py	Tue Nov 12 13:54:29 2013 -0800
+++ b/wikked/fs.py	Thu Nov 14 15:21:48 2013 -0800
@@ -102,12 +102,12 @@
         url = ''
         parts = unicode(name).lower().split(os.sep)
         for i, part in enumerate(parts):
-            if i > 0:
-                url += '/'
-            url += title_to_url(part)
+            url += '/' + title_to_url(part)
         return PageInfo(url, path)
 
     def _getPhysicalPath(self, url, is_file):
+        if url[0] != '/':
+            raise ValueError("Page URLs need to be absolute: " + url)
         if string.find(url, '..') >= 0:
             raise ValueError("Page URLs can't contain '..': " + url)
 
@@ -115,7 +115,7 @@
         # file-system entry that would get slugified to an
         # equal string.
         current = self.root
-        parts = unicode(url).lower().split('/')
+        parts = unicode(url[1:]).lower().split('/')
         for i, part in enumerate(parts):
             names = os.listdir(current)
             for name in names:
@@ -134,3 +134,4 @@
                 # Failed to find a part of the URL.
                 raise PageNotFoundError("No such page: " + url)
         return current
+
--- a/wikked/page.py	Tue Nov 12 13:54:29 2013 -0800
+++ b/wikked/page.py	Thu Nov 14 15:21:48 2013 -0800
@@ -32,6 +32,9 @@
         to load things from. Use `FileSystemPage` or `DatabasePage` instead.
     """
     def __init__(self, wiki, url):
+        if url[0] != '/':
+            raise ValueError("Page URLs need to be absolute: " + url)
+
         self.wiki = wiki
         self.url = url
         self._data = None
--- a/wikked/resolver.py	Tue Nov 12 13:54:29 2013 -0800
+++ b/wikked/resolver.py	Thu Nov 14 15:21:48 2013 -0800
@@ -168,9 +168,8 @@
             # Resolve link states.
             def repl1(m):
                 raw_url = str(m.group('url'))
-                raw_url = self.ctx.getAbsoluteUrl(raw_url)
-                url = namespace_title_to_url(raw_url)
-                self.wiki.logger.debug("Converting '%s' to '%s'" % (raw_url, url))
+                abs_raw_url = self.ctx.getAbsoluteUrl(raw_url)
+                url = namespace_title_to_url(abs_raw_url)
                 if self.wiki.pageExists(url):
                     return '<a class="wiki-link" data-wiki-url="%s">' % url
                 return '<a class="wiki-link missing" data-wiki-url="%s">' % url
@@ -320,9 +319,11 @@
         for v in include_meta_values:
             pipe_idx = v.find('|')
             if pipe_idx > 0:
-                included_urls.append(v[:pipe_idx])
+                incl_url = v[:pipe_idx]
             else:
-                included_urls.append(v)
+                incl_url = v
+            abs_incl_url = get_absolute_url(page.url, incl_url)
+            included_urls.append(abs_incl_url)
 
         # Recurse into included pages.
         for url in included_urls:
@@ -367,3 +368,4 @@
     if title is None:
         title = value
     return '<a class="wiki-link" data-wiki-url="%s" data-action="edit">%s</a>' % (value, title)
+
--- a/wikked/utils.py	Tue Nov 12 13:54:29 2013 -0800
+++ b/wikked/utils.py	Thu Nov 14 15:21:48 2013 -0800
@@ -4,9 +4,12 @@
 
 
 def get_absolute_url(base_url, url, do_slugify=True):
+    if base_url[0] != '/':
+        raise ValueError("The base URL must be absolute. Got: %s" % base_url)
+
     if url.startswith('/'):
         # Absolute page URL.
-        abs_url = url[1:]
+        abs_url = url
     else:
         # Relative page URL. Let's normalize all `..` in it,
         # which could also replace forward slashes by backslashes
@@ -22,6 +25,9 @@
 def namespace_title_to_url(url):
     url_parts = url.split('/')
     result = ''
+    if url[0] == '/':
+        result = '/'
+        url_parts = url_parts[1:]
     for i, part in enumerate(url_parts):
         if i > 0:
             result += '/'
@@ -60,4 +66,3 @@
         clean_name = name[1:]
     return (clean_name, modifiers)
 
-
--- a/wikked/views.py	Tue Nov 12 13:54:29 2013 -0800
+++ b/wikked/views.py	Thu Nov 14 15:21:48 2013 -0800
@@ -10,7 +10,7 @@
 from page import Page, PageData
 from fs import PageNotFoundError
 from formatter import PageFormatter, FormattingContext
-from utils import title_to_url
+from utils import namespace_title_to_url, get_absolute_url
 import scm
 
 
@@ -19,7 +19,12 @@
 CHECK_FOR_WRITE = 2
 
 
-def get_category_meta(category):
+def coerce_redirect(page, redirect):
+    target_url = get_absolute_url(page.url, redirect[0])
+    return namespace_title_to_url(target_url)
+
+
+def coerce_category(page, category):
     result = []
     for item in category:
         result.append({
@@ -29,8 +34,8 @@
     return result
 
 COERCE_META = {
-    'redirect': title_to_url,
-    'category': get_category_meta
+    'redirect': coerce_redirect,
+    'category': coerce_category
     }
 
 
@@ -59,6 +64,8 @@
 
 
 def get_page_or_none(url, force_resolve=False):
+    if url[0] != '/':
+        url = '/' + url
     try:
         page = g.wiki.getPage(url)
         if force_resolve:
@@ -97,7 +104,7 @@
     meta['url'] = page.url
     for name in COERCE_META:
         if name in meta:
-            meta[name] = COERCE_META[name](meta[name])
+            meta[name] = COERCE_META[name](page, meta[name])
     return meta
 
 
@@ -347,6 +354,7 @@
     if 'message' in request.form and len(request.form['message']) > 0:
         message = request.form['message']
 
+    url = '/' + url
     page_fields = {
             'rev': rev,
             'author': author,