Commits

benoitc@localhost.nymphormation.net  committed 29d337a

forgot to add these files ...

  • Participants
  • Parent commits ffd724a

Comments (0)

Files changed (8)

File _attachments/account.html

+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8"/>
+    <title>benoît chesneau, web craftman.</title>
+
+    <!--[if IE]>
+
+    <script>
+        // allow IE to recognize HTMl5 elements
+        document.createElement('section');
+        document.createElement('article');
+        document.createElement('aside');
+        document.createElement('footer');
+        document.createElement('header');
+        document.createElement('nav');
+        document.createElement('time');
+
+    </script>
+    <![endif]-->
+    <link rel="openid.server" href="http://www.myopenid.com/server">
+    <link rel="openid.delegate" href="http://bchesneau.myopenid.com/">
+    <link rel="home index" href="http://benoitc.org/" type="text/html">
+    <link rel="me author" href="http://benoitc.org/about.html" type="text/html">
+    <link rel="stylesheet" href="css/screen.css" type="text/css" media="screen, projection" />
+
+    
+
+
+</head>
+
+
+<body>
+        <header>
+        <h1>
+            <a rel="me author" href="about.html">Benoît Chesneau</a>
+        </h1>
+        </header>
+        <section id="content">
+            <h1>Login via the popup or hit cancel.</h1>
+            <p>Edit CouchDB's configuration to create admin users. You may be able to accomplish this via the HTTP API or <a href="http://wiki.apache.org/couchdb/Setting_up_an_Admin_account">of course by editing local.ini</a>.</p>
+            <p>This blog will eventually move to application users (stored in the DB) but using admin users for now gives me a platform to experiment with validation models.</p>
+        </section> 
+
+        <footer id="main-footer">
+            <p class="copyright">Copyright &copy; 2009 by <a rel="me author"
+                href="about.html">Benoît Chesneau</a> </p>
+        </footer>
+</body>
+
+    <script src="/_utils/script/json2.js"></script>
+    <script src="/_utils/script/jquery.js"></script>
+    <script src="/_utils/script/jquery.cookies.js"></script>
+    <script src="/_utils/script/jquery.couch.js"></script>
+
+
+    <script src="js/jquery.couchapp.js"></script>
+    
+    <script>
+        $.CouchApp(function(app) {
+            var redirectUrl = document.location.hash.replace('#','');
+            $('#content').append('<p>Attempting login.</p>');
+            app.attemptLogin(function(login) {
+                if (redirectUrl) document.location = unescape(redirectUrl).replace('_design/','_design%2F');
+                $('#content').append('<p>Success.</p>');
+                $('h1').text('Welcome '+login);
+                $('#content').append('<p>Now that you\'re logged in, you can <a href="edit.html">write blog posts.</a></p>');
+            }, function() {
+                $('h1').text('Incorrect login');
+                $('#content').append('<p>Fail.</p>');
+            });
+        }); 
+    </script>
+
+</html>

File _attachments/css/default.css

Empty file added.

File _attachments/css/typography.css

+
+pre,code    { margin: 1.5em 0; white-space: pre; }
+pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } 
+
+#mdpreview {
+    /*white-space: pre;*/
+}

File _attachments/js/blog.js

+
+
+function Blog(app) {
+
+    this.formatBody = function(body) {
+        var converter = new Showdown.converter();
+        return converter.makeHtml(body);
+    }
+}
+
+

File _attachments/js/editor.js

+function Editor() {
+    var converter = new Showdown.converter;
+    var converter_callback = function(value) {  
+              $('#mdpreview').html(converter.makeHtml(value));  
+    }
+
+    
+    $("#content").TextArea({
+        change_callback: converter_callback 
+    });
+
+    var lastRoomleft = 0;
+    var bottomEditHeight = $('#main-footer').offset().top - $('#content').offset().top -
+            $("#content").height();
+    
+    $(window).resize(resizePanes);
+    
+    window.setInterval(resizePanes, 250);
+    function resizePanes() {
+        var roomLeft =  $(window).height() - $('#main-footer').height() 
+         - $('#content').offset().top - bottomEditHeight;
+        
+        if (roomLeft < 0)
+            roomLeft = 0;
+        if (roomLeft == self.lastRoomLeft)
+            return;
+
+        lastRoomLeft = roomLeft;
+        
+        $('#content').height(roomLeft);
+        $('#mdpreview').height(roomLeft);  
+            
+    }
+}    

