Benoît Allard avatar Benoît Allard committed 1aa7acf Merge

restore MsgBoard functionnality, try to combine both

Comments (0)

Files changed (9)

+5407c0028867aa42cc9ef43998d32a93843efbba 1.0

msgboard/controller.py

 import calendar
 
-from WSGICal.models import User, Action
+from msgboard.models import User, Action
 
 class Month(object):
 
     def __init__(self, year, month, day):
         pass
 
+
+try:
+    from docutils.core import publish_parts as publish
+    from docutils.utils import SystemMessage
+except ImportError:
+    publish = None
+
+def as_html(text):
+    if publish is not None:
+        try:
+            parts = publish(source=text, writer_name="html")
+        except SystemMessage:
+            parts = {'html_body': "Error"}
+        return parts['html_body']
+    else:
+        return "<pre>%s</pre>" % self.content
+
+def render_flash(flash):
+    if isinstance(flash, str):
+        return flash
+    elif isinstance(flash, dict):
+        message = flash['message'] % (
+            "<a href='%s'>%s</a>" % (
+                flash['linkurl'], 
+                flash['linktitle']))
+        return message
+    else:
+        return str(flash)
+
 import os
 from subprocess import Popen, PIPE
 

msgboard/default_settings.py

 DEBUG = True
-SECRET_KEY = 'dev2'
-SQLALCHEMY_DATABASE_URI = 'sqlite:///calendar.db'
+SECRET_KEY = 'dev'
+SQLALCHEMY_DATABASE_URI = 'sqlite:///msgboard.db'

msgboard/jinjafilters.py

-from datetime import date
-from WSGICal import app
-from WSGICal.models import Action
+from datetime import datetime, date
+from msgboard import app, controller
+from msgboard.models import Action
+
+agescales = [("second", 1),
+             ("minute", 60),
+             ("hour", 3600),
+             ("day", 3600 * 24),
+             ("week", 3600 * 24 * 7),
+             ("month", 3600 * 24 * 30),
+             ("year", 3600 * 24 * 365)]
+agescales.reverse()
+
+@app.template_filter()
+def ago(once):
+
+    def fmt(unit, value):
+        if value > 1:
+            unit += 's'
+        return "%d %s" % (value, unit)
+
+    now = datetime.utcnow()
+    elapsed = now - once
+    deltasec = (elapsed.seconds + elapsed.days * 24 * 3600)
+    if deltasec == 0:
+        return 'right now'
+
+    for unit, scale in agescales:
+        n = deltasec // scale
+        if n >= 2 or scale == 1:
+            return '%s ago' % fmt(unit, n)
+
+@app.template_filter()
+def render_flash(flash):
+    return controller.render_flash(flash)
 
 @app.template_filter()
 def dayclass(day, month):

msgboard/models.py

 from datetime import datetime
 
