Beispiel #1
0
class AccountInfo(MonDoc):
    _id = StrField(desc="User id",
                   title="User Id",
                   required=True,
                   readOnly=True)
    bio = TextAreaField(desc="Biography of user (Markdown)",
                        cols=60,
                        rows=8,
                        monospaced=True)
    bioHtml = TextAreaField(desc="bio compiled to HTML",
                            monospaced=True,
                            readOnly=True,
                            displayInForm=False)
    title = StrField(title="Title of Blog")
    following_ids = FKeys('AccountInfo',
                          title="Following",
                          readOnly=True,
                          desc="users this user is following")
    realName = StrField(
        desc="your real name or anything else you want to put here")

    def url(self):
        """ The URL for an acocunt is that accounts blog page """
        return "/blog/" + self._id

    @classmethod
    def classLogo(self):
        return "<i class='fa fa-user-o'></i> "

    def preCreate(self):
        self.title = form("{id}'s blog", self.asReadableH('_id'))

    def preSave(self):
        """ before saving, create the bioHtml """
        self.bioHtml, _ = mark.render(self.bio)
Beispiel #2
0
class WikiPage(MonDoc):
    _id = StrField(readOnly=True)
    owner_id = FK('User', allowNull=False,
        desc="the owner of this wikipage")
    folder = StrField(desc="path to folder")
    filename = StrField(desc="canonical filename (in folder)")
    version = IntField(desc="version number")
    
    source = TextAreaField(monospaced=True, required=True)
    html = TextAreaField(monospaced=True, readOnly=True)
    published = DateTimeField(readOnly=True,
        dateTimeFormat=models.MESS_TIME_DISPLAY_FORMAT)

    @classmethod
    def classLogo(cls) -> str:
        return "<i class='fa fa-file-text-o'></i> "
    
    def preCreate(self):
        self.published = BzDateTime.now()
        
    def canAlter(self, userName: str) -> bool:
        """ Can a user alter this wiki page? 
        At the moment only the owner of a apge can alter it.
        Later we will enable collaborative wikis.
        """
        return canAlter(userName, self.owner_id, self.folder, self.filename)
Beispiel #3
0
class WikiPage(MonDoc):
    owner_id = FK('User', allowNull=False, desc="the owner of this wikipage")
    pageName = StrField(desc="page name")
    version = IntField(desc="version number")

    source = TextAreaField(monospaced=True, required=True)
    html = TextAreaField(monospaced=True, readOnly=True)
    published = DateTimeField(readOnly=True,
                              dateTimeFormat=models.MESS_TIME_DISPLAY_FORMAT)

    @classmethod
    def classLogo(cls) -> str:
        return "<i class='fa fa-file-text-o'></i> "

    def logo(self) -> str:
        return ("<i class='fa fa-home'></i> " if self.pageName == "home" else
                "<i class='fa fa-file-text-o'></i> ")

    def url(self) -> str:
        theUrl = form("/wiki/{u}/{pn}", u=self.owner_id, pn=self.pageName)
        return theUrl

    def getName(self) -> str:
        return self.pageName

    def preCreate(self):
        self.published = BzDateTime.now()

    def preSave(self):
        """ before saving, render the source into html """
        self.html, _ = mark.render(self.source, wikiExt=True)
Beispiel #4
0
class UserDemographics(MonDoc):
    """ demographics of a user """
    
    _id = StrField(desc="User id", title="User Id",
        displayInForm=False,
        required=True, readOnly=True)
    
    decadeBorn = ChoiceField(
        desc="which decade were you born in",
        choices=DECADE_BORN_CHOICES,
        showNull=True, allowNull=False)
    religionBroughtUp = ChoiceField(
        desc="religion you were brought up in",
        choices=RELIGION_CHOICES,
        showNull=True, allowNull=False)
    religionNow = ChoiceField(
        desc="religion you are now",
        choices=RELIGION_CHOICES,
        showNull=True, allowNull=False)
    
    savedAt = DateTimeField(desc="when the data was saved",
        displayInForm=False,
        readOnly=True)
     
    @classmethod
    def classLogo(cls):
        return "<i class='fa fa-male'></i> "
