Source

trac-userscriptservernotes-plugin / src / usernotes / notes_info.py

Full commit
import re, sys
import datetime, string
import collections

from trac.core import Component, implements, ExtensionPoint
from trac.util import Markup #, format_datetime

from trac.web import IRequestHandler
from trac.perm import IPermissionRequestor
from trac.web.chrome import INavigationContributor, ITemplateProvider
from trac.config import Option, ListOption, PathOption, BoolOption
from trac.web.chrome import add_stylesheet, add_script
from trac.util.presentation import Paginator
from trac.util import Markup, format_datetime
from trac.util.datefmt import localtz

from trac.util.translation import domain_functions
_, tag_, N_, ngettext, add_domain = domain_functions('usernotes',
    '_', 'tag_', 'N_', 'ngettext', 'add_domain')

import cgi
import logging
import string
from time import strftime, localtime
import os.path

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import create_session
from sqlalchemy.sql import desc
import sqlalchemy.sql.functions as by

from db.session import context
from db.model import NotesInfo, NotesFields

import web_nav

PACKAGE = "usernotes"
TITLE = "Userscript ServerNotes Info"
TYPES = { "user" : "string", 
          "date" : "date", 
          "text" : "string",
          "select" : "number" }

import os.path

def strU(text):
    try:
        return unicode(text)
    except:
        return unicode(str(text), "utf-8")
def str8_(text):
    if isinstance(text, unicode):
        return text.encode("utf-8")
    else:
        return str(text)
def str8(text):
    return str(text)

def esc(text):
    if not text: return u""
    return cgi.escape(strU(text))