-from WSGICal import app
+from msgboard import app
+from msgboard.controller import as_html
 from flaskext.sqlalchemy import SQLAlchemy
 
 db = SQLAlchemy(app)
 class User(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     username = db.Column(db.String(80), unique=True)
+    last_visit = db.Column(db.DateTime)
+    messages = db.relationship('Message', back_populates='user',
+                               primaryjoin="and_(User.id==Message.user_id, "
+                               "Message.archived==False)",
+                               order_by="desc(Message.last_modified_on)",
+                               lazy='dynamic')
+    all_messages = db.relationship('Message', back_populates='user',
+                                   order_by="desc(Message.last_modified_on)",
+                                   lazy='dynamic')
     org_id = db.Column(db.Integer, db.ForeignKey('organisation.id'))
     organisation = db.relationship('Organisation', backref='users')
 
     def __init__(self, username):
         self.username = username
+        self.updatelastvisit()
         db.session.add(self)
 
+    def updatelastvisit(self):
+        self.last_visit = datetime.utcnow()
+
+class Message(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    content = db.Column(db.Text)
+    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
+    user = db.relationship("User")
+    archived = db.Column(db.Boolean)
+    created_on = db.Column(db.DateTime)
+    last_modified_on = db.Column(db.DateTime)
+
+    def __init__(self, username, content):
+        self.user = User.query.filter_by(username=username).first()
+        self.content = content
+        self.archived = False
+        self.created_on = datetime.utcnow()
+        self.last_modified_on = self.created_on
+        db.session.add(self)
+
+    def update(self, content):
+        self.last_modified_on = datetime.utcnow()
+        self.content = content
+
+    @classmethod
+    def getUpdatesFor(classs, user, since=None):
+        if since is None:
+            since = user.last_visit
+        news = classs.query.filter(Message.user_id != user.id).\
+                            filter(Message.created_on > since).\
+                            count()
+        mods = classs.query.filter(Message.user_id != user.id).\
+                            filter(Message.created_on < since).\
+                            filter(Message.last_modified_on > since).\
+                            count()
+        return (news, mods)
+
+    def as_html(self):
+        return as_html(self.content)
+
+    @property
+    def edited(self):
+        return self.created_on != self.last_modified_on
+
 class Action(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     date = db.Column(db.Date)

msgboard/templates/index.html

 {% extends "layout.html" %}
+{% from "macros.j2" import render_message with context %}
 {% block body %}
+{% for user in users %}
+  <a href="#{{ user.username }}">{{ user.username }}</a>
+{% endfor %}
+{% for user in users %}
+  <article id="{{ user.username }}">
+    <h3><a href="{{ url_for('user', username=user.username) }}">{{ user.username }}</a></h3>
+    {% for msg in user.messages.limit(3) %}
+    {{ render_message(msg)|safe }}
+    {% endfor %}
+    {% if user.messages.count() > 3 -%}
+      <a class=more href="{{ url_for('user', username=user.username) }}#more" title="More ...">...</a>
+    {% endif %}
+    {% if user.username == session['username'] %}
+      <a href="{{ url_for("add_msg") }}">New message</a>
+    {% endif %}
+  </article>
+{% endfor %}
 {% endblock %}

msgboard/templates/layout.html

   {% if session.username %}
   <p class=logout>(Are you <a href="{{ url_for('nocookie') }}">not {{ session.username }}</a> ?)</p>
     {% if session.username != "Anonymous" %}
-      <h2>{{ session.username }}</h2>
+      <h2><a href="{{ url_for('user', username=session.username) }}">{{ session.username }}</a></h2>
     {% else %}
        <h2>Anonymous</h2>
     {% endif %}
   {% endif %}
   {% for message in get_flashed_messages() %}
-    <div class=flash>{{ message }}</div>
+    <div class=flash>{{ message|render_flash|safe }}</div>
   {% else %}
   <hr>
   {% endfor %}

msgboard/templates/nocookie.html

 
   <p>Maybe you prefer to remain <input type=submit name="user" value="Anonymous"> ?</p>
 </form>
-<div id="adduser"{{ ' style="display:none"' if users.count() }}>
+<div id="adduser"{{ ' style="display:none"' if (users.count() != 0) }}>
   <p>So you're new over there ! Welcome ! I hope you'll have good time.</p>
   <form action="{{ url_for('adduser') }}" method=post>
     <fieldset>

msgboard/views.py

-from WSGICal import app
-
-from WSGICal.controller import Month
+from msgboard import app, controller, ajax as ajaxmod
 
 from flask import request, session, flash, redirect, url_for, render_template, jsonify, abort
-from datetime import date
+from datetime import datetime, date
 
 @app.before_request
 def ifnocookie():
         flash('You were redirected.')
         return redirect(url_for('nocookie'))
 
+@app.before_request
+def getupdates():
+    if 'username' not in session:
+        return
+    user = User.query.filter_by(username=session['username']).first()
+    if user is None:
+        return
+    updates = Message.getUpdatesFor(user)
+    if updates == (0, 0):
+        return
+    msg = {'message': "There are %s since your last visit",
+           'linktitle': "%d new messages, and %d modified messages" % updates,
+           'linkurl': url_for('news', since=user.last_visit)}
+    flash(msg)
+
+@app.after_request
+def lastvisit(response):
+    if 'username' not in session:
+        return response
+    user = User.query.filter_by(username=session['username']).first()
+    if user is None:
+        return response
+    user.updatelastvisit()
+    return response
+
 @app.route('/')
 def index():
-    return render_template('index.html')
+    return render_template('index.html', 
+                           users = User.query.\
+                                        order_by('User.username'))
+
 
 @app.route('/show/', defaults={'year':date.today().year,
                                 'month':date.today().month})
 @app.route('/show/<int:year>-<int:month>')
 def show(year, month):
     user = User.query.filter_by(username=session['username']).first()
-    month = Month(year, month)
+    month = controller.Month(year, month)
     return render_template('show.html', month=month, users=User.query, organisations=Organisation.query)
 
 @app.route('/nocookie', methods=['GET', 'POST'])
             'linkurl': url_for('about')})
     return redirect(url_for('index'))
 
+@app.route('/edit/<int:id>', methods=['GET', 'POST'])
+def edit_msg(id):
+    msg = Message.query.get_or_404(id)
+    if session['username'] != msg.user.username:
+        flash("Message #%d was written by %s which is not you !" % (
+            id, msg.user.username))
+        return redirect(url_for('index'))
+    if request.method == 'GET':
+        return render_template('editmsg.html', content=msg.content,
+                               docutils=controller.publish is not None,
+                               id = id,
+                               action='edit')
+    if request.form.get('cancel'):
+        flash('Change discarded')
+        return redirect(url_for('index'))
+    if request.form.get('submit'):
+        msg.update(request.form['content'])
+        flash('Message modified')
+        return redirect(url_for('index'))
+
+@app.route('/archive/<int:id>')
+def arch_msg(id):
+    msg = Message.query.get_or_404(id)
+    if session['username'] != msg.user.username:
+        flash("Message #%d was written by %s which is not you !" % (
+            id, msg.user.username))
+        return redirect(request.referrer or url_for('index'))
+    msg.archived = not msg.archived
+    if msg.archived:
+        flash({'message':
+                   "Message has been archived, it remains visible on your %s.",
+               'linktitle': "own page",
+               'linkurl':url_for('user', username=session['username'])})
+    else:
+        flash({'message': "Message is editable again, %s can read it.",
+               'linktitle':"everyone",
+               'linkurl': url_for('index')})
+    return redirect(request.referrer or url_for('index'))
+
+@app.route('/news', defaults={'since':None})
+@app.route('/news/<since>')
+def news(since):
+    user = User.query.filter_by(username=session['username']).first()
+    if user is None:
+        # Anonymous
+        since = datetime(2000, 01, 01)
+        myid = -1
+    elif since is None:
+        since = user.last_visit
+        myid = user.id
+    else:
+        myid = user.id
+    news = Message.query.filter(Message.user_id != myid).\
+                         filter(Message.created_on > since)
+    mods = Message.query.filter(Message.user_id != myid).\
+                        filter(Message.created_on < since).\
+                        filter(Message.last_modified_on > since)
+    return render_template('news.html', news=news, mods=mods)
+
+@app.route('/add/', methods=['GET', 'POST'])
+def add_msg():
+    if request.method == 'GET':
+        user = User.query.filter_by(username=session['username']).first()
+        nummsg = user.messages.count()
+        return render_template('editmsg.html', 
+                               docutils=controller.publish is not None, 
+                               action='add', 
+                               nummsg=nummsg)
+    if request.form.get('cancel'):
+        flash('Message discarded')
+        return redirect(url_for('index'))
+    if request.form.get('submit'):
+        msg = Message(session['username'], request.form['content'])
+        flash('Message created')
+        return redirect(url_for('index'))
+
+@app.route('/+<username>')
+def user(username):
+    user = User.query.filter_by(username=username).first_or_404()
+    return render_template('user.html', user=user)
+
 @app.route('/about')
 def about():
     deps = []
     else:
         return res
 
-from WSGICal.models import User, Action, Organisation, Project
+from WSGICal.models import User, Message, Action, Organisation, Project
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.