Beispiel #5
0
class Foo(MonDoc):
    name = StrField()
    description = TextAreaField(monospaced=True)
    aNumber = IntField(minValue=0, maxValue=100)
    minSpeed = FloatField(title="Minimum Speed, mph", minValue=0.0)
    maxSpeed = FloatField(title="Maximim Speed, mph", minValue=0.0)
    favouriteDrink = ChoiceField(choices=DRINK_CHOICES,
                                 showNull=True,
                                 allowNull=True)
    fruitsLiked = MultiChoiceField(choices=FRUIT_CHOICES,
                                   desc="tick all fruits this person likes")
    tickyBox = BoolField()
    aDate = DateField()
    lastSaved = DateTimeField(desc="when this foo was last saved",
                              readOnly=True)
    aDateTime = DateTimeField(title="A Date and Time")
    anything = ObjectField(desc="can contain anything",
                           default=["any", "thing"])
    favouriteBook = FK(models.Book, allowNull=True, showNull=True)

    @classmethod
    def classLogo(self):
        return "<i class='fa fa-star-o'></i> "

    def formWideErrorMessage(self):
        if self.minSpeed > self.maxSpeed:
            return "Minimum speed cannot be greater than maximum speed"
        return ""  # no error message, validates OK

    def preSave(self):
        self.lastSaved = BzDateTime.now()
        d = self.mongoDict()
        d.pop('anything', "")  # remove the anything field
        dpr("d=%r", d)
        self.anything = d
Beispiel #6
0
class Book(MonDoc):
    title = StrField()
    yearPublished = IntField()
    authors_ids = FKeys(Author, title="Authors")
    
    @classmethod
    def classLogo(cls):
        return "<i class='fa fa-book'></i> "
Beispiel #7
0
class WikiPage(MonDoc):
    _id = StrField(readOnly=True)
    owner_id = FK('User', allowNull=False, desc="the owner of this wikipage")
    folder = StrField(desc="path to folder")
    filename = StrField(desc="canonical filename (in folder)")
    version = IntField(desc="version number")

    source = TextAreaField(monospaced=True, required=True)
    html = TextAreaField(monospaced=True, readOnly=True)
    published = DateTimeField(readOnly=True,
                              dateTimeFormat=models.MESS_TIME_DISPLAY_FORMAT)

    @classmethod
    def classLogo(cls) -> str:
        return "<i class='fa fa-file-text-o'></i> "

    def preCreate(self):
        self.published = BzDateTime.now()
Beispiel #8
0
class Answer(MonDoc):
    """ an answer by a user to a question 
    An answer can either be a string or an integer.
    If it is an integer, (ans) must be "" and ani is the answer
    If it is a string, ans cannot be "" and ani must be 0.
    """
    
    user_id = FK(userdb.User)
    question_id = StrField(desc="ID of question")
    savedAt = DateTimeField(desc="when the question was answered", 
        readOnly=True)
    ans = StrField(desc="the user's answer to the question, as a string") 
    ani = StrField(desc="the user's answer to the question, as an integer") 
 
    @classmethod
    def classLogo(cls):
        return "<i class='fa fa-check-square-o'></i> "
    
    def preSave(self):
        self.savedAt = BzDateTime.now()
Beispiel #9
0
class Tag(MonDoc):
    _id = StrField(desc="tag id")
    created = DateTimeField(desc="when tag was created")
    lastUsed = DateTimeField(desc="when tag was most recently used")
    timesUsed = IntField(desc="number of times used")

    def getName(self) -> str:
        return "#" + self._id

    @classmethod
    def classLogo(cls) -> str:
        return "<i class='fa fa-hashtag'></i> "
Beispiel #10
0
class FileExample(MonDoc):
    name = StrField()
    description = TextAreaField()
    timestamp = DateTimeField(desc="when this filex was last altered",
        readOnly=True)
   
    @classmethod
    def classLogo(self):
        return "<i class='fa fa-file'></i> "
        
    def preSave(self):
        self.timestamp = BzDateTime.now()
Beispiel #11
0
class TheTestForm(FormDoc):
    aaa = StrField()
    aNumber = IntField(minValue=0, maxValue=100)
    cost = FloatField(title="Cost, £", formatStr="{:.2f}")
    tickyBox = BoolField()
    toggleSwitch1 = BoolField(widget='toggleSwitch')
    toggleSwitch2 = BoolField(widget='toggleSwitch')
    showBody = BoolField(widget='toggleSwitch')
    order = BoolField(widget='toggleSwitch',
                      showTitle=False,
                      onText="Oldest First",
                      offText="Newest First")
    favouriteFruit = ChoiceField(choices=FRUIT_CHOICES,
                                 showNull=True,
                                 allowNull=False)
    slots = MultiChoiceField(choices=SLOT_CHOICES, required=True)
    note = TextAreaField()
    dateOfBirth = DateField(required=True)
