Webware, FunFormKit och Cheetah Template

Erik Forsberg, Lysator ACSLysator Logo

forsberg@lysator.liu.se
http://www.lysator.liu.se/~forsberg/

Agenda

Uppmaning och ursäkt

Vad är Webware?

Hur fungerar det?

Webserverintegration

I 0.8 finns även en egen webserver.

Uppbyggnad

En bunt "Kits".

WebKit

Hjärtat av Webware. Snabb och lättanvänd applikationsserver.

Servlets

Exempelservlet


from WebKit.Page import Page

class TestServlet(Page):
  def writeContent(self):
    self.write("Hello, World!")
TestServlet

Contexts

Hjälpmoduler

Arbetskataloger

Exempel på arbetskatalog

hostname:erik /s/lysmemb % ls -l
404Text.txt Texten som visas vid 404
AppServer Unix-varianten av AppServer
AppServer.bat Windows-varianten av AppServer
Cache/ Gissa!
Cans/ Kvarleva från gammal idé
Configs/ Konfigurationsfiler
 Application.configInnehåller bland annat Contextdefinitioner
 AppServer.configDiverse runtimekonfiguration för AppServer
ErrorMsgs/ Vid exception lagras felsidan här
Launch.py Används av AppServer.bat
Logs/ Index över felen i ErrorMsgs
MyContext/ Ett exempelcontext. Måste definieras i Application.config
NTService.py Används om man kör AppServer som en service i Windows.
OneShot.cgi Startar upp WebKit, kör requesten och dödar. Rekommenderas inte längre.
Sessions/ Disklagrade sessioner.
WebKit.cgi CGI-script som pratar med WebKit på port 8086
address.text Anger adressen WebKit lyssnar på.
appserverpid.txt Anger pid för AppServer.

AppServer, WebKit.cgi, OneShot.cgi

Debug

Requestens väg genom WebKit

  1. http://localhost/WK/UppLYSning/TestServlet
  2. http://localhost/WK/ får Apache att skicka requesten till WebKit
  3. /UppLYSning/ får WebKit att aktivera UppLYSning
  4. TestServlet aktiverar servleten vid namn TestServlet
  5. En instans av TestServlet hämtas från en pool av sådana. Alternativt skapas en ny.
  6. Ett transaktionsobjekt skapas.
  7. Metoderna nedan anropas på instansen av TestServlet
  8. Instansen av TestServlet returneras till poolen.

Transaktionsobjektet

HTTPRequest

HTTPResponse

Page : bekvämlighetsmetoder

Page : metoder som anropas under en request

Page : .writeBody()

Actions

Ett sätt att associera olika submitknappar med olika servletmetoder.

Sessioner

Tracebacks

Felsida

Varning: Opedagogiskt (felaktigt) exempel följer :-)

from WebKit.Page import Page

class ErrorServlet(Page):
  def writeContent(self):
    slf.write("Hello, World!<p>")

ErrorServlet

Evig sanning

Administrationswebsidor

Inloggning i Webware

from SitePage import SitePage
import time

class SecurePage(SitePage):
    def __init__(self):
        SitePage.__init__(self)

    def awake(self, trans):
        SitePage.awake(self,trans)
        if self.session().lastAccessTime() +\
           self.session().timeout() < time.time():
            self.session().expiring()

        if self.setting('RequireLogin'):
            if not trans.session().value('user', None) \
                   or trans.session().isExpired():
                trans.response().sendRedirect("Login")

        if trans.request().hasField("logout"):
            self.session().values().clear()
            trans.response().sendRedirect("Main")

    def respond(self, trans):
        if self.session().value('user', None) or \
               not self.setting('RequireLogin'):
            SitePage.respond(self, trans)

    def defaultConfig(self):
        return {'RequireLogin': 1}

Web Services

Något om MiscUtils

