changeset 17:8a4e0fe2c689

Added "Special Pages" section: - Only "orphaned" pages for now. - Made base view/model classes more extensible. Better way to export views and models from their module.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 01 Jan 2013 00:49:54 -0800
parents 8d6c2a5ed08d
children 67c150d5ed53
files wikked/static/css/wikked.less wikked/static/js/wikked/app.js wikked/static/js/wikked/models.js wikked/static/js/wikked/views.js wikked/static/tpl/special-nav.html wikked/static/tpl/special-orphans.html wikked/static/tpl/special-pages.html wikked/views.py
diffstat 8 files changed, 175 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/static/css/wikked.less	Mon Dec 31 22:02:39 2012 -0800
+++ b/wikked/static/css/wikked.less	Tue Jan 01 00:49:54 2013 -0800
@@ -13,16 +13,21 @@
 @colorNavLinkHover: rgb(96, 96, 96);
 
 @colorBlueDark: #48577D;
-@colorBlueLight: #7690CF;
+@colorBlueMedium: #7690CF;
+@colorBlueLight: #D9E3FF;
+
 @colorGrayBlueLight: #D3DCF2;
 @colorGrayBlueMedium: #9197A6;
 @colorGrayBlueDark: #43464C;
+
 @colorGrayLight: #EEE;
 @colorGrayMedium: #AAA;
 @colorGrayDark: #666;
+
 @colorCode: #523C37;
 
 @backgroundPage: white;
+@backgroundPageSpecial: #E5D19E;
 
 // Macros
 .box-shadow(@style, @c) {
@@ -136,6 +141,9 @@
         padding: @baseLineHeight;
         background: @backgroundPage;
     }