Beispiel #12
0
class Author(MonDoc):
    name = StrField()
    notes = TextAreaField()
    dateOfBirth = DateField()
    
    @classmethod
    def classLogo(cls):
        return "<i class='fa fa-pencil'></i> "
    
    def myBooks(self) -> Iterable['Book']:
        """ return this author's books """
        return self.getForeignDocs('Book')
    
    def myBooksLinks(self) -> str:
        """ return html giving a list of this author's books,
        with each book being a hyperlink to it.
        I.e. return HTML containing a series of <a href> elements,
        each containing the name of a book and a link to it.
        """
        return ", ".join(bk.a() for bk in self.myBooks())
Beispiel #13
0
class CreateAccountForm(FormDoc):
    userName = StrField(title="Enter New Username",
        required=True, 
        autocomplete=False,
        minLength=4, maxLength=30,
        charsAllowed=USER_ID_CHARS)
    password = PasswordField(title="Enter New Password",
        required=True, 
        autocomplete=False,
        minLength=4, maxLength=30,
        charsAllowed=USER_ID_CHARS)
    confirmPassword = PasswordField(title="Confirm Password",
        required=True,
        autocomplete=False, 
        minLength=4, maxLength=30,
        charsAllowed=USER_ID_CHARS)
    
    def formWideErrorMessage(self) -> str:
        if self.password != self.confirmPassword:
            return ("The New Password and Confirm Password fields "
                "must be the same.")
        
        return ""
Beispiel #14
0
class Message(MonDoc):
    title = StrField(readOnly=True)
    source = TextAreaField(monospaced=True, required=True)
    html = TextAreaField(monospaced=True, readOnly=True)

    replyTo_id = FK('Message',
                    allowNull=True,
                    desc="the message this is a reply to")
    author_id = FK('User', allowNull=False, desc="the author of this message")
    tags = ObjectField()
    published = DateTimeField(readOnly=True,
                              dateTimeFormat=MESS_TIME_DISPLAY_FORMAT)
    starredBy_ids = FKeys('User')
    numStars = IntField(desc='number of stars on this message')

    @classmethod
    def classLogo(cls) -> str:
        return "<i class='fa fa-comment-o'></i> "

    def preCreate(self):
        self.published = BzDateTime.now()

    def preSave(self):
        """ before saving, create the title """
        lines = self.source.split("\n")
        if len(lines) >= 1:
            self.title = maxChars(lines[0], 80, 90)
        else:
            self.title = ""
        self.numStars = len(self.starredBy_ids)

    def url(self):
        u = "/mess/" + self.id()
        return u

    def fullUrl(self):
        return config.SITE_STUB + self.url()

    #========== display a message ==========

    def viewH(self) -> str:
        """ return HTML displaying this message """
        h = form(
            """
<div class='mess'>            
    <div class='mess-header'>
        {messLink}/{userLink} at {published}{replyToText}
    </div>
    {body}
    <p class='mess-footer'>
    {context}
    - <a href='/thread/{id}'>thread</a>
    - <a href='/messSource/{id}'>source</a>
    {reply}
    - {star}
    </p>
</div>""",
            id=self.id(),
            messLink=self.linkA(),
            userLink=self.author.blogLink(),
            replyToText=self.replyToText(),
            published=self.asReadableH('published'),
            body=self.html,
            context=self.contextA(),
            reply=self.replyA(),
            star=self.starH(),
        )
        return h

    def starH(self) -> str:
        """ The HTML for a star underneath a message """
        h = ""
        c = ""
        if self.numStars >= 1:
            h = form("{} ", self.numStars)
        cun = currentUserName()
        if not cun or cun == self.author_id:
            #>>>>> not logged in, or author
            h += "<i class='fa fa-star-o fa-lg'></i> "
        else:
            #>>>>> user is not message author
            # has this user starred the message?
            starred = cun in self.starredBy_ids
            if starred:
                h += "<i class='starred fa fa-star fa-lg'></i> "
                c = "starred"
            else:
                h += form(
                    """<i onclick='starClicked("{mid}")' """
                    "class='can_star fa fa-star-o fa-lg'></i> ",
                    mid=self._id)
                c = "can_star"
        #//if
        h2 = form("<span class='{c}'>{h}</span>", c=c, h=h)
        return h2

    def linkA(self) -> str:
        """ link to this message's /mess page """
        h = form("<a href='/mess/{id}'>{id}</a>", id=htmlEsc(self._id))
        return h

    def replyA(self) -> str:
        """ html containing the link to reply to this message.
        If not logged in this is empty.
        """
        cun = currentUserName()
        if not cun: return ""
        h = form("- <a href='/messRep/{id}'>reply</a> ", id=self.id())
        return h

    def replyToText(self) -> str:
        """ if this message is a reply, text in the header linking to the
        message it's a reply to. """
        if not self.replyTo_id: return ""
        parent = self.replyTo
        if not parent: return ""
        h = form(" reply-to: {messLink}/{userLink}",
                 messLink=parent.linkA(),
                 userLink=parent.author.blogLink())
        return h

    def contextA(self) -> str:
        """ if post is a reply, return html for link to context of post """
        if self.isHeadPost(): return ""
        h = form("- <a href='/context/{id}'>context</a>", id=self.id())
        return h

    def viewOneLine(self, showAuthor: bool = True) -> str:
        """ View this message as one line 
        @parasm showAuthor = if true, show the author of this message
        """
        publishedShort = self.asReadableH('published')[2:]
        title = self.asReadableH('title')
        authorA = ""
        if showAuthor:
            authorA = form("<a class='author' "
                           "href='/blog/{u}'>@{u}</a> ",
                           u=self.author_id)
        h = form(
            "<br>{publishedShort} "
            "{authorA}"
            "<a href='/mess/{id}'>{title}</a>\n",
            publishedShort=publishedShort,
            authorA=authorA,
            id=self.id(),
            title=title)
        return h

    #========== misc utility functions ==========

    def context(self) -> List['Message']:
        """ the list of messages leading up to this one, including this 
        one, in chronological order 
        """
        if self.isHeadPost(): return [self]
        parent = self.getParent()
        if parent:
            return parent.context() + [self]
        else:
            return [self]

    def isReply(self) -> bool:
        return bool(self.replyTo_id)

    def isHeadPost(self) -> bool:
        return not self.isReply()

    def getParent(self) -> Union['Message', None]:
        """ if a post has a parent, return it, else return None """
        if self.isHeadPost(): return None
        return Message.getDoc(self.replyTo_id)

    def getNumChildren(self) -> int:
        """ return the number of replies this message has """
        return Message.count({'replyTo_id': self._id})

    def getChildren(self):
        """ return an iterator to the message's replies (oldest first) 
        returns an Iterable[Message]
        """
        ms = Message.find({'replyTo_id': self._id}, sort='published')
        return ms