File _attachments/js/jquery.couchapp.js

+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Usage:
+// $.CouchApp
+
+// Monkeypatching Date. 
+
+
+(function($) {
+
+  function f(n) {    // Format integers to have at least two digits.
+      return n < 10 ? '0' + n : n;
+  }
+
+  Date.prototype.toJSON = function() {
+      return this.getUTCFullYear()   + '/' +
+           f(this.getUTCMonth() + 1) + '/' +
+           f(this.getUTCDate())      + ' ' +
+           f(this.getUTCHours())     + ':' +
+           f(this.getUTCMinutes())   + ':' +
+           f(this.getUTCSeconds())   + ' +0000';
+  };
+  
+  function Design(db, name) {
+    this.view = function(view, opts) {
+      db.view(name+'/'+view, opts);
+    };
+  };
+
+  var login;
+  
+  function init(app) {
+    $(function() {
+      var dbname = document.location.href.split('/')[3];
+      var dname = unescape(document.location.href).split('/')[5];
+      var db = $.couch.db(dbname);
+      var design = new Design(db, dname);
+      
+      // docForm applies CouchDB behavior to HTML forms.
+      function docForm(formSelector, opts) {
+        var localFormDoc = {};
+        opts = opts || {};
+        opts.fields = opts.fields || [];
+        
+        // turn the form into deep json
+        // field names like 'author-email' get turned into json like
+        // {"author":{"email":"quentin@example.com"}}
+        function formToDeepJSON(form, fields, doc) {
+          var form = $(form);
+          opts.fields.forEach(function(field) {
+            var val = form.find("[name="+field+"]").val()
+            if (!val) return;
+            var parts = field.split('-');
+            var frontObj = doc, frontName = parts.shift();
+            while (parts.length > 0) {
+              frontObj[frontName] = frontObj[frontName] || {}
+              frontObj = frontObj[frontName];
+              frontName = parts.shift();
+            }
+            frontObj[frontName] = val;
+          });
+        };
+        
+        // Apply the behavior
+        $(formSelector).submit(function(e) {
+          e.preventDefault();
+          // formToDeepJSON acts on localFormDoc by reference
+          formToDeepJSON(this, opts.fields, localFormDoc);
+          if (opts.beforeSave) opts.beforeSave(localFormDoc);
+          db.saveDoc(localFormDoc, {
+            success : function(resp) {
+              if (opts.success) opts.success(resp, localFormDoc);
+            }
+          })
+          
+          return false;
+        });
+
+        // populate form from an existing doc
+        function docToForm(doc) {
+          var form = $(formSelector);
+          // fills in forms
+          opts.fields.forEach(function(field) {
+            var parts = field.split('-');
+            var run = true, frontObj = doc, frontName = parts.shift();
+            while (frontObj && parts.length > 0) {                
+              frontObj = frontObj[frontName];
+              frontName = parts.shift();
+            }
+            if (frontObj && frontObj[frontName])
+              form.find("[name="+field+"]").val(frontObj[frontName]);
+          });            
+        };
+        
+        if (opts.id) {
+          db.openDoc(opts.id, {
+            success: function(doc) {
+              if (opts.onLoad) opts.onLoad(doc);
+              localFormDoc = doc;
+              docToForm(doc);
+          }});
+        } else if (opts.template) {
+          if (opts.onLoad) opts.onLoad(opts.template);
+          localFormDoc = opts.template;
+          docToForm(localFormDoc);
+        }
+        var instance = {
+          deleteDoc : function(opts) {
+            opts = opts || {};
+            if (confirm("Really delete this post?")) {                
+              db.removeDoc(localFormDoc, opts);
+            }
+          },
+          localDoc : function() {
+            formToDeepJSON(formSelector, opts.fields, localFormDoc);
+            return localFormDoc;
+          }
+        }
+        return instance;
+      }
+      
+      function prettyDate(time){
+      	var date = new Date(time),
+      		diff = (((new Date()).getTime() - date.getTime()) / 1000),
+      		day_diff = Math.floor(diff / 86400);
+
+        // if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 ) return;
+
+      	return day_diff < 1 && (
+      			diff < 60 && "just now" ||
+      			diff < 120 && "1 minute ago" ||
+      			diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
+      			diff < 7200 && "1 hour ago" ||
+      			diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
+      		day_diff == 1 && "yesterday" ||
+      		day_diff < 21 && day_diff + " days ago" ||
+      		day_diff < 45 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
+      		day_diff < 730 && Math.ceil( day_diff / 31 ) + " months ago" ||
+      		Math.ceil( day_diff / 365 ) + " years ago";
+      };
+      
+      app({
+        showPath : function(form, docid) {
+          return '/'+[dbname, '_show', dname, form, docid].join('/')
+        },
+        attemptLogin : function(win, fail) {
+          // depends on nasty hack in blog validation function
+          db.saveDoc({"author":"_self"}, { error: function(s, e, r) {
+            var namep = r.split(':');
+            if (namep[0] == '_self') {
+              login = namep.pop();
+              $.cookies.set("login", login, '/'+dbname)
+              win && win(login);
+            } else {
+              $.cookies.set("login", "", '/'+dbname)
+              fail && fail(s, e, r);
+            }
+          }});        
+        },
+        loggedInNow : function(loggedIn, loggedOut) {
+          login = login || $.cookies.get("login");
+          if (login) {
+            loggedIn && loggedIn(login);
+          } else {
+            loggedOut && loggedOut();
+          }
+        },
+        db : db,
+        design : design,
+        docForm : docForm,
+        prettyDate : prettyDate
+      });
+    });
+  };
+  
+  $.CouchApp = $.CouchApp || init;
+  
+
+})(jQuery);
+

File shows/post.js

Empty file added.

File validate_doc_update.js

+function (newDoc, oldDoc, userCtx) {
+  var type = (oldDoc || newDoc)['type'];
+  var author = (oldDoc || newDoc)['author'];
+
+  function forbidden(message) {    
+    throw({forbidden : message});
+  };
+  
+  function unauthorized(message) {
+    throw({unauthorized : message});
+  };
+
+  function require(beTrue, message) {
+    if (!beTrue) forbidden(message);
+  };
+
+  // docs with authors can only be saved by their author
+  if (author) {
+    // dirty hack to provide userCtx.name to the client process
+    if (author == '_self') userCtx.name ? forbidden('_self:' + userCtx.name) : unauthorized('Please log in.');
+    
+    if (userCtx.roles.indexOf('_admin') == -1) {
+      // admin can edit anything, only check when not admin...
+      if (oldDoc && oldDoc.author != newDoc.author) 
+        forbidden("You may not change the author of a doc.");
+
+      if (author != userCtx.name)
+        unauthorized("Only "+author+" may edit this document.");      
+    }
+  }
+
+  /*
+  // general timestamps
+  if (oldDoc && oldDoc.created_at != newDoc.created_at) 
+    forbidden("You may not change the created_at field of a doc.");
+  
+  // this ensures that the date will be UTC, parseable, and collate correctly
+  if (newDoc.created_at) {
+    if (!newDoc.created_at.match(/\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2} \+0000/))
+      forbidden("Sorry, "+newDoc.created_at+" is not a valid date format. Try: 2008/12/10 21:16:02 +0000");
+  }
+    
+  if (type == 'post') {
+    // post required fields
+    require(author, "Posts must have an author.")
+    require(newDoc.body, "Posts must have a body field")
+    require(newDoc.html, "Posts must have an html field.");
+    require(newDoc.format, "Posts require a format field.");
+    require(newDoc.title, "Posts must have a title.");
+    require(newDoc.slug, "Posts must have a slug.");
+    require(newDoc.slug == newDoc._id, "Post slugs must be used as the _id.")
+    require(newDoc.created_at, "Posts must have a created_at date.");
+
+  } else if (type == 'comment') {
+    // comment required fields
+    require(newDoc.created_at, "Comments must have a created_at date.");
+    require(newDoc.post_id, "Comments require a post_id.");
+    require(newDoc.commenter && newDoc.commenter.name 
+      && newDoc.commenter.email, "Comments must include name and email.");
+    require(newDoc.html, "Comments require an html body.");
+    require(newDoc.comment, "Comments require a comment field.");
+    require(newDoc.format, "Comments require a format field.");
+    if (newDoc.commenter && newDoc.commenter.url) {*/
+   //   require(newDoc.commenter.url.match(/^https?:\/\/[^.]*\..*/), "Commenter URL is not valid.");      
+   // }
+  //}
+  return true;
+}