+    .page.special {
+        background: @backgroundPageSpecial;
+    }
 
     .page>pre {
         margin-top: @baseLineHeight;
@@ -192,7 +200,7 @@
 }
 .preview-wiki-meta {
     .meta-name {
-        background: @colorBlueLight;
+        background: @colorBlueMedium;
         color: #fff;
         padding: 0.2em 0.4em 0.2em 0.6em;
         border-radius: 0.5em 0 0 0.5em;
--- a/wikked/static/js/wikked/app.js	Mon Dec 31 22:02:39 2012 -0800
+++ b/wikked/static/js/wikked/app.js	Tue Jan 01 00:49:54 2013 -0800
@@ -25,7 +25,9 @@
             'diff/r/*path/:rev1/:rev2':"showDiff",
             'search/:query':         "showSearchResults",
             'login':                 "showLogin",
-            'logout':                "doLogout"
+            'logout':                "doLogout",
+            'special':               "showSpecialPages",
+            'special/:page':         "showSpecialPage"
         },
         readPage: function(path) {
             var view = new Views.PageReadView({ 
@@ -127,6 +129,23 @@
                     alert("Error logging out!");
                 });
         },
+        showSpecialPages: function() {
+            var view = new Views.SpecialPagesView({
+                el: $('#app'),
+                model: new Models.SpecialPagesModel()
+            });
+            view.model.setApp(this);
+            this.navigate('/special');
+        },
+        showSpecialPage: function(page) {
+            var view = new Views.GenericSpecialPageView({
+                el: $('#app'),
+                model: new Models.GenericSpecialPageModel({ page: page })
+            });
+            view.model.setApp(this);
+            view.model.fetch();
+            this.navigate('/special/' + page);
+        },
         getQueryVariable: function(variable) {
             var query = window.location.search.substring(1);
             var vars = query.split("&");
--- a/wikked/static/js/wikked/models.js	Mon Dec 31 22:02:39 2012 -0800
+++ b/wikked/static/js/wikked/models.js	Tue Jan 01 00:49:54 2013 -0800
@@ -10,7 +10,9 @@
         ],
     function(require, $, _, Backbone, Handlebars) {
 
-    var NavigationModel = Backbone.Model.extend({
+    var exports = {};
+
+    var NavigationModel = exports.NavigationModel = Backbone.Model.extend({
         idAttribute: 'path',
         defaults: function() {
             return {
@@ -69,12 +71,18 @@
         }
     });
 
-    var FooterModel = Backbone.Model.extend({
+    var FooterModel = exports.FooterModel = Backbone.Model.extend({
         defaults: function() {
             return {
-                url_extras: [ { name: 'Home', url: '/' } ]
+                url_extras: [
+                    { name: 'Home', url: '/' },
+                    { name: 'Special Pages', url: '/#/special' }
+                ]
             };
         },
+        clearExtraUrls: function() {
+            this.get('url_extras').length = 0;
+        },
         addExtraUrl: function(name, url, index) {
             if (index === undefined) {
                 this.get('url_extras').push({ name: name, url: url });
@@ -84,7 +92,7 @@
         }
     });
 
-    var LoginModel = Backbone.Model.extend({
+    var LoginModel = exports.LoginModel = Backbone.Model.extend({
         setApp: function(app) {
             this.app = app;
         },
@@ -100,7 +108,7 @@
         }
     });
 
-    var PageModel = Backbone.Model.extend({
+    var PageModel = exports.PageModel = Backbone.Model.extend({
         idAttribute: 'path',
         defaults: function() {
             return {
@@ -146,11 +154,11 @@
         }
     });
     
-    var PageStateModel = PageModel.extend({
+    var PageStateModel = exports.PageStateModel = PageModel.extend({
         urlRoot: '/api/state/'
     });
     
-    var MasterPageModel = PageModel.extend({
+    var MasterPageModel = exports.MasterPageModel = PageModel.extend({
         initialize: function() {
             this.nav = new NavigationModel({ id: this.id });
             this.footer = new FooterModel();
@@ -177,7 +185,7 @@
         }
     });
 
-    var PageReadModel = MasterPageModel.extend({
+    var PageReadModel = exports.PageReadModel = MasterPageModel.extend({
         urlRoot: '/api/read/',
         action: 'read',
         _onChangePath: function(path) {
@@ -187,12 +195,12 @@
         }
     });
 
-    var PageSourceModel = MasterPageModel.extend({
+    var PageSourceModel = exports.PageSourceModel = MasterPageModel.extend({
         urlRoot: '/api/raw/',
         action: 'source'
     });
 
-    var PageEditModel = MasterPageModel.extend({
+    var PageEditModel = exports.PageEditModel = MasterPageModel.extend({
         urlRoot: '/api/edit/',
         action: 'edit',
         doEdit: function(form) {
@@ -208,7 +216,7 @@
         }
     });
 
-    var PageHistoryModel = MasterPageModel.extend({
+    var PageHistoryModel = exports.PageHistoryModel = MasterPageModel.extend({
         urlRoot: '/api/history/',
         action: 'history',
         doDiff: function(form) {
@@ -224,7 +232,7 @@
         }
     });
 
-    var PageRevisionModel = MasterPageModel.extend({
+    var PageRevisionModel = exports.PageRevisionModel = MasterPageModel.extend({
         urlRoot: '/api/revision/',
         idAttribute: 'path_and_rev',
         action: 'revision',
@@ -254,7 +262,7 @@
         }
     });
 
-    var PageDiffModel = MasterPageModel.extend({
+    var PageDiffModel = exports.PageDiffModel = MasterPageModel.extend({
         urlRoot: '/api/diff/',
         idAttribute: 'path_and_revs',
         action: 'diff',
@@ -295,12 +303,12 @@
         }
     });
 
-    var IncomingLinksModel = MasterPageModel.extend({
+    var IncomingLinksModel = exports.IncomingLinksModel = MasterPageModel.extend({
         urlRoot: '/api/inlinks/',
         action: 'inlinks'
     });
 
-    var WikiSearchModel = MasterPageModel.extend({
+    var WikiSearchModel = exports.WikiSearchModel = MasterPageModel.extend({
         urlRoot: '/api/search/',
         action: 'search',
         title: function() {
@@ -318,18 +326,27 @@
         }
     });
 
-    return {
-        NavigationModel: NavigationModel,
-        FooterModel: FooterModel,
-        PageReadModel: PageReadModel,
-        PageEditModel: PageEditModel,
-        PageHistoryModel: PageHistoryModel,
-        IncomingLinksModel: IncomingLinksModel,
-        PageRevisionModel: PageRevisionModel,
-        PageDiffModel: PageDiffModel,
-        WikiSearchModel: WikiSearchModel,
-        LoginModel: LoginModel,
-        PageStateModel: PageStateModel
-    };
+    var SpecialPagesModel = exports.SpecialPagesModel = MasterPageModel.extend({
+        action: 'special',
+        title: function() {
+            return 'Special Pages';
+        },
+        initialize: function() {
+            SpecialPagesModel.__super__.initialize.apply(this, arguments);
+            this.footer.clearExtraUrls();
+        }
+    });
+
+    var GenericSpecialPageModel = exports.GenericSpecialPageModel = MasterPageModel.extend({
+        action: 'special',
+        urlRoot: '/api/special',
+        idAttribute: 'page',
+        initialize: function() {
+            GenericSpecialPageModel.__super__.initialize.apply(this, arguments);
+            this.footer.clearExtraUrls();
+        }
+    });
+
+    return exports;
 });
 
--- a/wikked/static/js/wikked/views.js	Mon Dec 31 22:02:39 2012 -0800
+++ b/wikked/static/js/wikked/views.js	Tue Jan 01 00:49:54 2013 -0800
@@ -11,18 +11,22 @@
         ],
     function($, _, Backbone, Handlebars, Models, Util) {
 
-    var PageView = Backbone.View.extend({
+    var exports = {};
+
+    var PageView = exports.PageView = Backbone.View.extend({
         tagName: 'div',
         className: 'wrapper',
         initialize: function() {
             PageView.__super__.initialize.apply(this, arguments);
-            var $view = this;
-            this.model.on("change", function() { $view.render(); });
+            if (this.model !== undefined) {
+                var $view = this;
+                this.model.on("change", function() { $view.render(); });
+            }
             return this;
         },
         render: function(view) {
             if (this.templateName !== undefined) {
-                this.renderTemplate(this.templateName, this.renderCallback);
+                this.renderTemplate(_.result(this, 'templateName'), this.renderCallback);
             }
             this.renderTitle(this.titleFormat);
             return this;
@@ -47,7 +51,7 @@
     });
     _.extend(PageView, Backbone.Events);
 
-    var NavigationView = PageView.extend({
+    var NavigationView = exports.NavigationView = PageView.extend({
         templateName: 'nav',
         initialize: function() {
             NavigationView.__super__.initialize.apply(this, arguments);
@@ -55,7 +59,7 @@
             return this;
         },
         render: function() {
-            this.renderTemplate('nav');
+            this.renderTemplate(this.templateName);
         },
         postRender: function() {
             var model = this.model;
@@ -106,7 +110,7 @@
         }
     });
 
-    var FooterView = PageView.extend({
+    var FooterView = exports.FooterView = PageView.extend({
         templateName:  'footer',
         initialize: function() {
             FooterView.__super__.initialize.apply(this, arguments);
@@ -120,7 +124,7 @@
         }
     });
 
-    var LoginView = PageView.extend({
+    var LoginView = exports.LoginView = PageView.extend({
         templateName: 'login',
         initialize: function() {
             LoginView.__super__.initialize.apply(this, arguments);
@@ -139,11 +143,11 @@
         }
     });
 
-    var MasterPageView = PageView.extend({
+    var MasterPageView = exports.MasterPageView = PageView.extend({
         initialize: function() {
             MasterPageView.__super__.initialize.apply(this, arguments);
-            this.nav = new NavigationView({ model: this.model.nav });
-            this.footer = new FooterView({ model: this.model.footer });
+            this.nav = this._createNavigation(this.model.nav);
+            this.footer = this._createFooter(this.model.footer);
             this.render();
             return this;
         },
@@ -152,10 +156,16 @@
             this.nav.postRender();
             this.footer.$el.appendTo(this.$el);
             this.footer.postRender();
+        },
+        _createNavigation: function(model) {
+            return new NavigationView({ model: model });
+        },
+        _createFooter: function(model) {
+            return new FooterView({ model: model });
         }
     });
 
-    var PageReadView = MasterPageView.extend({
+    var PageReadView = exports.PageReadView = MasterPageView.extend({
         templateName: 'read-page',
         initialize: function() {
             PageReadView.__super__.initialize.apply(this, arguments);
@@ -195,7 +205,7 @@
         }
     });
 
-    var PageEditView = MasterPageView.extend({
+    var PageEditView = exports.PageEditView = MasterPageView.extend({
         templateName: 'edit-page',
         renderCallback: function(view, model) {
             PageEditView.__super__.renderCallback.apply(this, arguments);
@@ -210,7 +220,7 @@
         }
     });
 
-    var PageHistoryView = MasterPageView.extend({
+    var PageHistoryView = exports.PageHistoryView = MasterPageView.extend({
         templateName: 'history-page',
         renderCallback: function(view, model) {
             PageHistoryView.__super__.renderCallback.apply(this, arguments);
@@ -225,40 +235,53 @@
         }
     });
 
-    var PageRevisionView = MasterPageView.extend({
+    var PageRevisionView = exports.PageRevisionView = MasterPageView.extend({
         templateName: 'revision-page',
         titleFormat: function(title) {
             return title + ' [' + this.model.get('rev') + ']';
         }
     });
 
-    var PageDiffView = MasterPageView.extend({
+    var PageDiffView = exports.PageDiffView = MasterPageView.extend({
         templateName: 'diff-page',
         titleFormat: function(title) {
             return title + ' [' + this.model.get('rev1') + '-' + this.model.get('rev2') + ']';
         }
     });
 
-    var IncomingLinksView = MasterPageView.extend({
+    var IncomingLinksView = exports.IncomingLinksView = MasterPageView.extend({
         templateName: 'inlinks-page',
         titleFormat: function(title) {
             return 'Incoming Links: ' + title;
         }
     });
 
-    var WikiSearchView = MasterPageView.extend({
+    var WikiSearchView = exports.WikiSearchView = MasterPageView.extend({
         templateName: 'search-results'
     });
 
-    return {
-        PageReadView: PageReadView,
-        PageEditView: PageEditView,
-        PageHistoryView: PageHistoryView,
-        IncomingLinksView: IncomingLinksView,
-        PageRevisionView: PageRevisionView,
-        PageDiffView: PageDiffView,
-        WikiSearchView: WikiSearchView,
-        LoginView: LoginView
-    };
+    var SpecialNavigationView = exports.SpecialNavigationView = NavigationView.extend({
+        templateName: 'special-nav'
+    });
+
+    var SpecialPagesView = exports.SpecialPagesView = MasterPageView.extend({
+        templateName: 'special-pages',
+        _createNavigation: function(model) {
+            model.set('show_root_link', false);
+            return new SpecialNavigationView({ model: model });
+        }
+    });
+
+    var GenericSpecialPageView = exports.GenericSpecialPageView = MasterPageView.extend({
+        templateName: function() {
+            return 'special-' + this.model.get('page');
+        },
+        _createNavigation: function(model) {
+            model.set('show_root_link', true);
+            return new SpecialNavigationView({ model: model });
+        }
+    });
+
+    return exports;
 });
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wikked/static/tpl/special-nav.html	Tue Jan 01 00:49:54 2013 -0800
@@ -0,0 +1,18 @@
+<nav class="row">
+    <div class="span12">
+        <a href="/">Home</a>
+        {{#if show_root_link}}
+        <a href="/#/special">Special Pages</a>
+        {{/if}}
+        <form id="search" class="search">
+            <input type="text" name="q" class="input-medium search-query" placeholder="Search...">
+            <button type="submit" class="btn">Search</button>
+        </form>
+        {{#if username}}
+        <a href="{{url_profile}}">{{username}}</a>
+        <a href="{{url_logout}}">Logout</a>
+        {{else}}
+        <a href="{{url_login}}">Login</a>
+        {{/if}}
+    </div>
+</nav>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wikked/static/tpl/special-orphans.html	Tue Jan 01 00:49:54 2013 -0800
@@ -0,0 +1,14 @@
+<article class="row">
+    <div class="page special span12">
+        <h1>Orphaned Pages</h1>
+        <p>The following pages are not linked to by any other page in the wiki.</p>
+        <p><span class="label label-info">Note</span> The main page can occasionally show up here but that's OK since it's the page visitors will see first anyway.</p>
+        {{#if orphans}}
+        {{#each orphans}}
+        <h3><a href="/#/read/{{meta.url}}">{{meta.title}}</a></h3>
+        {{/each}}
+        {{else}}
+        <p>No orphaned pages!</p>
+        {{/if}}
+    </div>
+</article>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wikked/static/tpl/special-pages.html	Tue Jan 01 00:49:54 2013 -0800
@@ -0,0 +1,7 @@
+<article class="row">
+    <div class="page special span12">
+        <h1>Special Pages</h1>
+        <h3><a href="/#/special/orphans">Orphaned Pages</a></h3>
+        <p>Lists pages in the wiki that have no links to them.</p>
+    </div>
+</article>
--- a/wikked/views.py	Mon Dec 31 22:02:39 2012 -0800
+++ b/wikked/views.py	Tue Jan 01 00:49:54 2013 -0800
@@ -209,6 +209,16 @@
     pass
 
 
+@app.route('/api/special/orphans')
+def api_special_orphans():
+    orphans = []
+    for page in wiki.getPages():
+        if len(page.in_links) == 0:
+            orphans.append({ 'path': page.url, 'meta': page.all_meta })
+    result = { 'orphans': orphans }
+    return make_auth_response(result)
+
+
 @app.route('/api/history/<path:url>')
 def api_page_history(url):
     page = get_page_or_404(url)