class UserNotesInfo(Component):
    """ allows to store extra fields for a user on a remote site """
    
    implements(IRequestHandler)

    domainlist = ListOption(PACKAGE, "domain_list", "domain1, domain2",
          doc="The domains that you want to create databases for")
    
    def __init__(self):
        locale_dir = os.path.join(os.path.dirname(__file__), "locale")
        add_domain(self.env.path, locale_dir)
        
    
    """
      Determines if request should be handled by this plugin.
    """
    def match_request(self, req):
        m = re.match(r"/%s\b/%s\b(/.*)?" % (web_nav.URL, web_nav.INFO), req.path_info)
        if not m:
            return False
        domainpage = m.group(1)
        if not domainpage: domainpage = ""
        req.args["domainpage"] = domainpage
        return True

    """
      Handles display, append and delete requests on this plugin.
    """
    def process_request(self, req):
        req.perm.assert_permission(web_nav.VIEW)
        self.locale_dir = os.path.join(os.path.dirname(__file__), "locale")
        domainpage = req.args["domainpage"]
        if domainpage:
            return self.do_info(req, domainpage)
        return self.do_default(req, message="[%s]" % domainpage)

    def do_default(self, req, message=""):
        data = {}
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = message
        data["ngettext"] = ngettext
        data["_"] = _
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return ('usernotes_default.html', data, None)

    def home_url(self, req, domain): 
        return Markup(u'<div><a href="%s">[welcome]</a></div>' % self.base_url(req, domain))
    def base_url(self, req, domain):
        return req.href(web_nav.URL + "/" + web_nav.INFO + "/" + domain)
    def do_info(self, req, path):
        username = req.authname
        m = re.match("^/([^/]+)(/.*)?", path)
        if m and username:
            add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
            add_script(req, 'common/js/trac.js')
            domain = m.group(1)
            render = m.group(2)
            base_url = self.base_url(req, domain)
            op = req.args.get("op", "")
            if not op: op = render
            # xx._scripts = _scripts
            if op == "showall":
                return self.do_showall(req, domain)
            elif op == "userlist":
                return self.do_userlist(req, domain)
            elif op == "fields":
                return self.do_fields(req, domain)
            elif op == "newfield":
                return self.do_newfield(req, domain)
            elif op == "setfield":
                return self.do_setfield(req, domain)
            elif op == "search":
                return self.do_search(req, domain)
            elif op == "save":
                return self.do_save(req, domain)
            else:
                return self.do_welcome(req, domain)
        else:
            return self.do_default(req, path)
    def fields(self, domain):
        try:
            session = context(self.env)
            q = session.query(NotesFields.note_field, 
                              NotesFields.note_typed, 
                              NotesFields.note_range)
            q = q.filter_by(note_domain = domain)
            fields = []
            for item in q.all():
                fields.append(item)
            return fields
        except:
            return [ "note "]
    def do_welcome(self, req, domain):
        data = {}
        data["domain"] = domain
        data["username"] = req.authname
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = None
        data["ngettext"] = ngettext
        data["_"] = _
        data["types"] = TYPES
        data["fields"] = self.fields(domain)
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return "notes_info_index.html", data, None
        
    def _domains(self, req):
        return self.domainlist
    def color_mail_user(self, mail_user):
        return u"#" + (((u"222" + strU(hash(mail_user)))[-3:]).replace(u"0", u"A").replace(u"1", u"B"))
    def do_userlist(self, req, domain):
        Item = collections.namedtuple("Item", ["note_user", "note_date"])
        session = context(self.env)
        q = session.query(NotesInfo.note_user, by.max(NotesInfo.modified_on))
        q = q.filter_by(note_domain = domain)
        q = q.group_by(NotesInfo.note_user).order_by(desc(NotesInfo.modified_on))
        datalist = []
        for item in q.all():
            datalist.append(Item(*item))
        message = "found %s entries for %s" % (len(datalist), domain)
        base_url = req.href(web_nav.URL + "/" + web_nav.INFO + "/" + domain)
        data = {}
        data["base_url"] = base_url
        data["color_mail_user"] = self.color_mail_user
        data["localtz"] = localtz
        data["datalist"] = datalist
        data["domains"] = self._domains(req)
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = message
        data["ngettext"] = ngettext
        data["_"] = _
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return ('notes_info_userlist.html', data, None)
    def do_fields(self, req, domain, message = None):
        if not domain:
            req.send_response(400)
            return self.do_default(req, "no domain given")
        session = context(self.env)
        q = session.query(NotesFields)
        q = q.filter_by(note_domain = domain)
        q = q.order_by(desc(NotesFields.modified_on))
        datalist = []
        for item in q.all():
            datalist.append(item)
        if not message:
            message = ""
        message += "found %s fields for %s" % (len(datalist), domain)
        base_url = req.href(web_nav.URL + "/" + web_nav.INFO + "/" + domain)
        data = {}
        data["base_url"] = base_url
        data["color_mail_user"] = self.color_mail_user
        data["localtz"] = localtz
        data["datalist"] = datalist
        data["domains"] = self._domains(req)
        data["types"] = TYPES
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = message
        data["ngettext"] = ngettext
        data["_"] = _
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return ('notes_info_fields.html', data, None)
    def do_newfield(self, req, domain):
        note_field = req.args.get("field", "")
        note_typed = req.args.get("typed", "")
        note_range = req.args.get("range", "")
        if not domain:
            req.send_response(400)
            return self.do_default(req, "no domain given")
        if not note_field:
            req.send_response(400)
            return self.do_default(req, "no field given")
        session = context(self.env)
        q = session.query(NotesFields)
        q = q.filter_by(note_domain = domain, note_field = note_field)
        if q.count():
            req.send_response(400)
            message = "field '%s' exists already" % note_field
            return self.do_default(req, message)
        item = NotesFields()
        item.note_field = note_field
        item.note_typed = note_typed
        item.note_range = note_range
        item.note_domain = domain
        item.modified_by = req.authname
        item.modified_on = datetime.datetime.now()
        session.add(item)
        session.flush()
        message = "new field '%s' created" % note_field
        return self.do_fields(req, domain, message)
    def do_setfield(self, req, domain):
        if not domain:
            req.send_response(400)
            return self.do_default(req, "no domain given")
        note_field = req.args.get("field", "")
        note_typed = req.args.get("typed", "")
        note_range = req.args.get("range", "")
        if not isinstance(note_field, basestring):
            req.send_response(400)
            return self.do_default("'field' is not a string")
        if not isinstance(note_field, basestring):
            req.send_response(400)
            return self.do_default("'typed' is not a string")
        if not isinstance(note_field, basestring):
            req.send_response(400)
            return self.do_default("'range' is not a string")
        session = context(self.env)
        q = session.query(NotesFields)
        q = q.filter_by(note_domain = domain, note_field = note_field)
        item = q.first()
        if not item:
            req.send_response(400)
            message = "field '%s' does not exist" % note_field
            return self.do_default(req, message)
        item.note_field = note_field
        item.note_typed = note_typed
        item.note_range = note_range
        item.modified_by = req.authname
        item.modified_on = datetime.datetime.now()
        session.flush()
        message = "modified field '%s' ('%s','%s') " % (note_field, note_typed, note_range)
        return self.do_fields(req, domain, message)
    def do_search(self, req, domain, message = None):
        Item = collections.namedtuple("Item", ["note_user", "note_field", "note_string"])
        note_user = req.args.get("user", "")
        if not note_user:
            self.log.info("no 'user' on search")
            req.send_response(400)
            return self.do_default(req, "no 'user' on search")
        data = {}
        data["home_url"] = self.home_url(req, domain)
        data["note_user"] = note_user
        session = context(self.env)
        q = session.query(NotesInfo.note_user, NotesInfo.note_field, NotesInfo.note_string)
        q = q.filter_by(note_user=note_user, note_domain=domain)
        q = q.order_by(desc(NotesInfo.modified_on))
        fields = self.fields(domain)
        values = {}
        for item in q.all():
            elem = Item(*item)
            values[elem.note_field] = item
        for item in fields:
            if item.note_field not in values:
                values[elem.note_field] = Item(note_user, item.note_field, None)
        if not message:
            message = len(values)
        data["domains"] = self._domains(req)
        data["fields"] = fields
        data["values"] = values
        data["empty"] = Item(None,None,None)
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = message
        data["ngettext"] = ngettext
        data["_"] = _
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return ('notes_info_textlist.html', data, None)
    def do_showall(self, req, domain):
        data = {}
        data["home_url"] = self.home_url(req, domain)
        data["mail_user"] = "ALL"
        session = context(self.env)
        q = session.query(NotesMail.mail_from, NotesMail.mail_user, NotesMail.mail_date,
                                NotesMail.mail_call, NotesMail.mail_text, NotesMail.mail_info,
                                NotesMail.mimetype, NotesMail.encrypted)
        q = q.filter_by(note_domain = domain)
        q = q.order_by(desc(NotesMail.mail_date))
        datalist = []
        for item in q.all():
            datalist.append(item)
        message = len(datalist)
        data["datalist"] = datalist
        data["markup_mail_info"] = self.markup_mail_info
        data["domains"] = self._domains(req)
        data["title"] = TITLE
        data["domains"] = self._domains(req)
        data["message"] = message
        data["ngettext"] = ngettext
        data["_"] = _
        add_stylesheet(req, PACKAGE + '/css/userscriptservernotes.css')
        add_script(req, 'common/js/trac.js')
        add_script(req, 'common/js/wikitoolbar.js')
        return ('notes_mail_textlist.html', data, None)
    def do_save(self, req, domain):
        note_from = req.args.get("from", "")
        note_user = req.args.get("user", "")
        session = context(self.env)
        updated = 0
        for arg in self.fields(domain):
            name = arg.note_field
            value = req.args.get("_%s" % name, None)
            if value is None:
                continue
            q = session.query(NotesInfo)
            q = q.filter_by(note_field = name, note_domain = domain, note_user = note_user)
            old = q.first()
            if old is None:
                item = NotesInfo()
                item.note_domain = domain
                item.note_user = note_user
                item.note_field = name
            else:
                item = old
            if item:
                item.modified_by = req.authname
                item.modified_on = datetime.datetime.now()
                if arg.note_typed in [ "date" ]:
                    import parsedate
                    date_value = parsedate.parse_datetime(value)
                    if not date_value:
                        req.send_response(400)
                        return self.do_fields("ERROR: can not parse as datetime: %s" % value)
                    item.note_date = date_value
                elif arg.note_typed in [ "number" ]:
                    try:
                        item.note_number = long(value)
                    except Exception, e:
                        req.send_response(400)
                        return self.do_fields("ERROR: can not parse as number: %s : %s" % (value, e))
                item.note_string = value # always
                updated += 1
            if not old:
                session.add(item)
        message = u"updated"
        return self.do_search(req, domain, message)