Saknas: dokumentation :(.

MiscUtils : Configurable

Konfigurationsfilformat:
{
	'dbHost':'localhost',
	'dbUser':'invuser',
	'db':'inv',
	'dbPass':ettlösenord',
	'TemplateDirectory':'InvTemplates',
	'PublicMenu':		[["Förstasidan","Main"]],
        'PrivateMenu':          [["Lista","List"]],
	'WriteMenu':            [["Lägg till","Edit"]],
        'LoginMenu':            {"login": ["Logga in","Login"],
                                 "logout": ["Logga ut","Main?logout=1"]},
	'WriteGroups':           ['root','styrelse', 'tilde'],
}

Webware : Version

Webware 0.8 släpptes 2003-02-09

Cheetah Template

Cheetah Template : Inspiration

Cheetah Template : Features

Cheetah Template : Dokumentation

Cheetah Template : Hur använder man det?

Cheetah Template : Kompilering av template till .py

Cheetah Template : Import från fil.

        t = Template(file="Members".tmpl,
                     searchList = [{'othermembers':othermembers,
                                    'societymembers':societymembers,
                                    'districtmembers':districtmembers}])

	print t

Cheetah Template : Grundläggande syntax

Cheetah Template : Variabelaccess

Cheetah Template : Enkla programkonstruktioner


<table>
#for $row in $societymembers
<tr>
<td align="left"><a href="Member?id=${row.id}">${row.name}</a></td>
</tr>
#end for
</table>

Cheetah Template : Pythonkod i Template

I en Cheetahtemplate har du tillgång till hela Python
Here is my #echo ', '.join(['silly']*5) # example.

Here is my silly, silly, silly, silly, silly example.

Cheetah Template : Caching

..eller
#cache 
$a $b
#end cache
Cache kan dessutom uppdateras på kommando.

Cheetah Template : Utdatafilter

Cheetah Template : Import

Cheetah Template : Arv

Templates kan ärva av varandra, och av andra pythonfiler

parent.tmpl

#block foo
Det här är innehållet i blocket foo
#end block foo

#block bar
Blocket bar
#end block bar

child.tmpl

#extends parent

#block foo
Child har ett annat innehåll i foo..
#end block foo

Cheetah Template : Arv - körexempel

hostname:erik ~/dev/Cheetah % cheetah compile parent.tmpl child.tmpl
Compiling parent.tmpl -> parent.py (backup parent.py_bak)
Compiling child.tmpl -> child.py (backup child.py_bak)
hostname:erik ~/dev/Cheetah % python parent.py  
Det här är innehållet i blocket foo

Blocket bar

hostname:erik ~/dev/Cheetah % python child.py  
Child har ett annat innehåll i foo..

Blocket bar

hostname:erik ~/dev/Cheetah % 

FunFormKit

"FunFormKit is the most fun, family-oriented form validation and generation package for Webware in the entire world!"

FunFormKit : Form Validation

FunFormKit : Ett litet exempel

from WebKit.Page import Page
from FunFormKit.Form import FormServlet, FormDefinition
from FunFormKit import Field
import random

formDef = FormDefinition("CCWinner",
                         [Field.TextField("randmax", maxLength=5, size=3,
                                          description="Upper border of random number wanted"),
                          Field.SubmitButton("submit",
                                             description="Randomize me!",
                                             methodToInvoke="randomize_me")],

                         )

class CCWinner(Page, FormServlet):
    def __init__(self):
        Page.__init__(self)
        FormServlet.__init__(self, [formDef])

    def title(self):
        return "A random example.."

    def randomize_me(self, fields):
        maxrandnr = int(fields['randmax'])
        randnr = random.randrange(0, maxrandnr)
        self.write("Your random number between 0 and %d is %d<p>" % (maxrandnr,
                                                                     randnr))

    def writeContent(self):
        submitted, data = self.processForm()
        rf = self.renderableForm()
        self.write(rf.htFormTable(bgcolor="#ddddff"))
                       

FunFormKit : Ett litet exempel

FunFormKit : Fälttyper

  • SubmitButton
  • ImageSubmit
  • HiddenField
  • SecureHiddenField
  • CompoundField
  • TextField
  • TextAreaField
  • PasswordField
  • MD5PasswordField
  • TimeMD5PasswordField
  • SelectField
  • OrderingField
  • OrderingDeletingField
  • RadioField
  • MultiSelectField
  • MultiCheckboxField
  • CheckboxField
  • DateField
  • FileField
  • TextareaFileField
  • ImageFileUploadField
  • FileUploadField
  • StaticText
  • ColorPickerField
  • VerifyField
  • PasswordVerifyField
  • CityStateZipField
  • CreditCardField

FunFormKit : Value Conversion/Validation

Value Conversion/Validation : Ett Exempel

from WebKit.Page import Page
from FunFormKit.Form import FormServlet, FormDefinition
from FunFormKit import Field
from FunFormKit.Validator import ValidatorConverter, InvalidField
import random

class IntValidator(ValidatorConverter):
    def convert(self, num):
        try:
            return int(num)
        except ValueError:
            raise InvalidField, "Field must be a number!"

formDef = FormDefinition("CCConvertedWinner",
                         [Field.TextField("randmax", maxLength=5, size=3,
                                          description="Upper border of random"\
                                          "number wanted",
                                          validators=[IntValidator()]),
                          Field.TextField("useless", maxLength=200, size=20,
                                          description="A useless field"),
                          Field.SubmitButton("submit",
                                             description="Randomize me!",
                                             methodToInvoke="randomize_me")],

                         )

class CCConvertedWinner(Page, FormServlet):
    def __init__(self):
        Page.__init__(self)
        FormServlet.__init__(self, [formDef])

    def title(self):
        return "A random example.."

    def randomize_me(self, fields):
        maxrandnr = int(fields['randmax'])
        randnr = random.randrange(0, maxrandnr)
        self.write("Your random number between 0 and %d is %d<p>" % (maxrandnr,
                                                                     randnr))

    def writeContent(self):
        submitted, data = self.processForm()
        rf = self.renderableForm()
        self.write(rf.htFormTable(bgcolor="#ddddff"))
                       

Value Conversion/Validation : Ett Exempel

Value Conversion/Validation : Två detaljer

Value Conversion/Validation : Färdiga validatorer

  • MaxLength
  • MinLength
  • NotEmpty
  • Empty
  • Regex
  • PlainText
  • InList
  • DictionaryConverter
  • IndexListConverter
  • DateValidator
  • AsInt
  • AsNumber
  • AsList
  • Email
  • StateProvince
  • PhoneNumber
  • DateConverter
  • PostalCode

FunFormKit : Form Validators

class ValidatePassword(FormValidator):
    def __init__(self, servlet):
        self.servlet = servlet    

    def validate(self, fieldDict):
        if self.servlet.comparePassword(fieldDict["username"][0],
                                       fieldDict["password"]):
            return None
        else:
            return {'password':self.servlet.setting('InvalidPasswordMessage')}
Koden som använder den:
        formDef = FormDefinition('Login', 
                                 [Field.TextField('username', maxLength=255,
                                                  size=20,
                                                  validators=\
                                                  [UsernameToUserID(self)]),
                                  Field.PasswordField('password', size=10,
                                                      maxLength=20),
                                  Field.SubmitButton('submit',
                                                     description='Login', 
                                                     methodToInvoke='login'),
                                  ],
                                 formValidators=[ValidatePassword(self)])

När vi kommer till login vet vi att användaren loggat in ordentligt
    def login(self, fields):
        self.session().setValue('user', fields["username"][0])
        self.session().setValue('username', fields["username"][1])
        self.response().sendRedirect("Main") 

FunFormKit : HTML generation

FunFormKit och Cheetah, tillsammans

Genom att hämta ut fält som strängar från FunFormKit kan man integrera med Webware.

Servlet:

from WebKit.Page import Page
from FunFormKit.Form import FormServlet, FormDefinition
from FunFormKit import Field
from Cheetah.Template import Template
from FunFormKit.Validator import Email

class FFF_Cheetah(Page, FormServlet):
    def __init__(self):
        formDef = FormDefinition("FFF_Cheetah",
                                 [Field.TextField("name",
                                                  maxLength=255,
                                                  size=20
                                                  ),
                                  Field.TextField("email", maxLength=255,
                                                  size=25,
                                                  validators=[Email()]),
                                  Field.SubmitButton("submit",
                                                     description="Send!")])
        Page.__init__(self)
        FormServlet.__init__(self, [formDef])

    def writeContent(self):
        submitted, data = self.processForm()
        if not submitted:
            rf = self.renderableForm()
            t = Template(file="/s/waresite/Templates/FFF_Cheetah.tmpl",
                         searchList=[{'rf':rf}])
            self.write(str(t))
        else:
            self.write("Your name and email is %(name)s <%(email)s>" % data)

FunFormKit och Cheetah, tillsammans

Template (FFF_Cheetah.tmpl):
## mode: html

$rf.start
<TABLE>
<TR>
<TD><B>Name: </B></TD>
<TD>$rf.name $rf.name.error</TD>
</TR>

<TR>
<TD><B>Email: </B></TD>
<TD>$rf.email $rf.email.error</TD>
</TR>

<TR>
<TD> </TD>
<TD>$rf.submit $rf.submit.error</TD>
</TR>
</TABLE>
$rf.end

Webware @ Lysator

Länkar

Kent vill ha ett randomnummer!

CCConvertedWinner