Beispiel #15
0
class LoginForm(FormDoc):
    userName = StrField()
    password = PasswordField()
Beispiel #16
0
class User(MonDoc):
    userName = StrField(charsAllowed=string.ascii_letters + string.digits +
                        "_",
                        minLength=2,
                        desc="user name")
    hashedPassword = StrField()
    pw = StrField(desc="unencrypted password, for testing")
    password = StrField(default=HIDDEN)  # for altering on /user page
    email = StrField(monospaced=True)
    isAdmin = BoolField(desc="is this user an admin?",
                        title="Is Admin?",
                        default=False)
    isActive = BoolField(desc="is this an active user?",
                         title="Is Active?",
                         default=True)

    @classmethod
    def classLogo(cls):
        return "<i class='fa fa-user'></i> "

    def __repr__(self):
        """ Return a string representation of myself.
        @return::str
        """
        s = "<User %r pw=%r email=%r>" % (self.userName, self.pw, self.email)
        return s

    #========== stuff Flask-login needs: ==========
    """ see
    <https://flask-login.readthedocs.org/en/latest/#your-user-class>
    """

    def get_id(self):
        return self.userName

    @property
    def is_authenticated(self):
        return self.isAuthenticated()

    @property
    def is_anonymous(self):
        return not self.has_key('_id')

    @property
    def is_active(self):
        return True

    def isAuthenticated(self):
        """ Do we have a logged-in user?
        @return::bool
        """
        return self.has_key('_id')

    #==========

    def preSave(self):
        """ We don't want to save the plaintext password to
        the database.
        """
        if self.password != HIDDEN:
            self.pw = self.password
            self.hashedPassword = hashPassword(self.password)
        self.password = HIDDEN

        # userName is a unique identifier, so use this as the _id
        self._id = self.userName

    #========== for display ==========

    def blogLink(self) -> str:
        """ return a link to the user's blog """
        h = form("<a href='/blog/{userName}'>@{userName}</a>",
                 userName=htmlEsc(self.userName))
        return h