diff --git a/.gitattributes b/.gitattributes index 39b603c..00a78f5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,25 +1,17 @@ * text=auto eol=lf -*.bat text eol=crlf -*.config text eol=lf *.css text eol=lf *.html text eol=lf *.js text eol=lf -*.prefs text *.py text eol=lf *.rst text eol=lf *.sh text eol=lf *.txt text eol=lf -*.po text eol=lf -*.pot text eol=lf -*.styl text eol=lf -*.xml text *.gif binary *.ico binary *.jpg binary *.lnk binary -*.mo binary *.png binary *.exe binary *.so binary diff --git a/Context/Main.py b/Context/Main.py deleted file mode 100644 index 747f73d..0000000 --- a/Context/Main.py +++ /dev/null @@ -1,1112 +0,0 @@ -import os -import re -import datetime -import mimetypes - -from lib import wikipage, htmldiff, menubar -from lib.common import dedent -from lib.format_date import format_date_relative - - -from SitePage import * -from WebKit.HTTPExceptions import * - -tidy_options = dict( - break_before_br=True, - show_body_only=True, - char_encoding='utf8') -try: - from tidy import parseString as utidy -except ImportError: - try: - from mx.Tidy import tidy as mxtidy - except ImportError: - tidy = lambda text: text - else: - tidy_options['output_error'] = False - tidy = lambda text: mxtidy(text, **tidy_options)[2] -else: - tidy = lambda text: utidy(text, **tidy_options) - - -class Main(SitePage): - - def __init__(self): - SitePage.__init__(self) - self._inputTypeCleaners = {} - self._inputTypeCleaners['xinha'] = self.cleanXinha - - def setupEarly(self): - req = self.request() - name = req.extraURLPath().strip('/') - if not name: - self.forward('/frontpage') - name, ext = self.splitExtension(name) - if name in self.wiki.globalWiki.specialNames: - # We got the .html ending or somesuch, and we shouldn't - # have, so we'll redirect - self.sendRedirectAndEnd(self.servletLink(name)) - return - version = self.getVersion() - self.page = self.wiki.page(name, version=version) - if name != self.page.urlName: - link = self.page.link - if '?' in link: - link += '&' - else: - link += '?' - link += self.encodeArgs() - self.sendRedirectAndEnd(link) - self.titlePrefix = '' - if ext == '.thumb.jpg': - req.setField('_action_', 'thumbnail') - elif ext != '.html': - req.setField('_action_', 'source') - - def sleep(self, transaction): - SitePage.sleep(self, transaction) - self.page = None - - def respondToPut(self, transaction): - self.webdav() - - respondToLock = respondToUnlock = respondToPut - - def actions(self): - return ['edit', 'preview', 'save', 'cancel', 'history', - 'externalEdit', 'webdav', 'backlinks', 'diff', - 'changeMimeType', 'simple', 'source', 'comment', - 'thumbnail', 'attach'] - - def pageClass(self): - if (not self.page.exists() - and self.request().field('commenting', None)): - return 'comment' - return self.page.pageClass - - def authorUser(self): - return self.page.authorUser - - def writeGoogleAds(self): - if self.view() not in ('writeEdit', 'writePreview', 'writeChangeMimeType'): - SitePage.writeGoogleAds(self) - - ######################################## - ## Utility methods - ######################################## - - def splitExtension(self, name): - if name.endswith('.thumb.jpg'): - ext = '.thumb.jpg' - name = name[:-len('.thumb.jpg')] - elif '.' in name: - name, ext = os.path.splitext(name) - else: - ext = '.html' - return name, ext - - def getVersion(self): - version = self.request().field('version', None) or None - if isinstance(version, (list, tuple)): - version = version[0] - try: - version = int(version) - if version < 1: - raise ValueError - except: - version = None - return version - - def cleanInput(self, inputType, text): - if text.startswith('base64,'): - text = text[7:].decode('base64') - text = self._inputTypeCleaners.get(inputType, lambda x: x)(text) - return text - - def assertEdit(self): - if self.page.exists(): - self.assertPermission('edit') - else: - self.assertPermission(['create', 'edit']) - - ######################################## - ## Edit - ######################################## - - def edit(self): - self.assertEdit() - req = self.request() - self.titlePrefix = 'Edit: ' - mimeType = req.field('mimeType', self.page.mimeType) - if mimeType != self.page.mimeType and self.page.exists(): - assert 0, "Content conversion has not yet been implemented" - self._setPageClass = 'posting' - self.commenting = req.field('commenting', 0) - if self.commenting: - self._setPageClass = 'comment' - self.attaching = req.field('attaching', 0) - if self.attaching: - self._setPageClass = 'attachment' - assert not self.commenting or not self.attaching - if self.commenting: - self.orig = self.wiki.page(self.commenting) - if (not req.hasField('title') - and not self.orig.title.lower().startswith('re:')): - req.setField('title', 'Re: %s' % self.orig.title) - if self.attaching: - self.orig = self.wiki.page(self.attaching) - if not req.hasField('title'): - req.setField('title', 'Attachment to: %s' % self.orig.title) - if not req.hasField('mimeType'): - req.setField('mimeType', 'application/*') - self.setView('writeEdit') - self.writeHTML() - - _comment_name_re = re.compile(r'-*comment-?\d+$') - def comment(self): - if self._comment_name_re.search(self.page.urlName): - newName = self._comment_name_re.sub('', self.page.urlName) - else: - newName = self.page.urlName - p = self.wiki.page(newName + '-comment-000') - raise HTTPTemporaryRedirect( - p.link + - '?_action_=edit&commenting=%s' - % self.urlEncode(self.page.name)) - - def attach(self): - p = self.wiki.page(self.page.name + 'attach-000') - raise HTTPTemporaryRedirect( - p.link + - '?_action_=edit&attaching=%s' - % self.urlEncode(self.page.name)) - - def preview(self): - self.saveCookieAuthorInfoFromRequest() - self.setView('writePreview') - self.titlePrefix = 'Preview: ' - req = self.request() - self.writeHTML() - - def save(self): - self.assertEdit() - req = self.request() - self.saveCookieAuthorInfoFromRequest() - if self.page.name.endswith('000'): - # This means we have to create a new page name that is - # uniquely numbered. @@: This isn't actually safe, - # since instantiating the page won't make it "exist", - # and another thread could still use the same number - name = self.page.name[:-3] - n = 1 - while 1: - if not self.wiki.page('%s%i' % (name, n)).exists(): - break - n += 1 - oldPage = self.page - self.page = self.wiki.page('%s%i' % (name, n)) - self.page.urlName = oldPage.urlName[:-3] + str(n) - self.page.title = req.field('title') - if self.page.title.find('000') != -1: - self.page.title = self.page.title.replace('000', str(n)) - else: - self.page.title = req.field('title') - inputType = req.field('inputType') - text = req.field('text', '') - textUpload = req.field('textUpload', '') - mimeType = req.field('mimeType') - try: - filename = textUpload.filename - if mimeType.startswith('application/'): - mt, encoding = mimetypes.guess_type(filename) - if mt: - mimeType = mt - textUpload = textUpload.value - self.page.originalFilename = filename - except AttributeError: - pass - if textUpload: - text = textUpload - text = self.cleanInput(inputType, text) - self.page.mimeType = mimeType - self.page.title = req.field('title') - if req.field('hidden', None): - self.assertPermission('hide') - self.page.hidden = req.field('hidden', '') - if req.hasField('comments'): - self.page.comments = req.field('comments') - self.page.lastChangeLog = req.field('changeLog', '') - if self.user(): - self.page.lastChangeUser = self.user().username() - else: - self.page.lastChangeUser = 'anonymous' - if req.field('authorName', None): - self.page.authorName = req.field('authorName') - if req.field('authorURL', None): - self.page.authorURL = req.field('authorURL') - if req.field('authorEmail', None): - self.page.authorEmail = req.field('authorEmail') - if self.user(): - self.page.authorUser = self.user() - if text or not self.page.text: - self.page.text = text or '' - self.page.distributionOriginal = False - pageClass = req.field('pageClass', None) - if req.field('commenting', None): - parentPage = self.wiki.page(req.field('commenting')) - self.page.connections += [(parentPage, 'comment')] - if pageClass is None: - pageClass = 'comment' - if req.field('attaching', None): - parentPage = self.wiki.page(req.field('attaching')) - self.page.connections += [(parentPage, 'attachment')] - if pageClass is None: - pageClass = 'attachment' - if pageClass is None: - pageClass = 'posting' - self.page.pageClass = pageClass - self.page.save() - self.message('Changes saved') - self.sendRedirectAndEnd(self.link(unversioned=True)) - - def cancel(self): - self.message('Edit cancelled') - self.sendRedirectAndEnd(self.link(unversioned=True)) - - def history(self): - self.assertPermission(['history', 'view']) - self.setView('writeHistory') - self.titlePrefix = 'History of: ' - self.writeHTML() - - def backlinks(self): - self.assertPermission(['navigation', 'view']) - self.setView('writeBacklinks') - self.titlePrefix = 'Backlinks to: ' - self.writeHTML() - - def diff(self): - if self.request().field('delete', None): - self.history() - else: - self.assertPermission(['diff', 'history', 'view']) - firstVersion = self.request().field('firstVersion', None) or None - self.firstPage = self.wiki.page(self.page.name, version=firstVersion) - otherVersion = self.request().field('otherVersion', None) or None - self.otherPage = self.wiki.page(self.page.name, version=otherVersion) - self.setView('writeDiff') - self.titlePrefix = ('Diff %s to %s of:' % - (firstVersion or 'Current', otherVersion or 'Current')) - self.writeHTML() - - def externalEdit(self): - self.assertEdit() - self.setView(None) - res = self.response() - req = self.request() - if not req.hasCookie('_SID_'): - raise HTTPBadRequest( - "Your browser must support cookies to use the external " - "editor.") - res.setHeader('content-type', 'application/x-zope-edit') - self.write('url:http://%s%s\n' - % (req.environ()['HTTP_HOST'], - self.link(action='webdav'))) - self.write('meta_type:Wiki\n') - # no HTTP or cookie authentication required: - self.write('auth:\n') - self.write('cookie: _SID_=%s\n' - % (req.cookie('_SID_'))) - self.write('\n') - self.write('## title=%s\n' % self.page.title) - self.write('## class=%s\n' % (self.page.pageClass or 'posting')) - self.write('## log=\n') - self.write('\n') - self.write(self.page.text.replace('\r', '')) - - def webdav(self): - self.assertEdit() - # @@: Really, this should make sure that we send a real - # 401, not an HTML login form. But that's not easy at the - # moment :( HTTPForbidden instead? - self.setView(None) - req = self.request() - method = req.environ()['REQUEST_METHOD'].upper() - metavars = {'user': 'lastChangeUser', - 'log': 'lastChangeLog', - 'title': 'title', - 'class': 'pageClass'} - - if method in ('LOCK', 'UNLOCK'): - # We don't support these now - return - - if method == 'GET': - self.write(self.page.text) - return - - if method == 'PUT': - f = req.rawInput(rewind=1) - text = f.read() - lines = text.splitlines() - metadata = {} - while 1: - if not lines: - break - line = lines[0].strip() - if not line: - lines.pop(0) - continue - if not line.startswith('##'): - break - if '=' not in line: - break - lines.pop(0) - line = line[2:] - name, value = line.split('=', 1) - name = name.strip().lower() - value = value.strip() - metadata[name] = value - - for name, value in metadata.items(): - assert metavars.has_key(name), "Bad name %r (must be one of %s)" % (name, ', '.join(metavars.keys())) - setattr(self.page, metavars[name], value) - # @@: this won't work for binary pages - self.page.text = '\n'.join(lines) - self.page.save() - - def changeMimeType(self): - self.assertEdit() - self.setView('writeChangeMimeType') - self.writeHTML() - - def simple(self): - self.suppressFooter = True - self.writeHTML() - - def source(self): - mimeType = self.page.mimeType - if mimeType in ['text/x-restructured-text', 'text/x-python']: - mimeType = 'text/plain' - self.response().setHeader('content-type', mimeType) - self.write(self.page.text) - - def thumbnail(self): - thumbnail = self.page.thumbnail - if not thumbnail: - raise HTTPNotFound - self.response().setHeader('content-type', 'image/jpeg') - self.write(self.page.thumbnail) - - ######################################## - ## Views - ######################################## - - def title(self): - if self.page.version: - title = '%s version %s' % (self.page.title, self.page.version) - else: - title = self.page.title - return self.titlePrefix + title - - def writeHeader(self): - if not self.page.name in ('wikisandbox',): - self.write('\n') - SitePage.writeHeader(self) - - def writeRelatedLinks(self): - SitePage.writeRelatedLinks(self) - if self.checkPermission('edit'): - # add universal edit button - self.write('' % self.link(action='edit')) - - def link(self, action=None, unversioned=False, **kw): - if action: - kw['_action_'] = action - if unversioned: - link = self.page.wiki.linkTo(self.page.name) - else: - link = self.page.link - if not kw: - return link - if '?' in link: - link += '&' - else: - link += '?' - link += '&'.join(['%s=%s' % (n, self.urlEncode(v)) - for n, v in kw.items()]) - return link - - def writeContent(self): - if self.page.hidden: - self.assertPermission('viewhidden') - self.write(self.page.html) - creation = self.page.config.getbool('displaycreationdate', False) - modified = self.page.config.getbool('displaymodifieddate', False) - self.write('
\n') - comments.sort(lambda a, b: cmp(a.creationDate, b.creationDate)) - for comment in comments: - self.write(comment.html) - authorName = comment.authorName or comment.lastChangeUser - authorName = self.htmlEncode(authorName) - if comment.authorURL: - authorName = '%s' % ( - self.htmlEncode(comment.authorURL), authorName or 'URL') - if comment.authorUser: - authorName = comment.authorUser.signature() - data = [] - if authorName: - data = [authorName] - showCreate = None - if comment.config.getbool('displaycreationdate', False): - showCreate = comment.creationDate - data.append(format_date_relative(comment.creationDate)) - if (comment.config.getbool('displaymodifieddate', False) - and comment.modifiedDate != showCreate): - data.append( - 'Modified: %s' % - format_date_relative(comment.modifiedDate)) - if self.checkPermission(['create', 'edit'], - pageClass='comment'): - link = comment.link + '?_action_=comment' - data.append('Reply' % self.htmlEncode(link)) - perm_link = '#' % self.htmlEncode(comment.link) - if data: - data[0] = perm_link + ' ' + data[0] - else: - data = [perm_link] - if data: - self.write('\n') - - def menus(self): - menu = SitePage.menus(self) - menu.insert(2, 'Page') - if not self.page.readOnly: - menu.insert(3, 'Edit') - return menu - - def menuPage(self): - menu_title = self.page.title - if len(menu_title) > 15: - menu_title = menu_title[:14] + '…' - menu = ('%s' % menu_title, [ - ('View', self.link(unversioned=True)), - ('Source', self.page.sourceLink), - ('Backlinks', self.link(action='backlinks', unversioned=True)), - ('History', self.link(action='history', unversioned=True))]) - return menu - - def menuEdit(self): - menu = [] - if self.checkPermission('edit'): - menu = [ - ('Edit this page', self.link(action='edit')), - ('External editor%s' - % '
\n'.join(data)) - self.write('
\n') - self.displayComments(comment) - self.write('
| - | - |
| - | - (will not be displayed) - |
| - | - |
Selected versions deleted.
') - myusername = self.user() and self.user().username() - if delete and users: - deleted = [] - for username in users: - if username != myusername: - try: - user = self.userManager().userForUsername(username) - self.userManager().deleteUser(user) - deleted.append(username) - except: - pass - if deleted: - msg = 'Users deleted: ' + ', '.join(deleted) - # @@ we should change ownership of all remaining pages of users - elif myusername in users: - msg = 'No hara-kiri allowed.' - else: - msg = 'Users were already deleted.' - self.write('%s
' % msg) - versions = self.page.versions() - if not versions: - self.write('There are no archived versions available.
') - return - delete = False - for page in versions: - if self.checkPermission('delete', page=page): - delete = True - break - self.write('\n') - - def writeBacklinks(self): - self.write('| %s' - ' |
Added to %s (present in %s)
'
- 'Deleted from %s (present in %s)
Current MIME type: %s\n%s
\n' - % (self.page.mimeType, - self.helpLink('mimetypes', 'Help on MIME types'))) - self.write('Edit with a different MIME type:
\n') - allTypes = [t for t in self.wiki.availableMimeTypes - if not t.startswith('image/')] - allTypes.append('image/*') - for mimeType in allTypes: - self.write('%s\n' - % (self.pageLink(self.page.name, - action='edit', - args={'mimeType': mimeType, - 'commenting': req.field('commenting', None)}), - mimeType)) - - _linkRE = re.compile('href\s*=\s*"(.*?)"', re.I+re.S) - def cleanXinha(self, text): - return self._linkRE.sub(self._urlSubber, str(tidy(text))) - - def _urlSubber(self, match): - url = match.group(1) - baseLink = self.servletLink('', absolute=True) - if url.startswith(baseLink): - url = url[len(baseLink):] - return 'href="%s"' % url diff --git a/Context/SitePage.py b/Context/SitePage.py deleted file mode 100644 index 4cd336a..0000000 --- a/Context/SitePage.py +++ /dev/null @@ -1,429 +0,0 @@ -import os -import shutil -import time - -from lib import wiki, wikiconfig, user, menubar -from lib.common import pprint, dprint, dedent -from lib.formatdate import format_date -from lib.securehidden import SecureSigner - -from Component import CPage -from Component.notify import NotifyComponent - -from LoginKit import UserComponent -from LoginKit.rfc822usermanager import RFC822UserManager - -from TaskKit.Task import Task -from TaskKit.Scheduler import Scheduler - -from WebKit import AppServer -from WebKit.HTTPExceptions import * - -from WebUtils.Funcs import htmlEncode - -__all__ = ['SitePage', 'pprint', 'dprint'] - - -class SitePage(CPage): - - components = [NotifyComponent()] - _adsenseID = None - - def awake(self, transaction): - CPage.awake(self, transaction) - domain = self.request().environ().get('HTTP_HOST', 'SERVER_NAME') - if ':' in domain: - domain = domain.split(':', 1)[0] - self.wiki = self.TheGlobalWiki.site(domain=domain) - self.wiki.basehref = self.request().adapterName() + '/' - self.suppressFooter = False - self.response().setHeader( - 'content-type', 'text/html; charset=utf-8') - self.setupEarly() - self.config = self.wiki.config.merge_page_class(self.pageClass()) - if self._adsenseID is None: - SitePage._adsenseID = self.config.get('googleadsenseid', '') - self.assertPermission('view', self.pageClass()) - self.setup() - - def setupEarly(self): - """ - This is called before setup(), and before we resolve .pageClass() - (which gets used to create the config). Most pages should - override seutp() instead. - """ - pass - - def setup(self): - pass - - def checkPermission(self, *args, **kw): - try: - self.assertPermission(*args, **kw) - return True - except (HTTPAuthenticationRequired, HTTPForbidden): - return False - - _cachedPermissions = {} - - def assertPermission(self, action='view', pageClass=None, page=None): - if isinstance(action, (str, unicode)): - action = (action,) - elif isinstance(action, list): - action = tuple(action) - userID = self.user() and self.user().userID() - if not page: - cacheKey = (userID, action, pageClass) - try: - result = self._cachedPermissions[cacheKey] - if result == 'auth': - raise HTTPAuthenticationRequired - elif result: - raise HTTPForbidden(result) - else: - return - except KeyError: - pass - if pageClass is not None and pageClass != self.pageClass(): - config = self.wiki.config.merge_page_class(pageClass) - else: - config = self.config - config_section = None - for actual_action in action: - config_section = config.get(actual_action) - if config_section: - break - if not config_section: - msg = ("You must configure the roles to %s this page" - % actual_action) - if not page: - self._cachedPermissions[cacheKey] = msg - raise HTTPForbidden(msg) - role = config_section.get('requiredrole', 'none').strip().lower() - role = filter(None, role.split()) - if not role or role == ['none']: - if not page: - self._cachedPermissions[cacheKey] = None - return - if not self.user(): - if not page: - self._cachedPermissions[cacheKey] = 'auth' - raise HTTPAuthenticationRequired - if 'user' in role: - if not page: - self._cachedPermissions[cacheKey] = None - return - user_roles = self.user().roles() - if userID: - if page: - authorID = page.authorUser and page.authorUser.userID() - else: - authorID = self.authorUser() and self.authorUser().userID() - if userID == authorID: - user_roles.append('author') - for has_role in user_roles: - if has_role in role: - if not page: - self._cachedPermissions[cacheKey] = None - return - msg = ("You must have the role %s to %s this page" - % (' or '.join(role), actual_action)) - if not page: - self._cachedPermissions[cacheKey] = msg - raise HTTPForbidden(msg) - - ######################################## - ## Utility methods - ######################################## - - def loginRequired(self): - return False - - def pageClass(self): - return 'posting' - - def authorUser(self): - return None - - def writeSimplePageList(self, pages): - self.write('| %s |
| - |
Recent Changes:' - ' a feed of all changes to the site.
\n' - 'New posts: ' - ' a feed of all new posts to the site.
' - % (self.wiki.linkTo('feeds/recent_changes.xml'), - self.wiki.linkTo('feeds/new_pages.xml'))) - - def writeFeed(self, name): - self.response().setHeader('Content-type', - 'application/rss+xml; charset=utf-8') - if name == 'recent_changes.xml': - f = open(self.wiki.syndicateRecentChangesFilename) - self.write(f.read()) - f.close() - elif name == 'new_pages.xml': - f = open(self.wiki.syndicateNewPagesFilename) - self.write(f.read()) - f.close() - else: - self.response().setHeader('Status', '404 Not Found') - self.write('Not Found') - diff --git a/Context/forgotten.py b/Context/forgotten.py deleted file mode 100644 index 9088436..0000000 --- a/Context/forgotten.py +++ /dev/null @@ -1,10 +0,0 @@ -from LoginKit.forgotten import ForgottenPasswordComponent -from SitePage import * - -class forgotten(SitePage): - - components = SitePage.components + [ForgottenPasswordComponent()] - - def defaultAction(self): - self.forgottenPasswordForm() - diff --git a/Context/frontpage.py b/Context/frontpage.py deleted file mode 100644 index 2cd4c6f..0000000 --- a/Context/frontpage.py +++ /dev/null @@ -1,66 +0,0 @@ -from SitePage import SitePage -import time - -class frontpage(SitePage): - - _cached_content = None - _cache_time = 0 - _cache_timeout = 60 - - def title(self): - return self.wiki.config.get('rss') and \ - self.wiki.config.get('rss').get('title') or 'The Wiki Frontpage' - - def writeRelatedLinks(self): - SitePage.writeRelatedLinks(self) - description = self.wiki.config.get('rss') and \ - self.wiki.config.get('rss').get('description') - if description: - self.write('\n' % description) - if self.user() and self.checkPermission('edit'): - # add universal edit button - page = self.wiki.page('index') - self.write('' % page.link) - - def writeContent(self): - if self.wiki.config.getbool('blog', False): - if not self._cached_contenttime or \ - time() - self._cache_time > self._cache_timeout: - frontpage._cached_content = self.getBlogContent() - frontpage._cache_time = time.time() - self.write(self._cached_content) - else: - self.write(self.getWikiContent()) - - def getBlogContent(self): - result = [] - write = result.append - recent = [p for p in self.wiki.recentCreated() - if p.pageClass == 'posting'][:10] - for page in recent: - write('To avoid abuse of this Wiki, we have to make a simple
- plausibility test that checks whether you are a serious user.
Please fill in:
These %i pages are not linked to from ' - 'any other pages.
\n' - % len(pages)) - self.writeSimplePageList(pages) diff --git a/Context/question_icon.gif b/Context/question_icon.gif deleted file mode 100644 index 6cfc061..0000000 Binary files a/Context/question_icon.gif and /dev/null differ diff --git a/Context/quickfind.py b/Context/quickfind.py deleted file mode 100644 index 3fa877a..0000000 --- a/Context/quickfind.py +++ /dev/null @@ -1,146 +0,0 @@ -from SitePage import * - -repopulate = ''' -var lastValue = ""; - -function repopulate() { - var search = document.getElementById("search").value.toLowerCase().split(/\s+/); - var resultNames = new Array(); - var resultTitlesCapped = new Array(); - var resultTitles = new Array(); - var resultMimeTypes = new Array(); - var item, i, title, pos; - for (i = 0; i < allNames.length; i++) { - item = allNames[i]; - if (searchMatches(search, item)) { - resultNames[resultNames.length] = item; - title = capitalizeSearch(search, allTitles[i]) - resultTitlesCapped[resultTitlesCapped.length] = title; - resultTitles[resultTitles.length] = allTitles[i]; - resultMimeTypes[resultMimeTypes.length] = allMimeTypes[i]; - } - } - var select = document.getElementById("pages"); - for (i = select.length; i >= resultNames.length; i--) { - select.options[i] = null; - } - for (i = 0; i < resultNames.length; i++) { - select.options[i] = new Option(resultTitlesCapped[i], - resultNames[i] + "**" + resultMimeTypes[i] - + "**" + resultTitles[i]); - } - if (! resultNames.length) { - select.options[0] = new Option("No results", ""); - } -} - -function searchMatches(search, term) { - term = term.toLowerCase(); - var i; - for (i = 0; i < search.length; i++) { - if (term.indexOf(search[i]) == -1) { - return false; - } - } - return true; -} - -function capitalizeSearch(search, term) { - var i; - for (i = 0; i < search.length; i++) { - pos = term.toLowerCase().indexOf(search[i]); - if (pos != -1) { - term = term.substring(0, pos) - + term.substring(pos, pos+search[i].length).toUpperCase() - + term.substring(pos+search[i].length, term.length); - } - } - return term; -} - -function research() { - var search = document.getElementById("search").value; - if (search != lastValue) { - lastValue = search; - repopulate(); - } -} - -function getSelected() { - var select = document.getElementById("pages"); - var items = select.options[select.selectedIndex].value.split("**"); - return items; -} - -function callParent(func_name) { - var items = getSelected(); - //window.alert("Calling " + func_name + " of " + window.opener - //+ " which is " + window.opener[func_name]); - window.opener[func_name](items[0], items[1], items[2]); -} - -function callParentBare(func_name) { - var search = document.getElementById("search").value; - var name = search.toLowerCase(); - name = name.replace(/[^a-z]/g, ""); - window.opener[func_name](name, "text/html", search); -} -''' - -class quickfind(SitePage): - - def awake(self, trans): - SitePage.awake(self, trans) - self.suppressFooter = True - self.callParent = self.request().field('callParent', '') - - def title(self): - return 'Quick Page Find' - - def writeArray(self, name, array): - self.write('var %s = new Array(\n' % name) - self.write(',\n '.join( - [repr(str(v)) for v in array])) - self.write(');\n') - - def writeContent(self): - onSelect = None - if self.callParent: - onSelect = 'callParent(%s); window.close();' \ - % repr(self.callParent) - if onSelect: - selectAttr = ' onChange="%s"' % onSelect - else: - selectAttr = '' - - onCreate = "callParentBare(%s); window.close();" \ - % repr(self.callParent) - - self.write(''' -All pages on system:
- Search:RSS feed of recent changes: \n'
- % (self.wiki.linkTo('feeds/recent_changes.xml'),
- self.wiki.linkTo('miniXmlButton.gif')))
- elif recentType == 'created':
- self.write('
RSS feed of new posts: \n'
- % (self.wiki.linkTo('feeds/new_pages.xml'),
- self.wiki.linkTo('miniXmlButton.gif')))
-
-
- self.write('''
| Page | -Last modified | -Log message | -User | -
|---|---|---|---|
| %s%s | \n' - % (rowClass, page.link, page.title, bang)) - self.write('%s | \n' - % self.format_date(page.modifiedDate, nonbreaking=True)) - self.write('%s | \n' - % self.htmlEncode(page.lastChangeLog or '')) - self.write('%s | \n' - % self.htmlEncode(page.lastChangeUser or '')) - self.write('
- indicates that errors occured while trying to parse the
- document.
No pages found
\n') - else: - self.writeResults(self.results) - - def title(self): - return self.explanation - - def htTitle(self): - return '' - - def writeForm(self): - self.write(''' - ''' - % (self.htmlEncode(self.genericSearch or self.titleSearch or self.bodySearch))) - - def writeResults(self, results): - results.sort(lambda a, b: cmp(a.name, b.name)) - self.write('These %i pages are being linked to, but ' - 'have not been created yet.
\n' - % len(pages)) - self.write('| Wanted page | \n') - self.write('Wanted by | \n') - self.write('
|---|---|
| ' - % ['odd', 'even'][index%2]) - if not subindex: - self.write('%s' - % (page.link, page.title)) - self.write(' | %s |
John Dickinson has created three files which, together, provide a fairly simple way to implement some Ajax functionality into Webware servlets. AjaxPage provides a super class to your servlet (but below your site page). All JavaScript necessary is contained in ajax.js and ajax_polling.js. ajax.js provides basic functionality useful for most situations, and ajax_polling.js provides support for long-running requests and out-of-band server to client commands.
+The AjaxPage class creates a function ajax_allowed() which returns a list of function names. These function names refer to Python functions that are able to be called by an Ajax-enabled web page. This is very similar in functionality to webware's actions. Child classes should override ajax_allowed().
+AjaxPage also provides the class PyJavascript. This class simply tanslates a Python expression into a string. For example:
+foo = PyJavascript('bar')
+foo.hello('hi').another.element.func() # evaluates as the string "bar.hello('hi').another.element.func()"
+PyJavascript is based in large part on code from Nevow 0.4.1 (nevow.org).
+The AjaxPage class uses PyJavascript to set up several useful references to JavaScript functions.
+ajax.js provides all the client-side heavy lifting required to get Ajax functionality into a web page. Several pieces of it are based on ideas from Apple developer code (developer.apple.com) and Nevow 0.4.1 (nevow.org).
+ajax_polling.js provides functionality that allows the server to process long-running requests from the client and allows the server to send commands to the client without the user on the client side triggering an event.
+Client triggers an action. This request is sent to ajax_controller().
Once the server-side function has returned, the results are sent back down the pipe to the client...
...unless it took longer that TIMEOUT seconds to complete (the timeout is defaulted to 100). If the function take a while to finish, ajax_controller() puts the results into the results bucket that ajax_response() is polling.
ajax_response(), which is being polled every few seconds by the client, sees that there is something for the client in the results bucket, grabs it, and sends it to the cient.
The polling behavior of an ajax-enabled page may be controlled by overriding ajax_clientPollingTimeout. Setting its return value to None prevents the polling from happening at all. Otherwise, this function needs to return the number of seconds to wait until the client polls the server again.
+class my_servlet(AjaxPage):
+ def ajax_allowed(self):
+ return ['check_if_even']
+
+ def check_if_even(self,val):
+ is_even = (int(val) % 2) == 0
+ if is_even:
+ ret = self.setTag('response_id','The value you entered is even!')
+ else:
+ ret = self.alert('Oh no! The value you entered is not even!')
+ return '%s; %s' % (self.setReadonly('even_num',False),ret) # don't forget to unlock the field
+
+ def writeContent(self):
+ wl = self.writeln
+ wl('<form action="some_page.py" method="post">')
+
+ # will first set the input field to read only, then call check_if_even with the field's value passed in
+ ajax_cmd = self.generic_ajax(self.setReadonly('even_num',True),'check_if_even',self.this.value)
+
+ wl('<input name="even_num" id="even_num" type="text" onchange="%s"/>' % ajax_cmd)
+ wl('<span id="response_id"></span>') # see check_if_even() for tag name
+ wl('</form>')
+Downloads:
+ +Robert Forkel has compiled an extended example named AjaxSuggest. This example shows how to implement functionality a la Google suggest. Note: This example will be already included in the Examples context of Webware for Python starting with version 0.9.1.
+OpenRico is a Javascript for Rich Application library including ajax, drag&drop and many visual effects. +Here explains ajax servlet with OpenRico.
+The Ajax Servlet library AjaxServlet.py:
+from WebKit.RPCServlet import RPCServlet
+
+class AjaxServlet(RPCServlet):
+ def awake(self, transaction):
+ RPCServlet.awake(self, transaction)
+ self._response = transaction.response()
+ self._request = transaction.request()
+
+ self._session = transaction.session()
+ self._resp = []
+ def sleep(self, transaction):
+ RPCServlet.sleep(self, transaction)
+ self._response = None
+ self._request = None
+ self._session = None
+ self._resp = None
+
+ def respondToGet(self, trans):
+ try:
+ # ajax servlet don't need cache
+ resp = self.response()
+ resp.setHeader('Cache-Control', 'no-cache')
+ resp.setHeader('Pragma', 'no-cache');
+
+ self.writeln('<ajax-response>')
+ self._respond()
+ self.writeln('</response></ajax-response>')
+
+ self.sendOK('text/xml', ''.join(self._resp), trans)
+ finally:
+ pass # do error handling or database release
+
+ def request(self): return self._request
+ def response(self): return self._response
+ def transaction(self): return self._transaction
+
+ def _respond(self):
+ """ Here goes the response
+ self.write('<span>Here is ajax response!!</span>')
+
+ NOTE: You must call responseToElement() or responseToObject() before write something
+ """
+ raise NotImplementedError
+
+
+ def responseToElement(self, elementId):
+ self.write('<response type="element" id="%s">' % elementId)
+
+ def responseToObject(self, objectId):
+ self.write('<response type="object" id="%s">' % objectId)
+
+ def write(self, *args):
+ for arg in args:
+ self._resp.append(self.encode(str(arg)))
+
+ def writeln(self, *args):
+ self.write(*args)
+ self.write("\n")
+
+ def decode(self, str, encoding='euc-kr'):
+ """ Decode JavaScript quoted string to native string """
+ ret = []
+
+ while s:
+ if s[:2].lower() == '%u': # 유니코드 인코딩
+ char = s[:6]
+ unicode_chr = int( char[2:], 16)
+ try: ret.append( unichr(unicode_chr).encode(encoding) )
+ except UnicodeEncodeError: ret.append( '&#%s;' % unicode_chr )
+ s = s[6:]
+ else:
+ ret.append( s[0] )
+ s = s[1:]
+
+ return ''.join(ret)
+
+
+ def encode(self, str, encoding='euc-kr'):
+ """ Encode string to HTML unicode representation """
+ p = string.printable
+ ret = []
+ for c in unicode(str, encoding):
+ if c in p: ret.append(c)
+ else: ret.append( '&#%d;' % ord(c) )
+ return ''.join(ret)
+search zipcode servlet example::
+from AjaxServlet import *
+from Zipcode import *
+
+class ajax_search_zipcode(AjaxServlet):
+ def _respond(self):
+ self.responseToElement('search_result')
+ zipcode_list = Zipcode.search(self.decode(self.request().field('dong')))
+
+ self.write("""<table cellspacing="1" cellpadding="1" width="100%" bgcolor="black" bordercolordark="#DDDDDD" bordercolorlight="#666666">
+ <tr align="center" bgcolor="#DDDDDD" height="22">
+ <td bgcolor="#DDDDDD">우편번호</td>
+ <td bgcolor="#DDDDDD">주 소</td>
+ <td bgcolor="#DDDDDD">선택</td>
+ </tr>\n""")
+
+ for z in zipcode_list:
+ self.write( """<tr bgcolor="#DDDDDD">
+ <td bgcolor="#FFFFFF" style="white-space: nowrap;">%s</td>
+ <td bgcolor="#FFFFFF" style="white-space: nowrap;">%s %s %s %s</td>
+ <td bgcolor="#FFFFFF"><input type="button" value="선택" onClick="postaddr('%s', '%s');"/></td>
+</tr>\n""" % ( z[0], z[1], z[2], z[3], z[4], z[0], z[1] + z[2] + z[3] ) )
+ self.write('</table>')
+for real working example:
+goto http://www.bodd.co.kr/member_register (only korean site)
click
button.
input "신림9" and click first button
-- whitekid
+Here is a list of ideas for open source apps to build with, or for +Webware. If you add an idea or comment, please sign your name and +leave a date stamp. Many of the project ideas listed below will be +interrelated, so use cross-references where appropriate.
+Extensions to the Webware and WebKit core (as opposed to other Kits or +stand-alone applications) belong on the `Wish List`_ page.
+I'm not sure what this would look like, but some object to facilitate +the collection of RSS feeds and embedding them into a web page. I +guess there would be a few parts coded into a single class, or a +single class with helper classes:
+Configuration info: which site to connect to, how often to refresh.
Read and parse the RSS file at the specified interval.
Send some kind of "observe me now" signal to the object that's +serving the final output, so it can refresh itself. Perhaps +Threading.Event or a port of Java's Observable/Observer interface +can help with this.
Offer the RSS data in a Python data structure to any routine that +asks for it. Perhaps a method in the RSS-retrieval object can offer +this, or it can be in a well-known attribute somewhere.
Have some boilerplate output objects that convert the RSS data into +HTML hyperlinks, for simple sites that want an "out of the box" +solution. These classes should be scalable for adding cosmetic +enhancements via stylesheets or subclasses.
-- Mike Orr - 14 Apr 2002
+The following other web application servers may have objects worth +porting to Webware:
+Horde Horde may have some objects worth porting. "The Horde +Application Framework is a general-purpose web application framework +in PHP, providing classes for dealing with preferences, compression, +browser detection, connection tracking, MIME handling, and more."
+ +-- Mike Orr - 04 Apr 2002
+Webmail software that could be ported to Webware or ideas stolen from.
+Horde IMP -- IMAP/POP e-mail access.
+(This isn't something I need anytime soon, but would be worthwhile +someday.
+-- Mike Orr - 04 Apr 2002
+BoboMail is a decent POP webmail client that runs off CGI and bobo +(an old version of Zope). It's fairly decent code, and even if a lot +of stuff gets rewritten it would serve a good starting point for +writing such a client. -- Ian Bicking - 08 Apr 2002
+(See Python Card Interface)
+PythonCard -- looks very interesting, and maybe it could provide an +alternative to a browser interface, in those cases where the browser +provides a less-than-optimal interface (e.g., data entry). Using +XmlRpc, maybe there's a way to make this sort of thing easy.
+-- Ian Bicking - 23 Jan 2002
+(See Webware Objects)
+Just finished reading MiddleKit docs. What are the chances of adding +a feature to MiddleKit that will read an XSD file, instead of CSV. +I'm more than willing to take the point on this ... since I'm bringing +it up, but I would like some help in some of the nitty-gritty details +of MiddleKit. It's new to me. Otherwise I'm just re-writing a whole +bunch of stuff that's already been done.
+Thanks -- Ray Leyva - 20 Jan 2002
+I know ambitious name, but it's an ambitious plan. Generate simple +reusable objects that know how to render, replicate, and store +themselves. I'm initially interested in creating objects that will +automagically tie themselves into a database once imported into a +framework, and immediately become available for data entry using +internally defined display / validation schemes
+This came from my initial, and now semi-abandoned attempts to create a +single interface WebwareReportKit.
+I have so far created a validation, rendering, and reporting scheme. +I'm starting work on the storage components right now. Don't want to +redo the MiddleKit stuff, but until I know where I'm going with the +general object layout I don't want to commit to any single OR mapping.
+Things to note, the items I've completed work with XML / XSL / XSD, +and AFAIK the current best crop of those tools live in the java world. +I don't have the time to be able to both build this infrastructure, +and help out on the PyXML stuff. Wish I did. I use XmlRpc to tie +them together, of course this could be SOAP / CORBA or whatever remote +invocation procedure you like to use.
+WebKit is the Application server that I'm basing the framework off of, +but I'm designing these objects with modularity in mind. IE : I don't +want to rule out using Java based App servers ... if push comes to +shove, and some client somewhere REALLY doesn't want to use Python / +Webware ;D
+Is this of interest to anyone. I'm trying to figure out the best way +to make this available ... I really want to give back to the +community.
+All this because I figured out that for me to be able to print the +stuff from WebwareReportKit I would need to have a semi-intelligent +data-entry component first ... duh! ;D
+PS : I'll get to that re-org sometime soon! :D +-- Ray Leyva - 19 Jan 2002
+I have been working on (and then reworking over and over again...) +some XML tools, imaginatively known as XMLTools, which attempt to +provide mappings from database schemas to XML "instances" through +descriptions of those instances, which are themselves highly +simplified documents with the same purpose as XML Schema documents +(which is what I presume you mean by XSD).
+The aim is to support XML instance validation, templating and storage +in various forms (such as in relational databases). An +object-relational mapping could be a consequence of this work, but +that doesn't interest me that much right now.
+Eventually, I want to bring XMLForms back with its core replaced by +XMLTools, so that the main purpose of XMLForms is the integration with +Webware (and possibly other Web frameworks).
+-- Paul Boddie - 02 Apr 2002
+This would be more of a general compendium of standard reports that +can be generated. For example:
+Invoices
Bill Of Materials ( BOM )
Legal documents
Real Estate Documents
Generic process mandated documents.
Heck .. tax forms!
This could be used in conjunction with WebWare, and a database to +generate data driven reports. It'd be a very useful component because +a great many applications require reporting functions.
+PS: Are we putting newest ideas at the top, and oldest at the bottom? +If so I'd like to ask for general permission to re-order the older +inputs to reflect that.
+-- Ray Leyva - 27 Nov 2001 <br />
+That certainly does sound useful! Have you looked at Biggles for +producing charts? ... fine with me about re-ordering -- Tavis Rudd +- 27 Nov 2001
+Sorry, moved away from python, and code based reporting tools +... moved towards XSL:FO solutions. -- Ray Leyva - 19 Jan 2002
+(See WebwareXManagement)
+the link point to a page about some meta discussion about the several +SomethingManagementSystems. The "X" stands for "something" and not a +well known display manager :-) -- Stephan Diehl - 22 Nov 2001
+(See Webware User Manager)
+A good user management system is essential for most of the ideas +listed below! Click on the link above for more notes and +discussion. -- Tavis Rudd - 20 Nov 2001
+(See Webware Document Management System)
+This might be the same than Webware Content Management System +and/or a Webware Knowledge Management System. More can be seen on +the topic page -- Stephan Diehl - 21 Nov 2001
+(See Webware Wiki)
+a wiki built with Webware. Wikis are wicked! -- Tavis Rudd - 30 +Oct 2001
+(See Webware CVS Manager)
+A WebKit/Webware implementation of the infamous viewcvs.cgi +script. WebKit's persistence and potential for caching could make a +huge improvement in responsiveness and allow sites like SourceForge to +consume less hardware for this feature.
+-- Chuck Esterbrook - 31 Oct 2001
+(See Webware DB Admin)
+A database management frontend. Like phpMyAdmin, but database +agnostic. phpMyAdmin is one of the most popular applications on +SourceForge. Someone said that "the application sells the platform", +well a high quality tool like this would be a huge selling point for +Webware. -- Tavis Rudd - 01 Nov 2001
+(See Webware Content Management System)
+A ContentManagementSystem built with Webware (Similar to Zope and +Macromedia's SiteSpring). Comprising:
+WebAdminInterface
WebwareCVSManager
WebwareTaskManager
SafeDelegation
WebwareSearchEngine
WebwareFolders (like ZopeFolders)
NOTE: this AppIdea is strongly interrelated with the +WebwareSourceforge AppIdea and WebwareXManagement.
+-- Tavis Rudd - 31 Oct 2001
+(See Webware Cofax)
+Cofax is the Java software that powers Knight-Ridder newspaper web +sites, like http://inq.philly.com/.
+(See Webware Sourceforge)
+a sourceforge/tigris like application built with Webware. -- +Tavis Rudd - 30 Oct 2001
+(See Webware List Archive)
+a geocrawler-like email archive viewer (that actually works!) -- +Tavis Rudd - 31 Oct 2001
+(See Webware Forum)
+a discussion forum component -- Tavis Rudd - 01 Nov 2001
+(See Python Web Stats)
+a modular web log analyzer with a webware front-end, that can also be +used programmatically or with other frontends. The existing Open +Source log analyzers suck! -- Tavis Rudd - 31 Oct 2001
+I have a totally (overly?) interface-agnostic web log analyzer that I +never polished with a nice frontend. If someone wants to try +something with this, contact me -- Ian Bicking - 02 Nov 2001
+(See Webware Slash)
+a slashdot news site that sould let users upgrade from a common news +app such as PHPNuke. The existing PHP sites get slow under load and +the code is getting pretty complex and ugly. A python update will get +attract frustrated PHP webmasters -- Aaron Held - 2 Nov 2001
+(See Webware Vaults)
+a Parnassus-like site, not just using WebKit's base features, but also +using some of the other kits and extensions -- Paul Boddie - 06 Nov +2001
+(See Webware Groupware)
+Looking to create a Webware based Groupware application. It should +include email, and calendaring functions. There's a million of them +out there, but not for Webware ;D
+With this as a starting point I would like to eventually get to +something like a WyattERP (it's hosted on SourceForge), except ASP +and Python/WebWare based.
+ +-- Ray Leyva - 17 Nov 2001 <br />
+Facilitating non-browser interfaces -- Ian Bicking
+Why is there the avoidance of discussion on commercial implications of Wiki and related material? An example would be for how we might implement it our (ASP) ecommerce software http://www.veracart.com +please inform me if such a discussion is taking place already: y@mytechsupport.com
+This pages links to recommendations and notes about how you should set +up your application:
+SitePage -- How to create a master template for your site.
Directory Structure -- Notes on how to create a robust directory +structure for your site.
ModelViewController -- A paradigm for keeping content, design, and +code separate.
Web Style Guidelines -- Links and notes related to good hypertext +style.
Managing The URI Space -- Creating persitent, meaningful resource +names.
Model Two plus One -- An example of using the Logic / View + +Actions design
See also: Webware Recipes
+Webware's core is its "Application Server", which is known as "WebKit".
+See http://webopaedia.com/TERM/a/application_server.html for more +notes on application servers.
+Several people have suggested that the name "WebKit" should be +scrapped when Webware is transitioned to a DistUtils based package +architecture. ChuckEsterbrook likes the name and has rejected these +suggestions so far, but the discussion isn't over yet. My argument is +that it is better to position and describe Webware as 'A +Python-Powered Application Server', that comes with a suite of useful +components, than saying that "Webware for Python is a suite of +software components for developing object-oriented, web-based +applications." The former is clearer and more direct. The latter is +ambiguous and needs clarification. The name "WebKit" and the latter +description imply that the AppServer is just another component of +Webware. This is misleading: the AppServer is the heart of Webware.
+Using the name WebKit also makes the module/package structure and path +management issues more complex than they should be.
+-- Tavis Rudd - 29 Oct 2001
+I wouldn't mind seeing other kits which handle other forms of +communication. What about MailKit, for example? One could imagine the +nature of requests and responses to be quite different in that kind of +environment.
+-- PaulBoddie - 30 Oct 2001
+Such 'kits' could still be created if the name 'WebKit' was removed. +A sensible package layout would be something like this:
+Webware [package]
+MultiPortServer [module]
AppServer [module]
Application [module]
Transaction [module]
Servlet [module]
Request [module]
Response [module]
Session [module]
SessionStore [module]
Cookie [module]
WebUtils [sub-package]
MiscUtils [sub-package]
MiddleKit [package]
UserKit [package]
FunFormKit [package]
Cheetah [package]
XMLForms [package]
other 'kits' and related packages ...
-- Tavis Rudd - 30 Oct 2001
+I brought this up again on the list a few weeks ago and Chuck said his +final answer to my request to get rid of the name 'WebKit' was no. +I'll agree to disagree.
+-- Tavis Rudd - 23 Nov 2001
+Doing "View source" from the browser doesn't work if your page was +produced in response to a POST, since your browser may not re-POST +the form variables (mozilla doesn't, at least).
Debugging by hand is tedious, and if posible should be avoided by +using HTML validators.
You can't use online web validators (which validate a given URL) in +the context of a web application, because your web application often +requires a session id, which the validator obviously can't send to +your server
This approach uses Web Design Group's HTML validator, which can be +installed on your local machine (available at +http://www.htmlhelp.com/tools/validator/ ).
+You hook it up to your servlets so that the HTML output your servlet +produces is automatically validated, and the validation output and +full HTML source (with highlighted errors) is appended to the normal +output and sent to the browser.
+Install the WDG offline-validator. In particular, you need the +"validate" script which can be called from the command line. Debian +users: apt-get install wdg-html-validator
Apply the patch at +http://www.opensky.ca/~jdhildeb/webware_validator_streambuffer.patch +to webware. This makes it possible to get the accumulated HTML in +the response object from within your servlets, before the response +has been committed.
Add the validateHTML method (available at +http://www.opensky.ca/~jdhildeb/webwarevalidator.py ) to your +SitePage, and call it at the end of writeBodyParts (you'll have to +override Page.writeBodyParts() if you haven't already done +so). validateHTML won't get all the HTML produced by your servlet if +you call response.flush() explicitly, so don't do that. :)
Enjoy!
+-- JasonHildebrand - 20 Sep 2002
+From the 0.6 Install Guide:
+++The most common installation problem is a Python exception +appearing in your browser that says "bad marshal data". This is +always caused by pointing the web browser to the app server:
+http://localhost:8086/WebKit.cgi/
+But the app server hides behind a web server so the correct URL +would be:
+ +That requires that web server and app server are set up to talk to +each other.
+
See InstallGuide.html in Webware/WebKit/Docs for full details, or +you can read the one for the last production release at:
+http://webware.sourceforge.net/Webware/WebKit/Docs/InstallGuide.html
+-- ChuckEsterbrook - 30 Nov 2001
+A packaged version of Webware that comes with the latest versions of +other tools that are commonly used with Webware.
+Examples of these tools are Cheetah, FunFormKit, XmlForms, and +Python database-adaptor modules such as psycopg.
+-- TavisRudd - 31 Oct 2001
+Create a BatteriesIncluded distribution of Webware that comes with +other related tools, such as Cheetah, FunFormKit, XmlForms, the +database adaptors, etc.
+TavisRudd
+TavisRudd
Packaging/installation system using distutils has been created as part +of WebwareExpRefactoring.
+There are several tools that are designed for, or complement, Webware +that are not currently distributed with it and must be downloaded and +installed separately. As a result Webware installations take longer +than they should.
+Uses Python's DistUtils to create a meta-distribution that gathers all +the most useful Webware tools in one place and installs them in one +operation.
+incompatible license problems
overkill
Webware 0.7
+Contents
+Webware and all the core components
Cheetah
FunFormKit
Database modules like psycopg
a plotting module like Biggles (http://biggles.sf.net) for +quickly adding charts to websites
example sites
Implementation
+use a master DistUtils setup.py script to control the whole +installation process so that all the components can installed in one +fell swoop. "python setup.py install" will install Webware, while +"python setup.py install-batteries" will install the third-party +packages.
the end-user will control what will and won't be installed out of +all the included stuff using the setup.cfg
make Webware's setup.py compile and install mod_webkit or the iis +version (when complete). This could be controlled via the setup.cfg +file.
Add your thoughts and comments here, along with your sig -- +TavisRudd - 31 Oct 2001
+empty
+(This not exactly a Webware issue, but you may experience this in the +course of using Webware.)
+On a LAN machine, I set my HTTP server to run on port 79. I could +browse it with http://foo:79/ using the lynx, konqueror and opera +browsers, but mozilla and galeon gave me this mysterious message:
+++Access to the port number given has been disabled for security +reason.
+
I posted this to netscape.public.mozilla.general and found out that +this behavior is in response to the "cross-protocol scripting" +vulnerability described at http://www.kb.cert.org/vuls/id/476267.
+I also learned that you can override the banned ports by putting a +preference in the all.js script of your mozilla site installation:
+pref("network.security.ports.banned.override", "79");
+My all.js was found at /usr/lib/mozilla/defaults/pref/all.js. I +located it with rpm -q mozilla -l | grep all.js.
+Credit: Thanks to the folks at netscape.public.mozilla.general for +their help. For details, see the thread starting on 2001-12-26, +"Access to the port number given has been disabled for security +reasons".
+-- ChuckEsterbrook - 27 Dec 2001
+Which ports are banned? All below 1024 except 80?
+Some might argue that building an opt-in mailing list is not e-commerce, +because it is not directly involved in the "transactional" commerce. +However, anyone who has been marketing online for more than a month +knows that the gold is in the list -- you will continue to struggle +selling online until you build a list, or become Yahoo!
+So how do you start, and what does it have to do with Webware?
+who are you selling to
what do they want
what makes you different from your competitors
Second, you will need an followup autoresponder service You can program the web part of +this is python/webware, but unless you want to become an expert in handling spam +complaints and staying off blacklists, you might want to leave the email broadcasting +to someone else. YMMV.
+relevant and specific to your offer
valuable to the prospect
easy for you to provide (low cost in money and time)
The plan is to offer the bribe as a motivation to get +the suspect (e.g. fist time visitor) to opt-in. Once +the suspect opts-in, he becomes a prospect, and you can +begin to follow up with him until he knows, likes, and +trusts you enough to buy when the time is right.
+What does this have to do with Webware?
+The ability to generate dynamic pages allows you to create +a website that captures the opt-in aggressively without +being annoying.
+You can control when exit popups appear.
You can test multiple offers and easily +track where the visitor came from.
You can offer a repeat visitor something +he hasn't seen before.
If someone visits your website, and leaves without opting in +it would be nice to know why. An exit popup survey is the +perfect tool.
+Before you go hollering, let me say that I work primarily in +B2B markets, and my target market is not too terribly annoyed +by popups. In a B2C market, you'll need to test it.
+What about popup blockers? Well, there is not a lot we can +do about them. But even if 90% of your popups get blocked, +that's 10% that got through, and 10% of your abandonment +traffic got a chance to tell you why they left. That's better +than nothing.
+If your visitor is coming from a Google AdWords Ad, you need +to supress the popup. That's pretty easy with Webware.
++++
+- if "google" not in referrer:
+- +
# include popup code
+
Ok, now the part about not being annoying.
+Suppose someone visits your site, then leaves and gets the popup. +He answers the question, and clicks ok.
+Later, he comes back, pokes about, then leaves again. Are you +going to show him the same popup? No.
+What if he just closed the popup the first time he left? Are you +going to annoy him again by showing it again? Probably not.
+How do you do this? Set a cookie (about 90 days) the first time +the popup code gets loaded. When he comes back, you know from +the cookie that he has already seen the popup, so you suppress it +just like you did for Google.
+If you have flash audio clips that play automatically, you'll want +to use the same technique to say that they don't play automatically +when the page is revisited.
+In order to make your marketing messages most effective, you need +to continually measure, change, and measure again.
+The most significant Response Modifier is "traffic source." It's +easy to set up different pages for different traffic sources if +you have control of the link -- you can just link to a different +page in the site. (But in terms of concentrating page rank, you +may not want to do this all of the time.) If you don't have control +of the link, you'll just need to trust the http-referrer header.
+In any case, you'll want to track each traffic source, measure +how many "hits" vs. "actions" you get (that's your conversion rate) +and possibly create different offers for each.
+Another important Response Modifier is the headline. How do you +test a headline?
+When someone first visits your page, you randomly select one of +two (or more) headlines, and include that in the page. You also +set a cookie (about 30 days) so he sees the same headline when +he comes back or refreshes the page. When the visitor finally +does take action (opt-in or purchase), you record that as an +action for that particular headline. The first version to get +40 actions is the winner and becomes the new control.
+Use the same pattern to test as many other response modifiers +as you want. (BTW: you should never stop testing.)
+Caveat: This part is just a theory. I have not tested it, and I +don't know whether it works or not. After I do test it and get +significant results, I'll come back and update this paragraph.
+Suppose you have a dozen different sites that are related. Each +site has a different ethical bribe, a different offer, and a +different autoresponder sequence. Because the sites are related, +someone who opts-in at one site is likely to be interested in +the other sites and offers.
+Suppose someone visits site A, opts-in, and downloads the ebook. +The next time he visits site A, it may make sense to cross-promote +site B. You can do that because you know he has already opted-in +on A, so including the same offer as before is just wasting +screen real estate. And if he goes to site B after opting-in +on A and B, you can make offer C. Similarly if he keeps ignoring +offer B, stop showing it to him.
+If you would like more information about testing and tracking, +or about building pages that compell the opt-in, you can join +my shy-yes list.
+-- TerrelShumway 10 July 2004
+People often ask 'Is Webware ready for prime-time use?' or 'Can +Webware really handle it?' The answer is yes, but this is the wrong +question.
+A better question would be 'can Webware help me build an application +that can reliably handle heavy loads?' Webware is not an application, +methodology, or recipe for success. It is a development framework and +a collection of useful tools. If you use the framework and tools +intelligently, and design your application well, then yes it will help +you build an application that can reliably handle heavy loads. If you +use them poorly, or botch your application design, don't cross your +fingers.
+An even better question is 'does Webware's architecture encourage and +facilitate effective application designs?' I believe it does, for the +following reasons:
+it uses Python: a well designed, modular, object-oriented, easy to +learn, and easy to maintain language
it is thoroughly object-oriented and encourages object-oriented +applications, unlike other frameworks like PHP and ColdFusion, which +tend to spawn a maze of spaghetti code and include files.
it is fairly simple to understand and very fast to develop with once +you've groked it. This encourages rapid prototyping and testing, +and helps weed out bad designs early on. Webware is a great fit for +software engineering methodologies such as eXtreme Programming +and Agile methods.
it doesn't dictate how you handle data storage and object +persistence.
it is extremely portable, allowing you great flexibility in your +choice of operating systems and other tools.
the Python and Webware user community is supportive, intelligent, +and experienced. If you run into technical or conceptual hurdles, +ask some smart questions and you'll get some real gems of clear +thought in response.
objects (servlets, sessions, data caches) persist in memory between +requests. This can lead to huge performance gains over other +frameworks without in-memory object persistence.
it is extensible by design.
Drop by the Webware Propaganda for some more info.
+Keep in mind that Webware is only one part of your toolkit. +Non-trivial applications will rely on many other resources. These +might include server and network hardware; operating system(s); +persistence mechanisms, such as filesystems and databases; web +servers(s); web caches, such as squid; external credit card +processors; shipping processors; and, of course, web browsers. The +performance and reliability of your application will depend on how you +utilize these resources in conjunction with Webware.
+Here's some notes on Using Webware Effectively.
+-- TavisRudd - 30 Mar 2002
+Have a look at some recent Webware benchmarks.
+-- ChrisZwerschke - 18 Apr 2010
+Chad Walstrom is a long-time Linux and Python enthusiast. He is also a Debian +Developer, maintaining packages such as pnm2ppa, cheetah, clamsmtp, and +GNU GNATS (gnats).
+Cheetah is a Python-powered template engine and code generator. It +can be used as a standalone utility or it can be combined with other +tools. Cheetah has many potential uses, but web developers looking +for a viable alternative to ASP, JSP, PHP and PSP are expected to be +its principle user group.
+Cheetah:
+generates HTML, SGML, XML, SQL, Postscript, form email, LaTeX, +or any other text-based format.
cleanly separates content, graphic design, and program code. This +leads to highly modular, flexible, and reusable site architectures; +faster development time; and HTML and program code that is easier to +understand and maintain. It is particularly well suited for team +efforts.
blends the power and flexibility of Python with a simple template +language that non-programmers can understand.
gives template writers full access to any Python data structure, +module, function, object, or method in their templates.
makes code reuse easy by providing an object-orientated interface to +templates that is accessible from Python code or other Cheetah +templates. One template can subclass another and selectively +reimplement sections of it.
provides a simple, yet powerful, caching mechanism that can +dramatically improve the performance of a dynamic website.
compiles templates into optimized, yet readable, Python code.
Cheetah integrates tightly with Webware for Python +(http://webwareforpython.org): a Python-powered application server and +persistent servlet framework. Webware provides automatic session, +cookie, and user management and can be used with almost any +operating-system, web server, or database. Through Python, it works +with XML, SOAP, XML-RPC, CORBA, COM, DCOM, LDAP, IMAP, POP3, FTP, SSL, +etc.. Python supports structured exception handling, threading, object +serialization, unicode, string internationalization, advanced +cryptography, and more. It can also be extended with code and +libraries written in C, C++, Java and other languages.
+Like Python, Cheetah and Webware are Open Source Software and are +supported by active user communities. Together, they are a powerful +and elegant framework for building dynamic web sites.
+Like its namesake, Cheetah is fast, flexible and powerful.
+-- TavisRudd - 29 Oct 2001
+Is maintaining the project since 2007.
+Please use our mailing lists for any questions, suggestions or contributions.
+I founded the Webware project in the spring of 2000. For a long time, I managed the +project, wrote code, applied patches, made design choices, wrote +documentation, cut releases and answered questions on the discussion +list. Of course, other team members were doing similar things.
+In 2007, I handed project management and leadership to the capable hands of Christoph Zwerschke so that I could focus more on my next project, The Cobra Programming Language.
+I'm still the primary maintainer for MiddleKit. If you need help in that area, I'll probably respond to your webware-discuss message. If I don't, track me down.
+My home page is at http://ChuckEsterbrook.com.
+The Webware home page is at http://webware.sourceforge.net.
+My latest project is The Cobra Programming language, found at http://cobra-language.com/
+This is the place to document all known compabilities and +incompatibilities between Webware, Python, and third-party +modules. The information here supplements official documentation, and +is meant to provide information on a more frequently updated basis +than the documentation revision cycle may allow.
+| + | Python 1.5 |
+Python 2.0 |
+Python 2.1 |
+Python 2.2 |
+
|---|---|---|---|---|
Webware 0.7 |
+no |
+ok |
+ok |
+ok |
+
Cheetah 0.9.12 |
+ok |
+ok |
+ok |
+ok |
+
FunFormKit 0.3.2 |
+ok |
+ok |
+ok |
+ok |
+
-- EdmundLian - 11 April 2002
+Contents
+ +Homepage: wiki.webwareforpython.org/component.html
Download: webwareforpython.org/downloads/Component/Component-0.2.tar.gz
Source repository: svn.webwareforpython.org/Component/trunk
Discussion: webware-discuss@lists.sourceforge.net
The repository is a Subversion repository. To check out a copy:
+$ svn co http://svn.webwareforpython.org/Component/trunk Component+
FreeBSD ports
+cd /usr/ports/www/py-webware-component && make install clean
+Component is licensed under an MIT-style license. This gives you +permission to most anything you want with Component.
+Component is a toolkit for Webware For Python. Webware is the only +prerequisite.
+Many enhancements in Webware require subclassing WebKit.Page. But +each of these enhancements is incompatible with the others, because +you have to choose an specific inheritance hierarchy to use them. You +can't subclass SecurePage and SidebarPage and MVCPage all +at once.
+One solution is Mixins, using multiple inheritance to add various +functions. This technique is available in Webware through +MiscUtils.MixIn. However, there are many places where Mixins are +mutually incompatible -- for instance, code often has to intercept the +awake() method, but delegating to each Mixin requires that they +all know about each other.
+Components are essentially a way of creating Mixins that all know +about each other.
+Components are useful if you have code that should know about the +request and response, and possible intercept it. If the code doesn't +need to know about the request at all, you should just create a normal +Python library, and call into it explicitly. Components can also be a +convenient way to add new convenience methods to your servlet.
+In order to use components, all your servlets must inherit from +Component.CPage. This subclass of WebKit.Page adds some new +convenient methods, but mostly just adds component support.
+New methods:
+Called automatically in writeBodyParts. If you are not using +a template, this can be used to write the HTML that goes at the +top of every page (the text written goes just after <body>).
+writeHeader compliment.
+CPage adds the idea of views. The default view is +writeContent -- that's the innermost method that is called. +This can be used with actions so that the action method performs +some work, then changes the view to display a different body. +This way actions occur before content display, but can effect +later display without losing the normal Python templating +mechanism (writeHead, etc). Templating components (like +ZPTKit) may use this concept of view to indicate a template to +use.
+Returns the view, as a string.
+For error responses. This can be used like:
+def awake(self, trans):
+ CPage.awake(self, trans)
+ try:
+ self.id = int(
+ self.request().extraURLPath().split('/')[1])
+ except (ValueError, IndexError):
+ self.sendResponseAndEnd(500)
+A simple error body is created if you don't pass one in.
+preAction and postAction are modified to do nothing (in +WebKit.Page they output a little HTML).
+CPage looks for any components you have in the .components +class variable (which should be a list). One very simple component is +included, NotifyComponent. As an example you can use this +component like:
+from Component import CPage +from Component.notify import NotifyComponent + +class SitePage(CPage): + + components = [NotifyComponent()] + + def writeHeader(self): + self.writeMessages()+
If the component was configured, it would probably take arguments to +its constructor (this simple component has no configuration). A +component can add new methods to the servlet. In this case it adds a +.message(text) method, which adds your message to the session, and +a .writeMessages() method which writes the messages and clears them +from the session. (Also a .messageText() method if you don't want +to write the messages immediately.)
+It's up to the component to add methods or attributes to the servlet. +It can also respond to "events" -- one example (and one of the only +current events) is the awake event, where it could intercept the +request. An example of this is in LoginKit, where the component +checks the servlet for permission settings, and may abort the +transaction with a login screen (or a Forbidden response) during +awake().
+Read the source -- there are many docstrings in Component.py.
+About this trick:
++++
+- def compute():
+- +
# ...
+data = compute()
+
I think I succeeded in applying this. +But, +Where/How can I check that the socalled 'data' variable is imported/computed only once ?
+P-Y Delens
+On Thu, 2002-04-25 at 07:47, Geoffrey Talvola wrote: +> Already partially done. Look at WebKit/Docs/Application.configlist and +> WebKit/Docs/AppServer.configlist. These files are used to auto-generate the +> documentation sections of the user's guide using +> WebKit/DocSupport/config.py.+
OnceAndOnlyOnce suggests that the foo.configlist should come from +the same place as defaultConfig.
+class ConfigurationOption:
+ def __init__(self,name,default,help):
+ ...
+
+class ConfigurationSchema:
+ def __init__(self,namespace,version,options):
+ ...
+
+ def default(self,key):
+ return self._optionMap[key].default
+
+ def defaults(self):
+ config = {}
+ for opt in self._options:
+ config[opt.name] = opt.default
+ ...
+
+class Configureable:
+ #configSchema() supercedes defaultConfig()
+ #config() uses the schema
+ #setting() can (optionally) warn if a key is requested that
+ #is not in the schema
+
+ def loadConfig(self,file): # factor out from userConfig
+ """
+ can (optionally) warn if there are keys present in the
+ file that are not in the schema (e.g. a setting gets
+ refactored or removed, user upgrades and cannot figure
+ out why his setting is now ignored.)
+ """
+# Application.py
+
+class Application(...):
+
+ _configSchema = ConfigSchema( "Application", "0.7",
+ [
+ ConfigOption("PrintConfigAtStartup", 1, "does what it says"),
+ ConfigOption("AdminPassword","webware", "blah blah blah"),
+ ...])
+
+ def configSchema(self):
+ return self._configSchema
+documentation and code are less likely to get out of sync
developers are more likely to provide documentation consistently
when schema changes, things are less likely to break without giving a clue as to what the problem is
config files can be upgraded automatically (if versioning is implemented)
an introspective tool can be written for configuring webware components/applications
if setting names or default values change, they change in one place.
future proofing:
+In Application, there are a dozen settings that deal with error +handling, half of a dozen that deal with sessions, and a few that are +already dictionaries with only an informal internal structure.
+It would be nice to be able to group these into their own schema with +a separate namespace.
+"AdminPassword" is in Application.config. If the current Admin +context were to get replaced (or if the "ErrorEmail" stuff were to get +refactored), it would be nice to be able to break these out into +another config file.
+it would be nice to be able to automatically upgrade .config files, +either in an =upgrade.py= script, or as the file is read into +memmory. This could accommodate name changes, namespace changes,
+an option could be deprecated by adding a deprecated attribute to the +ConfigOption.
+an option could be marked as experimental in the same way.
+a reserved "ConfigSchema" key/namespace to be included in all +configuration files:
+# foo.config
+{
+ "ConfigSchema": {
+ "Namespace": "Foo",
+ "Version": "0.0.1",
+ "Owner": "FooKit",
+ }
+
+ ...
+
+}
+this could also extend to "inline" namespaces such as "ErrorControl" +or "SessionControl": if a dictionary has a "ConfigSchema" key, it can +be versioned and handled by the config tool and treated as a +"namespace".
+If a config file does not have a "ConfigSchema" key, it is treated as +a legacy config with a mild suggestion that the user update the file +to match current conventions.
+e.g. current code does: +application.setting("Debug")["Sessions"]. New code might say +application.setting("ErrorControl")["EmailHost"] ... but this +would get out of control quickly. Maybe Configurable could be made +to handle "Debug.Sessions" or "ErrorControl.EmailHost"
+Maybe Configurable should become a delegate instead of a +superclass, then a refactored ErrorManager, could look up +setting("EmailHost") which might delegate to +application.setting("EmailHost",namespace="ErrorControl") or +even:
+import config
+host = config.setting("EmailHost",namespace=("Application","ErrorControl"))
+...
+OK. Lets calm down and take it one step at a time.
+---++ Blue Sky
+if ConfigOption could be related somehow to optik.Option, +settings could be overridden on the startup command +line. (OnceAndOnlyOnce again)
+A simple type system could eliminate the need to look at the name for +"Filename" or "Dir" in ConfigurableForServerSidePath. Use +option.type == TYPE_SERVER_PATH as the predicate.
+-- TerrelShumway - 25 Apr 2002
+Since tabs (at least at the moment) are a reality in Webware, perhaps +we can collect some practical tips for coping with them. It probably +makes sense to collect these by editor.
+-- JasonHildebrand - 27 Sep 2002
+?? why can't we use spaces? I use spaces for my Python coding using +Vim, and I use WebKit. I don't seem to have any +trouble.. -JamesBecker
+You can use spaces in your own Webware-based application -- no problem. +But if you need to modify Webware itself, you'll run into trouble if you +mix tabs and spaces.
+-- JasonHildebrand - 15 Apr 2004
+To teach Vim to switch to "noexpandtabs" mode when editing a file in +any subdirectory of "Webware", add these lines to your ~/.vimrc
+autocmd BufEnter */Webware/* set noexpandtab +autocmd BufLeave */Webware/* set expandtab+
Converting python files from tabs to spaces is a snap.
+> cd ~/dev/Webware +> for file in `find . -name "*.py"` +do + sed -e "s/^V<tab>/ /g" $file > $file.new # hit ctrl-v and the tab key after the first slash + mv $file.new $file +done+
--VictorNg April 15, 2004
+The GNU Coreutils contains the utilties expand and unexpand convert tabs to spaces and vice-versa. A tab is considered to align at specific columns, by default every eight columns or four columns with option -t4, so the specific number of spaces that correspond to a tab depends on where those spaces or tab occur. Unless you specify the -a option, unexpand will only entab the initial whitespace (which goes along with the Webware styleguides).
+--ChrisZwerschke July 26, 2005
+The Editor/Mini-IDE "DrPython" supports converting from tabs to spaces and vice versa via the menu entries "Edit - Whitespace - Set indentation to tabs..." and "Edit - Whitespace - Set indentation to spaces..."
+--ChrisZwerschke July 26, 2005
+This page is about accepting, storing and processing credit cards.
+A brief, high level overview of accepting payments on-line, with a focus on credit cards is at http://selfpromotion.com/credit.t
+-- ChuckEsterbrook - 18 Mar 2002
+This information comes courtesy of Jeff Johnson:
+For starters, you'll likely store them in a SQL database. You'll want +to encrypt them for safekeeping, but might also choose to store the +last 4 digits as plain text so that customer service reps can tell a +customer which card they used (without seeing the whole number).
+You can encrypt them using either M2Crypto or the Python rotor +library.
+The rotor module implements an encryption algorithm used in the second +world war. Beware that data encrypted with this algorithm are easily +cracked!
+--AlbertBrandl
+Here's an interesting link about credit card handling, including the +pitfalls of not retaining card information: +http://www.arsdigita.com/books/panda/ecommerce#credit_cards
+-- PaulBoddie - 06 Mar 2002
+Here's what Jeff Johnson said:
+I store them encrypted using the rotor library, I might use M2Crypto if I +were to do it again but so far rotor is fine. I also store the last 4 +digits as plain text so CSRs can tell the customer which card they used +without seeing the whole number. Then, I store the hash so we can search +the database and compare numbers without passing the actual number. Here's +my code, altered a little for security reasons:
+def decryptCreditCard(self,number):
+ r = rotor.newrotor(???,???)
+ number = binascii.a2b_hex(number)
+ number = r.decrypt(number)
+ number = re.sub(r"\D","",number)
+ return number
+
+def encryptCreditCard(self,number):
+ number = re.sub(r"\D","",number)
+ r = rotor.newrotor(???,???)
+ number = r.encrypt(number)
+ number = binascii.b2a_hex(number)
+ return number
+
+def creditCardShaHex(self,number):
+ number = re.sub(r"\D","",number)
+ return binascii.b2a_hex(sha.new(number).digest())
+
+def creditCardFromNumber(self,number):
+ number = re.sub(r"\D","",number)
+ numberShaHex = self.creditCardShaHex(number)
+ sql = " select creditCardID,number,expiration,numberShaHex,numberPart from CreditCard where numberShaHex = '%s' " % numberShaHex
+ rs = self.recordset("DEG",sql)
+ if rs:
+ return rs[0]
+ else:
+ return None
+
+def creditCardFromID(self,creditCardID):
+ sql = " select creditCardID,number,expiration,numberShaHex,numberPart from CreditCard where creditCardID = %s " % creditCardID
+ rs = self.recordset("DEG",sql)
+ if rs:
+ return rs[0]
+ else:
+ return None
+
+def insertCreditCard(self,number,expiration):
+ # Get rid of non-digits (\D).
+ number = re.sub(r"\D","",number)
+ expiration = re.sub(r"\D","",expiration)
+
+ # Encrypt the number before storing it.
+ numberRotor = self.encryptCreditCard(number,expiration)
+ # To find a credit card in the database, get the sha hex value, then query the database.
+ numberShaHex = self.creditCardShaHex(number)
+ # If a customer wants to know which card they used, read the last 4 digits back to them.
+ numberPart = number[-4:]
+
+ sql = """ select nextval('CreditCardSeq') as creditCardID """
+ rs = self.recordset("DEG",sql)
+ creditCardID = rs[0].creditCardID
+
+ sql = """
+ insert into CreditCard
+ (creditCardID,number,expiration,numberShaHex,numberPart)
+ values
+ (%(creditCardID)d,'%(numberRotor)s','%(expiration)s','%(numberShaHex)s','%(numberPart)s')
+ """ % locals()
+ rs = self.recordset("DEG",sql)
+ return creditCardID
+You'll need SSL for encryption. You can run Apache 1.3.x + mod_ssl and +get SSL certs from a variety of +companies. http://google.com/search?q=buy+ssl+certificates
+You can use stunnel to talk SSL to the credit card company.
+-- ChuckEsterbrook - 04 Mar 2002
+Eventually, you are going to need persistent objects, or just some way +to store and retrieve data. The approach you use depends on many +factors, such as:
+Do you have a lot of data to store?
Must you work with an existing database schema, or can you start from scratch with a new schema?
Do you need/want to have a traditional relational model so that you or your users can use standard report generating tools?
Do you need close control over the database schema for purposes of optimization?
If you must use an RDBMS, does it have object-relational features (e.g., inheritance) like say, PostgreSQL?
Do you need support for transactions to commit changes to one or more objects/data fields atomically?
Do you care about ACID compliance (because your data and/or transactions are mission critical)?
Do you care about closed source vs. open source?
Must you support ad-hoc online queries, report generation, hot database backups, and heavy multi-user or transaction intensive use?
How much do you want to spend?
The following is a list of possible solutions:
+Use an object-oriented database or object store like ZODB
Use an RDBMS and the Webware MiddleKit object-relational mapper
Use an RDBMS and some other object-relational mapper. Possibilities include: +* PyDO +* Database Objects (dbObj) +* SQLObject +* Modeling
Forget about object-relational mapping--use the Python DB API and an RDBMS directly
Use DBUtils on top of the Python DB API
Use Python's pickle and shelve modules
Roll your own system with a flat file
dbObj
Python's pickle and shelve modules
Informix (proprietary)
InterBase (open source)
MySQL (open source)
Oracle (proprietary)
PostgreSQL (open source)
SAP DB (now open source, based on ADABAS code)
SQL Server (proprietary)
Sybase (proprietary)
Database Debunkings is a great place to look for intelligent +discussion about database issues by people like C.J. Date and Fabian +Pascal.
searchDatabase.com seems to be another great site to start +exploring the literature on databases.
When will they ever learn is a critique (by Fabian Pascal) of an +exchange on whether MySQL and Innobase are DBMSs, or even +relational. C.J. Date makes an appearance, and you get to hear the +horses speak directly.
Linux RDBMS Library Compilation of free readings on Linux +relational databases.
What exactly is a relational database? C.J. Date explains in A +Closer Look at Relational Database. This site requires free +registration.
Fabian Pascal explains why SQL sucks and why all SQL DBMS violate +important relational features, some more than others in Little +Relationship to Relational.
Think OODBMS are the bee's-knees? Better read this first.
-- EdmundLian - 30 Dec 2001
+DBUtils is a suite of tools providing solid, persistent and pooled connections +to a database that can be used in all kinds of multi-threaded environments +like Webware for Python or other web application servers. The suite supports +DB-API 2 compliant database interfaces and the classic PyGreSQL interface.
+The current version is available for download here and at
+ +The documentation is also contained in the download package.
+Here are some guidelines for Webware Developers:
+If your change is non-backward-compatible or involves design +considerations, please discuss it on the webware-devel list before +checking it in (and preferably before implementing it!). Your ideas +are probably good, but someone else on the list may come up with +better ones!
+Following Python's development model, Webware also has a BDFL. Chuck +Esterbrook is Webware's BDFL. As such, he has final say over Webware +design issues and if he makes a pronouncement, please obey it.
+Jay and Geoff can also make pronouncements if Chuck isn't available or +if they are better qualified to decide on whatever issue is at hand. +But pronouncements from Jay or Geoff are overruled by a pronouncement +from the BDFL.
+Follow the Webware Style Guidelines. +http://www.webwareforpython.org/Docs/StyleGuidelines.html
+Please test before checking in changes. If the component you are +modifying has a test suite, please add a test case to the test suite +if appropriate.
+Please update the Release Notes. If a new feature has been added, +please update the User's Guide. If a new setting has been added, +please update the Configuration Guide.
+If you agree to abide by the rules listed above and you want SVN write +access, send an email to Chuck Esterbrook and Geoff Talvola. You +should let them know what it is you plan to do with SVN -- fix bugs, +improve the docs, add new features, write a regression test suite, or +whatever. Also, if you can show evidence of one or more of these:
+Previous history of writing quality Webware patches
Previous history of writing quality 3rd-party extensions to Webware
Previous history of writing quality open-source Python code
Or any other evidence that you'll be a good addition to the Webware team
Then Chuck or Geoff will probably give you write access.
+-- GeoffTalvola - 06 Jun 2002
+There are two aspects to DirectoryStructure: client-side (URI) and +server-side (file-system, database, etc.).
+This article by Tim Berners-Lee +http://www.w3.org/Provider/Style/URI.html is a great start if you're +trying to decide on a URI directory structure for your site (thanks to +IanBicking and KendallClark for posting about it)!
+-- TavisRudd - 16 Nov 2001
+At 10:39 AM 10/27/2001 +0200, Georg Balmer wrote: +>I have 2 basic questions +> +>--[1]-------------------------------------------- +> +>How do you layout the directory structure ? +[snip] +>or case B: +> +>someDirectory/Webware/WebKit/ +> ... +> +>someOtherDirectory/appOne/pages/ +>someOtherDirectory/appOne/lib/+
Case B. Mingling your projects with Webware is a bad idea. It makes it +harder to upgrade Webware and confuses people into thinking they have +to modify Webware to make Webware apps.
+One notable exception is when you bake your own plug-ins. Those can be +convenient to drop into Webware/ because then they get picked up +automatically. However, there are 2 settings related to this in +WebKit/Configs/AppServer.config which allows you to put your +plug-ins outside of Webware.
+Just for clarification: If you are building a Webware site, you are +probably NOT creating a plug-in.
+---
+I recommend using MakeAppWorkDir.py to create a working directory +for your application separate from your Webware directory.
+-- GeoffTalvola - 29 Oct 2001
+The term "distutils" refers to the "Python Distribution Utilities". +Since their introduction in 2000 they have become the standard way to +package, distribute and install Python modules and applications.
+See http://python.sourceforge.net/devel-docs/dist/dist.html for more +information.
+The current version of Webware does not use distutils. The transition +to a distutils architecture will hopefully be made by Webware 0.8.
+users don't need to maintain a local copy of all the Webware modules.
the core modules can be used easily by other frameworks
it can be packaged for Debian and other OS distros.
installs on Windows now make use of the GUI installer.
distutils offers many advanced packaging facilities!
distutils is the Pythonic way to do installs.
-- TavisRudd - 29 Oct 2001
+A style of Wiki writing that presents a synopsis, or synthesis, of a +discussion. The synopsis is owned by all of the participants of that +discussion, although it might be moderated. See +http://www.usemod.com/cgi-bin/mb.pl?DocumentMode for a fuller +explanation. It is complemented by ThreadMode, which is better for +freeform discussions.
+-- TavisRudd - 23 Nov 2001
+Price points are extremely important in business as their selection +can make a huge difference in gross revenue.
+To illustrate the importance of price point, consider a product which +might be priced between $5 and $20. The number of customers that +purchase at these different prices will change for different reasons:
+$/month |
+% that buy |
+score |
+comment |
+
|---|---|---|---|
$5 |
+5% |
+25 |
++ |
$10 |
+7% |
+70 |
+higher price sometimes yields higher +perceived value by customer |
+
$15 |
+6% |
+90 |
+higher price eventually drives +customers away |
+
$20 |
+3% |
+60 |
+higher price eventually causes many +customers to balk |
+
So $15 is head and shoulders above the rest: more that 29% higher +grossing than the second most profitable price point.
+Selecting a price point is done by almost all major businesses that +have goods and services to sell. Often a combination of surveys, +consultants, spreadsheets and software are employed by businesses +large enough to afford all that. Price testing is also conducted.
+It's notable that Amazon's initial random price testing caused an +uproar among customers. Some reports state that Amazon refined their +approach by giving customers the lowest price available at the end of +the test and others state that Amazon ceased testing +indefinitely. See: +http://www.google.com/search?hl=en&q=amazon+dynamic+pricing
+A dynamic web site offers a unique environment to determing price +points dynamically, e.g., to adjust prices automatically as they need +adjusting.
+One approach could be to set a base price and after a certain number +of potential sales, adjust it. If the last batch scored less than its +predecessor, the price drops. Otherwise, the price increases.
+A period is defined in terms of the number of visitors that actually +get to the pricing information for the opportunity to purchase. We'll +call those visitors the potential customers. The purchase rate is +the # of customers divided by the # of potential customers. The +purchase rate times the price gives the score for that period.
+Here are some fragmented thoughts expressed in Python:
+bp = 4.95 # base price +inc = 1.00 # increment amount +dec = 1.00 # decrement amount +period = 100 # number of visitors before analyzing + +periods = [] # list of periods + +class Period: + """ + attributes: + date - the date the period started + score - the important number. how well this period did + rate - rate of customers who saw pricing information and purchased + parameters - dictionary of parameters at the time such as bp, inc, dec, etc. + """ + pass + +if len(periods)>1: + prev = period[-1] + beforePrev = period[-2] + if prev.score>beforePrev.score: + newPrice = price + inc + elif prev.score<beforePrev.score: + newPrice = price - dec+
By giving all potential customers the same price for a given period of +time, customers are treated fairly (contrasted with the original +Amazon approach). The period price adjustments are no different than +manual adjustments made by other companies for their products. What's +special is that the adjustments are automatic and precise.
+What's also special is that the price adjustments are made whenever +needed. The underlying factors could be all kinds of things:
+change in product quality
change in economy
appearance or disappearance of a competitor
presentation of product
and much, much more
But the computation doesn't need to know the factors and will +therefore, never miss any, because the score is the bottom line.
+Additional ideas to refine this approach are:
+Have a minimum number of days for the current price point, at least 1. This would avoid customers experiencing changing prices in a single day, which could make them feel uncomfortable.
Likewise, have a maximum number of days in order to put a limit to the timewise duration of a pricing period.
Refrain from price adjustments if the score difference was less than some minimum percentage such as 1%.
Adjust prices dynamically according to the difference in scores. Here's a thought:
+factor = 0.5 +scoreRelDiff = (period[-1].score - period[-2].score) / period[-2].score +price += price*scoreRelDiff*factor+
Another approach could use multiple moving averages. For example, if the average score of the past 5 days is higher than that of the past 10 days, increase the price. This "moving average" approach is used by some stock market investors.
Some sites would need to be cautious of the cost of goods sold (COGS) in order to avoid selling product at a loss.
The advantages of Dynamic Pricing are:
+maximizing profits
adjusting price whenever needed
automating price adjustment
treating customers in a given period equally
There's a huge number of approaches that could be taken and lots of +different topics to be discussed. Feel free to add to this Wiki page.
+-- ChuckEsterbrook - 17 Mar 2002
+---
+This is brilliant -- thanks Chuck! JamesBecker
+I began experimenting with Webware after declaring myself a refugee of +the complexity and inflexibility in +"a-well-known-Python-application-server-that-will-remain-anonymous". You +know which one...
+I am an independent consultant (specializing in complex +marketing/strategy/technology interaction issues) who also dabbles in +software development.
+-- EdmundLian - 31 Oct 2001
+Many Python scripts start with the following line:
+#!/usr/bin/env python+
On UNIX-like systems, this instructs the operating system to find the python executable according to the environment. To determine which version of Python gets run, just make sure that your PATH environment variable is set up correctly.
+If Python 2.0 is installed as /usr/local/bin/python and Python 1.5.2 is installed as /usr/bin/python, and you want to run Python 2.0, then you should make sure that /usr/local/bin appears on your "path" before /usr/bin. How and when this is done depends a lot on your system and how you want to work.
+Let's say you want to always use Python 2.0 as a particular user. This +means that you need to edit your shell's resource file so that when it +is started, it puts the directory in which Python 2.0 is found before +the directory in which Python 1.5.2 is found.
+Check your system documentation for details of which file you should +edit and how you should edit it. Here's an example, though - for +bash (the Bourne Again Shell), edit the .bashrc file in your +home directory and add this at the end of the file:
+export PATH=/usr/local/bin:${PATH}
+This assumes that you did install Python 2.0 in /usr/local when +considering MultiplePythonVersions.
+If you just want to run AppServer with a changed environment so that +only that program uses Python 2.0, and you are running bash, you +can try this:
+PATH=/usr/local/bin:${PATH} ./AppServer
+Why not put this in a script:
+#!/bin/sh
+PATH=/usr/local/bin:${PATH} ./AppServer
+Don't be tempted to go round changing the first line of all Python +scripts to something like this:
+#!/usr/bin/env python20+
If you do have Python 2.0 installed as something other than python +then consider making a symbolic link from another directory to the +executable, like this:
+cd /usr/local/bin +ln -s /usr/bin/python20 python+
Now add that directory (in this case, /usr/local/bin) to the start +of your "path", like we did above.
+-- PaulBoddie - 31 Oct 2001
+I work as the System Administrator for TEI Internet Serices +(http://www.teisprint.net). I'm a big fan of open source software, and keep a +journal of my experiences at http://eradman.com.
+ +If Webware's ExtraPathInfo setting is true and you type a URL with a +nonexistent directory component (e.g., +http://site.com/dir/subdir/NONEXISTENT), WebKit returns the +parent directory's index.py instead of a 404 Not Found error, because +it thinks the invalid directory is actually extra path info. But the +servlet, if it's not designed to use the extra path info, will just +display its normal thing, which is wrong because it's the wrong +servlet. Worse, any relative links on the page will loop back to that +servlet, because they'll have the invalid pathname component(s) in the +middle of the URL.
+The consensus among Webware developers is, this is not a bug, it's a +feature. It's just something to be aware of if you use +ExtraPathInfo.
+-- MikeOrr - 21 Feb 2002
+I'd like to do some cleaning up of Webware after our v0.9 build. The automated tests are good preparation to that cleanup. When there are a good set of test cases in place, I'd like to make Webware easier to install. I'm also improving my Cheetah ServletFactory which I can add as a kit. And I have a very simple script that functions like Ant or make--it's a place to put short scripts when developing such as "generate MiddleKit sql", "run sql on my database", "clean up temporary files", "run python interactively with the same python path as the appserver", "upload my web application to the production server." "run webware, but using the CVS version instead of v0.81"
+What improvements would other people like? I will compile a list and post it on the wiki. I'll also sort it in priority order as judged by the enthusiastic responses on this list.
++++
+- +
29 Dec 04 - Ian Bicking - While we're talking about improvements, I might re-note the existance of WSGIKit.
- +
04 Jan 05 - Tom Schwaller - Are Aspects (simple solution in CherryPy) something people would like to see integrated in Webware? Or are continuations? Maybe using generators and annotations could make Webware much more sophisticated?
- +
+
+- 19 Feb 05 - Lloyd
+- +
+
+- +
recently i was looking over the (abandoned?) "webware experimental refactoring" project, and it had some interesting improvements that, to me, seemed like a natural progression for webware:
- +
contexts become "applications" - get their own sessions and state;
- +
multiple applications/contexts can run under a single server instance;
- +
formalized method for executing (context/)application-specific code once on startup, with access to various webware components. (what's the recommended way to do this now? stick it in a site page at module level?)
- +
out-of-the-box connection pooling
- +
get webware into debian's repository? :-)
WSGIKit - Should it be developed in parallel, or should the old Webware be dropped? There are several votes for keeping them in parallel.
+I created a pure HTML Cheetah test.tmpl file, and compiled it to +test.py. When I tried to visit the url "~MyContext/test", I got a 404 +error. But if I went to "~MyContext/test.py", the page is returned +correctly. Why is this happening?
+IanBicking wrote:
+Maybe because there's an ambiguity of what file you are calling -- +test.tmpl or test.py. When you give the extension it is unambiguous.
+I thought Webware would ignore the unregistered extension, though.
+ChuckEsterbrook wrote:
+Actually, it's the other way around. You have to specify what +extensions to ignore. An old TO DO item is to switch WebKit to a +setting called ExtensionsToServe.
+But in the mean time, go to WebKit/Configs/Application.config and add +.tmpl to the ExtensionsToIgnore setting.
+I (Edmund) add:
+It's not enough to do this. You must also add things like .tmpl~ +(backup files created by emacs, in my case) to the +ExtensionsToIgnore setting, or you will still get 404 errors.
+-- EdmundLian - 02 Dec 2001
+I noticed also that Cheetah leaves _bak files around, too, so I needed +to add .py_bak to the list of extensions to ignore. This may be a +problem if you regenerate an HTML file with Cheetah, too (meaning, +.html_bak would need to be ignored, too).
+-- DavidHancock - 07 Jul 2002
+This is a followup from the Webware-discuss list (see Has anyone +tried to stream a reportlab)
+The Content Disposition Header is defined in RFC 2183. Its purpose +(among others) is to give a hint about the name of the following +content. This is quite useful, when one tries to pipe a file to the +browser. Without this header, the browser will show the scriptname, +instead of the filename, when trying to use the Save as function.
+The following code should run right away as a servlet. It shows the +content of some directory and lets you download its files. It is +tested with Webware (10 day old CVS), OneShot.cgi Adapter and the +following browsers. The following list gives you an idea, what +browsers will show the right filename (or not):
+Opera on Linux (works)
Konqueror (doesn't work)
Mozilla 0.96 on Linux (doesn't work, though it should according to +documentation) IE5 should work.
---
+from WebKit.Page import Page
+import os, mimetypes
+
+FILEDIR = "/path/to/directory"
+
+class Main(Page):
+ def title(self):
+ return 'Webware Download Test Page'
+ def writeBody(self):
+ response = self.response()
+ request = self.request()
+ adapterName = request.adapterName()
+ if request.hasField('file'):
+ filename = '%s/%s' % (FILEDIR,request.field('file'))
+ (mtype,enctype) = mimetypes.guess_type(filename)
+ fd = open(filename)
+
+ response.setHeader('Content-Type',mtype)
+ response.setHeader('Content-Disposition','attachment; filename="%s"' % request.field('file'))
+ response.flush()
+
+ chunksize = response.streamOut().bufferSize()
+ outchunk = fd.read(chunksize)
+ while outchunk:
+ self.write(outchunk)
+ outchunk = fd.read(chunksize)
+
+ else:
+ files = os.listdir(FILEDIR)
+ self.writeln('<h1>Webware Download Test</h1>')
+ self.writeln('<ul>')
+ for file in files:
+ self.writeln('<li><a href="%s/Test/Main.py?file=%s">%s</a>' % (adapterName,file,file))
+ self.writeln('</ul>')
+---
+I'm not entirely sure if this code is really streaming the file or if +the Webware outStream is buffering. I think that "response.flush()" +will disable buffering, but I'm not sure.
+Please comment and feel free to put more elegant versions in +place. Maybe we should start a UsefullRfcs page.
+-- StephanDiehl - 14 Nov 2001
+As far as the buffering goes, by calling response.flush(), you tell +webware to go ahead and send what you have written to the stream so +far. By default, the outStream waits until you are done writting to +send anything, calling flush allows you to send data as it becomes +available.
+-- JayLove - 27 Dec 2001
+It seems to me that the above code is not correct because the streamed +content consists not only of the contents of the intended file but also +the html headers, which are not desired. To solve this I override +writeHTML like this:
+def writeHTML(self):
+ if self.request().hasField('file'):
+ self.writeBody()
+ else:
+ Page.writeHTML(self)
+-- RubenMendes - 4 Oct 2004
+Please see Model Two plus One for an example of FormAuthentication.
+-- LukeHolden - 07 Jan 2003
+siehe: webhelpers.html.tags
+Note
+Test with cut and paste sphinx source
+Feldtyp |
+Syntax |
+
|---|---|
Text |
+text(name, value=None, id=None, **attrs) |
+
Textarea |
+text(name, content='', id=None, **attrs) |
+
Hidden |
+hidden(name, value=None, id=None, **attrs) |
+
Password |
+password(name, value=None, id=None, **attrs) |
+
Checkbox |
+checkbox(name, value='1', checked=False, label=None, id=None, **attrs) |
+
Radio |
++ |
Select |
++ |
Submit |
++ |
Form validation serves to ensure the validity of the data submitted to +the servlet. Not checking your data is one of the primary security +holes in web applications. The most egregious holes are when you use +code like this:
+req = self.request()
+os.system("echo hello there | mail -s 'subscribe request' %s" % req.field('email'))
+You might have a select box with several email address, which will +work just fine. But if the user manipulates the page so as to submit +something like: user@public.com;rm -rf /, you'll see the problem.
+Besides security, form validation can deal with the +submit-signal-error-resubmit loop that complex form should allow. +Simply signalling an error and asking the user to go back is a +less-than-optimal situation.
+Some systems that deal with this for Webware are FunFormKit, +FormEncode and XMLForms. Zope has Formulator, and Python CGIs have +pyweblib.
+-- IanBicking - 02 Nov 2001
+FunFormKit is a form validation system, as well as form generation. It +is currently maintained by IanBicking, and exists at
++ ++
FunFormKit is defunct, and is replaced by FormEncode.
+-- IanBicking
+Future Work
+Webware for Python
+1.1b1
+04/18/10
+You can find more information about known bugs and future work +in the Wiki and in the SourceForge tracker systems, +both accessible from the Webware Home Page. +The SourceForge task manager is currently not used since these systems should +be sufficient and we do not want to scatter issues in too many systems.
+Sprinkled throughout the code are comments tagged with @@ which are +hopefully accompanied by a date and someone's initials. These comments +represent things to be done. The double at-sign (@@) convention was +chosen because it doesn't appear to be used for anything else.
+In addition to the inline comments, some significant items have been +recorded below. These are future ideas, with no commitments or timelines +as to when/if they'll be realized. The Python WebKit is open source, +so feel free to jump in!
+All major known bugs that existed previously have been fixed.
+Some of our old style guidelines are in conflict with newer Python +conventions (PEP8, properties etc.). We have already changed the +Webware convention of using tabs, but most naming conventions cannot +be changed so easily without breaking the API.
Think about a migration path towards Pyhon 3, check code with 2to3 tool.
Use distutils/setuptools instead of our own installer, use Python eggs +instead of our own plug-in concept.
The installer should try building and/or installing mod_webkit and wkcgi +automatically.
Use Sphinx instead of our own tools in DocSupport, use reStructuredText +as the format for all docs and maybe also for the docstrings.
Separate more clearly and systematically which messages are printed to +stdout and which to stderr.
Make use of the logging module (so far it is only used with unit testing) +instead of conditional print statements with "debug" and "verbose" flags.
Role-based security and user-authentication. Goal is to eliminate, +as much as possible, developer-written security logic. +This should be provided by the WebKit and be configurable.
Better support and documentation for distribution, load balancing and +fault tolerance.
More sophisticated admin tools including password protection, +clearing logs, displaying a maximum of information at a time, etc. +Consider using module 'resource'.
Investigate case insensitive URLs, especially for the Windows platform.
Support unicode strings and different encodings. HTTPContent should get an +encoding attribute with 'utf-8' as the default (set in Application.config). +This could be output in Page.writeMetaData() and used as the default for +an optional encoding parameter in HTTPContent.write(), so you can write +unicode strings to output. So far, Webware works best if you use ordinary +strings with a consistent encoding throughout the whole application.
Integrate support for i18n and l10n.
Plug-ins:
+In ExamplePage, automatically support examples of any plug-in
Better docs
Properties.config. 'Load', 0, 1 or the name of the required op sys
Hunt down: @@ tags (which signify "To Be Done"s), FUTURE items +in class doc strings, NotImplementedErrors, -- tags
Code clean up. Use bin/checksrc and pylint.
Right now, if the Application itself (as opposed to Servlets) throws +an exception, it doesn't get captured nicely. However, it is displayed +in the app server's console.
The exception handler is pretty nice and has features like logging, +e-mail, gathering debugging info, etc. +However, on occasions it can throw exceptions too. +There should be a simpler, secondary exception handler for when this happens.
Review the timestamp caching logic and its relation to .pyc files if any.
Add "Last-modified:" to generic files that are served via WebKit.
If a Python file has only one class that inherits from Servlet, +then use that as the Servlet class +(rather than requiring the name be the same as the file).
Provide testing web page where people can report their testing results +including version numbers, etc.
Provide higher level automation of testing. For example, a testing script +should be able to launch various app servers multiple times.
Provide highly automated benchmarking so we can track changes in performance.
Add more unit tests, expand the regression test suite.
Use Sphinx instead of our own tools in DocSupport, use reStructuredText +as the format for all docs and maybe also for the docstrings.
Add a Getting Started Guide and a screencast.
Beef up the User's Guide and Tutorial.
User's Guide: Create a caching section to discuss the virtues of doing so +(the Color example became 12 X faster on the server side).
Consider including FormKit, FunFormKit or FormEncode: +A plug-in to aid the construction and validation of forms.
Consider adding a simple helper lib for generating HTML +(such as SimpleHTMLGen) to the WebUtils package.
Better support for WSGI. +Currently we only have the WSGIAdapter interfacing to the app server.
Consider this statement from the FastCGI docs: +"Redirects are handled similar to CGI. Location headers with values +that begin with "/" are treated as internal-redirects; otherwise, +they are treated as external redirects (302)."
FastCGI app server: +The idea is that if the app server itself supports FastCGI, +then it can be used directly with FastCGI enabled web servers +sans the infamous "adapter". +Dan Green has brought this up in Webware-discuss.
Consider if we need to support <form action="x.py?a=1" method="post"> +where you will have both a query string and posted data.
Application modifies sys.path so that servlets can say +from SuperServlet import SuperServlet where SuperServlet is located in +the same directory as the Servlet. We'd prefer a more sophisticated technique +which does not modify sys.path and does not affect other servlets. (Or maybe +this would go away with a new one-process-per-application architecture.)
The ThreadedAppServer is not optimal for multiprocessor systems and modern +multi-core CPUs, due to the Python GIL. On Python 2.x, the GIL implementation +is particularly bad. Check whether we need to build something new such as +a MultiprocessAppServer, or use existing app server or tools such as Twisted.
I live in Cambridge, MA and work for Parlance Corporation, a +speech recognition company in Medford, MA. I am one of the developers +of Webware. And of course, I am a heavy user of Webware in my work.
+-- 29 Oct 2001
+HTTP authentication is the standard way to authenticate access to +resources. It has a number of flaws, and it not the most common way +to do authentication on the web -- using standard forms is more +common. However, in some circumstances it is the only possible way -- +in particular, using non-browser tools to access pages (like a DAV +client, an XML-RPC client, etc.)
+HTTP authentication occurs when the server returns a 401 status (as in +self.response().setStatus(401, 'Authorization Required') ) The +server also must set another header, WWW-Authenticate: Basic realm="<i>realm name</i>" The realm name is the only message the +user will see explaining what they are logging into (though they +should know what page they are trying to access).
+Clients will sent a header like Authorization: Basic xxx (where xxx is +the authorization information)
+Clients generally do not send authentication information until they +are asked to do so (by receiving a 401 error). In typical usage, this +back and forth will occur for every page access. (Though I don't see +any reason you can't use the session and cookies like you would with +normal, form-based authentication).
+There is another style of authentication (this style is termed +"Basic"), called "Digest" -- it sends a hash of the password, in a +challenge-response style. This is more secure, as you can't sniff +passwords. However, for reasons I cannot fathom, many major browsers +<i>still</i> do not support this kind of authentication. Instead, you +may use SSL to secure the password.
+Apache does not normally send the Authorization header onto clients. +To get around this you must use mod_rewrite (this is also typical with +Zope, but I haven't gotten their technique to work for me)
+Put this rewrite rule in:
+<Location /WK>
+RewriteEngine On
+RewriteCond %{HTTP:Authorization} ^(.*)
+RewriteRule /WK(.*) /WK2$1?AUTH=%1 [QSA,L,PT]
+</Location>
+/WK2 is the real location of the WebKit hander -- either a +location set up for mod_webkit, or another adapter. The +RewriteCond line will capture the Authorization: header, which can +be referenced by %1. The RewriteRule line will add a variable +AUTH to the query string.
+(I'm not entirely sure about the security of this -- the +authentication information doesn't show up in the log, though)
+In the servlet, you must both send the right information, and then +receive that authorization. To require authentication:
+res = self.response()
+res.setStatus(401, 'Authorization Required')
+res.setHeader('WWW-Authenticate', 'Basic realm="%s"' % realm)
+To process it:
+import base64
+
+def authorized(self):
+ req = self.request()
+ httpAuth = req.field('AUTH', None)
+ if not httpAuth: return 0
+ authType, auth = httpAuth.split(' ', 1)
+ assert authType.lower() == 'basic', 'Only basic HTTP authentication is supported'
+ name, password = base64.decodestring(auth.strip()).split(':', 1)
+ return self.authorizeUser(name, password)
+(If you are using HTTPAdapter, you will receive the authentication +information in self.request().environ()['HTTP_AUTHORIZATION'] )
+-- IanBicking - 10 May 2002
+You can compile Apache 1.3 with SECURITY_HOLE_PASS_AUTHORIZATION +which will cause apache to pass the HTTP_AUTHORIZATION environment +variable to your script. If you don't want to recompile apache, you +can use a much simpler mod_rewrite setup to add an additional +environment variable:
+<Location /WK>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+ RewriteEngine On
+ RewriteRule /WK(.*) - [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},PT]
+</Location>
+With this you scan check +self.request().environ()['HTTP_AUTHORIZATION'] and +self.request().environ()['X-HTTP_AUTHORIZATION'] to find +authorization Information.
+There is also a patch to mod_webkit implementing passing of +Authorisation Info to the Application Server available at +http://blogs.23.nu/c0re/stories/1410/
+Ian Bicking, me...
+I wrote this Wiki software
+Welcome to the archived Wiki of the Webware for Python project.
+This Wiki has been created by Ian Bicking in October 2001, +using the Webware framework. Its purpose was to facilitate discussion +about Webware: its design, its use and its future. This Wiki was intended +to supplement, not replace, the Webware homepage, webware lists +and docs . It collected interesting ideas and recipes by Webware users +and developers until May 2010. It now has been closed and archived as a +read-only reference and historical document. Please note that many links +are now broken, and some of the content may not be applicable to the latest +versions of Webware for Python any more.
+| + | + |
| + | + |
See also the Register for a list of all pages of this Wiki.
+| + | How to design your webware application. |
+
| + | How to integrate Webware with various webservers. |
+
| + | Detailed explanations on how to accomplish common tasks. |
+
| + | Tools to generate charts on demand. |
+
| + | Detailed explanations and links to tools +for working with databases. |
+
| + | Things that have tripped people up. |
+
| + | How to secure your Webware applications. |
+
| + | What to look for in a host. |
+
| + | Apache's mod_rewrite: the solution to all questions +regarding URLs, eliminating Webware-specific parts of the URL, +and dealing with virtual hosts. |
+
| + | E-Commerce, Credit cards, dynamic pricing, etc. |
+
| + | A catch-all for items that don't fit anywhere else. |
+
| + | News items from the webware project, in chronological order |
+
| + | Describes the internal process for handling a transaction. |
+
| + | Documents all known compatibility issues between Webware, +Python, and third-party modules. |
+
Off-Site Tutorial to a simple calendar control.
+Offsite: from a talk at LinuxTag 2002 (50% Webware, 50% MiddleKit)
+This is a copy of the current Webware/WebKit/Docs/Future.txt document.
+Jan 2005 - Summary of discussion on mailing lists about what we should +work on in the near future.
+An informal wishlist of things people would like to see improved in +Webware. Wishes that become planned features are moved from this +list to the ToDo list.
+A more formal list of improvements that are planned for specific +releases (e.g. 0.9).
+a list of wishes and todo items that have been completed.
+An index of the development sub-projects that are focussed on +discussing and implementing specific items on the ToDo list.
+An 'unofficial' refactoring of the Webware 0.6 codebase that addresses +many of the items on the WishList and ToDo list.
+Third-party documents on high-level design issues
+Guidelines for submitting patches
+Guidelines for Webware Developers (Read this if you want CVS access)
+As of March 2005, the Webware source is stored in Subversion. Here is how to access it.
+links to other pages that discuss Webware
+TcpWatch is a great program to view what goes on during a transaction. +You can find it at http://www.zope.org/Members/hathawsh/tcpwatch
+You run it as:
+$ python tcpwatch.py 8080 myserver.com 80</code>+
And when you connect to localhost:8080, you'll get pages from +myserver.com:80 -- except that everything that goes back and forth +will be tracked through a nice Tk interface.
+-- IanBicking - 15 Nov 2001
+SourceForge.net provides a valuable service to the Open Source +community, but its implementation and user interface could be improved +in many ways. (TavisRudd in WebwareSourceforge)
+The biggest improvement would be to get the browser out of the way. I +cringe every time I need to search a mailing list (or worse, a +Sourceforge "forum") for a specific keyword or conversation through +some strange web-interface. IMAP, which was designed for exactly that +purpose, does the job thousands of times faster with far less drain on +both human and computer/network resources. (NNTP is also ok, but +requires the client software to do the searching, thus causing +more load on the server by having to transfer all of the articles, +rather than just the interesting ones.)
+I think that a key benefit of the webware approach over the Zope +approach is that you can design your application without being +constrained by a browser. Roundup's design is a good example of +this. It uses the most approriate protocol/tool for each task, +instead of trying to shoehorn everything into "stateless" HTTP +transactions.
+Therefore: a "webware application" such as this proposed collaboration +suite, should be first an application with a thin adapter to +create a browser-based UI generated using webware components.
+Please read these before you start:
+ +And beware of this (another "feature" of the current sourceforge development model):
+ +(Can you tell that I strongly dislike web-only interfaces? 8-D)
+-- TerrelShumway - 16 Apr 2002
+As we all know, JavaScript sucks in many ways, one of them being its lack of a module import system. So, if you have a substantial amount of JavaScript code, there are basically three choices:
+stuff everything into one huge file,
break the code down into multiple files that load recursively by issuing multiple HTTP requests,
manually add all JS URLs, in carefully maintained order, to each and every page that needs them.
The first choice is unpleasant for the developer, whereas the second is unpleasant for the user (who gets to watch the sometimes slow loading). Approach 3 is unpleasant to both parties.
+An alternative is to use dependency resolution between JavaScript files on the server side. The dependencies can be declared in JavaScript comments:
+// requireScript script1, script2, script0815
+The resolution is done in a servlet, whose URL is prefixed to the topmost JS file. In this way, one big JS file is assembled that can be sent in a single transaction. The file will include all required files recursively and in proper order, such that required files are always included before their dependent ones. The assembled JS file can be cached. The servlet file JSResolver.py +contains additional explanations.
+*escape* `with` "\"
+This is an ordinary paragraph, introducing a block quote.
+++"It is my business to know things. That is my trade."
+—Sherlock Holmes
+
Take it away, Eric the Orchestra Leader!
+++++A one, two, a one two three four++Half a bee, philosophically,+++must, ipso facto, half not be.+But half the bee has got to be,+++vis a vis its entity. D'you see?++But can a bee be said to be+++or not to be an entire bee,+++when half the bee is not a bee,+++due to some ancient injury?++Singing...+
Header styles:
+paragraph |
+
paragraph |
+
Definition 1.
+Definition 2, paragraph 1.
+Definition 2, paragraph 2.
+Definition 3.
+Definition 4.
+Header row, column 1 +(header rows optional) |
+Header 2 |
+Header 3 |
+Header 4 |
+
|---|---|---|---|
body row 1, column 1 |
+column 2 |
+column 3 |
+column 4 |
+
body row 2 |
+Cells may span columns. |
+||
body row 3 |
+Cells may +span rows. |
+
|
+|
body row 4 |
+|||
Some care must be taken with grid tables to avoid undesired interactions with cell text in rare cases. For example, the following table contains a cell in row 2 spanning from column 2 to column 4:
+row 1, col 1 |
+column 2 |
+column 3 |
+column 4 |
+
row 2 |
++ | ||
row 3 |
++ | + | + |
If a vertical bar is used in the text of that cell, it could have unintended effects if accidentally aligned with column boundaries:
+row 1, col 1 |
+column 2 |
+column 3 |
+column 4 |
+
row 2 |
+Use the command ls | more. |
+||
row 3 |
++ | + | + |
Several solutions are possible. All that is needed is to break the continuity of the cell outline rectangle. One possibility is to shift the text by adding an extra space before:
+row 1, col 1 |
+column 2 |
+column 3 |
+column 4 |
+
row 2 |
+Use the command ls | more. |
+||
row 3 |
++ | + | + |
Another possibility is to add an extra line to row 2:
+row 1, col 1 |
+column 2 |
+column 3 |
+column 4 |
+
row 2 |
+Use the command ls | more. |
+||
row 3 |
++ | + | + |
Simple Tables
+Simple tables provide a compact and easy to type but limited row-oriented table representation for simple data sets. Cell contents are typically single paragraphs, although arbitrary body elements may be represented in most cells. Simple tables allow multi-line rows (in all but the first column) and column spans, but not row spans. See Grid Tables above for a complete table representation.
+Simple tables are described with horizontal borders made up of "=" and "-" characters. The equals sign ("=") is used for top and bottom table borders, and to separate optional header rows from the table body. The hyphen ("-") is used to indicate column spans in a single row by underlining the joined columns, and may optionally be used to explicitly and/or visually separate rows.
+A simple table begins with a top border of equals signs with one or more spaces at each column boundary (two or more spaces recommended). Regardless of spans, the top border must fully describe all table columns. There must be at least two columns in the table (to differentiate it from section headers). The last of the optional header rows is underlined with '=', again with spaces at column boundaries. There may not be a blank line below the header row separator; it would be interpreted as the bottom border of the table. The bottom boundary of the table consists of '=' underlines, also with spaces at column boundaries. For example, here is a truth table, a three-column table with one header row and four body rows:
+A |
+B |
+A and B |
+
|---|---|---|
False |
+False |
+False |
+
True |
+False |
+False |
+
False |
+True |
+False |
+
True |
+True |
+True |
+
Underlines of '-' may be used to indicate column spans by "filling in" column margins to join adjacent columns. Column span underlines must be complete (they must cover all columns) and align with established column boundaries. Text lines containing column span underlines may not contain any other text. A column span underline applies only to one row immediately above it. For example, here is a table with a column span in the header:
+Inputs |
+Output |
+|
|---|---|---|
A |
+B |
+A or B |
+
False |
+False |
+False |
+
True |
+False |
+True |
+
False |
+True |
+True |
+
True |
+True |
+True |
+
Each line of text must contain spaces at column boundaries, except where cells have been joined by column spans. Each line of text starts a new row, except when there is a blank cell in the first column. In that case, that line of text is parsed as a continuation line. For this reason, cells in the first column of new rows (not continuation lines) must contain some text; blank cells would lead to a misinterpretation. An empty comment ("..") is sufficient and will be omitted from the processed output (see Comments below). Also, this mechanism limits cells in the first column to only one line of text. Use grid tables if this limitation is unacceptable.
+Underlines of '-' may also be used to visually separate rows, even if there are no column spans. This is especially useful in long tables, where rows are many lines long.
+Blank lines are permitted within simple tables. Their interpretation depends on the context. Blank lines between rows are ignored. Blank lines within multi-line rows may separate paragraphs or other body elements within cells.
+The rightmost column is unbounded; text may continue past the edge of the table (as indicated by the table borders). However, it is recommended that borders be made long enough to contain the entire text.
+The following example illustrates continuation lines (row 2 consists of two lines of text, and four lines for row 3), a blank line separating paragraphs (row 3, column 2), and text extending past the right edge of the table:
+col 1 |
+col 2 |
+
|---|---|
1 |
+Second column of row 1. |
+
2 |
+Second column of row 2. +Second line of paragraph. |
+
3 |
+
|
+
Explicit Markup Blocks
+In real life:
+Kevin J. Rice
+I work for Textura, LLC (http://texturallc.com) in Lake Bluff, Illinois, USA.
+My home page is : http://justanyone.com
+I work for IronPort Systems and am evaluating Webware for use in some of our web projects.
+I believe in spaces not tabs.
+yourcode" only) - colors=null,mono,lite,dark,idle,or pythonwin - """ - Parser(sourcestring, colors).format(form) - -def path2stdout(sourcepath, colors=None, form=None): - """Converts code(file) to colorized HTML. Writes to stdout. - - form='code',or'snip' (for "
yourcode" only) - colors=null,mono,lite,dark,idle,or pythonwin - """ - sourcestring = open(sourcepath).read() - Parser(sourcestring, colors, title=sourcepath).format(form) - -def str2html(sourcestring, colors=None, form=None): - """Converts a code(string) to colorized HTML. Returns an HTML string. - - form='code',or'snip' (for "
yourcode" only) - colors=null,mono,lite,dark,idle,or pythonwin - """ - stringIO = cStringIO.StringIO() - Parser(sourcestring, colors, out=stringIO).format(form) - stringIO.seek(0) - return stringIO.read() - -def str2file(sourcestring, outfile, colors=None, show=0): - """Converts a string to a file. - - makes no attempt at correcting bad pathnames - """ - html = str2html(sourcestring, colors) - f = open(outfile,'wt') - f.writelines(html) - f.close() - if show: - showpage(outfile) - -def path2html(sourcepath, colors=None, form=None): - """Converts code(file) to colorized HTML. Returns an HTML string. - - form='code',or'snip' (for "
yourcode" only) - colors=null,mono,lite,dark,idle,or pythonwin - """ - stringIO = cStringIO.StringIO() - sourcestring = open(sourcepath).read() - Parser(sourcestring, colors, title=sourcepath, out=stringIO).format(form) - stringIO.seek(0) - return stringIO.read() - -def convert(source, outdir=None, colors=None, - show=0, markup='html', quiet=0): - """Takes a file or dir as input and places the html in the outdir. - - If outdir is none it defaults to the input dir - """ - c=0 - # If it is a filename then path2file - if not os.path.isdir(source): - if os.path.isfile(source): - c+=1 - path2file(source, outdir, colors, show, markup, quiet) - else: - raise PathError, 'File does not exists!' - # If we pass in a dir we need to walkdir for files. - # Then we need to colorize them with path2file - else: - fileList = walkdir(source) - if fileList != None: - # make sure outdir is a dir - if outdir != None: - if os.path.splitext(outdir)[1] != '': - outdir = os.path.split(outdir)[0] - for item in fileList: - c+=1 - path2file(item, outdir, colors, show, markup, quiet) - _printinfo('Completed colorizing %s files.'%str(c), quiet) - else: - _printinfo("No files to convert in dir.", quiet) - -def path2file(sourcePath, out=None, colors=None, show=0, - markup='html', quiet=0): - """ Converts python source to html file""" - # If no outdir is given we use the sourcePath - if out == None:#this is a guess - htmlPath = sourcePath + '.html' - else: - # If we do give an out_dir, and it does - # not exist , it will be created. - if os.path.splitext(out)[1] == '': - if not os.path.isdir(out): - os.makedirs(out) - sourceName = os.path.basename(sourcePath) - htmlPath = os.path.join(out,sourceName)+'.html' - # If we do give an out_name, and its dir does - # not exist , it will be created. - else: - outdir = os.path.split(out)[0] - if not os.path.isdir(outdir): - os.makedirs(outdir) - htmlPath = out - htmlPath = os.path.abspath(htmlPath) - # Open the text and do the parsing. - source = open(sourcePath).read() - Parser(source, colors, sourcePath, open(htmlPath, 'wt'), - markup).format() - _printinfo(" wrote %s" % htmlPath, quiet) - if show: - # load HTML page into the default web browser. - showpage(htmlPath) - return htmlPath - -def walkdir(dir): - """Return a list of .py and .pyw files from a given directory. - - This function can be written as a generator Python 2.3, or a genexp - in Python 2.4. But 2.2 and 2.1 would be left out.... - """ - # Get a list of files that match *.py* - GLOB_PATTERN = os.path.join(dir, "*.[p][y]*") - pathlist = glob.glob(GLOB_PATTERN) - # Now filter out all but py and pyw - filterlist = [x for x in pathlist - if x.endswith('.py') - or x.endswith('.pyw')] - if filterlist != []: - # if we have a list send it - return filterlist - else: - return None - -def showpage(path): - """Helper function to open webpages""" - try: - webbrowser.open_new(os.path.abspath(path)) - except: - traceback.print_exc() - -def _printinfo(message, quiet): - """Helper to print messages""" - if not quiet: - print message - -########################################################### Custom Exceptions - -class PySourceColorError(Exception): - pass# base for custom error - -class PathError(PySourceColorError): - pass# custom error - -class InputError(PySourceColorError): - pass# custom error - -########################################################## Python code parser - -class Parser: - - """MoinMoin python parser heavily chopped :)""" - - def __init__(self, raw, colors=None, title='', out=sys.stdout, - markup='html', header=0, footer=0): - """Store the source text & set some flags""" - if colors == None: - colors = defaultColors - self.raw = raw.expandtabs().strip() - self.title = os.path.basename(title) - self.out = out - self.argFlag = 0 - self.classFlag = 0 - self.defFlag = 0 - self.decoratorFlag = 0 - self.markup = markup.upper() - self.colors = colors - self.header = header - self.footer = footer - - def format(self, form=None): - """Parse and send the colorized source""" - if form in ('snip','code'): - self.addEnds = 0 - else: - self.addEnds = 1 - # Store line offsets in self.lines - self.lines = [0, 0] - pos = 0 - - # Gather lines - while 1: - pos = self.raw.find('\n', pos) + 1 - if not pos: break - self.lines.append(pos) - self.lines.append(len(self.raw)) - - # Wrap text in a filelike object - self.pos = 0 - text = cStringIO.StringIO(self.raw) - - # Html start - if self.addEnds: - self._doPageStart() - else: - self._doSnippetStart() - - # Parse the source and write out the results. - ## Tokenize calls the __call__ - ## function for each token till done. - try: - tokenize.tokenize(text.readline, self) - except tokenize.TokenError, ex: - msg = ex[0] - line = ex[1][0] - self.out.write("
')
-
- def _sendHTMLText(self, toktype, toktext):
- # If it is an error set a red box around the bad tokens
- # older browsers will ignore it
- if toktype == ERRORTOKEN:
- style = ' style="border: solid 1.5pt #FF0000;"'
- else:
- style = ''
- tags, color = self._getStyle(toktype)[:2]
- tagstart=[]
- tagend=[]
- # check for styles and set them if needed.
- if 'b' in tags:#Bold
- tagstart.append('')
- tagend.append('')
- if 'i' in tags:#Italics
- tagstart.append('')
- tagend.append('')
- if 'u' in tags:#Underline
- tagstart.append('')
- tagend.append('')
- # HTML tags should be paired like so : Doh!
- tagend.reverse()
- starttag="".join(tagstart)
- endtag="".join(tagend)
- sendtext = cgi.escape(toktext)
- # send text
- ## Output optimization
- # skip font tag if black text, but styles will still be sent. (b,u,i)
- if color !='#000000':
- startfont = ''%(color, style)
- endfont = ''
- else:
- startfont, endfont = ('','')
- self.out.write(''.join([startfont,starttag,sendtext,endtag,endfont]))
- return
-
- def _doHTMLHeader(self):
- # Optional
- color = self._getForeColor(token.NAME)
- self.out.write(' #%s %s
\n'%
- (color, self.title, time.ctime()))
-
- def _doHTMLFooter(self):
- # Optional
- color = self._getForeColor(token.NAME)
- self.out.write(' #%s %s
\n'%
- (color, self.title,time.ctime()))
-
- def _doHTMLEnd(self):
- # End of html page
- self.out.write('\n')
- # Write a little info at the bottom
- if self.footer:
- self._doPageFooter()
- self.out.write('\n')
-
- #################################################### CSS markup functions
-
- def _getCSSStyle(self, key):
- # Get the tags and colors from the dictionary
- tags, forecolor, backcolor = self._getStyle(key)
- style=[]
- if tags:
- if 'b' in tags:# Bold
- style.append('font-weight:bold;')
- if 'i' in tags:# Italic
- style.append('font-style:italic;')
- if 'u' in tags:# Underline
- style.append('text-decoration:underline;')
- style.append('color:%s;'% forecolor)
- if backcolor:
- style.append('background-color:%s;'%backcolor)
- return (self._getMarkupClass(key),''.join(style))
-
- def _doCSSStart(self):
- # Start of css/html page
- self.out.write('')
- self.out.write('')
-
- def _sendCSSText(self, toktype, toktext):
- ##Output optimization
- # send text plain if black and no tags.
- tags, color = self._getStyle(toktype)[:2]
- if color == '#000000' and not tags:
- startspan, endspan = ('','')
- else:
- startspan = ''%(self._getMarkupClass(toktype))
- endspan = ''
- sendtext = cgi.escape(toktext)
- self.out.write(''.join([startspan,sendtext,endspan]))
- return
-
- def _doCSSHeader(self):
- # Optional
- self.out.write('#%s %s
\n'%
- (self.title, time.ctime()))
-
- def _doCSSFooter(self):
- # Optional
- self.out.write('#%s %s
\n'%
- (self.title, time.ctime()))
-
- def _doCSSEnd(self):
- # End of css/html page
- self.out.write('\n')
- # Write a little info at the bottom
- if self.footer:
- self._doPageFooter()
- self.out.write('\n')
-
-#############################################################################
-
-if __name__ == '__main__':
- cli()
-
-#############################################################################
-# 2004 M.E.Farmer Jr.
-# Python license
diff --git a/lib/__init__.py b/lib/__init__.py
deleted file mode 100644
index 792d600..0000000
--- a/lib/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/lib/captchas/__init__.py b/lib/captchas/__init__.py
deleted file mode 100644
index 792d600..0000000
--- a/lib/captchas/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/lib/captchas/music.py b/lib/captchas/music.py
deleted file mode 100644
index d6d775b..0000000
--- a/lib/captchas/music.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""music - a Python captcha for musicians"""
-
-__version__ = "1.0"
-__license__ = "Python"
-__copyright__ = "Copyright 2005 by Christoph Zwerschke"
-
-from random import randint
-
-class Captcha:
- """A captcha for Python programmers.
-
- Methods:
- create(width): create the HTML form fields
- width: the length of the input field
- check(input, id): check the input from the user
- input, id: use the result values from the form
- """
-
- size = (6,4)
- colors = ['black', 'white']
- names = 'do-re-mi-fa-so-la-ti-do-re'.split('-')
-
- def create(self, width=6, id=None):
- if id:
- notes = map(int, id)
- else:
- notes = []
- for i in range(width):
- notes.append(randint(0, 8))
- id = ''.join(map(str, notes))
- s = ['| '] - s.append('Please enter the melody' - ' in solfege notation:') - s.append(' | ||
| ') - s.append('(e.g., "doremiresola")') - s.append(' | ||
| '
- % ((width*2+1)*self.size[0], 12*self.size[1]))
- s.append(self.__notes(notes))
- s.append(' | ' - ' = | ') - s.append('' % ((width*3,)*2)) - s.append('' % id) - s.append(' |
Have a look' - ' at the following expression:
'] - s.append('%s' % expr) - s.append('
Now please enter the result: ') - s.append('
' % ((width,)*2)) - s.append('' % id) - return '\n'.join(s) - - def check(self, input, id): - expr = self.__expr(id) - input = input.strip().strip('\'"') - try: - solution = eval(expr) - except: - solution = None - return input == solution - - def __expr(self, id): - if not isinstance(id, str): - return None - code = id[-1:] - try: - code = int(code) - except: - code = 0 - if not 1 <= code <= 8: - return None - word = id[:-1] - for c in word: - if c not in self.alphabet: - return None - return ("''.join(map(lambda c:" - "'%s'[\n'%s'.index(c)^%d],'%s'))" - % (self.alphabet, self.alphabet, code, word)) diff --git a/lib/common.py b/lib/common.py deleted file mode 100644 index 3edea33..0000000 --- a/lib/common.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -These are routines common to the entire application. -We keep them separate to avoid circular dependencies. -""" - -import cgi -import re -import inspect -import pprint as pprint_module -pprint = pprint_module.pprint -import os - -__all__ = ['canonicalName', 'htmlEncode', 'guessURLName', 'dprint', - 'pprint', 'guessTitle', 'dedent'] - -_canonicalNameRE = re.compile(r'[^a-z0-9]') -def canonicalName(name): - """Turns a wiki name into its canonical form. - - This may only have letters, numbers and hyphens in it. - """ - return str(_canonicalNameRE.sub('', name.lower())) - -_urlNameRE = re.compile(r'[^a-z0-9 ]') -def guessURLName(name): - name = str(_urlNameRE.sub('', name.lower())) - return name.replace(' ', '-') - -def guessTitle(name): - """Turns a canonical name into a title.""" - return ' '.join([w.capitalize() for w in name.split('-')]) - -def htmlEncode(val, cgiEscape=cgi.escape): - return cgiEscape(val, 1) - -def dprint(*args, **kw): - caller_frame = inspect.stack()[1] - caller_name = caller_frame[3] - caller_line = caller_frame[2] - caller_filename = caller_frame[1] - caller_module = os.path.splitext(os.path.basename(caller_filename))[0] - del caller_frame - print "%s from %s.%s:%s %s" % ( - '-'*10, caller_module, caller_name, caller_line, '-'*10) - for arg in args: - if isinstance(arg, (str, unicode)): - print arg, - else: - print pprint_module.pformat(arg) - items = kw.items() - items.sort() - for name, value in items: - print name, pprint_module.pformat(value) - -try: - from textwrap import dedent -except ImportError: # Fallback for Python < 2.3 - def dedent(t): - lines = text.expandtabs().split('\n') - margin = None - for line in lines: - content = line.lstrip() - if not content: - continue - indent = len(line) - len(content) - if margin is None: - margin = indent - else: - margin = min(margin, indent) - if margin is not None and margin > 0: - for i in range(len(lines)): - lines[i] = lines[i][margin:] - return '\n'.join(lines) diff --git a/lib/config/__init__.py b/lib/config/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/lib/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/lib/config/configwrapper.py b/lib/config/configwrapper.py deleted file mode 100644 index 53e4414..0000000 --- a/lib/config/configwrapper.py +++ /dev/null @@ -1,263 +0,0 @@ -import os -import iniparser - -class _ConfigParser(iniparser.INIParser): - - """ - An internal subclass of the abstract ini parser, which saves - everything into one dictionary. - """ - - def __init__(self, allow_empty_sections=True): - self.allow_empty_sections = allow_empty_sections - iniparser.INIParser.__init__(self) - - def reset(self): - self.data = {} - self.key_order = {} - self.section_order = [] - iniparser.INIParser.reset(self) - - def assignment(self, name, content): - section = self.data.setdefault(self.section, {}) - name = clean_name(name) - section.setdefault(name, []).append(content) - self.key_order.setdefault(self.section, []).append(name) - - def new_section(self, section): - section = section.strip().lower() - if not section or section == 'general': - section = None - self.section = section - if not section in self.section_order: - self.section_order.append(section) - -class NoDefault: - pass - -def clean_name(name): - name = name.strip().lower() - for char in ' \t_-': - name = name.replace(char, '') - return name - -class Config(object): - - """ - A configuration object. Acts a little like a dictionary, but not - really. - - Configuration can be nested with ``[section]`` markers, or can be - flat (everything globally available). If in sections then - ``section.key`` is used, otherwise just ``key``. - """ - - # If False, then no global sections are allowed - allow_empty_sections = True - - def __init__(self, filename, file=None, - allow_empty_sections=NoDefault): - if allow_empty_sections is not NoDefault: - self.allow_empty_sections = allow_empty_sections - self.filename = filename - p = _ConfigParser(allow_empty_sections=self.allow_empty_sections) - if file is None: - p.load(filename) - else: - p.loadfile(file) - self.raw_data = p.data - self._section_order = p.section_order - self._key_order = p.key_order - self.name = os.path.splitext(os.path.basename(filename))[0] - - def keys(self, section=NoDefault): - if section is NoDefault: - all = [] - for section_name in self._section_order: - for key_name in self._key_order[section_name]: - if section_name: - key_name = '%s|%s' % (section_name, key_name) - all.append(key_name) - return all - else: - try: - return self._key_order[section] - except KeyError: - raise KeyError( - "Section [%s] not found (from sections %r)" - %(section, self._key_order.keys())) - - def sections(self): - return self.raw_data.keys() - - def getraw(self, key, section=None): - """ - Mostly for internal use, returns a list of all matching keys. - """ - if '|' in key: - assert section is None, ( - "Cannot have | in a key (%r) and an explicit section (%r)" - % (key, section)) - section, key = key.split('|', 1) - section = self.clean_section(section) - try: - return self.raw_data[section][key] - except KeyError: - return () - - def clean_section(self, section_name): - if not section_name or section_name == 'general': - return None - return section_name.lower() - - def get(self, key, default=None, section=None): - """ - Get a single value, returning `default` if none found. - """ - value = self.getraw(key, section=section) - if not value: - return default - else: - return value[0] - - def getlist(self, key, default=(), section=None): - """ - Get a list of all matching keys. Example:: - - foo = bar - foo = baz - - Then:: - - >>> config.getlist('foo') - ['bar', 'baz'] - """ - value = self.getraw(key, section=section) - if not value: - return default - else: - return value - - true_values = ['t', 'true', '1', 'y', 'yes', 'on'] - false_values = ['f', 'false', '0', 'n', 'no', 'off'] - - def getbool(self, key, default=False, section=None): - """ - Get a single boolean value. Boolean values are things - like ``true``, ``false``, etc. - """ - value = self.getraw(key, section=section) - if not value: - return default - value = value[0] - if isinstance(value, (str, unicode)): - value = value.lower() - if value in self.true_values: - return True - elif value in self.false_values: - return False - else: - raise ValueError( - "Yes/No value expected for %s (got %r)" - % (key, vaule)) - else: - return value - - def getint(self, key, default=None, section=None): - """ - Get an integer value. - """ - v = self.get(key, default=default, section=section) - if v is None: - return v - else: - return int(v) - - def getinlinelist(self, key, default=(), section=None): - """ - Get a list, where the list is defined like: - - foo = bar, baz - - Gives:: - - >>> config.getinlinelist('foo') - ['bar', 'baz'] - """ - result = [] - for item in self.getlist(key, default, section): - item = item.strip() - if not item: - continue - if ',' in item: - result.extend([i.strip() for i in item.split(',')]) - else: - result.append(item) - return result - - def getstartswith(self, startswith, section): - """ - Returns a list of [(key, value)] for all keys in the section that - start with startswith. - """ - result = [] - for key in self.keys(section=section): - if not key.startswith(startswith): - continue - for value in self.getraw(key, section=section): - result.append((key, value)) - return result - - def __repr__(self): - return '<%s filename=%s>' % ( - self.__class__.__name__, self.filename) - - def __str__(self): - data = [] - data.append('%s from %s:' % ( - self.__class__.__name__, self.filename)) - for name, value_list in self.raw_data.get(None, {}).items(): - for value in value_list: - data.append('%s: %s' % (name, value)) - for section_name, value_dict in self.raw_data.items(): - if section_name is None: - continue - data.append('[%s]' % section_name) - for name, value_list in value_dict.items(): - for value in value_list: - data.append('%s: %s' % (name, value)) - return '\n'.join(data) - -def load_options(parser, options, config, if_exists=True): - """ - Given a parser object and the parsed options and a configuration - object or filename, this will load the configuration into the - parsed options. - """ - types = {} - dests = {} - aliases = {} - for option in parser.option_list: - if not option.dest: - # Some builtin options, like --help - continue - name = clean_name(option.dest) - dests[name] = option.dest - if option.action in ('store_true', 'store_false'): - types[name] = 'getbool' - else: - types[name] = 'get' - for lst in option._long_opts, option._short_opts: - for opt in lst: - aliases[clean_name(opt)] = name - if isinstance(config, str): - if if_exists and not os.path.exists(config): - return - config = Config(config) - for config_key in config.keys(): - name = aliases.get(config_key, config_key) - if name not in dests: - raise ValueError( - "Bad configuration file: option %r unexpected" % config_key) - value = getattr(config, types[name])(config_key) - setattr(options, dests[name], value) diff --git a/lib/config/iniparser.py b/lib/config/iniparser.py deleted file mode 100644 index eb8b7a4..0000000 --- a/lib/config/iniparser.py +++ /dev/null @@ -1,241 +0,0 @@ -""" -A parser for .ini-syntax files. INIParser should be subclassed to -create a data structure from the file. See INIParser for more -""" - -class ParseError(Exception): - - def __init__(self, message, filename=None, lineno=None, column=None, line=None): - # Note: right now column is not used, but in some contexts it - # could be used to print out a line like: - # error, must be an integer: - # a = one - # ^ - self.message = message - self.filename = filename - self.lineno = lineno - self.column = column - self.line = line - - def __str__(self): - msg = self.message - if self.filename and self.lineno: - msg += ' in %s:%s' % (self.filename, self.lineno) - elif self.filename: - msg += ' in %s' % self.filename - elif self.lineno: - msg += ' at line %s' % self.lineno - return msg - -class INIParser: - - """ - A parser for .ini-syntax files. - - Implements all features I know of in .ini files: - * sections with a [ in the first column - * assignment via a=b or a: b - * rfc822-style continuation lines (i.e., start the next line - with indentation to make it a continuation). - * ; or # for comments - - This class should be subclassed. Subclasses may only need to - implement the .assignment() method. You may want to use the - .section attribute, which holds the current section (or None if no - section has been defined yet). - - Use .parse_error(message) when you encounter a problem; this will - create an exception that will note the filename and line in which - the problem occurs. - """ - - def __init__(self): - self.reset() - - def reset(self): - pass - - def load(self, filename, encoding='ascii'): - fileobj = open(filename, 'rb') - self.loadfile(fileobj, filename=filename, encoding=encoding) - fileobj.close() - - def loadfile(self, fileobj, filename=None, encoding='ascii'): - self.start_lineno = 0 - if filename is None: - filename = getattr(fileobj, 'name', None) - self.filename = filename - # lineno is what we are parsing, start_lineno is the last - # assignment we processed (for multi-line assignments) - self.start_lineno = 0 - self.lineno = 0 - self.encoding = encoding - def strip_newline(l): - if l.endswith('\n'): - return l[:-1] - else: - return l - self.stream = [strip_newline(l) for l in fileobj.readlines()] - self.process_file() - del self.filename - del self.encoding - del self.lineno - - def loadstring(self, string, filename=None): - self.stream = string.splitlines() - self.filename = filename - self.start_lineno = 0 - self.lineno = 0 - self.encoding = None - self.process_file() - del self.stream - del self.filename - del self.lineno - del self.encoding - - def process_file(self): - self.section = None - last_name = None - accumulated_content = None - for line in self.stream: - self.lineno += 1 - # @@: should catch encoding error: - if self.encoding: - line = line.decode(self.encoding) - end_assignment = False - if not line.strip(): # empty line - if last_name: - self.process_assignment( - last_name, - accumulated_content) - last_name = accumulated_content = None - self.start_lineno = self.lineno - continue - elif line[0] in (' ', '\t'): # continuation line - if not last_name: - self.error_continuation_without_assignment(line) - else: - accumulated_content.append(line) - continue - elif self.get_section(line) is not None: # section line - if last_name: - self.process_assignment( - last_name, - accumulated_content) - last_name = accumulated_content = None - self.start_lineno = self.lineno - self.new_section(self.get_section(line)) - elif self.get_comment(line) is not None: # comment line - if last_name: - self.process_assignment( - last_name, - accumulated_content) - last_name = accumulated_content = None - self.start_lineno = self.lineno - self.add_comment(self.get_comment(line)) - else: # normal assignment - if last_name: - self.process_assignment( - last_name, - accumulated_content) - last_name, accumulated_content = self.split_name_value(line) - self.start_lineno = self.lineno - if last_name: - self.process_assignment( - last_name, - accumulated_content) - - def split_name_value(self, line): - colon_pos = line.find(':') - equal_pos = line.find('=') - if colon_pos == -1 and equal_pos == -1: - self.error_missing_equal(line) - return None - if (colon_pos == -1 - or (equal_pos != -1 and equal_pos < colon_pos)): - pos = equal_pos - else: - pos = colon_pos - return line[:pos], [line[pos+1:]] - - def get_comment(self, line): - """ - Returns None if not a comment - """ - line = line.lstrip() - if line.startswith(';') or line.startswith('#'): - return line[1:] - return None - - def get_section(self, line): - """ - Returns None if not a section - """ - line = line.strip() - if not line.startswith('['): - return None - if not line.endswith(']'): - self.error_section_without_end_bracket(line) - return None - return line[1:-1] - - def process_assignment(self, name, accumulated_content): - content = '\n'.join([l.lstrip() for l in accumulated_content]) - self.assignment(name.strip(), content) - - def assignment(self, name, content): - raise NotImplementedError - - def new_section(self, section): - if not section: - self.error_no_section_name() - self.section = section - - def add_comment(self, comment): - pass - - def error_continuation_without_assignment(self, line): - self.parse_error('Invalid indentation', line) - - def error_section_without_end_bracket(self, line): - self.parse_error('Invalid section (must end with ])', line) - - def error_missing_equal(self, line): - self.parse_error( - 'Lines should look like "name=value" or "name: value"', - line) - - def error_no_section_name(self): - self.parse_error( - 'Empty section name ([])') - - def parse_error(self, msg, line=None): - raise ParseError( - msg, - filename=self.filename, - lineno=self.lineno, - line=line) - -class BasicParser(INIParser): - - """ - A simple subclass of INIParser; creates a nested data structure - like ``{'section_name': {'variable': ['values']}}`` - - Usage:: - - >>> p = BasicParser() - >>> p.load('config.ini') - >>> data = p.data - """ - - def reset(self): - self.data = {} - INIParser.reset(self) - - def assignment(self, name, content): - if not self.section: - self.parse_error( - 'Assignments can only occur inside sections; no section has been defined yet') - section = self.data.setdefault(self.section, {}) - section.setdefault(name, []).append(content) diff --git a/lib/config/inischema.py b/lib/config/inischema.py deleted file mode 100644 index 683eb16..0000000 --- a/lib/config/inischema.py +++ /dev/null @@ -1,431 +0,0 @@ -""" -.ini file schemas - -You can define a schema for your .ini configuration files, which -defines the types and defaults for the values. You can also define -a catchall attribute. - -TODO ----- - -Currently, this does not deal with sections at all, and sections are -not allowed. That wil take some more thought, as the schemas will -probably be per-section (though one section may inherit from another, -or defaults may be inherited, etc, which should be allowed for). - -Documentation isn't kept track of, nor is it generated. It should -possible to indicate with ``opt(help=...)``, and as an option -to ``INISchema.ini_repr()`` you should be able to put in documentation -in comments. - -Comments aren't kept track of. - -Key order isn't kept track of. - -Minimal .ini files should be possible to generate -- only generating -keys when the default doesn't match the actual value. - -There should be a way to check that the entire config is loaded, and -there are no missing values (options which weren't set and have no -default). - -Usage ------ - -:: - - class VHostSchema(INISchema): - - server_name = opt() - port = optint(default=80) - # optlist means this can show up multiple times: - server_alias = optlist(default=[]) - document_root = opt() - - vhost = VHostSchema() - vhost.load('config.ini') - connect(vhost.server_name, vhost.port) - # etc. - -Any schema can contain an ``optdefault()`` object, which will -pick up any keys that aren't specified otherwise (if not present, -extra keys are an error). Then you'll get a dictionary of lists, -for all the extra config values. (Use -``optdefault(allow_multiple=False)`` if you want a dictionary of -strings). - -If you expect multiple values, use ``optlist(subtype=optsomething())`` -for a key. The values will be collected in a list; if you don't -indicate ``subtype`` then ``opt()`` is used. Other types are -fairly easy to make through subclassing. - -You can generate config files by using ``schema.ini_repr()`` which -will return a string version of the ini file. - -Implementation --------------- - -This makes heavy use of descriptors. If you are not familiar with -descriptors, see http://users.rcn.com/python/download/Descriptor.htm - -It also makes light use of metaclasses. -""" - - -import iniparser - -class INIMeta(type): - - def __new__(meta, class_name, bases, d): - cls = type.__new__(meta, class_name, bases, d) - cls.__classinit__.im_func(cls, d) - return cls - -class ParseValueError(Exception): - pass - -class NoDefault: - pass - -class INISchema(object): - - __metaclass__ = INIMeta - - _default_option = None - _default_values = None - - _config_names = {} - _config_names_lower = {} - - case_insensitive = False - - def __classinit__(cls, d): - # We don't initialize INISchema itself: - if cls.__bases__ == (object,): - return - cls._config_names = cls._config_names.copy() - cls._config_names_lower = cls._config_names_lower.copy() - for name, value in d.items(): - if isinstance(value, opt): - cls.add_option(name, value) - # @@: We should look for None-ified options, and remove - # them from cls._config_names - - def add_option(cls, attr_name, option): - """ - Classmethod: add the option using the given attribute name. - This can be called after the class has been created, to - dynamically build up the options. - """ - if isinstance(option, optdefault): - # We use a list so that the descriptor behavior doesn't - # apply here: - cls._default_option = [option] - option.attr_name = attr_name - return - if option.names is None: - option.names = [attr_name] - option.attr_name = attr_name - for option_name in option.names: - cls._config_names[option_name] = option - cls._config_names_lower[option_name.lower()] = option - option.set_schema(cls) - setattr(cls, attr_name, option) - - add_option = classmethod(add_option) - - def __init__(self): - self._ini_attrs = {} - - def set_config_value(self, name, value): - if self.case_insensitive: - name = name.lower() - config_names = self._config_names_lower - else: - config_names = self._config_names - if config_names.has_key(name): - setattr(self, config_names[name].attr_name, value) - elif not self._default_option: - raise ParseValueError( - "The setting %r was not expected (from %s)" - % (name, ', '.join(config_names.keys()) or 'none')) - else: - self._default_option[0].set_config_value( - self, name, value) - - def _parser(self): - return SchemaINIParser(self) - - def load(self, filename, **kw): - """ - Loads the filename. Use the encoding keyword argument to - specify the file's encoding. - """ - self._parser().load(filename, **kw) - - def loadstring(self, string, **kw): - """ - Loads the string, which is the content of the ini files. - Use the filename keyword argument to indicate the filename - source (or another way to identify the source of the string - in error messages). - """ - self._parser().loadstring(string, **kw) - - def as_dict(self, fold_defaults=False): - """ - Returns the loaded configuration as a dictionary. - """ - # @@: default values won't show up here - v = self._ini_attrs.copy() - if fold_defaults: - if self._default_values: - v.update(self._default_values) - elif self._default_option: - v[self._default_option[0].attr_name] = self._default_values or {} - return v - - def ini_repr(self): - """ - Returns the loaded values as a string, suitable as a - configuration file. - """ - config_names = [] - used_options = {} - for option in self._config_names.values(): - if used_options.has_key(option): - continue - used_options[option] = None - config_names.append((option.names[0], option)) - config_names.sort() - if self._default_option: - config_names.append((None, self._default_option[0])) - result = [] - for name, option in config_names: - result.append(option.ini_assignment( - self, getattr(self, option.attr_name))) - return ''.join(result) - -class opt(object): - - default = NoDefault - - def __init__(self, names=None, **kw): - self.names = names - self.attr_name = None - self.schema = None - for name, value in kw.items(): - if not hasattr(self, name): - raise TypeError( - "The keyword argument %s is unknown" - % name) - setattr(self, name, value) - - def set_schema(self, schema): - self.schema = schema - - def __get__(self, obj, type=None): - if obj is None: - return self - try: - return obj._ini_attrs[self.attr_name] - except KeyError: - if self.default is not NoDefault: - return self.default - raise AttributeError( - "The attribute %s has not been set on %r" - % (self.attr_name, obj)) - - def __set__(self, obj, value): - new_value = self.convert(obj, value) - self.validate(obj, new_value) - self.set_value(obj, new_value) - - def convert(self, obj, value): - return value - - def validate(self, obj, value): - pass - - def set_value(self, obj, value): - obj._ini_attrs[self.attr_name] = value - - def __delete__(self, obj): - try: - del obj._ini_attrs[self.attr_name] - except KeyError: - raise AttributeError( - "%r does not have an attribute %s" - % (obj, self.attr_name)) - - def ini_assignment(self, obj, value, name=None): - if name is None: - name = self.names[0] - return '%s=%s\n' % (name, - self.ini_fold(self.ini_repr(obj, value))) - - def ini_fold(self, value): - lines = value.splitlines() - if len(lines) <= 1: - return value - lines = [lines[0]] + [' ' + l for l in lines[1:]] - return '\n'.join(lines) - - def ini_repr(self, obj, value): - return str(value) - -optstring = opt - -class optint(opt): - - max = None - min = None - - bad_type_message = "You must give an integer number, not %(value)r" - too_large_message = "The value %(value)s is too large" - too_small_message = "The value %(value)s is too small" - coerce = int - - def convert(self, obj, value): - try: - new_value = self.coerce(value) - except ValueError: - raise ParseValueError( - self.bad_type_message % {'value': value}) - if self.max is not None and new_value > self.max: - raise ParseValueError( - self.too_large_message % {'value': value}) - if self.min is not None and new_value < self.min: - raise ParseValueError( - self.too_small_message % {'value': value}) - return new_value - -class optfloat(optint): - - coerce = float - bad_type_message = "You must give a float number, not %(value)r" - -class optbool(opt): - - true_values = ('yes', 'true', '1', 'on') - false_values = ('no', 'false', '0', 'off') - - def convert(self, obj, value): - if value.lower() in self.true_values: - return True - elif value.lower() in self.false_values: - return False - else: - raise ParseValueError( - "Should be a boolean value (true/false, on/off, yes/no), not %r" - % value) - - def ini_repr(self, obj, value): - if value: - return 'true' - else: - return 'false' - -class optlist(opt): - - subtype = opt - - def __init__(self, *args, **kw): - opt.__init__(self, *args, **kw) - if isinstance(self.subtype, type): - self.subtype = self.subtype() - - def convert(self, obj, value): - return self.subtype.convert(obj, value) - - def validate(self, obj, value): - self.subtype.validate(obj, value) - - def set_value(self, obj, value): - obj._ini_attrs.setdefault(self.attr_name, []).append(value) - - def ini_assignment(self, obj, value, name=None): - if name is None: - name = self.names[0] - assert not isinstance(value, (str, unicode)), ( - "optlist attributes should receive lists or sequences, not " - "strings (%r)" % value) - all = [self.subtype.ini_assignment(obj, sub, name=name) - for sub in value] - return ''.join(all) - -class optdefault(opt): - - allow_multiple = True - - def set_config_value(self, obj, name, value): - if obj._default_values is None: - obj._default_values = {} - if self.allow_multiple: - obj._default_values.setdefault(name, []).append(value) - else: - if obj._default_values.has_key(name): - raise ParseValueError( - "You have already set the configuration key %r" - % name) - obj._default_values[name] = value - - def __get__(self, obj, type=None): - if obj is None: - return self - return obj._default_values or {} - - def __set__(self, obj, value): - raise AttributeError( - "The attribute %s cannot be set" % self.attr_name) - - def ini_assignment(self, obj, value, name=None): - assert name is None, "default values can't accept a name" - all = [] - all_values = self.__get__(obj).items() - all_values.sort() - for name, value in all_values: - if isinstance(value, (str, unicode)): - value = [value] - for subvalue in value: - all.append('%s=%s\n' % ( - name, self.ini_fold(self.ini_repr(obj, subvalue)))) - return ''.join(all) - -class optconverter(opt): - - misc_error_message = "%(error_type)s: %(message)s" - converter_func = None - - def convert(self, obj, value): - try: - return self.converter_func(value) - except Exception, e: - raise ParseValueError( - self.misc_error_message % { - 'message': str(e), - 'error_type': e.__class__.__name__}) - - _converters = {} - def get_converter(cls, name, converter_func): - if cls._converters.has_key(id(converter_func)): - return cls._converters[(name, id(func))] - else: - converter = cls(name, converter_func=converter_func) - cls._converters[(name, id(converter_func))] = converter - return converter - get_converter = classmethod(get_converter) - -class SchemaINIParser(iniparser.INIParser): - - def __init__(self, schema): - self.schema = schema - - def new_section(self, section): - self.parse_error("Schemas do not yet support sections") - - def assignment(self, name, content): - try: - self.schema.set_config_value(name, content) - except ParseValueError, e: - self.parse_error(e.args[0]) diff --git a/lib/config/lazyiniparser.py b/lib/config/lazyiniparser.py deleted file mode 100644 index 628a900..0000000 --- a/lib/config/lazyiniparser.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -A parser that keeps lots of information around, so the file can be -reconstructed almost exactly like it originally was entered. Also, if -there are errors with values, they can be tracked back to a file and -line number. -""" - -from iniparser import INIParser, ParseError - -class ConversionError(Exception): - pass - -def canonical_name(name): - return name.lower().replace(' ', '').replace('_', '') - -class LazyINIParser(INIParser): - - def __init__(self, allow_empty_sections=False): - self.allow_empty_sections = allow_empty_sections - INIParser.__init__(self) - - def reset(self): - self.configuration = Configuration() - self.last_comment = [] - - def add_comment(self, line): - if line.startswith(' '): - line = line[1:] - self.last_comment.append(line) - - def assignment(self, name, content): - item = Item(section=self.section, - name=name, - content=content, - comment='\n'.join(self.last_comment), - filename=self.filename, - lineno=self.start_lineno) - self.last_comment = [] - if not self.section: - self.new_section('') - self.section.add_item(item) - - def new_section(self, section): - if not section and not self.allow_empty_sections: - self.error_no_section_name() - self.section = Section( - name=section.strip(), - comment='\n'.join(self.last_comment)) - self.configuration.add_section(self.section) - self.last_comment = [] - -class Configuration(object): - - def __init__(self): - self.sections = [] - self.sections_by_name = {} - - def add_section(self, section): - self.sections.append(section) - # @@: I shouldn't be doing this here, I'll do it in lazyloader - #names = self.split_names(section.name) - #d = self.sections_by_name - #for name in names[:-1]: - # d = d.setdefault(name, {}) - #d.setdefault(names[-1], []).append(section) - - def split_names(self, name): - names = [] - while 1: - dot_pos = name.find('.') - paren_pos = name.find('(') - if dot_pos == -1 and paren_pos == -1: - next = canonical_name(name) - if next: - names.append(next) - return names - if (dot_pos == -1 or (dot_pos > paren_pos - and paren_pos != -1)): - next = canonical_name(name[:paren_pos]) - if next: - names.append(next) - name = name[paren_pos+1:] - next_pos = name.find(')') - assert next_pos != -1, ( - "Bad section name, ) expected: %r" % name) - names.append(name[:next_pos]) - name = name[next_pos+1:] - else: - assert dot_pos != -1 - assert paren_pos == -1 or dot_pos < paren_pos - next = canonical_name(name[:dot_pos]) - assert next, ( - "Empty name") - names.append(next) - name = name[dot_pos+1:] - - def source(self): - return '\n\n'.join([s.source() for s in self.sections]) - -class Section(object): - - def __init__(self, name, comment): - self.name = name - self.comment = comment - self.items = [] - self.canonical = {} - - def add_item(self, item): - self.items.append(item) - self.canonical.setdefault( - canonical_name(item.name), []).append(item) - - def __repr__(self): - return '<%s name=%r>' % (self.__class__.__name__, self.name) - - def source(self): - s = '' - if self.comment: - s += '\n'.join(['# ' + l - for l in self.comment.splitlines()]) + '\n' - s += '[%s]\n' % self.name - s += ''.join([i.source() for i in self.items]) - return s - - -class Item(object): - - def __init__(self, section, name, content, comment, - filename, lineno): - self.section = section - self.name = name - self.content = content - self.comment = comment - self.filename = filename - self.lineno = lineno - - def value(self, name, converter=None, catch_all_exceptions=False): - if catch_all_exceptions: - ExcClass = Exception - else: - ExcClass = ConversionError - if converter is not None: - try: - return converter(self.content) - except ExcClass, e: - msg = str(e) - raise ParseError( - msg, - filename=self.filename, - lineno=self.lineno, - column=None) - else: - return self.content - - def __str__(self): - return self.content - - def __repr__(self): - return '<%s name=%r; value=%r>' % ( - self.__class__.__name__, self.name, self.content) - - def source(self): - s = '' - if self.comment: - s += '\n'.join(['# ' + l - for l in self.comment.splitlines()]) + '\n' - s += '%s = %s\n' % (self.name, self.content) - return s diff --git a/lib/config/lazyloader.py b/lib/config/lazyloader.py deleted file mode 100644 index dd1a58a..0000000 --- a/lib/config/lazyloader.py +++ /dev/null @@ -1,228 +0,0 @@ -""" -A config file loader that can load and nest multiple config files, the -config files can have structure, and the values can be tracked back to -their original file and line number. - -To start, you'd do something like: - - >>> config = LazyLoader() - -You could use ``.load(filename)`` to load a config file; for the -examples it is convenient to instead use loadstring, and give a fake -filename: - - >>> config_data = \"\"\" - ... [server] - ... port = 8000 - ... host = localhost - ... document_root = /var/www - ... \"\"\" - >>> config.loadstring(config_data, filename='config_data.conf') - >>> config['server']['port'] - '8000' - >>> config['server'].convert('port', int) - 8000 - >>> config['server'].convert('host', int) - Traceback (most recent call last): - ... - ValueError: Error in config_data.conf (section [server]), line 4 ('localhost'): - ValueError: invalid literal for int(): localhost - -Note that names are normalized, removing case, underscores, and -spaces. So to get to the document root: - - >>> config['server']['documentroot'] - '/var/www' - -You can also merge in values; for instance, consider a virtual host -that overrides global values: - - >>> vhost_data = \"\"\" - ... [vhost(my.host.com)] - ... document_root = /path/to/root - ... \"\"\" - >>> vhost_config = LazyLoader() - >>> vhost_config.loadstring(vhost_data, filename='vhost_data.conf') - >>> vhost_config['vhost'].keys() - ['my.host.com'] - -Note that key and section names can be nested with .'s, and ()'s quote -the values (so the key is ['my.host.com'] instead of -['my']['host']['com']). Then we may want to merge this in, based on a -condition (e.g., the hostname matches my.host.com): - - >>> config['server'].merge(vhost_config['vhost']['my.host.com']) - >>> config['server']['documentroot'] - '/path/to/root' - -""" - -from lazyiniparser import LazyINIParser, Item -from nested import NestedDict -import inischema -import re -from UserDict import UserDict, DictMixin - -class LazyLoader(NestedDict): - - def __init__(self, configs=None, mutable=False, nest=True, - section_name=None, master=None): - self.section_name = section_name - self.master = master - NestedDict.__init__(self, configs=configs, mutable=mutable, - nest=nest) - - def load(self, filename): - parser = LazyINIParser(allow_empty_sections=True) - parser.load(filename) - config = self._convert_configuration(parser.configuration) - self.add_config(config) - - def loadstring(self, s, filename=None): - parser = LazyINIParser(allow_empty_sections=True) - parser.loadstring(s, filename=filename) - config = self._convert_configuration(parser.configuration) - self.add_config(config) - - def merge(self, lazyloader): - if self.master: - self.master._propagate_merge([self.section_name], lazyloader) - else: - self._propagate_merge([], lazyloader) - - def _propagate_merge(self, slave_keys, lazyloader): - if self.master: - slave_keys.insert(0, self.section_name) - self.master._propagate_merge(slave_keys, lazyloader) - else: - configs = filter(None, lazyloader.configs) - new_configs = [] - for config in configs: - if slave_keys: - new_config = {} - set_config = new_config - for key in slave_keys[:-1]: - set_config[key] = {} - set_config = set_config[key] - set_config[slave_keys[-1]] = config - else: - new_config = config - self.add_config(new_config) - - def __getitem__(self, key): - try: - return NestedDict.__getitem__(self, key) - except KeyError, e: - if self.section_name is None: - section = 'global section' - else: - section = 'section [%s]' % self.section_name - raise KeyError( - "Key %r not found in %s" % (key, section)) - - def _convert_configuration(self, conf): - data = {} - for section in conf.sections: - section_keys = self._parse_keys(section.name) - if section_keys == ['global']: - section_keys = [] - for item in section.items: - item_keys = self._parse_keys(item.name) - all_keys = section_keys + item_keys - pos = data - for key in all_keys[:-1]: - pos = pos.setdefault(key, {}) - pos.setdefault(all_keys[-1], []).append(item) - return data - - def _parse_keys(self, name): - keys = [] - orig_name = name - name = name.strip() - while 1: - next_period = name.find('.') - next_paren = name.find('(') - if next_paren == -1 and next_period == -1: - next = self._canonical_name(name) - if next: - keys.append(next) - return keys - elif (next_paren == -1 - or next_period != -1 and next_period < next_paren): - next = self._canonical_name(name[:next_period]) - if next: - keys.append(next) - name = name[next_period+1:] - else: - assert next_paren != -1 - assert next_period == -1 or next_paren < next_period - next = self._canonical_name(name[:next_paren]) - if next: - keys.append(next) - name = name[next_paren+1:] - next_close = name.find(')') - if next_close == -1: - raise inischema.ParseValueError( - "Key name contains a ( with no closing ): %r" - % orig_name) - next = name[:next_close] - keys.append(next) - name = name[next_close+1:] - - _canonical_re = re.compile('[_ \t-]') - def _canonical_name(self, name): - return self._canonical_re.sub('', name.lower()) - - def _convert_single(self, key, value_list): - if isinstance(value_list[0], (dict, DictMixin, UserDict)): - return self.__class__( - value_list, mutable=self.mutable, nest=True, - section_name=key, master=self) - elif isinstance(value_list[0][-1], Item): - return value_list[0][-1].content - else: - return value_list[0][-1] - - def getlist(self, key, add_inherited=True): - results = self.raw_get(key, add_inherited=add_inherited) - converted = [] - for result_set in results: - if not isinstance(result_set, (list, tuple)): - result_set = [result_set] - for item in result_set: - if isinstance(item, (dict, UserDict, DictMixin)): - value = self.__class__( - [item], mutable=self.mutable, nest=self.nest) - elif isinstance(item, Item): - value = item.content - else: - value = item - converted.append(value) - return converted - - def _make_converter(self, key, converter): - if not isinstance(converter, inischema.opt): - converter = inischema.optconverter.get_converter( - key, converter_func=converter) - return converter - - def convert(self, key, converter): - converter = self._make_converter(key, converter) - try: - return converter.convert(None, self[key]) - except inischema.ParseValueError, e: - item = self.raw_get(key)[0][0] - message = ('Error in %s (section [%s]), line %i (%r):\n%s' - % (item.filename, item.section.name, item.lineno, - self[key], e)) - raise ValueError(message) - - def convertlist(self, key, converter, add_inherited=True): - converter = self._make_converter(key, converter) - value_list = self.getlist(key, add_inherited=add_inherited) - new_list = [] - try: - for value in value_list: - new_list.append(converter.convert(value)) - except inischema.ParseValueError, e: - pass diff --git a/lib/config/nested.py b/lib/config/nested.py deleted file mode 100644 index 24b2f65..0000000 --- a/lib/config/nested.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -Layers multiple dictionaries. - -Nested dictionaries can be traversed, with each dictionary shadowing -the previous dictionaries. So for example: - - >>> d = NestedDict([{'foo': 'bar'}]) - >>> d['foo'] - 'bar' - >>> d2 = d.clone(add_dict={'foo': 'bar2'}) - >>> d2['foo'] - 'bar2' - >>> d2.getlist('foo') - ['bar2', 'bar'] - -This works for deeply nested dictionaries, not just at the top level; -each nested dictionary gets wrapped in a NestedDict as well. -""" - -from UserDict import DictMixin, UserDict - -class NestedDict(DictMixin): - - def __init__(self, configs=None, mutable=True, nest=True): - if configs is None: - configs = [{}] - assert isinstance(configs, (list, tuple)), ( - "The configs must be a list or tuple, not: %r" - % configs) - self.configs = [conf.copy() for conf in configs] - self.mutable = mutable - self.nest = nest - - def __getitem__(self, key): - results = self.raw_get(key) - if not results: - raise KeyError, "Key not found: %r" % key - return self._convert_single(key, results) - - def add_config(self, config, position=0): - assert not isinstance(config, (str, unicode)), ( - "Bad configuration (not a mapping): %r" % config) - if position is None: - self.configs.append(config) - else: - self.configs.insert(position, config) - - def set_configs(self, new_configs): - self.configs[:] = [] - for config in new_configs: - self.add_config(config, None) - - def raw_get(self, key, add_inherited=True): - results = [] - if add_inherited: - all_configs = self.configs - else: - all_configs = [self.configs[-1]] - for config in all_configs: - if key in config: - if isinstance(config, (str, unicode)): - print [key, config, all_configs, self.configs] - results.append(config[key]) - return results - - def getlist(self, key, add_inherited=True): - results = self.raw_get(key, add_inherited=add_inherited) - converted = [] - for result_set in results: - if not isinstance(result_set, (list, tuple)): - result_set = [result_set] - converted.extend(self._convert_many(key, result_set)) - return converted - - def _convert_single(self, key, value_list): - if (not self.nest - or not isinstance(value_list[0], - (dict, DictMixin, UserDict))): - # @@: doesn't handle all dict alternatives - return value_list[0] - elif len(value_list) == 1 and self.mutable: - return value_list[0] - else: - return self.__class__( - value_list, mutable=self.mutable, nest=True) - - def _convert_many(self, key, value_list): - return [self._convert_single(key, [v]) for v in value_list] - - def __setitem__(self, key, value): - if self.mutable: - self.configs[0][key] = value - else: - raise KeyError, ( - "Dictionary is read-only") - - def __delitem__(self, key): - if not self.mutable: - raise KeyError, ( - "Dictionary is read-only") - if self.configs[0].has_key(key): - del self.configs[0][key] - elif self.has_key(key): - raise KeyError, ( - "You cannot delete the key %r, as it belongs to the " - "a master configuration %r" - % (key, self.master)) - else: - raise KeyError, ( - "Key does not exist: %r" % key) - - def keys(self): - return [key for key in self] - - def __contains__(self, key): - for config in self.configs: - if config.has_key(key): - return True - return False - - def has_key(self, key): - return key in self - - def __iter__(self): - used = {} - for config in self.configs: - for key in config: - if key in used: - continue - used[key] = None - yield key - - _clone_sentry = [] - - def clone(self, add_dict=None, mutable=_clone_sentry, - nest=_clone_sentry): - if mutable is self._clone_sentry: - mutable = self.mutable - if nest is self._clone_sentry: - nest = self.nest - new = self.configs[:] - if add_dict is not None: - new.insert(0, add_dict) - return self.__class__(new, mutable=mutable, nest=nest) - - def copy(self): - return dict(self.iteritems()) - - def __eq__(self, other): - if other is None: - return False - if (not hasattr(other, 'keys') - or not hasattr(other, '__getitem__')): - return False - for key in other: - if not key in self: - return False - for name, value in self.iteritems(): - if other[name] != value: - return False - return True - - def __cmp__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return '<%s %r>' % (self.__class__.__name__, - dict(self.iteritems())) diff --git a/lib/config/test_iniparser.py b/lib/config/test_iniparser.py deleted file mode 100644 index e10031f..0000000 --- a/lib/config/test_iniparser.py +++ /dev/null @@ -1,78 +0,0 @@ -import iniparser -import re - - -def parse(s, filename='test.ini'): - p = iniparser.BasicParser() - p.loadstring(s, filename=filename) - return p - - -def raises(sample, match): - if isinstance(match, str): - match = re.compile(match) - try: - parse(sample) - except iniparser.ParseError, e: - if match and not match.search(str(e)): - raise - else: - assert 0, "Parsing %r should have raised an error" % sample - - -def test_no_section(): - raises( - """# A file... -a=10 -""", r'^Assignments can only.*') - -def test_no_equal(): - raises( - """[a file] -this is a test -""", r'^Lines should look like.*') - -def test_bad_section(): - raises( - """[file -""", r'^Invalid section.*') - -def test_empty_section(): - raises( - """[] -""", r'^Empty section name.*') - -def test_equal_colon(): - sec = parse("""[test] -a=1:2 -b: 1=3""").data['test'] - assert sec['a'] == ['1:2'] - assert sec['b'] == ['1=3'] - -def test_multi_section(): - data = parse("""[test] -#a=1 -a=2 -a=3 -a=4 -[test2] -a=1 -""").data - assert data['test']['a'] == ['2', '3', '4'] - assert data['test2']['a'] == ['1'] - -def test_continuation(): - data = parse("""[test] -a = a - pretty - bird -\tb=3 -b=another - line -[test2] -[test3] -c=[blah] - [blah]""").data - assert data['test']['a'] == ['a\npretty\nbird\nb=3'] - assert data['test']['b'] == ['another\nline'] - assert data['test3']['c'] == ['[blah]\n[blah]'] diff --git a/lib/config/test_inischema.py b/lib/config/test_inischema.py deleted file mode 100644 index 04e1ac1..0000000 --- a/lib/config/test_inischema.py +++ /dev/null @@ -1,111 +0,0 @@ -from inischema import * - - -class Schema1(INISchema): - - number = optint() - maybe = optbool(names=['maybe', 'or not']) - float = optfloat() - string = opt() - string2 = opt() - rep = optlist(subtype=optint()) - whatever = opt(default=5) - - -class Schema2(INISchema): - - a = optint() - default = optdefault() - - -class Schema3(Schema2): - pass - -Schema3.add_option('default', optdefault(allow_multiple=False)) -Schema3.add_option('b', optlist(subtype=optint())) - - -def parse_schema(schema, string, filename='test.ini'): - if not isinstance(schema, INISchema): - schema = schema() - schema.loadstring(string) - return schema - - -load_ini = """ -number = 1 -float = 1 -or not = true -string = something -string2=something -rep=1 -rep=5 -rep=10 -""" - - -def test_load(): - s = parse_schema(Schema1, load_ini) - assert s.as_dict() == { - 'number': 1, - 'maybe': True, - 'float': 1.0, - 'string': 'something', - 'string2': 'something', - 'rep': [1, 5, 10]} - assert s.number == 1 - assert s.rep == [1, 5, 10] - assert s.whatever == 5 - assert s.string == 'something' - -def test_unload(): - s = parse_schema(Schema1, load_ini) - assert s.ini_repr() == """\ -float=1.0 -maybe=true -number=1 -rep=1 -rep=5 -rep=10 -string=something -string2=something -whatever=5 -""" - -default_ini = """ -a = 1 -b = 2 -c = 3 -""" - -def test_default(): - s = parse_schema(Schema2, default_ini) - assert s.as_dict() == { - 'a': 1, - 'default': {'b': ['2'], - 'c': ['3']}} - assert s.as_dict(fold_defaults=True) == { - 'a': 1, - 'b': ['2'], - 'c': ['3']} - -def test_default2(): - s = parse_schema(Schema3, default_ini) - assert s.as_dict() == { - 'a': 1, - 'b': [2], - 'default': {'c': '3'}} - assert s.as_dict(fold_defaults=True) == { - 'a': 1, 'b': [2], 'c': '3'} - -def test_gen(): - s = parse_schema(Schema2, default_ini) - assert s.ini_repr() == "a=1\nb=2\nc=3\n" - s.a = 2 - assert s.ini_repr() == "a=2\nb=2\nc=3\n" - -def test_gen2(): - s = parse_schema(Schema3, default_ini) - assert s.ini_repr() == "a=1\nb=2\nc=3\n" - s.a = 2 - assert s.ini_repr() == "a=2\nb=2\nc=3\n" diff --git a/lib/config/test_lazyiniparser.py b/lib/config/test_lazyiniparser.py deleted file mode 100644 index ff653ef..0000000 --- a/lib/config/test_lazyiniparser.py +++ /dev/null @@ -1,27 +0,0 @@ -import lazyiniparser - - -def parse(s, filename='test.ini'): - p = lazyiniparser.LazyINIParser() - p.loadstring(s, filename=filename) - return p.configuration - - -data = """\ -# a test -[section 1] -a = 2 -a2 = 3 -# one more... -a = 4 -""" - -def test_simple(): - c = parse(data) - assert len(c.sections) == 1 - assert c.sections[0].name == 'section 1' - assert c.sections[0].comment == 'a test' - items = c.sections[0].items - assert [i.name for i in items] == ['a', 'a2', 'a'] - assert [i.lineno for i in items] == [3, 4, 6] - assert c.source() == data diff --git a/lib/config/test_lazyloader.py b/lib/config/test_lazyloader.py deleted file mode 100644 index 9d178fd..0000000 --- a/lib/config/test_lazyloader.py +++ /dev/null @@ -1,111 +0,0 @@ -import sys -import lazyloader -from test_fixture import DoctestCollector, sorted, assert_error - -data1 = """\ -[ages] - -jeanette = 72 -dave = 54 - -[children] - -jeanette = dave -dave = ian -dave = monica -""" - -def test_create(): - config = lazyloader.LazyLoader() - config.loadstring(data1, filename="data1.conf") - assert sorted(config.keys()) == ['ages', 'children'] - assert config['ages']['jeanette'] == '72' - assert config['ages']['dave'] == '54' - assert config['children']['dave'] == 'monica' - assert config['children']['jeanette'] == 'dave' - assert config['children'].getlist('dave') == ['ian', 'monica'] - assert config['ages'].convert('jeanette', int) == 72 - assert_error( - config['children'].convert, 'jeanette', int, - error=ValueError, - text_re=r'data1.conf.*line 8.*invalid literal') - assert_error( - config['children'].convert, 'joe', str, - error=KeyError, - text_re=r'joe.*section.*children') - assert_error( - config.__getitem__, 'child', - error=KeyError) - -data2 = """\ -[children] -# Another child... -jeanette = mary -""" - -def test_fold(): - config = lazyloader.LazyLoader() - config.loadstring(data1, filename="data1.conf") - config.loadstring(data2, filename="data2.conf") - assert config['children']['jeanette'] == 'mary' - assert (sorted(config['children'].getlist('jeanette')) - == ['dave', 'mary']) - assert config['children']['dave'] == 'monica' - assert sorted(config.keys()) == ['ages', 'children'] - assert sorted(config['children'].keys()) == ['dave', 'jeanette'] - -data3 = """\ -children.mary = vada -[global] -children.mary = armen -""" - -data4 = """\ -george = chelsea -""" - -data5 = """\ -[something(here!)] -test.this.out = foo -""" - -def test_global(): - config = lazyloader.LazyLoader() - config.loadstring(data1, filename="data1.conf") - config.loadstring(data2, filename="data2.conf") - config.loadstring(data3, filename="data3.conf") - assert config['children']['mary'] == 'armen' - assert config['children'].getlist('mary') == ['vada', 'armen'] - merged = lazyloader.LazyLoader() - merged.loadstring(data4, filename="data4.conf") - config['children'].merge(merged) - assert config['children']['george'] == 'chelsea' - config.merge(merged) - assert config['george'] == 'chelsea' - merge2 = lazyloader.LazyLoader() - merge2.loadstring(data5, filename="data5.conf") - assert merge2['something']['here!']['test']['this']['out'] == 'foo' - config.merge(merge2) - assert config['something']['here!']['test']['this']['out'] == 'foo' - -collect_doctest = DoctestCollector(lazyloader) - -def parse_keys(n): - p = lazyloader.LazyLoader() - return p._parse_keys(n) - -def test_parse_section(): - data = [ - ('a', 'a'), - ('a.b', 'a', 'b'), - ('this . that', 'this', 'that'), - ('this...that', 'this', 'that'), - ('foo_bar(foo_bar)', 'foobar', 'foo_bar'), - ('A.(B).C', 'a', 'B', 'c'), - ('A(B)C', 'a', 'B', 'c'), - ('a ( b ) . c . d . ( e )', 'a', ' b ', 'c', 'd', ' e '), - ] - for trial in data: - input = trial[0] - output = list(trial[1:]) - assert parse_keys(input) == output diff --git a/lib/config/test_nested.py b/lib/config/test_nested.py deleted file mode 100644 index 83521de..0000000 --- a/lib/config/test_nested.py +++ /dev/null @@ -1,56 +0,0 @@ -import nested -from test_fixture import DoctestCollector, sorted - -NestedDict = nested.NestedDict - - -def test_empty(): - d = NestedDict() - d['a'] = 1 - assert d['a'] == 1 - d['a'] = 2 - assert d['a'] == 2 - src = {'c': 2} - d['b'] = src - assert d['b'] == src - assert d['b'] is src - assert d['b']['c'] == 2 - d2 = d.clone() - assert d == d2 - d2['a'] = 3 - assert d.items() != d2.items() - assert list(d.iteritems()) != list(d2.iteritems()) - print list(d.iteritems()), list(d2.iteritems()) - print d.configs, d2.configs - assert d != d2 - assert repr(d) != repr(d2) - assert d2['a'] == 3 - d3 = d.copy() - assert d == d3 - assert d != None - assert d != [] - assert d != object() - -def test_nested(): - src = { - 'a': 1, - 'b': { - 'c': 2, - 'd': 3, - }} - shadow = { - 'b': { - 'c': 5, - 'e': 6, - }} - d = NestedDict([shadow, src]) - assert d['a'] == 1 - assert d['b']['c'] == 5 - assert d['b']['d'] == 3 - assert sorted(d.keys()) == ['a', 'b'] - assert sorted(d['b'].keys()) == ['c', 'd', 'e'] - assert isinstance(d['b'], NestedDict) - concrete = {'a': 1, 'b': {'c': 5, 'd': 3, 'e': 6}} - assert d == concrete - -collect_doctest = DoctestCollector(nested) diff --git a/lib/convert_html.py b/lib/convert_html.py deleted file mode 100644 index 778bc74..0000000 --- a/lib/convert_html.py +++ /dev/null @@ -1,54 +0,0 @@ -from common import htmlEncode -#import py2html, PyFontify -import PySourceColor -import converter_registry - -def convert_html(page, text, mime_type): - return text - -converter_registry.register(convert_html, 'text/html') - -def convert_text(page, text, mime_type): - return ''' - -%s''' % (page.sourceLinkForMimeType(mime_type), - htmlEncode(text)) - -converter_registry.register(convert_text, 'text/*') - -def convert_application(page, text, mime_type): - return ''' - View file (%s) - ''' % (page.sourceLinkForMimeType(mime_type), mime_type) - -converter_registry.register(convert_application, 'application/*') - -def convert_image(page, text, mime_type): - attrs = '' - if page.width: - attrs += ' width="%i"' % page.width - if page.height: - attrs += ' height="%i"' % page.height - return '
, tag='pre', attrs=[('class', 'screen')]
- if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
- uattrs = []
- # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
- for key, value in attrs:
- if type(value) != type(u''):
- value = unicode(value, self.encoding)
- uattrs.append((unicode(key, self.encoding), value))
- strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
- if tag in self.elements_no_end_tag:
- self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
- else:
- self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
- def unknown_endtag(self, tag):
- # called for each end tag, e.g. for , tag will be 'pre'
- # Reconstruct the original end tag.
- if tag not in self.elements_no_end_tag:
- self.pieces.append("%(tag)s>" % locals())
-
- def handle_charref(self, ref):
- # called for each character reference, e.g. for ' ', ref will be '160'
- # Reconstruct the original character reference.
- self.pieces.append('%(ref)s;' % locals())
-
- def handle_entityref(self, ref):
- # called for each entity reference, e.g. for '©', ref will be 'copy'
- # Reconstruct the original entity reference.
- self.pieces.append('&%(ref)s;' % locals())
-
- def handle_data(self, text):
- # called for each block of plain text, i.e. outside of any tag and
- # not containing any character or entity references
- # Store the original text verbatim.
- if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
- self.pieces.append(text)
-
- def handle_comment(self, text):
- # called for each HTML comment, e.g.
- # Reconstruct the original comment.
- self.pieces.append('' % locals())
-
- def handle_pi(self, text):
- # called for each processing instruction, e.g.
- # Reconstruct original processing instruction.
- self.pieces.append('%(text)s>' % locals())
-
- def handle_decl(self, text):
- # called for the DOCTYPE, if present, e.g.
- #
- # Reconstruct original DOCTYPE
- self.pieces.append('' % locals())
-
- _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
- def _scan_name(self, i, declstartpos):
- rawdata = self.rawdata
- n = len(rawdata)
- if i == n:
- return None, -1
- m = self._new_declname_match(rawdata, i)
- if m:
- s = m.group()
- name = s.strip()
- if (i + len(s)) == n:
- return None, -1 # end of buffer
- return name.lower(), m.end()
- else:
- self.handle_data(rawdata)
-# self.updatepos(declstartpos, i)
- return None, -1
-
- def output(self):
- '''Return processed HTML as a single string'''
- return ''.join([str(p) for p in self.pieces])
-
-class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
- def __init__(self, baseuri, baselang, encoding):
- sgmllib.SGMLParser.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
-
- def decodeEntities(self, element, data):
- data = data.replace('<', '<')
- data = data.replace('<', '<')
- data = data.replace('>', '>')
- data = data.replace('>', '>')
- data = data.replace('&', '&')
- data = data.replace('&', '&')
- data = data.replace('"', '"')
- data = data.replace('"', '"')
- data = data.replace(''', ''')
- data = data.replace(''', ''')
- if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- data = data.replace('<', '<')
- data = data.replace('>', '>')
- data = data.replace('&', '&')
- data = data.replace('"', '"')
- data = data.replace(''', "'")
- return data
-
-class _RelativeURIResolver(_BaseHTMLProcessor):
- relative_uris = [('a', 'href'),
- ('applet', 'codebase'),
- ('area', 'href'),
- ('blockquote', 'cite'),
- ('body', 'background'),
- ('del', 'cite'),
- ('form', 'action'),
- ('frame', 'longdesc'),
- ('frame', 'src'),
- ('iframe', 'longdesc'),
- ('iframe', 'src'),
- ('head', 'profile'),
- ('img', 'longdesc'),
- ('img', 'src'),
- ('img', 'usemap'),
- ('input', 'src'),
- ('input', 'usemap'),
- ('ins', 'cite'),
- ('link', 'href'),
- ('object', 'classid'),
- ('object', 'codebase'),
- ('object', 'data'),
- ('object', 'usemap'),
- ('q', 'cite'),
- ('script', 'src')]
-
- def __init__(self, baseuri, encoding):
- _BaseHTMLProcessor.__init__(self, encoding)
- self.baseuri = baseuri
-
- def resolveURI(self, uri):
- return _urljoin(self.baseuri, uri)
-
- def unknown_starttag(self, tag, attrs):
- attrs = self.normalize_attrs(attrs)
- attrs = [(key, ((tag, key) in self.relative_uris) and self.resolveURI(value) or value) for key, value in attrs]
- _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-
-def _resolveRelativeURIs(htmlSource, baseURI, encoding):
- if _debug: sys.stderr.write('entering _resolveRelativeURIs\n')
- p = _RelativeURIResolver(baseURI, encoding)
- p.feed(htmlSource)
- return p.output()
-
-class _HTMLSanitizer(_BaseHTMLProcessor):
- acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
- 'blockquote', 'br', 'button', 'caption', 'center', 'cite', 'code', 'col',
- 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset',
- 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
- 'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu', 'ol', 'optgroup',
- 'option', 'p', 'pre', 'q', 's', 'samp', 'select', 'small', 'span', 'strike',
- 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
- 'thead', 'tr', 'tt', 'u', 'ul', 'var']
-
- acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
- 'action', 'align', 'alt', 'axis', 'border', 'cellpadding', 'cellspacing',
- 'char', 'charoff', 'charset', 'checked', 'cite', 'class', 'clear', 'cols',
- 'colspan', 'color', 'compact', 'coords', 'datetime', 'dir', 'disabled',
- 'enctype', 'for', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace',
- 'id', 'ismap', 'label', 'lang', 'longdesc', 'maxlength', 'media', 'method',
- 'multiple', 'name', 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly',
- 'rel', 'rev', 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
- 'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title', 'type',
- 'usemap', 'valign', 'value', 'vspace', 'width']
-
- unacceptable_elements_with_end_tag = ['script', 'applet']
-
- def reset(self):
- _BaseHTMLProcessor.reset(self)
- self.unacceptablestack = 0
-
- def unknown_starttag(self, tag, attrs):
- if not tag in self.acceptable_elements:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack += 1
- return
- attrs = self.normalize_attrs(attrs)
- attrs = [(key, value) for key, value in attrs if key in self.acceptable_attributes]
- _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-
- def unknown_endtag(self, tag):
- if not tag in self.acceptable_elements:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack -= 1
- return
- _BaseHTMLProcessor.unknown_endtag(self, tag)
-
- def handle_pi(self, text):
- pass
-
- def handle_decl(self, text):
- pass
-
- def handle_data(self, text):
- if not self.unacceptablestack:
- _BaseHTMLProcessor.handle_data(self, text)
-
-def _sanitizeHTML(htmlSource, encoding):
- p = _HTMLSanitizer(encoding)
- p.feed(htmlSource)
- data = p.output()
- if TIDY_MARKUP:
- # loop through list of preferred Tidy interfaces looking for one that's installed,
- # then set up a common _tidy function to wrap the interface-specific API.
- _tidy = None
- for tidy_interface in PREFERRED_TIDY_INTERFACES:
- try:
- if tidy_interface == "uTidy":
- from tidy import parseString as _utidy
- def _tidy(data, **kwargs):
- return str(_utidy(data, **kwargs))
- break
- elif tidy_interface == "mxTidy":
- from mx.Tidy import Tidy as _mxtidy
- def _tidy(data, **kwargs):
- nerrors, nwarnings, data, errordata = _mxtidy.tidy(data, **kwargs)
- return data
- break
- except:
- pass
- if _tidy:
- utf8 = type(data) == type(u'')
- if utf8:
- data = data.encode('utf-8')
- data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
- if utf8:
- data = unicode(data, 'utf-8')
- if data.count(''):
- data = data.split('>', 1)[1]
- if data.count('= '2.3.3'
- assert base64 != None
- user, passw = base64.decodestring(req.headers['Authorization'].split(' ')[1]).split(':')
- realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
- self.add_password(realm, host, user, passw)
- retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
- self.reset_retry_count()
- return retry
- except:
- return self.http_error_default(req, fp, code, msg, headers)
-
-def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers):
- """URL, filename, or string --> stream
-
- This function lets you define parsers that take any input source
- (URL, pathname to local or network file, or actual data as a string)
- and deal with it in a uniform manner. Returned object is guaranteed
- to have all the basic stdio read methods (read, readline, readlines).
- Just .close() the object when you're done with it.
-
- If the etag argument is supplied, it will be used as the value of an
- If-None-Match request header.
-
- If the modified argument is supplied, it must be a tuple of 9 integers
- as returned by gmtime() in the standard Python time module. This MUST
- be in GMT (Greenwich Mean Time). The formatted date/time will be used
- as the value of an If-Modified-Since request header.
-
- If the agent argument is supplied, it will be used as the value of a
- User-Agent request header.
-
- If the referrer argument is supplied, it will be used as the value of a
- Referer[sic] request header.
-
- If handlers is supplied, it is a list of handlers used to build a
- urllib2 opener.
- """
-
- if hasattr(url_file_stream_or_string, 'read'):
- return url_file_stream_or_string
-
- if url_file_stream_or_string == '-':
- return sys.stdin
-
- if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp'):
- if not agent:
- agent = USER_AGENT
- # test for inline user:password for basic auth
- auth = None
- if base64:
- urltype, rest = urllib.splittype(url_file_stream_or_string)
- realhost, rest = urllib.splithost(rest)
- if realhost:
- user_passwd, realhost = urllib.splituser(realhost)
- if user_passwd:
- url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
- auth = base64.encodestring(user_passwd).strip()
- # try to open with urllib2 (to use optional headers)
- request = urllib2.Request(url_file_stream_or_string)
- request.add_header('User-Agent', agent)
- if etag:
- request.add_header('If-None-Match', etag)
- if modified:
- # format into an RFC 1123-compliant timestamp. We can't use
- # time.strftime() since the %a and %b directives can be affected
- # by the current locale, but RFC 2616 states that dates must be
- # in English.
- short_weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- request.add_header('If-Modified-Since', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (short_weekdays[modified[6]], modified[2], months[modified[1] - 1], modified[0], modified[3], modified[4], modified[5]))
- if referrer:
- request.add_header('Referer', referrer)
- if gzip and zlib:
- request.add_header('Accept-encoding', 'gzip, deflate')
- elif gzip:
- request.add_header('Accept-encoding', 'gzip')
- elif zlib:
- request.add_header('Accept-encoding', 'deflate')
- else:
- request.add_header('Accept-encoding', '')
- if auth:
- request.add_header('Authorization', 'Basic %s' % auth)
- if ACCEPT_HEADER:
- request.add_header('Accept', ACCEPT_HEADER)
- request.add_header('A-IM', 'feed') # RFC 3229 support
- opener = apply(urllib2.build_opener, tuple([_FeedURLHandler()] + handlers))
- opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent
- try:
- return opener.open(request)
- finally:
- opener.close() # JohnD
-
- # try to open with native open function (if url_file_stream_or_string is a filename)
- try:
- return open(url_file_stream_or_string)
- except:
- pass
-
- # treat url_file_stream_or_string as string
- return _StringIO(str(url_file_stream_or_string))
-
-_date_handlers = []
-def registerDateHandler(func):
- '''Register a date handler function (takes string, returns 9-tuple date in GMT)'''
- _date_handlers.insert(0, func)
-
-# ISO-8601 date parsing routines written by Fazal Majid.
-# The ISO 8601 standard is very convoluted and irregular - a full ISO 8601
-# parser is beyond the scope of feedparser and would be a worthwhile addition
-# to the Python library.
-# A single regular expression cannot parse ISO 8601 date formats into groups
-# as the standard is highly irregular (for instance is 030104 2003-01-04 or
-# 0301-04-01), so we use templates instead.
-# Please note the order in templates is significant because we need a
-# greedy match.
-_iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-MM', 'YYYY-?OOO',
- 'YY-?MM-?DD', 'YY-?OOO', 'YYYY',
- '-YY-?MM', '-OOO', '-YY',
- '--MM-?DD', '--MM',
- '---DD',
- 'CC', '']
-_iso8601_re = [
- tmpl.replace(
- 'YYYY', r'(?Por
tag, find all - the html up to the
tag, allowing for nested tags. - """ - fullHTML = html - tagNest = 0 - while 1: - match = tagRE.search(html) - if not match: - return fullHTML - tag = match.group(1).lower() - if tag == tagStart: - tagNest += 1 - elif tag == '/' + tagStart: - if not tagNest: - return fullHTML[:-(len(html)-match.start())] - tagNest -= 1 - html = html[match.end():] - -def guess_abstract(html): - """ - Finds the first tag, and we consider it to be the
- abstract.
- """
- fullHTML = html
- while 1:
- match = tagRE.search(html)
- if not match:
- return fullHTML
- html = html[match.end():]
- tag = match.group(1).lower()
- if tag == 'p':
- return find_match_tag(html, tag)
diff --git a/lib/htmldiff.py b/lib/htmldiff.py
deleted file mode 100755
index 4b51692..0000000
--- a/lib/htmldiff.py
+++ /dev/null
@@ -1,261 +0,0 @@
-#!/usr/bin/env python
-"""
-htmldiff.py
-(C) Ian Bicking This is a test and more here
-there
- The first para.
-
- The second para
- Related terms:\n')
- out.write(', '.join([
- '%s' % (p.link, htmlEncode(p.title))
- for p in keywordPages]))
- out.write(' %s I noticed that the list of settings in the users guide
+http://webware.sourceforge.net/Webware-0.7/WebKit/Docs/UsersGuide.html#Configuration
+cannot possibly be up to date. will give you a list of the settings in the code. gives just the names. (Love that line noise!) may give you a little context of how it is used. Can we come up with a convention for documenting settings in-line? see ConfigSchema -- TerrelShumway - 24 Apr 2002 These are divided into open source vs. others. Open Source: analog: http://analog.cx/ awstats: http://awstats.sourceforge.net/ visitors: http://www.hping.org/visitors/ Others: LiveStats: http://www.deepmetrix.com/ Statomatic: http://www.statomatic.com/ Summary: http://summary.net/ Search Links: Log Analysis Category: http://sourceforge.net/softwaremap/trove_list.php?form_cat=245 -- ChuckEsterbrook - 04 May 2002
+-- SergeySchetinin - 06 Dec 2003 Contents LoggingKit provides an flexible approach to include various kinds of
+log messages in your applications. You can get the latest version of LoggingKit at: http://www.scourbase.com/LoggingKit/ Include log messages in your application. Example: There are methods for every possible LogLevel (INFO, TRACE, WARN,
+ERROR, FATAL). Capture those messages LoggingKit uses the concept of <nop>LogListeners with respond to
+incoming log messages. Say you want to save all messages which occur in the category
+'TestCategory' and are in the LogLevel INFO into the file
+/var/log/TestCategoryInfo.log and discard all TRACE messages. The following Logger.config will do it: -- SaschaMatzke - 16 Nov 2001 I realized that this package wasn't available a long time (I somehow
+f***ed up the URL). If anyone is still interested in this piece of
+software it can be found at: http://www.scourbase.com/LoggingKit/ -- SaschaMatzke - 03 Feb 2003 <br /> LoginKit handles logins in Webware, using Component. Servlets can add
+authentication (cookie and HTTP) without changing their
+inheritance or their logic. Additional (optional) methods can be
+added to servlets to indicate the needed permission level. Authentication/identification is done with a separate UserManager
+object, which abstracts out the identification source, as well as the
+details of your user class. Only minimal requirements are made of
+your user and authentication system. View the Source: http://svn.w4py.org/LoginKit/ Download the Package: http://www.w4py.org/downloads/LoginKit/ Unfortunately, the package does not contain a User's Guide yet. Webware has a logging facility you can use for basic debugging. If you are running the application server in OneShot mode, output of
+self.log('Message') calls will appear toward the very bottom of the
+page if you have the showconsole parameter set to 1 in
+conf/oneshot.conf. Otherwise, the log messages will appear in the
+Webware console. FYI, the logging method is defined in WebKit.Servlet, and this is
+inherited by HTTPServlet, and then by Page. So, you should have this
+method available pretty much everywhere, unless it was over-ridden
+accidentally. -- EdmundLian - 18 Mar 2002 I'd also recommend Sacha's LoggingKit. It's very well designed and
+quite flexible. -- TavisRudd - 18 Mar 2002 More information from BenParker: The constant AppServer will write to stdout, which could be the
+console, or you could redirect it to a file, which is what most
+people do. Then you can "tail -f" the file to monitor the output
+via the console, and still have a record of it in the file. You
+could also pipe the output to a logging program instead of just
+appending straight to a file. OneShot errors always appear in our apache error logs. Apache
+runs the CGI process, so error output from the process feeds back
+into Apache. Standard "print ..." messages appear at the bottom
+of every OneShot page as long as you have verbose=1 in your
+OneShot config file; the stdout is captured by the OneShotAdapter
+I believe ... maybe the OneShotAppServer... -- EdmundLian - 18 Mar 2002 If this PEP becomes part of the core (which seems likely), this could
+be the basis for logging: PEP 282 -- IanBicking - 19 Mar 2002 MakeAppWorkDir is a script located in the bin directory of your
+webware installation. If you run it, it will create a working
+directory for your application, complete with config files, sample
+context, etc. You can then add a Lib directory and whatever other
+directories you need. My directory structure looks like this: From my servlets I can import Python modules like SitePage out of Lib
+using for example: The great thing about using a working directory created with
+MakeAppWorkDir is that you don't have to modify your webware directory
+at all. All configuration files are contained in your working
+directory in the Config subdirectory. -- GeoffTalvola - 29 Oct 2001 How do you tell Webware where /MyWebSite is? I had to do some simple changes to use the <nop>AppServer from this
+new working directory as a daemon. the "webkit" script needs the path to the new "AppServer" script the PID, needed for stopping Webware, now comes from the
+"<nop>AppServer" script comment the line "echo $! > $PID_FILE" from
+"webkit" in and change <nop>AppServer to: Hope this make sense :-) -- StephanDiehl - 03 Jan 2002 Best Practices a URI is not a file, and the process of dereferencing uris into source
+resources shouldn't make assumptions about origin server filesystems some common assumptions to watch out for: the filesystem is writable directory names are valid python package names MVC: controller servlet manages its own subspace search engines and query strings WebDAV and Web Folders preserve semantics of HTTP GET and HEAD must be "safe" UriMapping -- "next generation" URI decoding for Webware -- TerrelShumway - 26 Apr 2002 Why not just have some kind of simple Python-based resource-finding
+plug-in function/method which parses paths (URIs or whatever) as they
+come in? I can see some advantages in having a function or object
+method which takes the raw string and scans it for keywords, for
+example. Perhaps everyone considered this in the discussions on the
+mailing list which I obviously wasn't following closely enough. -- PaulBoddie - 29 Apr 2002 That is what UriMapping is about: the goal is to refactor
+Application.serverSidePathForRequest into a separate class with
+pluggable behaviors (PPR:StrategyPattern). URLDecode is one way to do
+it. UriMapping explores many different strategy implementations, none
+of which should be considered "cannonical" (current behavior would be
+the "default" strategy). -- TerrelShumway - 29 Apr 2002 PSP provides a <psp:method> tag but that only lets you define a
+method using Python syntax. Sometimes you want to define a method
+using PSP syntax instead. If you're using Python 2.1 or higher, you
+can use nested scopes to accomplish this, in a somewhat hackish way.
+The best way to see how to do it is with an example: Save this as a PSP file and try it out. Note that this hack may be unsuitable for some applications because
+the method doesn't actually exist until the function definition is
+executed and the instance method created and assigned to self. This technique can also be used to create normal functions (not
+methods) in PSP syntax -- just leave out the call to instancemethod()
+and the assignment to self and be sure not to call the function until
+after it is defined. -- GeoffTalvola - 23 Apr 2002 MiddleKit (MK) is a component of Webware that provides for developing
+your application with an object model which is in turn used for other
+purposes. In short, an object model is simply a listing of your classes and
+their attributes including details such as type, name, min, max,
+is-required, samples, delete rules, etc. Some would refer to this as
+"meta data" or "configuration" or a "class hierarchy description". This Wiki page helps document some items that haven't made it to the
+main docs yet, but are still important and often appear on the mailing
+list. Originally, MK was developed purely as an "object-relational mapper"
+or ORM, eg, software that maps objects to SQL databases. However,
+experience shows that the object model can be used for many other
+purposes including user interface. This makes MK much more than just an ORM. Here is an excerpt from a
+reply I wrote: The more I use MiddleKit the more uses I have for the object model: generating forms generating object browsing pages generating detailed views generating sample data generating SQL table definitions generating runtime SQL Note that SQL now occupies less than half the list of uses for an
+object model. The pay off to using the object model is that it is very easy to
+edit and all of the above things automagically work right after
+the edit (with the occasional caveat). Another use for the object model is as a concise, precise document of
+what data is being tracked and what their relationships, delete rules,
+etc. are. The object model is much more clean to read and write than
+either Python or SQL. I even use them in meetings with non-technical
+clients. MK will have its documentation revamped to reflect the object-model
+focus and eventually receive additional code from a project of mine to
+aid in the above tasks. You can read the MK docs online at: If you wish to begin using your object models in a generic fashion,
+read this section. Although it certainly encourages it. In order to simulate object
+references and lists, as Python programmers are used to the terms,
+MiddleKit has some interesting schema approaches. In particular, an
+object reference is implemented as a 64-bit id split into 2 parts: the
+class id and object id. This approach allows for the full power of
+pointing to arbitrary objects. As a Python user you will never notice
+this (MK hides it from you), but it doesn't jive well with other SQL
+tools such as a SQL-oriented report generator. If that is a problem for your projects, then do not use the "obj ref"
+type described in the docs. Instead use whatever type you are using
+for your SQL joins (such as "int" or "string") and implement the
+method yourself to fetch the object. You will still get the other MK
+benefits such as an object model (which makes a nice form of concise
+documentation), uniquing, autogeneration of UPDATEs and INSERTs, the
+other object model uses described above, etc. When you change your object model, your SQL schema changes as well. If
+your database is already live and you can't discard the data, you have
+to ALTER it into shape. I used to do this by hand, but GeoffTalvola
+pointed me to mysqldiff which saves enormous effort. Before you use
+it, read the important MiddleKit note on the MySQLThirdParty page! See MiddleKitAndWebKit. For unknown reasons, the question comes up periodically, "Is MiddleKit
+still being actively developed?" The answer is "yes". I use MiddleKit
+in all my projects on a daily basis and still check in fixes and
+enhancements. MK will only get bigger and better. I think this might come up because MK is less active than WebKit for a
+couple reasons. One, is that WebKit is more popular and hence has more
+users peering at it, asking questions, submitting patches,
+etc. Another is that MK is exceptionally stable with an excellent test
+suite. While MK has a wish list like any other project, stability
+and testing are already there. Frequent patches aren't required. -- ChuckEsterbrook - 31 Dec 2001 MiddleKit and WebKit Unfortunately, the MiddleKit docs don't do a good job of describing
+the use of MiddleKit from WebKit. This page will be a place to grow
+such documentation. The most commonly asked question is about getting a store going for
+the WebKit application. Here is a reply I made to that question: -- ChuckEsterbrook - 29 Mar 2002 It makes sense to want to store Middle objects in the session, so that
+these objects can be retrieved easily. There are a few issues to
+consider with this: since Middle objects contain database connections, they cannot be
+pickled (stored to disk) in a straightforward fashion you wouldn't want the data fields pickled anyways, due to
+concurrency issues. For example, if an object is pickled on disk in
+one user's session, and then gets modified (in-memory) by another
+user, you now have two copies of the object with different field
+values. How do you resolve this? My solution to this problem is to implement an alternate
+encoder/decoder for WebKit.SessionStore which recognizes Middle
+objects, and simply pickles a reference to the object. When reading
+in sessions from disk, the decoder finds the references and fetches
+the full Middle objects from the store. This is transparent to user code -- you can simply put the Middle objects in the session, and it will work. Note that this implementation assumes you have a single global store for your application. Also, if you're using Webware 0.7 or older, you'll need to apply <a href="http://www.opensky.ca/~jdhildeb/webware.session.patch">this small patch</a> so that calling setEncoderDecoder will work properly when using the Dynamic session store. MiddlePickle.py: You'll have to tell the session handler to use this new implementation
+for encoding/decoding objects. Use the contextInitialize() function
+for this, which Webware will invoke to initialize your application.
+For example, put the following in your MyContext/__init__.py file: -- JasonHildebrand - 18 Feb 2003 No content... Issues with MIME Types When you are using the Webware provided Webserver, chances are that
+Microsoft documents are not recognised. The reason for that is that
+the python module mimetypes doesn't have them predefined. Do one of the following: add a fully functional "mime.types" file reference into the
+"knownfiles" list add the correct mimetype in the "types_map". "doc" documents have
+the type application/msword -- StephanDiehl - 14 Nov 2001 TextEditors - Pointers to and pointers about various text editors. WebwareListArchive - Pointers to the mail archives for the various Webware mailing lists. RelatedReading - Pointers to documents about the web, ORM, etc. TestingFramework - How to test your application. LogAnalyzers - Points to programs that analyze web traffic logs. VerboseOutput - How to enable verbose output only for your own servlets. MiscUtils is a collection of useful classes and functions for general
+use. Unlike most Webware components, it does not constitute an
+object-oriented framework. Pick and choose what you like. The online docs are at:
+http://webware.sourceforge.net/Webware/MiscUtils/Docs/ DataTable and Configurable are my favorite classes in MiscUtils. -- ChuckEsterbrook - 31 Dec 2001 The Model2+1 design is a bit like the ModelViewController paradigm,
+although maybe a bit better suited for web design. The idea, is to seperate display logic (pulling data from a
+db... etc), display and control logic (inserting data into a
+db). Luckly Webware makes this very easy to do. My approach to the Model2+1 paradigm is based on my experience with
+Jakarta Turbine. This approach features: Site logic: You provide almost all of your logic in utility
+classes. For example, you might have a UserUtils class that handles
+things like authentication. Using MiddleKit derived classes to
+access a database is a very good example of site logic. View: This is where all your front end design goes. Almost NO
+logic should exist in your view except the min required to display
+data from a logic class. Actions: Actions are the reverse of Views. The action will never
+directly display anything to the user. They handle things like forms
+where an action has to be performed. For example... Say you have an
+authentication form. When the user submits their login data, the
+data is sent directly to your login action. If the login action
+found errors in the submited data, it would return the user to the
+login form. If the action found no errors, it would perform
+authentication via your Auth/UserUtils class, then redirect the user
+to the next page. I am going to demonstrate using this approach, by showing you a simple
+WebKit site that uses a basic form of authentication. The code
+included on this page might not be perfect. I just wanted to provide a
+working example of how to use this model =) PLEASE NOTE: This example is based on a copy of Webware from CVS as of
+01/07/2002. You might want to use a current version of Webware form
+CVS if wish to use the examples provided. In addition to this, the
+version I am using requires the following patch to let the Security
+mixin work right with PSP. This patch has been commited to CVS, so if
+your CVS tree is newer than 01/07/2002 you should not need the patch. Here is an example of what the directory structure of this site looks
+like: SiteContext/action/: This is where specific actions are
+stored. For example, actions for dealing with a specific form. SiteContext/layout/: Simple classes which contain the basic
+layout(s) of the site. SiteContext/navigation/: This is where you have the bits and
+peices that make up a large portion of the design. Most of your
+navigations links/etc will go here. Main.psp, login.psp, ...: These files hold the design portion of
+the sites pages. Very little logic should exist inside these
+pages. Instead, call logic from other classes and/or use Action
+logic to handle working with data. lib/: This is where most of your site logic goes. Most of the
+modules in this directory will probably have Util in their name. WebKit/: this is a directory I created with Webware's
+MakeAppWorkDir command. It contains some of the WebKits Configs,
+logs and sessions for this specific site. Note that I am using mod_rewrite (see mod_rewrite recipes). This allows me to have apache serve
+up static content in addition to using WebKit: First we will start by setting up our Security system. We are going to
+use MiddleKit to help us do this. To start, you should Create the
+directories: In the Security.mkmodel directory, we need to define our specification
+files. These are in comma-separated value format and you can use your
+favorite spread sheet program to edit them. I personally use
+OpenOffice. See the MiddleKit documentation for more details. This contains our model definition: This contains example data for use with our model: This is used to set some settings in reguard to our model: Once you have these files setup... you will need to generate the
+python and SQL source. You can do this by typing: Once this is done... you should have a bunch of files sitting in your
+Security, Security/GeneratedPy and Security/GeneratedSQL
+directories. Now would be a good time to setup the database with the tables needed
+for our security system... This can be done by doing the fillowing: If there where no errors, things should be going well. We have three steps left before the security system is done... Edit
+User.py and create the SecureMixIn and UserFactory classes. All of
+these files live in the lib/Security directory. We are going to add a method to this class so we can check if a user
+has the permissions we need: This class is used to add security features to a page. You will see
+later how this is mixed into the PSP pages. Note that the page can overwrite the reqPermissions method to require
+any specific permissions needed: The UserFactory is used to create new User objects from a username. It
+would be best to have this as a singleton class... but I will leave
+that up to the user =) NOTE: You should replace (USER) and (PASSWD) with that of a user
+which has proper access to the Security table we added earlier. This is the core layout class. layout Functionality generic to all
+the layout classes should be put in this file: This is the main layout class for the site. We use this to include all
+of our navigation components: The home page for the site. This page requires the user to be
+authenticated before it can be displayed. Notice how the SecureMixIn
+class is the first class to be extended. We need to do this so it can
+overwrite the preLayout method from SiteLayout: This is our login page: This is the action we use to handle logging the user in. This action
+should do the following: Make sure the form data is valid Make sure the requested user is valid Log the user in If the data/user is not valid, go back to login page. And finally... the logout action. This action is a lot simpler because
+we do not care about form data: By now you should have a basic site using the Model2+1
+paradigm. Although it is not perfect, you should have an idea on how
+to use this design within your own site. More information on the Model2+1 paradigm can be found on the Jakarta
+Turbine website. If you have any questions reguarding any of the information here, just
+shoot me an email =) -- LukeHolden - 07 Jan 2003 01/08/2003 6:53 pm: Login.py now returns error data back to
+login.psp -- LukeHolden Here is an example how to use the Model-View-Controller
+paradigm with Webware. I'll also show how to integrate
+unit testing into the development. Let's assume the task is to model a basket with apples. You
+can either add an apple or empty the basket. The basket can
+hold at most 10 apples. We'd like to have a web interface for adding apples and
+emptying the basket. The interface should show the contents
+of the basket and be able to react to the most common errors
+(e.g. emptying an empty basket or adding an apple to a full
+basket). I'll use three classes: Basket, Basket Controller and Basket Page.
+The basket (i.e. model) is responsible for administering the data
+structure and persistence. The controller is responsible for
+high-level operations on the basket and the translation of exceptions
+to user messages, and the page (i.e. view) is responsible for
+presenting the user interface (containing details on the basket) and
+forwarding user requests to the controller. The Basket class can be tested e.g. with the following lines: For smaller projects, I usually write tests like this and insert them
+into the file that is tested. Using the Emacs Python mode, I type
+Ctrl-C Ctrl-C each time I changed something. This key combination starts
+the Python interpreter and executes testBasket(). If you are using SciTe
+instead of Emacs as your editor, you can use the F5 key for this. If you do not use Emacs, SciTe or a Python IDE, you can run the tests
+from the commandline via: or doubleclick it, if you use e.g Windows Explorer. Larger projects need some more thought - you might e.g. want to switch
+to the unittest-Framework and / or write a test module which runs all
+tests that are available. With Emacs, you can import this module as
+above in every source file, otherwise you can start it as standalone
+program. As a next step, you add a controller which knows how to modify the
+basket and report the results to the (not yet implemented) view class: For testing this class, you do not need any knowledge about Webware
+data structures like requests, transactions, sessions etc. The class
+is not very complex, so the tests are rather straightforward (which
+means I won't present them here...). Finally, the Webware page is written. It communicates with themodel
+(to present data) and the controller (to modify data). It also
+translates the Webware specific data structures (transactions,
+requests and so on) to Webware independent structures that are
+understood by the controller: As you can see, this page does not contain much logic. Most of
+the issues will be about the layout - and this is something that
+cannot be automated anyway ;-) -- AlbertBrandl - 09 Nov 2001 This page describes how to use Apache's mod_rewrite module with
+WebKit, and mod_webkit in particular. It starts with a general
+overview and then presents some recipes for solving specific
+problems. Related reading: mod_rewrite reference docs, mod_rewrite guide mod_rewrite is an Apache module that is used to remap URLs. Here's how the Apache manual describes it: This module uses a rule-based rewriting engine (based on a
+regular-expression parser) to rewrite requested URLs on the
+fly. It supports an unlimited number of rules and an unlimited
+number of attached rule conditions for each rule to provide a
+really flexible and powerful URL manipulation mechanism. The URL
+manipulations can depend on various tests, for instance server
+variables, environment variables, HTTP headers, time stamps and
+even external database lookups in various formats can be used to
+achieve a really granular URL matching. It is an incredibly flexible tool that can be used for a wide range of
+tasks. Here's some examples: change the file layout of your site without breaking bookmarked or
+search-engine indexed URLs make a certain directory or file visible in every subdirectory of
+your site without needing to symlink to it in every subdir or manage
+complex relative paths. use mod_webkit to serve files that appear to the user as if they are
+in Apache's top directory ('DOCUMENT_ROOT'). Without mod_rewrite,
+each URL would look like www.mysite.com/WK/MyServlet.py. With
+mod_rewrite, you can use a simpler URL like
+www.mysite.com/MyServlet.py redirect to files on remote servers parsing 'variable assignment url prefixes' out of URLs before
+passing them on to webkit and adding the variables to the cgi
+ENVIRONMENT dictionary (trans.request().environ()) To use mod_rewrite you need to compile it with Apache and then enable it in your httpd.conf configuration file. See the apache docs for compilation instructions. To enable it in your httpd.conf file, uncomment or add the following lines to the DSO section in part 1: -- TavisRudd - 05 Mar 2002 To use ModWebKit, you'll usually have some lines like this in your
+http.conf: Then /WK/Servlet will run Servlet.py in the default context. The problem with this is that you now have an implementation dependent
+fragment "WK" (standing for "WebKit") in your application URLs. This
+is considered bad for various reasons. Ideally, the URL should only
+reflect the content and be human readable (you might want to read the
+passage about The right URL for your link from the Tutorial The
+Care and Feeding of Hyperlinks). You cannot simply omit the Location
+"/WK" or replace it by "/" if you want everything to be served through
+WebKit (I don't know why, but it won't work). However, you could
+replace "WK" by a memorable and reasonable keyword pointing to the
+application itself, not the technology behind it. The more sophisticated solution is to map the URL using the URL
+manipulation capabilities of Apache provided by mod_rewrite. Perhaps
+you want to map different URLs to different contexts, or even to the
+root of the website: The L flag means "this the LAST rule, don't apply any more" and
+PT means "PASS-THROUGH the changed URI to the next handler." You
+can leave the L off, but the PT is essential if you are using
+mod_webkit. See the mod_rewrite reference guide for an explanation. To redirect all images to a static location, so that there's no
+overhead from calling Webware, you would put this rewrite directive
+before the above: For a virtual domain, do something like: This assumes /WK (not virtual) is where the AppServer is located,
+and that you've created a context BobsYourUncle for that virtual
+domain. What if you aren't using mod_webkit? Say you are using a CGI adapter
+at http://mainhost.com/cgi-bin/WebKit.cgi ? Then just replace /WK
+with /cgi-bin/WebKit.cgi in all these examples. _(can someone
+confirm this for virtual domains?)_ You can use [L] instead of
+[L,PT] if you aren't using mod_webkit. _(Do you need [L,PT] if
+you are using mod_snake, etc?)_ -- IanBicking ... with changes by TavisRudd - 05 Mar 2002 If the image location is the same as the original URL, use a hyphen as the second argument. You don't need $1's ()s in this case: -- MikeOrr - 10 Dec 2001 I had two problems. The first was that I put my httpd.conf rewrite
+rules in a <Directory> such as / or the doc root. This gave either a
+repeating path that was HTTP Forbidden, or gave no response. The
+solution was to put the rules above the first <Directory>. The second problem was that if I put a slash in front of $1, the path
+could not be found. Finally, I was able to remove the ^ and $, which aren't needed to
+capture the whole path. My final solution was: I used Apache 1.3.22 (built from source) on Mandrake Linux 8.1. -- ChuckEsterbrook - 22 Jan 2002 I used .htaccess files to set the rewrite rule, and had the following
+rules as above: This caused the recursion mentioned above in another case, to solve
+this I went to /cgi/.htaccess and placed there: This solved it. Now I can get to actually try to build my application. -- BaruchEven - 17 Feb 2002 To clarify, if you want Webware to serve URLs in the top-level
+directory (/index.py) without a /WW prefix AND serve certain files
+statically: (The line numbers are not in the Apache file.) (1) and (2) rewrite /images, /pix/images and /pix/thumbs to their
+original URLs ("-") and use "L" to prevent rule 4 from executing. (3) rewrites URLs with the WebKit prefix to their original URLs, and
+uses "L" to prevent 4 from executing, which would wrongly rewrite
+/WW/index.py to /WW/WW/index.py (4) does most of the work, rewriting /index.py to /WW/index.py so that
+users don't have to type the /WW prefix. "PT" is necessary or the
+request won't be passed properly to WebKit. I don't know if it's possible to make WebKit serve directly from /
+using <Location /> AND serve some files statically. If it were
+possible, you'd need to supercede the "SetHandler webkit-handler",
+thus: but I don't know how you set the handler back to the default once
+you've set it to something else. -- MikeOrr - 16 Apr 2002 My server is hosting 3 unrelated URLs. Each URL resolves to a virtual
+host. One of these virtual hosts is used as the example. The Apache config for the Virtual Server includes both the Location
+directive as well as the RewriteRule directives. This allows for
+different configs for each of the three virtual hosts. The RewriteRules accomplish two things. One, the third rule adds the "/wk" to the start of the URL passed
+to Apache. This makes Webware happy. However, the first rule
+ensures that a "/wk" does not already exist in the URL, as two
+"/wk" values would be a problem. Two, the second rule sends request for images used by mywebcontext
+to the proper non-context directory. The "[L,PT]" on both the
+second and third rules allows only one of those rules to be processed. Given the server directory structure: The Webware context is: The Apache config is: -- TacticalJack - 04 Sep 2002 I use this trick: This makes WebKit serve everything including the root folder. Another one is: This tells apache to see if it has the file to serve for the path requested, then check it's not a directory and then serve it, else webkit handles request as ususal. That's a very nice thing to have! This enables you to have files like css and favicon in the root of the site, yet in a different directory than code (in htdocs or something) - and no special cases are needed for different file types and stuff. Unlike the recipes above. Another possible use is to generate static cached pages for paths that give the most load on your appserver (don't forget to delete them someday after). Make sure to have this for the root folder: Else it will spit out directory listing instead of letting webkit respond - my apache believes root folder is that special that if it can be listed it passes "RewriteCond %{REQUEST_FILENAME} -F" test positive and serves it even it really being a folder. Maybe a misconfiguration of mine. --maluke http://maluke.com/ 17 Sep 2004 The WebKit application server uses a custom import mechanism, which is needed for the auto-reload (a highly welcome feature in development, to be sure). However, I had a case with a module from the BioPython library that tripped this mechanism up. Solution: A hack. I imported this module directly into the AppServer, which apparently caused it to be exempt from the custom import mechanism. mod_webkit is an Apache module that dispatches incoming requests to
+Webware's Webkit application server, which runs in a separate process
+from Apache. It is written in C for efficiency. JayLove wrote and
+maintains it. -- TavisRudd - 29 Oct 2001 This is a short recipe how to make a multi-language site First of all, I load all my languages during the WebKit start-up into a variable, which is visible from any place, i.e. I create this variable in __builtin__ dictionary. So, in my contextInitialize() method is: where Languages() is a dictionary-like object, where keys are language codes and items are gettext.translation() objects: So. I have all the translations in memory and now I need to use them. I need to know, which language to choose. Because my site is also for registered users, there are two ways: If the user is logged in, language code is read from his profile. If the user is not logged in, I pass choosen language code via GET method from page to page, but if the user did not choose language, some default one is used. In awake() method of a servlet I set right gettext function: Every string, that needs to be translated, I mark as usually _('stringToTranslate') and in the beginig of every method Where I need it, I just do: That's it. :-) One last thing. Strings that needs to be translated are not just in servlets, but on other places too (for example in some modules), so I need some _() function there. At the begining of such a place I have: If you have lots of situations where you need to internationalize code
+that won't have access to your context (i.e., to self._), then you
+might want to try keeping track of language on a per-thread manner.
+(If you are using that last recipe, _ = lambda x: x, then you
+might want to try this instead.) An untested example of this: Then use languagecontext.language() to get your _ object, even
+after you've lost track of the servlet you are associated with. (If someone implements this, please correct this code as necessary) -- Ian Bicking Based on very light testing the above approach seems to work. Although I implemented it slightly differently. Instead of gettext -functions
+TheLanguageContextTracker keeps track of preferred translation languages (as strings like 'en', 'fi',...). My LangUtil.py looks like this: In a Page -class from which all other Page -classes in the site inherit I have the following: And into Launch.py I've added the following: -- jranki One issue with the above implementation is that LangUtil can be loaded multiple times. Once in Launch.py and another time in your Page class. Having the effect that when _ is called, there is never anything in the LanguageContextTracker._threads variable. A better solution is within Launch.py to store the module you loaded: and then in your Page class: within the LangUtil you could use the threading.local() object which has been around since Python2.4 to store the thread specific language string. Instead of using the _threads dictionary. -- bcctech There are two obvious ways of installing multiple versions of Python. In the Python source distribution, one would usually do a make install to install Python in the pre-configured place. However, you
+can also do a make altinstall which should install a new version of
+Python in that place without replacing the python program. Thus, if you have Python 1.5.2 as /usr/bin/python and you have
+configured Python 2.0 to install in /usr/bin then you can
+(theoretically) do a make altinstall to keep /usr/bin/python
+as Python 1.5.2 whilst creating /usr/bin/python20 as Python 2.0. For various reasons covered in EnvironmentVariables, I think it's
+easier to install Python in the following way... On my Red Hat system, Python 1.5.2 is installed under /usr - so,
+the executable is found at /usr/bin/python. To install Python 2.0,
+I downloaded the source distribution of Python and configured it so
+that the prefix was given as /usr/local (this is an option to
+the configure program). I then installed Python 2.0 in the normal
+way using make install, so that the executable is found at
+/usr/local/bin/python. To determine which program is run when python is invoked depends
+on certain EnvironmentVariables, of course. -- PaulBoddie - 31 Oct 2001 Pro: The most widely-used RDBMS in Python, so lots of developer support. One of the easiest SQL languages to understand. Can do date calculations and pattern searches at the C level (faster than doing it in Python). This also applies to most other RDBMSs. Many books and MySQL third party tools are available. -- ChuckEsterbrook - 31 Dec 2001 High availability among web host providers. -- ChuckEsterbrook - 31 Dec 2001 It's OpenSource. -- ChuckEsterbrook - 31 Dec 2001 Con: MySQL was designed for write occasionally, read quickly, rather than for intense write-frequently situations. Transactions were added recently and are available only with a non-default data storage type. MySQL is easier to crash under high load than PostgreSQL or Oracle. -- MikeOrr - 31 Dec 2001 Nowadays it is best to use UTF-8 encoded strings throughout your application.
+Here is how you can configure MySQL to work with UTF-8, if you were using latin-1 before. Adapt your configuration in /etc/mysql/my.cnf: Using mysql oder phpmyadmin: This should look like: Restart the server: If you already have data stored, change the encoding in the database dump
+from varchar(n) to varchar(n) collate utf8_unicode_ci and change
+the DEFAULT CHARSET=latin1 to DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci. See also:
+http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html
+(note the post by Yuri Tsarev) and
+http://tahpot.blogspot.com/2005/06/mysql-and-python-and-unicode.html -- Tobias - 14 Apr 2010 The MySQL web site already lists plenty of third party tools, but
+doesn't necessary do a good job of mentioning which ones are really
+nice and of course, doesn't include any Webware related notes. That's
+what this page does. mysqldiff: http://adamspiers.org/computing/mysqldiff/ Can diff any combination of SQL files or live databases. Outputs the SQL statements necessary to transform one database to another. Caveat: Understandably, mysqldiff can't tell when a column's name has changed, so such name changes will result in a removal of the original column and the addition of a new one. That means you lose the data for that column, so if you expect name changes (including tables as well) you must scan the results for them. Fairly easy in practice. This tool is very useful for MiddleKit users to update an existing database whose object model has changed. An important warning: Since MK's generated Create.sql includes USE, CREATE and DROP statements, you can screw up your live database (mysqldiff uses the mysql server to grok files). The current solution is to copy Create.sql, axe those statements and use that file. -- ChuckEsterbrook - 31 Dec 2001 MySQLfront: http://anse.de/mysqlfront/ An excellent GUI client for Windows. Too bad I switched to Linux. I haven't found a GUI on Linux as good as this one. -- ChuckEsterbrook - 31 Dec 2001 title: name_of_wiki_page
+this is a test page, i think webware is great, i like it, thanks to its fathers... WebWare don't manage it's logs. it print log in stdin. so I run AppServer like: But this method cause to large log file. so I fix problem with newsyslog and some fix on Launch.py In your application's Launch.py add below lines: and edit main(): edit /etc/newsyslog.conf: then run newsyslog. you will see: (moved from AppIdeas) For certain parts of a website, interfacing information through a web browser can be tedious -- for instance, uploading multiple files. It would be nice to have a clear structure/framework for creating non-web interfaces to complement the other controls -- often a way to fill out forms without a browser. -- IanBicking - 18 Nov 2001 Automation of Web interfaces, like the automation of applications that one can do with COM in Windows environments, could be done using something like XML-RPC or SOAP. Indeed, I think that such technologies could lend themselves quite well to such activities, since you're likely to avoid the need to deal with cookies and other things which could complicate automation unnecessarily. As an aside, I added some features to my XmlForms package to process XML "instances"; these could be used as similar but more lightweight alternatives to SOAP for automating applications written to use that framework. -- PaulBoddie - 19 Nov 2001 Yes, after using XmlRPC some, it is a pretty easy way to control the website from an external application -- much better than what I was using (though the error messages because of unsupported data types are less than helpful). I'm thinking of making FunFormKit capable of verifying the inputs to an XmlRPC call just like it does normal HTTP requests -- at some point, it would be nice if every pretty form for human use was easily turned into something XmlRPC could handle. It would be nice if authentication and such could also be more seemlessly added. (I edited some of the original comment to take out some of my less informed ideas) -- IanBicking - 19 Nov 2001 XmlRPC is something I've been using on and off for about a year now. It's fairly flexible, and I haven't really run into anything I couldn't get it to work with. I sort of disagree that HTML / ASP / JSP can't give decent data entry feedback. With well written server side validation you can create a fairly robust application that gives the users decent feedback. Throw in a smidge of server generated Javascript, and you've got a workable feedback system for a user. You can even mix Javascript, XmlRPC and a messageQ system to give even better immediate feedback, w/o having to reload an entire page ... on some platforms ( IE ). (Un)fortunately ( mixed-blessing ) HTML is pretty much the only game in town if you really want to write a single front end to a system. Otherwise you end up with having to maintain clients in a goodly number of systems just to make sure it's useful accross OS's. I use OSX / Linux / BSD / and pretty much all the Win line after W95 on a daily basis. Don't know if I'm just dreaming but I believe that the HTML interface is / can be manipulated so as to make data entry less painful, but the inherent latency ( quite a bit on the client side rendering the HTML ) of an HTTP / HTML application even if it's only a few seconds is too slow for data intensive entry operations. That's where a green screen environment is still the best way to go, unless you can create an intelligent batching / queing mechanism that can hold udpates on a local memory resident status, and only send to the ( server ) after a set period of time has passed ( still has the latency problem with rendering HTML ). Other possibilities include utilizing some form of <nop>FrontEndKit to actually generate a front end via wxWindows / GTK / QT / Tk / etc. depending on host system, and based on some form of Widget factory. yeah ... ok ... now I know I need to get some sleep. <grin> -- RayLeyva - 21 Nov 2001 The OneShot adapter is a simple CGI that loads the entire AppServer, responds to the request, and then shuts down. Developers edit many of their Python modules and other file resources during development, so they find using OneShot convenient because it guarantees that everything is loaded fresh. Docs are provided in the WebKit Install Guide which you can read on-line. -- ChuckEsterbrook - 30 Oct 2001 I find OneShot.cgi to be too slow for testing, perhaps because I have
+a relatively slow machine. In any case, I modified WebKit.cgi (which
+is run on every request) to first look for changes in my source tree,
+and restart the appserver if there were any changes made. That way I
+have a delay on the first request after making a change, but for
+subsequent pages it's almost as fast as WebKit. I had to allow the www-data user (under which cgi runs on my
+machine) write permissions to my app directory, so that it can create
+a timestamp and write to the log file. Here's the source, hope it's useful: -- JasonHildebrand - 17 Jan 2002 This lists lots (all) of the Wikis:
+http://www.c2.com/cgi/wiki?WikiEngines Use Emacs for editting! http://zwiki.org/EmacsAndWiki That whole WebLog thing...? SpamBot detection, freezing out users that pull too many pages too
+quickly (worth it? Worth anything? I'd want Google to be able to
+go through the pages) Inline inclusion of other wiki pages There's a ton of possible widgets: Twiki plugins IE allows WYSIWYG HTML editing. Some comments My WebOS had some
+stuff along these lines, perhaps (they are now gone -- links to
+archive.org of them) MS Wiki also does this. Open Wiki might be of interest in terms of XML backend and diffs. RSS feeds. I'm not sold on making the Wiki the central metaphor for
+everything... WebLogs and external feeds might be overdoing it. InterWiki (or inter-site) linking:
+http://openwiki.com/ow.asp?InterWiki -- I think this does kind of
+belong, since linking is central to Wiki-ness. BreadCrumbs of some sort...? Not being naturally hierarchical, this
+is subtle. Base it on navigation (I believe OpenWiki might), or
+annotated (like TWiki). Attachments? I have better ideas with hierarchical Wikis... (i.e.,
+non-HTML/XML pages, and a page can become a SubWiki) Nice diffing, like:
+http://openwiki.com/ow.asp?p=OpenWiki&a=diff&difffrom=146 Branches in versioning (?):
+http://openwiki.com/ow.asp?p=OpenWiki&a=changes RDF -- totally right for Wiki. Special Wiki RDF extensions:
+http://www.usemod.com/cgi-bin/mb.pl?ModWiki -- somehow related to RSS? Page aliases -- e.g., for plural versions of a term. UseModWiki has some stuff with subpages. Automatically notice homonyms, e.g. GroupNameReuse, ReuseGroupName:
+StrikiWiki The whole wiki interface weirds me out. But maybe that
+means there something novel there...? Actually, it just seems to have
+a wacked-out interface (hint: hit the arrows, not the heading
+words), potentially a rich navigation (if it was more intuitive) Manila might have some interesting ideas -- more CMSish. Ditto
+PARC's Sparrow SWiki is a popular branch of Wikiness... still trying to find what's
+novel about it... maybe not a lot. But they're working on it...? AtisWiki allows multiple formats for input. But it's kind of gone --
+very early offshoot of WikiWiki. ZWiki's remote URLs kind of elegant: http://zwiki.org/RemoteWikiLinks http://tavi.sourceforge.net/ -- I think it is actually expanding the textarea width to my screen? -- IanBicking - 13 Apr 2002 <br /> I would like to see a CMS that handles standard HTML documents or at
+least standard HTML document bodies. I could use this in a couple
+projects, although none that's urgent to start right away. The thing
+is, when ppl already know HTML, or you're dealing with outside authors
+that have already written an HTML article, it's easier to use that as
+your source rather than insisting on some "pure" XML or wiki format.
+Yes, it causes complications for converting the article body to
+another format later... IF that ever becomes an issue for the
+particular application. Maybe it won't. A flexible CMS with a configuration option to accept only HTML format,
+or only wiki format, or the author's choice, or both in one document,
+would be the best. It would also help to have an option of "accept the source format
+without conversion and serve it as is without touching its internal
+structure" alongside "convert the source document to an in-between
+format so it's easy to reconvert into any output format." -- MikeOrr - 14 Apr 2002 I have been experimenting with Webware since 0.4 and have produced
+some packages which are available from my home page. You might want to look at the following resources: XMLForms documentation is no longer available. Subsequent work is
+underway, however. I'm working on a document which compares Python Web modules and
+frameworks to Java 2 Enterprise Edition frameworks. I believe it would
+be good to strengthen Webware's capability as a generic application
+server, but not necessarily by just copying from J2EE - there should
+be benefits in using Webware instead of J2EE application servers, just
+as there are benefits in using Python instead of Java for certain
+kinds of software development. Here is an extremely simple example servlet that generates a PDF
+document on the fly (i.e. not saving it to a temporary file on the
+harddisk, everything takes place in memory) and feeds it back to the
+web browser. It requires the ReportLab package, and if you want to
+include images in your PDF documents, you will also need the PIL
+package (Python Imaging Library). NOTE: You should place a bogus GET variable at the end of the URI
+referencing the page with MSIE. Example URI: http://www.mysite.com/wk/hello?type=.pdf Without this type=.pdf appendix, MSIE fails to recognize the incoming
+stream as a valid PDF file. It will instead display the PDF file
+contents as text. Very disconcerting to your user, as they might
+think the reporting engine is broken when in fact it's not. The
+problem is that MSIE seems to focus on the file extension only, even
+ignoring an explicit content disposition header
+(FileStreamingAndContentDisposition) declaring the content as PDF.
+With Apache as webserver, a nifty solution would be to map the servlet
+name to something with a .pdf extension (ModRewriteRecipes). -- RayLeyva
+ChrisZwerschke Here is the minimum code you need to serve a PDF document. Note that
+the getpdfdata method was added in ReportLab version 1.18. With
+earlier versions, you had to use an additional temporary StringIO
+object, as in the Platypus example below: -- ChrisZwerschke - 10 Jul 2003 The following code generates a more complex PDF file with a header,
+footer, and a simple table, using the Platypus library included in
+<nop>ReportLab. Included in the code are several sample grid styles.
+These are grabbed basically verbatim from the ReportLab Platypus code
+(tables.py): -- RayLeyva - 27 Nov 2001, 19 Jan 2002 Hacking around some pdf files, I've found that the TableStyle objects contains a lists of commands, that include, from lines 1308 of tables.py: "FONT", "FONTNAME", "FACE", "SIZE", "FONTSIZE", "LEADING", "TEXTCOLOR", "ALIGN", "ALIGNMENT", "VALIGN", "LEFTPADDING", "RIGHTPADDING", "TOPPADDING", "BOTTOMPADDING", "HREF" and "DESTINATION", and from line 1290: "GRID", "BOX", "OUTLINE", "INNERGRID", "LINEBELOW", "LINEABOVE", "LINEBEFORE", "LINEAFTER", and finally, from line 1166 "ROWBACKGROUNDS", "COLBACKGROUNDS", and finally "BACKGROUND". There is probably a lot of other cool functionality disseminated into the code... Depending on the function, they need different argument, which are somewhat readable from the code (I've search the doc on reportlab website and there is no mention of the table flowable from platypus...). But the main thing I've tested to some extent is that the two tuple after the function represent the targeted cells. Those works as string in python, so (0,0), (0, -1) mean first column, (0,0), (-1, 0) mean first row, and (0, -1), (-1, -1) mean last row. It seems like the only way to get some bold text is by using reportlab getAvailableFonts, or rely on fonts that are usually standard (Times-Bold), or register fonts by yourself... You can have a lot of commands that are the same on different columns and rows, and even weird overwriting statements, it seems to work OK. You have to start your table by specifying the constructor a list of the width and heights for each columns, but you don't have to, if not it will be autosized. I don't think it is safe in the case of long lines, which tends to overwrite the nexts cells. -- satan - 18 Feb 2007 Postgres advertises "object-oriented" features which seem to include
+table inheritance and OIDs (eg, references to objects). AFAIK table inheritance seems to work fine and saves you some typing
+in that you can modify a super class rather than all the
+subclasses. (I'm not sure what the performance implications are
+compared to the horizontal mapping approach done by MiddleKit.) However, OIDs seem rather useless. Suppose you have a class hierarchy
+that includes Thing, Shape, Circle(Shape) and Rectangle(Shape). If
+Thing.shape were to hold an OID to a Shape, I know of no way for
+Postgres to return the correct shape. Instead, I believe you have to
+SELECT on all subclasses of Shape (in this case Circle and Rectangle)
+to find it. You also have to expand thoses SELECTs as new Shape
+subclasses are created. Not so! Actually, PostgreSQL does exactly what you want: in
+PostgreSQL v7.1 and up, SELECT automatically includes all subclass
+tables unless the keyword ONLY is used to prevent inherited table
+access (see p. 145 of Bruce Momjian's book "PostgreSQL"). Since I
+am a PostgreSQL newbie, I did a little experiment to verify this,
+and found that this even applies to OID's, which (although they are
+"invisible" in the sense of not being returned unless specifically
+asked for in a SELECT) can be included in a WHERE clause just like
+any other attribute. Therefore, in your example above, if
+Thing.shape holds an OID to a Shape, you simply SELECT * from Shape
+WHERE OID = [Thing.shape.oid], and you'd automatically get the
+correct shape. (OID's are unique within a PostgreSQL database.) --
+SteveWaterbury - 17 Apr 2002 This ruins the idea of object references and pointing to base classes
+which is something I would consider fundamental to any
+"object-oriented" system. I also believe that Postgres does not provide lists and dictionaries
+in the same convenient matter as what OO developers (and especially
+Python developers) are accustomed to. I have scanned the docs and posed this question to Postgres users but
+not come up with a real answer. If you want real OO features in your database I think you either have
+to use a framework like MiddleKit to "fake it" in the RDBMS or use a
+bona fide OODB such as ZODB. (Note that even if the OO features turn out to be fairly useless, that
+makes Postgres no worse than any other SQL database. eg, I wouldn't
+suggest that someone refrain from Postgres given the issues I raise.) See also: MiddleKit, PostgreSQL, DatabaseIntegration -- ChuckEsterbrook - 31 Dec 2001 I think the OO features in PostgreSQL aren't quite "there" yet. There
+is a discussion about this here and more. I think the use of any RDBMS-specific feature (e.g., PostgreSQL's OO,
+if it actually worked well) might not be a good idea since it would
+effectively tie something like MiddleKit to the RDBMS. -- EdmundLian 31 Dec 2001 No, MiddleKit uses specific classes for the different types of
+databases, eg, MySQLObjectStore, MSSQLObjectStore, etc. which all
+inherit from SQLObjectStore (which has most of what is needed). Each
+specific class is free to take advantage of proprietary features to
+optimize the underlying schema while still keeping the Python API the
+same. Although, these specific classes could also provide proprietary
+features. Of course, if a user takes advantage of them, then yes, they
+would be tied to the RDBMS. But I think that should be their choice in
+the same way Python allows you to write platform specific or
+independent code as you see fit. -- ChuckEsterbrook - 31 Dec 2001 MiddleKit uses specific classes for the different types of databases,
+eg, MySQLObjectStore, MSSQLObjectStore, etc. Oh yes, I forgot about this... I was thinking of the OpenACS
+situation, where there a different level of abstraction used. -- EdmundLian - 31 Dec 2001 I don't think there's any concensus on what OO features in a database
+should look like -- unlike relational calculous, there isn't a
+formalism for OO -- there isn't even anything close to concensus about
+what OO is or what it should look like in programming languages, and
+databases are a step removed. Dictionaries and lists are really fundamental parts of the database --
+each table is a dictionary, and rows themselves are sometimes referred
+to as "tuples" -- vaguely list like, though the table is more often
+used for a list (though I always find ordering rows to be awkward).
+Providing them as types in the database would be a classic non-normal
+feature (though convenient at times). In the cases when the type is
+opaque and it's not necessary for the database to understand the
+list/dictionary, you can generally find a way -- Pickle, multiple
+columns, SET type, etc. -- IanBicking - 08 Mar 2002 Pro: The most sophisticated of all the open source RDBMS. Has declarative
+referential integrity constraints, triggers, stored procedures,
+mature transaction support, multi-versioning concurrency control
+(MVCC) and write-ahead logging for safety and excellent performance
+under load. More SQL-92 compliant than most RDBMS, including Oracle. Stored procedures can be written in PL/pgSQL (a PL/SQL workalike),
+PL/Tcl, PL/Perl, PL/Python (in 7.2). Very easy to port between Oracle and PostgreSQL. Makes it easier to
+migrate to Oracle if the customer wants it. Supports hot backups, OLAP and ad-hoc queries easily since use of
+MVCC means that readers never block writers, and writers never block
+readers. This is also the reason why PostgreSQL and Oracle scale so
+well. <nop>InterBase has MVCC, but has some internal design issues
+that cripple its performance. Well documented. No need to accumulate 1.5 ft of manuals and still
+wonder how to do/manage something, like Oracle. Con: Does not seem to have as widespread support amongst web developers,
+probably because of the nonsense <nop>NuSphere puts out regarding
+need for transactions, declarative referential integrity, etc. They
+are backpeddling on transactions now though, and might eventually
+acknowledge that the last 30 years of research into RDBMS systems is
+actually right. My perception on the lack of web developer support goes like this:
+Virtual web host providers typically provide MySQL, but not
+PostgreSQL because the former requires less CPU cycles (in fact
+some providers have just outright said this), so many people using
+such hosts learned MySQL because that was _the_ SQL database
+available. Also, as a MySQL user (for the reason just stated) I
+have never had any problems in _my_ projects and so never find the
+time to convert to Postgres among my list of TO DOs. --
+ChuckEsterbrook - 31 Dec 2001 I don't think it's an anti-PostgreSQL bias, just the ordinary hurdle
+of not being the first, like wxWindows trying to bust into the
+Tkinter scene. Many MySQL developers (like me) want to switch to it
+eventually (because it's "better" -- more features, more robust,
+better OO, etc), but it's going against inertia. I can add a MySQL
+database like that <snaps fingers>, but every time I browse
+the PostgreSQL manual I think, "Aargh! <recoil in horror> I
+don't have time to read all this, choose among PostgreSQL's many
+column types, or translate my queries! Maybe later." --
+Main.MikeOrr - 31 Dec 2001 Does not have as many third party books and software tools as
+MySQL. -- ChuckEsterbrook - 31 Dec 2001 OTOH, you don't need anything except the excellent manuals if you
+already know about RDBMS. If you don't, Bruce Momjian's book is a
+good starter, and is freely downloadable at:
+http://www.ca.postgresql.org/docs/awbook.html . There's also
+Practical PostgreSQL which is also available at:
+http://www.postgresql.info/ . Frankly though, I find that the
+online manuals are more current since PostgreSQL is developing so
+quickly. For third-party tools, well anything that can handle
+SQL/ODBC will work. Crystal Reports, MS Excel, MS Access,
+etc. Just use the ODBC driver. -- EdmundLian - 31 Dec 2001 Well in that case, can you recommend a GUI client that works with
+Postgres that is as nice as: http://anse.de/mysqlfront/ , and a
+tool like: http://www.new.ox.ac.uk/~adam/computing/mysqldiff/ ? I
+would have a hard time migrating to Postgres without the
+latter. -- ChuckEsterbrook - 31 Dec 2001 There is http://ns.flex.ro/pgaccess/ for graphical
+management/editing of tables, users, sequences functions,
+etc. There is also
+http://gborg.postgresql.org/project/pgmonitor/projdisplay.php for
+monitoring the backend, and
+http://pgadmin.postgresql.org/pgadmin2.php?ContentID=1 for the
+W95/8/NT client, which seems to be very nice and more capable than
+pgaccess. pg_dump and pg_dumpall will extract a database into a
+script file, which you can then diff. People use it for moving
+databases and all the associated functions, etc. to another
+installation. For most stuff, I actually just use psql (http://psql.sourceforge.net/), the
+terminal-based front-end since I can script it. -- EdmundLian -
+31 Dec 2001 -- EdmundLian 31 Dec 2001 I have outstanding questions regarding the so-called "object oriented"
+features of PostgreSQL. I created PostgresOOFeatures for this. -- ChuckEsterbrook - 31 Dec 2001 Pro: More well-tested than ZODB and MiddleKit. Relational databases are based on solid mathematical foundations
+(relational algebra, set theory, etc.), but OODBMS have no such
+theoretical underpinning (Link to comments by Fabian Pascal). Full control over relations, transactions, etc. All queries are programmer-written and optimized, and by reducing
+the number of queries you give the RDBMS more opportunity to do
+optimizations You can leverage the database's abilities to cache and store
+information compactly Con: Non-Pythonic: requires you to translate your objects and
+manipulations to/from SQL statements No layer of abstraction between the database schema and the Python
+interface -- if you change the schema you must change all the Python
+code unless you create your own abstraction layer or use views more,
+either of which is highly recommended Queries are programmer-written and optimized, which means more work It is difficult to cache results (though the database does caching
+of its own) Some operations are difficult to do in SQL -- MikeOrr - 31 Dec 2001
+-- EdmundLian -- 03 Jan 2002
+-- IanBicking - 04 Jan 2002 Although Webware in general is designed to run with Python 1.5.2 or
+later, some parts of Webware require Python 2.0 or later. Each of the
+Webware kits (or plug-ins) have their own properties files indicating
+which version of Python they require. While you might want to upgrade to Python 2.0 or later, it may be the
+case that you already have Python 1.5.2 installed because it is needed
+to run some programs supplied with your operating system. This is the
+case with Red Hat Linux and Debian GNU/Linux where certain
+configuration tools supposedly require Python 1.5.2. You may not be
+able to just "install over" Python 1.5.2 without breaking those
+programs. So, what can you do about this? Installing and dealing with MultiplePythonVersions. Configuring your EnvironmentVariables. -- PaulBoddie - 31 Oct 2001 "Python is an interpreted, object-oriented, high-level programming
+language with dynamic semantics. Its high-level built in data
+structures, combined with dynamic typing and dynamic binding, make it
+very attractive for Rapid Application Development, as well as for use
+as a scripting or glue language to connect existing components
+together. Python's simple, easy to learn syntax emphasizes readability
+and therefore reduces the cost of program maintenance. Python supports
+modules and packages, which encourages program modularity and code
+reuse. The Python interpreter and the extensive standard library are
+available in source or binary form without charge for all major
+platforms, and can be freely distributed. Often, programmers fall in love with Python because of the increased
+productivity it provides. Since there is no compilation step, the
+edit-test-debug cycle is incredibly fast. Debugging Python programs is
+easy: a bug or bad input will never cause a segmentation
+fault. Instead, when the interpreter discovers an error, it raises an
+exception. When the program doesn't catch the exception, the
+interpreter prints a stack trace. A source level debugger allows
+inspection of local and global variables, evaluation of arbitrary
+expressions, setting breakpoints, stepping through the code a line at
+a time, and so on. The debugger is written in Python itself,
+testifying to Python's introspective power. On the other hand, often
+the quickest way to debug a program is to add a few print statements
+to the source: the fast edit-test-debug cycle makes this simple
+approach very effective." -- Guido van Rossum (quoted from
+http://python.org/doc/essays/blurb.html) For more introductory material on Python please visit
+http://python.org/doc/Intros.html For comparisons between Python and other languages please visit
+http://python.org/doc/Comparisons.html. Here's some notes on Python
+vs. PHP comparing PHP and Python. -- TavisRudd - 23 Nov 2001 Pro: Easy to use. Just like dictionary accesses plus an open and close, and remembering not to change mutable values in place. Pickle and Shelve have been standard Python modules for years. Users needn't do anything to set them up: they're included with Python. Con: Shelve requires a dbm library (gdbm, ndbm, Berkely db, etc, or the slow-but-universal dumbdbm). Each dbm library has its own file format. If you open a dbm file with the wrong library, it destroys the file. No support for concurrency unless you write a daemon or module to broker the connections. With large pickled lists or dictionaries, you can't just unpickle one element and modify it, you have to unpickle/repickle the entire list/dictionary. -- MikeOrr - 31 Dec 2001 I've been fiddling with this a little, and have come up with a simple
+mixin and object that seem to deal nicely with the concurency issues,
+and make Pickle a nice alternative to something as heavy as ZODB. The first part is a persistency mixin. It pickles or marshals your
+object, with locking, to a file. You are expected to use multiple
+files for multiple objects -- like shelve in the filesystem (and
+perhaps shelve would be faster...?) The second is a simple factory-generator. The object constructor
+should take generally an ID, or list of IDs (no default arguments, no
+keyword arguments), and the factory will return an already-existing
+object or create a new one. This way their won't be multiple objects
+that are pickling to or from the same file. Together, this makes it very easy to make a simple, persistent object.
+For instance: It ain't fancy, but it's very transparent. You can look at
+Persist.py and ParamFactory.py -- IanBicking - 03 Jan 2002 Oops! I just noticed that PaulBoddie had documented this problem
+under PythonIssues, but without the added chatter here. Not sure if I
+should just delete this, or try to merge the entries... -- EdmundLian - 02 Dec 2001 Pretty much all the Webware and 3rd party plug-ins invoke the Python
+interpreter by calling "#!/usr/bin/env python" in their scripts. The
+trouble with this is that this call brings up Python 1.5.2 in the
+current version of Debian. The only ways I can think of to get Python 2 invoked instead are to
+(1) use the Debian alternatives system, or else (2) search through all
+Webware + plug-in code to change "#!/usr/bin/env python" to
+"#!/usr/bin/env python2". Neither option seems good. Messing with the Webware code means that I
+can't keep current easily. Using the alternatives system to point
+/usr/bin/python at python2 will break some Debian packages that depend
+on 1.5.2, and which rely on /usr/bin/python to point to Python 1.5.2. Indeed, there is quite a raging debate going on now amongst Debian
+developers about about how to deal with this kind of problem (and
+others). So, if you use Debian, Webware and Python2, how did you
+manage to keep the balls in the air at once? IanBicking wrote: Well, I switched "python" to 2.0 some time ago on Debian, and I've had
+very few problems (pysol is the only one I can remember). Of course, I'm sure there are packages I don't use that do cause these
+problems. A general "change the first line of everything" script would probably
+be useful anyway -- some systems have broken /usr/bin/env's anyway.
+It could be something like: ChuckEsterbrook notes: I'd like to point out that scripts that are touchy about their Python
+version should specify it: In a sense, you could say they are the source of the problem. It also
+doesn't help that Debian is so far behind the current version of Python. But most importantly, I wanted to say that there are only 2 places in
+Webware you should have to worry about this. One is the appserver and
+the other is any CGI adapter (OneShot.cgi and WebKit.cgi). You have to modify the CGI adapters anyway, so you might as well set
+them to #!/usr/bin/python2.1 while you're at it. For the AppServer,
+you can either modify Launch.py one time, which isn't so bad, or you
+can specify on the command line: I suppose if you use MiddleKit Generate.py you will have a similar
+situation. But that's about it. Regarding keeping current, if you were to have a CVS workspace and you
+made these mods, I think all your "cvs updates" would go smoothly. I
+typically have some mods myself, usually in the configuration files. Before anyone gets confused, Webware only requires Python 1.5.2,
+however some packages like MiddleKit and UserKit require 2.0 and will
+gracefully exit with a message saying so when the app server launches
+(under 1.5.2). Of course, since Python 2.x has complete garbage
+collection and more features, I recommend it. PaulBoddie writes: Only if you have the path to Python 1.5.2 before the path to other
+Python versions, or you have done a "make altinstall" with those
+versions, so that Python 2.0 (for example) is installed as python20. The only ways I can think of to get Python 2 invoked instead is to
+use the Debian alternatives system, or else search through all
+Webware + plug-in code to change "#!/usr/bin/env python" -->
+"#!/usr/bin/env python2". What I have done is to have Python 2.0 installed in /usr/local, thus
+retaining the Red Hat default of Python 1.5.2 (or whatever it is) in
+/usr. Then, I change my environment to find Python 2.0 first - this
+usually involves changing one's .bashrc file on Linux: Or something like that. All "#!/usr/bin/env python" at the top of a
+script does is force the operating system to find the python program
+as if you had typed "python" at the shell prompt. Otherwise, one would
+have to hard code the path to python in instead - I have seen numerous
+Perl scripts with "#!/usr/bin/perl" at the top, for example. Neither option seems good. Messing with the Webware code means
+that I can't keep current easily. Using the alternatives system to
+point /usr/bin/python at python2 will break some Debian packages
+that depend on 1.5.2, and which rely on /usr/bin/python to point
+to Python 1.5.2. Don't change any code - just change your environment! If you need to
+run any special Debian programs alongside Webware then I'd suggest
+creating a new user just for Webware. After all, you're using a
+multi-user operating system. ;-) Another option is to create a start-up script which wraps up
+AppServer. In this, you could put your environment changes: So, if you use Debian, Webware and Python2, how did you manage to keep
+the balls in the air at once? I use Red Hat, as I noted above, but the issue is pretty much the
+same. Well, apart from the assertion that Debian's package system
+sucks less than Red Hat's, but then I'd rather build from source
+anyway - it doesn't take me long to find that the configuration of a
+packaged version of some software doesn't suit me, thus demanding a
+reconfigure and a recompile. MikeOrr notes: On Wed, Oct 31, 2001 at 02:04:05AM -0800, Chuck Esterbrook wrote: It also doesn't help that Debian is so far behind the current
+version of Python. It's not behind. python2 was forked into a separate package for
+political reasons. Namely, the former GPL-compatibility brou-ha-ha.
+Python 1.5.2 remained the default version because Python 2's licence
+wasn't acceptable enough. Now the license thing has been straightened
+out, but they're stuck with the current version of Python in a side
+package. (And now there is a python2.2 package for the beta...) I made /usr/bin/python a symlink to
+/local/opt/Python-2.2b1/bin/python2.2 and have not had any problems
+yet. I do install my own extensions rather than Debian's (MySQLdb,
+PIL, egenix-mx, Cheetah). -- EdmundLian - 02 Dec 2001 Every month or so someone posts to comp.lang.python asking how Python
+compares to PHP. Python's 'language comparisons' page doesn't have an
+entry for PHP so here's a first draft. This work-in-progress compares
+PHP and Python and evaluates their relative strengths for web
+development. Please contribute! Once there's some flesh on this skeleton, I'll tidy
+it up and submit it to: http://python.org/doc/Comparisons.html -- TavisRudd - 23 Nov 2001
+-- MikeOrr - 15 Dec 2001
+-- DiogoResende - 21 Jun 2007 :)
+-- SamuelCochran - 6 Jan 2008
+-- Pomax - 23 Jan 2010 Both PHP and Python: are interpreted, high level languages with dynamic typing are full programming languages, rather than specifically "for the web" languages. are OpenSource (except PHP's ZEND engine?) are supported by large developer communities are easy to learn (compared to Java, and even Perl) are easy to extend in C, C++ and Java are extremely portable. They run on almost all platforms in
+existence without recompilation. support for variable number of function arguments. have the ability to freeze live objects in a string representation
+(for storing arbitrary objects on disk, moving them over the
+network, etc); they can then be converted back to identical objects
+with data intact. PHP's serialize function; Python's pickle and
+marshal modules. Note that PHP, handling of serialized objects and
+classes is much weakier and error prone than Python's due to PHP's
+lack of modules. When an object is serialized, only its attributes
+are stored, not its methods. Thus, the object's class must be
+present (with the exact same name) in the script that unserializes
+it. In Python this is handled automatically via the module/import
+framework. (this COULD be handled with PHP 5's __autoload(), but is not done automatically) lack strict boolean conditionals (strings and numerals are considered valid conditional statements) syntactic scoping (using curly braces) variable syntax (dollar signs indiciate variables) 'switch/case' statement and 'do ... while' construct increment and decrement and assignment operators (assignment is a statement only in Python) the ternary conditional operator (... ? ... : ...) script-global variable protection (script-global variables must be imported by a function before they can be used) class-global variable protection (class-global variables must be indicated as $this->varname, $varname variables are local scope) primitive variable declaration-on-initialisation: a primitive variable (including array) does not need to be explicitly declared. when an assignment involved a variable that does not exist, it is created on the fy. an expedient (commonly installed) environment references - in assignment such as '$a =& $b', $a becomes an alias for $b (modifying the data via $a is functionally equivalent to modifying the data via $b). single primitive for arrays and maps - the 'array' primitive is actually a map which will can either map incremental numbers to data (acting as an array) or map explicit keys to data (action as a hash, dictionary, or whatever other name you know maps as) excellent on-line manual with registration-less user contributions to further explain functions and their use "in the wild" object level functions. Where in python all class-defined functions are class function, and must be passed a 'self' argument in order to operate on an instantiaed objects, in php all-class defined functions are object functions unless explicitly marked static. scoping based on indentation level (rather than relying on curly braces to indicate scope) namespaces and modules supports self-documenting code (via docstrings and pydoc) keyword arguments to functions and methods everything is an object (there are no primitives) multiple inheritance object-oriented file handling built in reflection everything is pass-by-reference consistent case sensitivity (PHP is case-sensitive for variable names, but not for function names) true lambdas and other builtin functional programming constructs operator overloading SWIG integration threading many predefined common high-level data types (lists, tuples, dictionaries, datetimes, arrays, etc.) distinction between enumerated lists (arrays) and and maps (associative arrays) builtin concept of date that doesn't rely on computation involving the UNIX epoch (dates can go <1970, >2038) strong internationalization strings work with all unicode 5.1 characters (php only supports multi-byte strings via an extension, which does not offer all the operations that exist for normal strings) module importing (php relies on code inclusion instead) uniform function naming (PHP's built-in library does not stick to a single naming convention) (support for all major GUI frameworks -- a list is missing, so until someone specifies what they consider major, this is a moot point) (maturity, stability and upward-compatibility -- PHP is stable and mature [but not clean] and python 2.6 is not compatible with 3.x [and presumably every version after that], so this point is fairly useless) (a small core -- but small for what purpose? PHP's 5Mb is small if used on servers or workstations, but big if it needs to be embedded, so this again looks like a moot argument unless a context is stated) Unlike PHP, which has web development features built directly into the
+core language, Python's web development capabilites are provided by
+add-on modules. Basic CGI capabilities are provided by the 'cgi'
+module which comes in Python's standard library. There's also a wide
+range of third-party modules available for Python; some are
+complementary, others compete. As a result, Python provides a more
+flexible base for web development. There are some adverse side effects of this flexibility. First, the
+range of choices can be bewildering. Unless you are working
+experienced Python web developers, PHP is easier to get started with.
+Second, support for PHP is more common with shared-hosting companies
+than support for the various Python options. Another difference is that PHP is embedded in the web server, whereas
+Python web applications can either be embedded in the web server like
+PHP or run in a separate process. Here's a simple classification of
+Python web development platforms: emdedded in the web server process Apache modules mod_python and mod_snake embed the Python
+interpreter in Apache and allow other Apache modules to be written
+with Python. This is the same idea as mod_perl. PyWX is an extension to AOLServer that serves the same purpose as
+the Apache modules above. MS ASP scripts can be written using Python via Active Scripting
+Host. running in a separate process non-persistent process (a new process is spawned for each request) custom CGI scripts persistent process (all requests are sent from the web server to
+one persistent process, sometimes via a CGI wrapper) custom Fast-CGI scripts custom SCGI scripts custom WSGI modules 'Application Servers' Zope -- comes with its own web server
+but can also communicate with Apache via the CGI
+or Fast-CGI protocols. Webware -- works with any web server via CGI. With Apache it
+can communicate via a custom Fast-CGI like protocol. SkunkWeb -- only works with Apache, communicates via a custom
+Fast-CGI like protocol. (Note: PHP programs can be run standalone or as ordinary CGI
+scripts. However, when called indirectly from the web (e.g., from a
+Perl CGI script), it requires unsetting an undocumented number of web
+environment variables so that PHP doesn't think it's running from the
+web and look in the wrong place for the script. Likewise, it's
+_possible_ to write a long-running server in PHP, but there are
+precious few examples of it.) The vast majority of Python web applications are run in a separate
+process. This has some important implications. ... to be continued ... ... please add your observations ... ... Here's some interesting notes on security in PHP: (Not true. The tutorial assumes default conf values that are not default anymore or don't even exist)
+http://www.securereality.com.au/studyinscarlet.txt PHP huge installed user base, but the figures are probably distorted by
+shared hosting (Not true. It has PEAR and PECL repository) low signal-to-noise ratio -- because PHP is so expedient, many of
+the users are not invested in the technology (or even their own
+code) or the community (Not true. IBM and Oracle invested in PHP) Python sizable, but not huge, installed user base Python Software Foundation lots of specialized interest groups very high signal-to-noise ratio PHP Python Although both PHP and Python have excellent core documentation,
+Python's is more extensive and generally higher quality. PHP has a
+large number of translations available. Python doesn't. For PHP see
+http://www.php.net/docs.php and for Python see http://python.org/doc/
+Python allows documentation on modules, classes, and functions to be
+included in the program code. The documentation becomes an attribute
+of the module/class/function, accessible from inside of the language
+itself. (This is so untrue. Python docs are short and don't explain
+in detail how something can or should be used. PHP uses examples (L.B.E.).
+PHP has comments on every page so you can check for someone that tried
+to do the same thing you did. PHP can be documented just like many other
+languages like C for example; http://www.phpdoc.org/ ) For Python see http://python.org/doc/current/tut/tut.html,
+http://python.org/doc/Newbies.html For PHP see http://www.devshed.com/Server_Side/PHP/,
+http://phpbuilder.com and http://zend.com, http://www.devshed.com/c/b/PHP/, http://www.codewalkers.com/ and the PHP Manual itself. For books on PHP see http://www.amazon.com/gp/community-content-search/results/002-7542231-3949660?ie=UTF8&search-alias=rp-listmania&query=php . For
+Python, see
+http://www.amazon.com/exec/obidos/tg/listmania/list-browse/-/12QMUFII6951W
+and http://www.awaretek.com/book.html it is not up to the rest of us to tell you which programming language to favour. Learn both, and learn which to use when. If you find out you end up using PHP or Python more than the other, then you know which programming language works best for you. If the job you need to get done relies on specific features that are available to one, but not the other, then there is no choice to make, go with the one that offers this feature. Q1 ... what is the best way to build web-based database applications
+like accounting systems, on Python? i.e. what is the equivalent of
+ASP pages or PHP pages or JSP pages, for Python?? Shouldn't we be
+learning THAT instead of PHP??? A1 There is no simple answer to this question. There are many tools
+available for doing this in Python. So many that the choices can
+be bewildering. You're best to think of the tools as falling into
+two complementary categories: Application Servers (there's
+probably a better term to use here) and Template Engines. Application Servers: plain old Python CGI scripts Zope (includes a Content Management System and a whole lot more) Webware SkunkWeb see http://www.boddie.org.uk/python/web_frameworks.html for more Templating Engines: Zope's DTML Zope's Page Templates Webware's Python Server Pages Cheetah (integrates with Webware) Yaptu SkunkWeb's STML Mako Templates (http://www.makotemplates.org/) see http://webware.sourceforge.net/Papers/Templates/ for more. My personal preference is Apache/Webware/Cheetah. I'll write more
+about this later. -- TavisRudd - 23 Nov 2001 (add your comments here, along with a signature and date) I haven't used PHP with XML, and I've only used Python with XML a
+little, but I get the impression Python is much better suited --
+specifically because it is strongly types, where PHP is rather weak in
+its typing (like C). But I can't say from experience -- IanBicking - 26 Nov 2001 To clarify, PHP's types are more dynamic than Python's. All
+variables are automatically initialized to NULL. It automatically
+converts between strings, numbers and NULL when a different type is
+needed for the operation, or with a type case. It also "does
+something" to convert to/from array and object. '123' -> 123,
+'non-numeric' -> 0, NULL -> '', etc. Sometimes all this is
+convenient, especially when a missing CGI param converts to NULL which
+converts to '' in the VALUE= attribute, but it also leads to wrong
+ouput rather than errors if you misspell a variable or key. PHP does not have first-class functions. There are equivalents to
+Python's map, reduce, filter, and there's array_walk, but they all
+take the name of a function rather than the function itself. Examples of PHP's schizophrenic function naming: (string functions)
+stripslashes vs strip_tags; (array functions) array_keys vs asort vs
+count and shuffle; (variable functions etc) isset vs is_array. PHP has constants, which are initialized with a totally different
+syntax than variable assignment. The advantage of constants is that
+they can be defined only once, allowing you to define sensitive
+constants in a secure include file, then prevent untrusted code from
+overriding them. Constants are global: they bypass the scoping rules.
+One disadvantage is that a constant is a bare word: if you misspell
+it, you'll silently get a NULL value. Syntax errors show the file and line number, but not the character
+position. Sometimes the error message is specific, sometimes just
+"parse error". This leads to staring at the code for a while until
+you realize you're missing '$' or ';', or you put something besides a
+semicolon on the END line matching <<<END . -- MikeOrr - 15 Dec 2001 This document has become rather old. With PHP 5.2.x and Python 3.0 out,
+and almost 10 years after this document was first drafted, it has become
+clear that, from the perspective of a person who intends to use these
+languages, the choice between them is essentially arbitrary. For
+trivial tasks it is irrelevant which is being used (because then it's not
+a choice between PHP or Python, but between every programming language
+for which there are compilers and interpreters available), and for complex
+tasks more often than not someone has already done what you need in both
+PHP and Python, as well as typically in Perl and Ruby. Also, do not be taken in by trivial differences: Python's indented scoping
+vs. PHP's explicit scoping is only syntactic sugar: the transformation from
+one method of scoping to the other is the most basic of trivial
+transformations, and if you want you can set up your interpreter so that it
+always runs your script through that transformation before executing, so you
+can code PHP with indentation scoping, or Python with curly braces. Python's use of module imports rather than PHP's code inclusion is a strong
+argument to use Python in structured complex situations, but careful
+programming and using project-specific conventions (and sticking to them)
+will achieve the same in a PHP project (although you won't find a single
+experienced programmer out there who will not tell you that modules and
+packages are a cleaner, and easier to debug approach). With IDEs such as Eclipse offering plugins do PHP as well as Python development,
+the IDE availability argument is also moot. As ever, the conclusion must
+necessarily be: use the language that gets the job done best, in the least
+amount of time, with the least amount of debugging. Everything PHP can do,
+Python can do, and vice versa; you want to select your language based on how
+they behave when things don't work. Is it easier to debug a PHP application
+or a Python application? No one knows, actually, because a well designed
+application doesn't break, and a poorly designed application is as poor as
+the programmer's oversight. -- Pomax - 2010/01/23 See also Sisynala: http://mithrandr.moria.org/code/sisynala/ , written in Python. I've been trying to find a good OpenSource web log analysis tool. Here
+are tools I found so far: IMO, all of them are very inflexible and completely tied to their web
+interfaces (which suck for the most part). None of them provide a
+programer's interface, which is what I want. Here's the details of what I'm looking for: must be OpenSource, preferably with a Python / BSD or LGPL style
+license rather than GPL <BR> (well, not everyone necessarily agrees
+with that: I consider The GPL a good thing -- Main.IanBicking <BR>I
+agree for applications, but I'm thinking about something that can be
+used as a library without dictating the license terms of the
+applications that use it. --TavisRudd Of course, the GPL only affects proprietary programs that are
+distributed, so I don't see a big problem...? I wouldn't be happy with
+people making my work proprietary when I gave it to them in good
+faith, and that's exactly what the GPL prevents --IanBicking) must provide a programmer's interface, not just a web interface must be completely portable must be easy to install and configure must work with Apache combined log format + IIS format Actually, IIS uses the W3C logfile format (http://www.w3.org/TR/WD-logfile.html), so W3C compliance would do. --ChristianPackmann should be able to work with the Common Log Format should be possible to write new parser modules for new log formats fast parsing of the log-files preferably written in Python, or providing a Python interface provides reverse DNS lookup process log files split by load balancing mechanisms or log rotation reports: pages views unique visits unique human visits referrers authentificated users robots (+ can filter them out) file mime types browsers os http errors 404 errors, kewords/phrases from search engines entry pages exit pages domains/countries, allows filtering by anything: IP address, mime type, domain, whatever can provide the following summaries: hourly, daily, weekly, monthly, yearly summary of all logged variables ranked (highest-to-lowest + vice versa) listings of all logged variables event-based summary periods -- e.g., you'd want to reset logs
+after doing a lot of search engine submittal, which may not be
+aligned with a weekly or monthly summary, but you'd like grouped
+together in a larger unit than daily. can use cookie vars to trace and summarize traffic flows (i.e. userid cookies) [see mod_session] Flexibility and extensibility matter more to me than raw performance. Unless a package that meets these requirements exists, I'm proposing
+that we start a project to build one. -- TavisRudd - 03 Nov 2001 Implemention thoughts the parsing and log reading classes should be completely separate from the rest of the classes some (configurable) manager to see (a) where you left off parsing last time and (b) where all possible logfiles are. This should deal both with a logfile growing since the last parsing, and with logfile rotation. the raw log data should be parsed and then stored in a simple DBM style database (there may be faster formats, depending on how the data is queried -- usually I imagine it would be sequentially based on a start/stop time) all derived data should calculated from the DBM store and in turn stored in a DBM format the dates should be handled using mx.DateTime all config settings should be managed using the SettingsManager API used in Cheetah Rough Class layout: SettingsManager Parser & associated classes for getting raw data out of the logs Storage classes for getting data in and out of the internal data store Classes for calculating all the basic derived data (reverse dns lookup, etc.) Classes for calculating the advanced derived data and summaries --
+with some sort of caching mechanism for storing the results. Interface classes for putting it all together -- TavisRudd - 03 Nov 2001 Many applications need to present data using charts and graphs. There
+seems to be a scarcity of good Open Source Python charting libraries. Please add any solution and experiences you have with it here. Sean True of WebReply, Inc. ported JpGraph to Python. JpGraph is a
+substantial presentation graphics module written in PHP by Johan
+Persson. Sean wrote: "JpGraph is a fully OO graph library which makes it easy to both draw
+a "quick and dirty" graph with a minimum of code and quite complex
+graphs which requires a very fine grain of control. The library tries
+to assign sensible default values for most parameters hence making the
+learning curve quite flat since for most of the time very few commands
+is required to draw graphs with a pleasing aesthetic look." -- Johan
+Persson Our port of JpGraph depends on gdmodule, and of gd itself. Both are
+included in the tar file. The port also expects Freetype and
+appropriate TTF fonts, which are not included. Gantt charts and image
+maps still need work, but the package in general pretty well. Sample
+output is available from the download site. Download from http://metagram.webreply.com Note that the PHP version of JpGraph is licensed under the QPL 1.0 (Qt
+Free Licensee) for non-commercial, open-source and educational use and
+JpGraph Professional License for commercial or proprietary use. What
+this means for the Python port is an open question. ChartDirector looks very similar to JpGraph, but is from
+Advanced Software Engineering Ltd. The package seems to be easy to use, and the licensing cost seems
+reasonable. However, the lack of source code is worrying given what
+can happen to companies. From the DISLIN homepage: http://www.linmpi.mpg.de/dislin/ DISLIN is a high-level plotting library for displaying data as curves,
+polar plots, bar graphs, pie charts, 3D-color plots, surfaces,
+contours and maps. DISLIN is intended to be a powerful and easy to use software package
+for scientists and programmers. There are only a few graphics routines
+with a short parameter list needed to display the desired graphical
+output. A large variety of parameter setting routines can then be
+called to create individually customized graphics. DISLIN is free for the operating systems Linux and FreeBSD and for the
+MS-DOS and Windows compilers GCC, G77 and LCC. The DISLIN plotting
+extensions for Java, Python and Perl and the DISLIN interpreter DISGCL
+can be used freely on all operating systems. Other DISLIN versions are
+available at low prices and can be tested free of charge. The charts that DISLIN generates favors correct, scientific
+presentation over incorrect business presentation (which often
+distorts the interpretation of the underlying data). This means it wil
+l do amazing graphs, but not flashy ones. The Python interface seems awkward to use. Prabhu Ramachandran, author of MayaVi writes: MayaVi is a free, easy to use scientific data visualizer. It is
+written in Python and uses the amazing Visualization Toolkit (VTK) for
+the graphics. It provides a GUI written using Tkinter. MayaVi is free
+and distributed under the conditions of the BSD license. It is also
+cross platform and should run on any platform where both Python and
+VTK are available (which is almost any *nix, Mac OSX or Windows). An intriguing data visualizer written in Python, so it should feel
+right to Pythonistas. This looks easier to use than DISLIN. The
+documentation looks excellent (so far), and the license looks right. -- EdmundLian - 16 Nov 2002 ReportLab includes a charting package. It is easy to use and very complete.
+The charts are not as fancy as the commercial options outlined above,
+but I actually like them better at this point. Open source in Python too. Your servlet might do this: Which works fine with CGI-based adapters, but fails with others. This
+is not a bug. CGI goes the extra mile by setting the HTTP status for
+you. In practice, redirects are supposed to also be accompanied by the
+status. Also, you should provide a small bit of HTML to describe the
+redirection, in case the user's browser fails to follow it. Finally,
+it might not hurt to check that the WebKit response object has not
+already been committed. So use the sendRedirect() method instead, which does all of this for
+you: -- ChuckEsterbrook - 07 Dec 2001 This describes a pattern for navigation among web pages. Problem: when people perform an action that modifies the server-side
+state (such as deleting an item from a table), the action should only
+be performed once. Having users reload pages that result from such
+actions can lead to confusing error messages and invalid data. Also, having boring pages with minimal content (such as a page consisting only of "mail sent") is annoying for the user. You can alleviate these problems by immediately redirecting to another
+-- likely more interesting -- page after every such action. This
+should be done in the HTTP headers (via self.sendRedirect() ) so
+that it is transparent to the user (and doesn't break their back
+button). The user still needs to receive some confirmation that the action was
+performed. I generally add a session variable, which is a list of
+messages to be displayed. At the top of every page, I check if
+there's anything in that session variable, and if there is I display
+it (usually offset by a different background color), and then clear
+the variable. When an action is performed, it will add any messages
+to that variable. This provides a very pleasing UI, and saves a lot of tedious
+navigation. Unfortunately, it would be nice if the status messages worked even
+when you weren't doing a redirect. However, because there's no good
+way to buffer the written output of a servlet, you won't know if later
+portions of the page are going to produce a status message when you
+write the top of the page. If you place status-message-producing
+actions in the awake method of the servlet, you won't have this
+problem. -- IanBicking - 20 Nov 2001 I was developing a WebKit application using Mozilla 0.9.x as my
+browser and OneShot.cgi as my adapter. Upon hitting Reload, Mozilla
+would churn on the CGI wrapper, but then show the old page
+again. Nothing in my Mozilla cache preferences indicated that this was
+the correct behavior. TavisRudd pointed to the solution: If you hold down Shift while
+clicking Reload, Mozilla will actually reload the page, instead of
+pretending to. You can also press Control-Shift-R<. This feature originally came from Netscape, so if you have problems
+with that web browser, you might try this trick as well. -- ChuckEsterbrook - 30 Nov 2001 Pro: You have total control over it. Can leverage existing parsing libraries: rfc822, ConfigParser, xml,
+CSV, MiscUtils.DataTable. Con: You have to create the entire infrastucture yourself. No support for concurrent access unless you provide it. -- MikeOrr - 31 Dec 2001 I just want to expand on Mike's point of no support for concurrent access unless you provide it. That item is huge and in a recent project I briefly considered trying
+this approach just to experience the pros and cons first hand. As I
+thought about it more it became less attractive. Web sites are inherently concurrent and the issues around concurrency
+can be subtle and numerous. Unless your application is exceedingly
+simple, don't go this route. -- ChuckEsterbrook - 31 Dec 2001 These same issues of concurency exist for
+PythonsPickleAndShelveModules -- but are not horribly difficult to
+avoid, especially when you use a factory. I've written a note at
+PythonsPickleAndShelveModules about how I approached this problem. -- IanBicking - 03 Jan 2002 How to make Webware run under its own user account ('webware') Of course, you don't actually "run Webware". You run the AppServer.py
+daemon in WebKit. The following steps are for UNIX-like operating systems. (Somebody
+else please add the steps for NT and other OSes.) Create the user ("useradd webware" or edit /etc/passwd, etc). Since
+the user shouldn't log in, give them /bin/false for a login shell
+and no home directory. Change the password in /etc/passwd or
+/etc/shadow to 'x' or precede it with '!' to prevent them from
+logging in. The user can be in group 'nogroup', or you can create a
+'webware' group if your OS normally has a separate group for each
+user. Set the permissions of your Webware/ files according to which files
+the AppServer must read and/or write while running. The stricter
+the better (e.g., it must write log files, but it should not write
+to config files or AppServer.py). Add write permission to any
+directory it must create files in. In your startup script, you can use <pre> su -c COMMAND webware
+</pre> to run a command as the webware user. The WebKit/webkit init
+script already has the appropriate line for this. Uncomment it and
+comment out the line above it, which runs the app server as
+root. Both lines are clearly labeled with a preceding comment: -- MikeOrr - 02 Nov 2001
+-- ChuckEsterbrook - 08 Nov 2001 Is it really wise to run AppServer as root? The advice about disabling login shells and home directories is quite
+important, but how does one set up the webware user's environment?
+I have seen nasty cases of shell resource files and moving home
+directories, which would be avoided here, but how and where should the
+environment be set up? -- PaulBoddie - 08 Nov 2001 Don't forget to make sure that the user you are going to run it as
+owns all of the webware files and can write new files in the webware
+directory. so do a: in the Webware directory (Thanks to Jay Love) -- MattFeifarek - 14 Feb 2002 That's a bit dangerous! The user running WebKit should only have
+write access to those directories that it absolutely must be able to
+write to: Webware/WebKit/Cache, Webware/WebKit/Logs,
+Webware/WebKit/Sessions. It will also need write access to
+Webware/WebKit so that appserverpid.txt and address.txt can be written
+(this is really the wrong place for these, IMHO). The webware user
+should NOT own those directories, but should be given write
+permission. -- TavisRudd - 01 Mar 2002 And if you use the highly-recommended MakeAppWorkDir, you don't need
+to write anything into the Webware directories. You will have to make
+some of the directories in your working dir writeable though. --
+GeoffTalvola - 04 Mar 2002 The trick with /bin/false as the user's login shell didn't seem to
+work for me on a recent Red Hat Linux. Doing an su -c didn't cause
+the command to be executed. -- PaulBoddie - 25 Mar 2002 Thanks to a tip from Charles Cazabon, who noticed that this works if you give apache bogus Main Doc Root, here's the recipe. There are several changes to be made... change DocumentRoot directive to a NON-existant directory. Mine is
+now /var/www/BOGUS change the corresponding Directory block header, like this: if you want to have it global (as opposed to Virtual Hosts) then remove the <Directory /> block and replace it with: The same location directive can be added to a VirtualHost block in
+Section 3. That's what I'm doing now: In Section 2 leave alone your <Directory /var/www/BOGUS> turn on virtual hosts like this: setup your Virtual Host something like this: if you want other virtual hosts (or a default host) just set up
+another virtual host block. They are in order of importance. This is
+all covered in the apache docs Good luck! -- MattFeifarek - 09 May 2002 Run Webkit as Document root with Apache 2
+=========================================v As many of you may have noticed with Apache2 you can no longer use the
+bogus document root trick mentioned above. To get around this, this
+is what has worked for my (at least on Windows with Apache2 and
+mod_webkit) Add your apache Document root as the default Webkit context Add your webkit location: Now make Apache associate py files with the /wk location: Virtual hosts are almost the same thing turn on virtual hosts as metioned above now add the virtual host directory to your webkit contexts within the virtual host declaration add a new webkit location: That should do it all without the use of rewrites -- JoseGalvez - 28 Dec 2002 Model Two plus One Has slightly different example of using doing
+this with Apache2 -- LukeHolden - 07 Jan 2003 Here's another trick for users of the cgi-wrapper, tested in
+apache1.3: This is neat if you actually want to have all webware stuff below a
+certain directory ("/mywebware" in the example) but have the apache
+start page to be the webware start page as well. All webware stuff is
+in /mywebware now, all images in /images or css in /css get served
+statically, but still apache root is Webware powered. (I wanted to have it this way on http://normalmailorder.de where
+/index.html is routed to be http://normalmailorder.de/shop/ ) -- FrankBarknecht As seen on comp.lang.python, it is apparently possible to run Web
+servers "behind" Apache. Since WebKit now has a Web server component,
+this should provide an interesting solution for serving mixed content. Of course, even without using any built-in Web serving capability
+provided with WebKit, it's probably possible to run two Apache servers
+and to "pipeline" them. (I've seen worse things done in real life!) Holger Krekel wrote that the configuration option is ProxyPass: Written in your domain's configuration section, this would pass
+requests such as: to: ---+++ References http://httpd.apache.org/docs/mod/mod_proxy.html#proxypass http://groups.google.com/groups?th=1d6c5a1de5088a50 -- PaulBoddie - 14 May 2002 Pros: Freely available. Python DB-API module available. Cons: Difficult to get up and running - some of the procedures just seem
+arcane (whereas PostgreSQL and MySQL make it easy to create
+databases and other things). Python module didn't seem to be readily available for the latest
+release of SAP DB and didn't work either, even though Jython could
+connect using JDBC. Bizarre reliability problems on Windows 2000 - try and start a
+server and it claims that the server needs restarting, even though
+the server has just been stopped. In my opinion, it's better to use PostgreSQL or MySQL, or to evaluate
+a commercial database system. Your mileage may vary. -- PaulBoddie - 08 Mar 2002 You can further speed up static file serving by exploiting client-side caching.
+The idea is to send the client a version-specific URL for static files, and
+tell the client that it may cache that data forever.
+When the underlying file changes, your Webware app generates a new URL.
+This reduces traffic to the server, but more importantly,
+it can result in noticeably faster response at the client end. This requires co-operation from Apache version 2.
+Here's the details: Configure apache-2 so that urls like /@X/... are mapped to /...,
+and the client is told it may cache the result for a long time: Modify your webware app to prefix version info to your static URLs.
+For example, for data that almost never changes, like images,
+return URLs like /@0/images/foo.gif: If such an object really does change, change images_url to /@1/images
+and restart webkit. For files that occasionally do change, like CSS and javascript,
+set the version prefix to the underlying file's modification time.
+A function like this is useful: Thanks to Cal Henderson for the original idea. -- KenLalonde - 20 November 2006 After some trial and error, I came up with this which seems to work: Add this to your SitePage and you're all set. The cookie will last
+for 10 years, which is close enough to "forever" :-) -- GeoffTalvola - 11 Dec 2001 Of course, you'll also have to call setPermanentCookie() at least once
+after you've added this to SitePage: -- TavisRudd - 11 Dec 2001 RFC 2109 recommends expiring cookies by setting the "max-age"
+attribute to zero. Setting both "expires" and "max-age" is redundant,
+but apparently doesn't hurt and maintains compatibility.
+Add this as the second last line in deletePermanentCookie(): -- KenLalonde - 15 Jan 2002 Also, make sure that the servlet setting the cookie is in the same
+directory as where you plan to retrieve the cookie. So, if you set
+the cookie in http://myhost.com/webkit/users/ a servlet sitting at
+http://myhost.com/webkit will not be able to see the cookie. To
+override this, use cookie.setPath(). E.g. to set a cookie for the
+entire host, do: cookie.setPath('/') -- CostasMalamas - 17 Jan 2002 Simple SOAP Servlet Servlet Example Working example -- whitekid You may sometime need to create classes that can only be instantiated
+once. There are many ways to create these so-called "Singleton"
+classes. Refer to this site and also for example code. One way not
+mentioned in these links (code is from here) is: MySingleton = MySingleton()
+</verbatim> -- EdmundLian - 21 Jan 2002 Note that all these singleton ideas are equivalent to a module-level
+global -- the only advantage is that they allow using a class-like
+syntax. Like a module-level global, if you have ambiguities in your
+path such that a module is imported twice, the object will not be a
+true singleton. Also, like a global, you must be particularly careful
+about thread-safety. Singleton's are in some ways a case of using a factory: that is,
+instead of normal class instantiation, you use something that looks
+like instantiation but may return an already-created object. I've
+written a small class that assists in creating factories:
+ParamFactory.py -- useful for the case when you have an external ID
+(e.g., a username), and you want to be sure you create only one object
+for each ID. -- IanBicking - 21 Jan 2002 The main servlet directory, specified in Application.config -- LukeHolden - 24 Dec 2002 When building a WebKit application, you should always create an
+abstract SitePage class from which all your other Page classes
+inherit. This allows you to build site-specific conveniences,
+utilities, etc. that will be accessible to all pages. Create a file named SitePage.py with these contents: In your concrete page classes, inherit SitePage: As you develop your website you will think of utility methods that you
+commonly want access to. For example, I often have a utility method
+called fragment() which given a name, loads an HTML fragment file
+from disk for my use. Here is a simple example: Now all your pages can pull in fragments such as:
+self.fragment('Header') See FragmentsRecipe for more information on that particular technique. Also, you shouldn't put files that are abstract or private in your
+public context where they could be susceptible to hacking. That means
+your SitePage will go into a directory often named Lib and your
+imports will look like this: See DirectoryStructure for details. -- ChuckEsterbrook - 27 Oct 2001 SourceForge is a free, public site for hosting OpenSource
+applications. Webware is one of the thousands of applications hosted
+at SourceForge. We could say a lot more, but the site says it already:
+http://sourceforge.net/ The Webware project page at SourceForge is at:
+https://sourceforge.net/project/?group_id=4866 -- ChuckEsterbrook - 31 Oct 2001 (from WishList) Using tabs for indentation is inconsistent with 99% of current Python
+code on the net. It causes specific problems in Emacs, and leads to
+common mistakes when spaces are mixed in (especially when tab width is
+set to 4). Cheetah and FunFormKit already use spaces. Webware should do the same! -- IanBicking - 02 Nov 2001 I agree completely. Tabs suck!! -- TavisRudd - 02 Nov 2001 I disagree completely. Spaces suck! They suck up space. They suck up
+time cursoring through them. They are another example of the "15%" bloat that technologists
+incorporate into their source code, their standards (XHTML), etc. Good editors like UltraEdit and SciTE have NO problems dealing with
+TABs which have been around for decades. -- ChuckEsterbrook - 03 Nov 2001 Chuck, are you seriously concerned about the bytes taken up by
+spaces?? Tabs have their uses, and sure they're minimally more
+storage efficient, but in this case they cause real problems for
+developers. Also, with most good editors you don't have to cursor
+through spaces! In Emacs, or vi, you just hit the [TAB] key and it
+will put you in the right place and deal with all the spaces for you.
+If spaces suck, why does most existing Python code use them and not
+Tabs? Tabs also go against Guido's informal style guidelines for
+Python code. Furthermore, why do the Webware style guidelines suggest using a tab
+for the first indent and spaces thereafter? That's a recipe for
+confusion. -- TavisRudd - 03 Nov 2001 I have this book from when I was a kid -- how to program Adventure
+Games in TRS-80 Basic. The author proposes saving valuable memory by
+eliminating all possible spaces, with code like SETX=10:SETY=20 (line
+numbers take up space too -- and the Basic tokenizer apparently knew
+that SET was a keyword and split SET from X). It was a noble goal,
+because I believe the target machine had 16K of available memory (at
+least for the Basic program). As you can imagine, it did sacrifice
+legibility. At some point that stopped being a problem. I think it was when
+people weren't writing in basic on 16K machines. I really don't know
+what everyone's systems look like -- some people are programing on
+Windows, some on Linux, some on FreeBSD. Perhaps Chuck is trying to
+get Webware to run on a TRS-80? An interesting hack. If we are concerned with the compactness of the code with respect to
+editing, it should be noted that there is a large amount of space
+devoted to excessively long variable and method names. For instance,
+defaultConfig could certainly be defConf,
+configFilename could be confFN, etc. Why not? And why, indeed, would I write all this on an issue as small as tabs
+vs. spaces? Because it's really annoying. When I'm editing normal
+Python code I turn off "insert tabs" in Emacs. Whenever I edit
+anything in Webware I have to either turn that on (which will break
+the other files I might edit), or hit C-Q tab for every indent,
+usually forgetting some and causing SyntaxErrors. Some people are indifferent to this. However, I have yet to hear
+anyone other than Chuck who actually likes tabs. And you can take
+Emacs from me when you pry it from my cold, dead hands, so arguments
+as to what "good" editors do don't hold much weight. -- IanBicking - 05 Nov 2001 When you gzip a file, the spaces get compacted anyway. Tabs appeal to my aesthetic sense. However, they tend to be more
+trouble than they're worth in distributed programs. Mixing tabs and
+spaces in the same line (e.g., comment continuation lines) is
+especially problematic as Tavis said. -- MikeOrr - 05 Nov 2001 I use "hard tabs" in my source code, but if it ever gets to the state
+where other people are going to be modifying it, and if they aren't up
+to dealing with tabs, I suppose I'll have to run it through one of the
+conversion programs and have it as "all spaces". I don't think arguing about editor support for either adherence is
+worthwhile - it's the lazy or uninterested developer who doesn't set
+up editor features who is most likely to mess things up. If I use
+"hard tabs", someone may well come along and indent manually with
+spaces, and if I use "all spaces" then someone may then start to put
+tabs into the code. Perhaps it's just a case of better procedures around submitting and
+committing code - have a program (like tabnanny) which processes the
+code and rejects it if it doesn't come up to scratch. -- PaulBoddie 07 Oct 2001 I don't think 'messing things up' is the issue here. Rather, it's
+that tabs are a pain in the ass. Most Python code out there on the
+net uses spaces and it requires effort (and notes to self) to switch
+back and forth when working with code that use tabs. -- TavisRudd - 07 Nov 2001 I have run into this issue often when editing Webware code, and it's
+needlessly annoying. The Python libraries use 4 spaces. Let's go
+with Guido on this one. -- KenLalonde - 09 Nov 2001 Tavis wrote: "Furthermore, why do the Webware style guidelines suggest
+using a tab for the first indent and spaces thereafter? That's a
+recipe for confusion." What are you talking about? The Webware style guidelines don't say
+anything like that. You're creating confusion. The guidelines say tabs are not used past initial indentation. eg,
+<tab><tab>'x':<tab>5 is bad. Here the coder is trying to line up his
+dictionary values, but that won't work because people have different
+tab widths. Regarding "no one else but Chuck likes tabs", I think Geoff expressed
+a preference for tabs over spaces on the mailing list (but that's from
+memory and a while ago). Regarding "why not confFN instead of configFilename,
+readability comes before compactness. Does FN mean "function" or
+"filename"? Regarding trouble editing code, the only trouble I have ever
+experienced with Python modules is when users mix tabs and spaces for
+indentation. Something everyone agrees is bad. -- ChuckEsterbrook - 10 Nov 2001 Chuck, that's what I thought you meant by "The guidelines say tabs are
+not used past initial indentation." I assumed you meant
+<tab><space><space><space><space>, etc. I happy you didn't! But, the
+issue remains. I'm not just nit-picking over this, it's a BIG issue
+for me as it DOES cause major headaches when I work with (or even try
+to read) the webware code. Like it or not, Emacs is used by alot of
+the people who might read/work with the webware code so this issue
+isn't going to go away. Unlike tabs, spaces don't cause problems for
+anyone so let's make switch over to them? I'm even volunteering to go
+through the source code and do it. -- TavisRudd - 10 Nov 2001 Aesthetically, I have a very mild preference for tabs. But I really
+don't care much -- the editors I use are smart enough to handle either
+one just fine. Chuck, I assume the editor you prefer also handles
+spaces acceptably? If so, why not switch Webware to spaces -- it will
+reduce pain considerably for some people, without adding much if any
+pain for you. And it will match the preference expressed in the
+Python style guidelines (yes I know they don't require spaces...).
+http://www.python.org/peps/pep-0008.html -- GeoffTalvola - 13 Nov 2001 Mixing tabs and spaces is generally agreed to be a bad idea. Beyond
+that, I believe the choice of tabs or spaces is secondary to the need
+for a universal convention, whatever that is. Though I am sympathetic
+to the case that tabs should have been universally adopted throughout
+Python land, the fact is that spaces have become (with Guido's
+informal blessing) the overwhelmingly prevailing convention. I feel
+that conformance to this Python-wide convention greatly outweighs any
+technical merits that tabs may have. -- HamishLawson - 16 Nov 2001 There seems to be near universal agreement that the mixing of tabs and
+spaces is dysfunctional. While I happen to prefer using tabs for
+indentation over spaces, my personal policy is to preserve the style
+that the original author has chosen. What really bites is the editor
+centric view of the world. If you use an editor that dysfunctionally
+mixes spaces and tabs by default, fine. But, don't blindly expect
+that others will use the same editor to read or edit the code. Most
+editors in widespread use have the facility to do bulk mappings or
+translations. Avail yourself of that capability to change leading
+tabs to 4 spaces or groups of 4 spaces to tabs when you fire up your
+editor. Then, when you're done, reverse the mapping. It's a mistake
+to cater to the whims of folks who happen to use one particular editor
+whose default behavior screws things up for folks who use something
+else. With that said, as the original author of Webware began using
+tabs, I'm in favor of keeping tabs as the indenting scheme. -- KipLehman - 17 Nov 2001<br/> Of course, one must note: if now or any time in the future Webware was
+to convert to spaces for indentation, this discussion would become
+moot and no one would raise the TabsNotSpaces argument. We could end
+this petty argument over style forever. I think that indicates
+something. I am not one to raise long, tedious, pedantic debates... I
+recognize this debates for what it is... and yet I still will say
+that I think tabs are annoying, yet again! That is the level of my
+passion! I avoid petty things, but this petty thing refuses to avoid
+me. -- IanBicking - 17 Nov 2001 Regardless of whether we keep tabs or move to spaces, we really need
+to have strict enforcement of that convention, especially when it
+comes to CVS. Otherwise, there are likely to be some misleading
+reports made by cvs diff and other commands. And there should be
+conventions around text formats too: enforce UNIX text throughout, if
+this isn't done already. I can't stand it when people check files into
+revision control systems in the wrong format and the "diff" command
+tells you that the whole file has changed, especially when they have
+just changed one line or something. -- PaulBoddie - 19 Nov 2001 Long ago, Jim Fulton beat the notion into me that everything should
+fit in eighty columns. This makes lots of sense in that viewing code
+in any system is easy - particularly when having to jump into
+character mode Emacs or VI on a terminal. It means that there's no
+weird wrapping going on, depending on the editor being used. To fully
+make this work, you have to use spaces since tab sizes can vary. And
+we all know that mixing tabs and spaces is in very bad taste. Maybe I've just been spoiled by all my years of working with Zope, but
+I've found its code to generally be more readable at a glance due to
+their enforcement of "indent by four spaces" and "keep those lines
+less then eighty chars". This is very useful when using 'less' or
+just opening up another buffer quickly to see what a certain module
+does. -- JeffreyShell - 12 Feb 2002 I have no issues using spaces or tabs in vim, but tabs do require more
+careful editing, and usually don't copy and paste for terminal windows
+correctly. I'm personally opposed to using tabs because it does make trading
+code difficult. -- EricRadman - 20 Apr 2004 I will throw my hat in the spaces-only, 4-per-indent-level camp. This seems to be the prevailing indentation method used in the Python community. As a public project, going along with the crowd on this point will only gain more support for Webware. I, personally, was going to look into fixing some things in the codebase, but find it very difficult to deal with the mixed tab/space codebase. 4-spaces is the common ground that will gain the most support and likely grow the list of willing contriubutors. -- KyleVanderBeek - 28 Sep 2004 I work for NASA at Goddard Space Flight Center in Greenbelt, MD. My team here is developing software to support an advanced collaborative and knowledge-based computer-aided engineering environment. We are using Python for this (for many reasons :^), and I am planning to use Webware for some aspects of our architecture. -- SteveWaterbury - 17 Apr 2002 Read the Webware Style Guidelines, and try to follow them when making your
+changes. Create a patch against the latest SVN (using svn diff) and submit it on
+Webware's SourceForge page. Then send email to the webware-devel
+list. It would be highly appreciated also if you could modify the WebKit
+documentation in the appropriate places to describe the new patch.
+The thing you're patching may be currently undocumented, in which case
+you're not expected to write full documentation unless you feel like
+it :-) At the very least, please include in the patch submission a sentence
+or two describing the patch that could be pasted into the release
+notes. And if you've added new settings to Application.config or
+AppServer.config, please edit the Configuration Guide to describe
+the new settings, and include the diff in your patch. -- GeoffTalvola - 20 Mar 2002 And remember to test your patch right before you sumbit it so you
+don't accidentally submit syntax errors and the like along with your
+patch (not that I've ever done something so silly ;). -- TavisRudd - 20 Mar 2002 And if you have more than one distinct enhancement and/or fix, try to
+make individual patches rather than one monolithic patch. That makes
+it easier for us to absorb the contributions. -- ChuckEsterbrook - 29 Mar 2002 A SubProject is used to discuss and implement a specific item on the
+ToDo list or the WishList. See SubProjects for more information. -- TavisRudd - 23 Nov 2001 This is the home of Webware development sub-projects. These projects
+are used to discuss and implement specific items on the ToDo list or
+the WishList. We are using dev.zope.org's FishBowl process as a
+model for these projects. Here is some more info on that process. TavisRudd is responsible for maintaining this page, but feel free to
+edit it. Project Proposals Active Projects Implementing a comprehensive UnitTesting framework for Webware,
+that verifies its core behaviour and can also be used for unit
+testing of user applications. Implementing a flexible and seamless framework for handling
+authentication and authorization. The WebwareUserManager
+application will be built on top of this framework. Creating a BatteriesIncluded distribution of Webware that comes
+with other related tools, such as Cheetah, FunFormKit,
+XmlForms, the database adaptors, etc. If you want to start new project add a link to the list of project
+proposals and create a summary page following the guidelines below.
+You might also want to use Python's PEP's as a guide for developing
+your proposal (http://python.sourceforge.net/peps/). Once you've
+completed the summary page, announce the proposal on the
+webware-discuss email list to get some initial feedback and recruit
+volunteers. If you decide to continue with the project after the
+initial feedback move the project link from 'Proposed Projects' to
+'Active Projects'. If you decide not to continue, move the link to
+'Withdrawn/abandoned Projects' instead. All active projects should have summary page that addresses the
+following: Who is the project moderator and who else is working on it? What is its current status? What is the problem being solved? What (broadly) is the proposed solution? What are the major project risks? What is the scope of the project? (what will, and will not be addressed) What are the deliverables of the project? What is the delivery target (a date or Webware release number)? just some quick notes for now: use the wiki to keep the discussion open keep the project's summary page current. occasionally post an update to the webware-discuss email list for people
+who aren't following the project's wiki directly. if you are doing any coding, make sure you coordinate your activities with the other developers.
+Keep them informed about what you are doing via the webware-devel list. -- TavisRudd - 31 Oct 2001 As of March 2005, Webware source code is stored in Subversion at
+svn.w4py.org, instead of at SourceForge.net The Subversion repository can be viewed online with your web browser: Anonymous SVN access is available to anyone. The repository can be
+accessed at svn.w4py.org or svn.webwareforpython.org, and it's set up
+for both native SVN and WebDAV access: ...but the password file for write access via svn:// is not the
+same as http:// access so unless you request otherwise, you're
+username will be entered in the WebDAV password file. Note: the subversion repository is large and will become larger;
+it is best to check out just a portion of the repository. To see what
+projects are in the server do: Or deeper trees, like: And then check out a single tree. To use tabs, not spaces, for indentation: the argument no one raises. Actually, in environments where editors like vi are the only ones
+available, it can be a nicer experience to use tabs to indent blocks
+due to the poor handling of multiple space indents in, for example,
+the "standard" editions of vi. And if you've ever had to share an
+office with someone whacking the space bar to do their indents, you'd
+be a rabid tab advocate by now, too! -- PaulBoddie - 19 Nov 2001 Terrel Shumway is "That Web Guy Who Knows Marketing" I use Python and Cheetah and Webware to build sticky direct-response websites.
+I also provide high-touch python hosting and webware programming services. I have an application with a class like: ie an object holding information about a job which I want to allow the users
+to be able to add (and remove) keywords. The keyword table is simply: The application has a search page (a web form) which works by looking for
+various <inputs> for each field in the job, creating an SQL statement for each
+of these and joining these together with "AND " then doing
+Job.select(sqlstring). Most of the fields are fairly trivial but the keywords
+are more interesting which is what I'm going to describe here. There are two operations the user wants to be able to do from the search page: find jobs with any of a list of keywords find jobs with all of a given list of keywords For pedagogic purposes I'm assuming that input_keys is a list of strings,
+one for each of the keywords selected on the form. There is also a radio
+button to decide between AND and OR which is what these cases boil down to. The first case is relatively simple. If the input was for three keywords it
+just needs an SQL statment like: so a simple piece of code like: The second one is more challenging because you need to do multiple joins with
+the intermediate job_keyword table. So a piece of code like: does the trick. The final search is done by: the main trick, which took me ages to think of, is that the clauseTables list is really a list of expresssions not just table names. I didn't need to do the Keyword.byName(k) lookup in the code, it could have been added as another join but I know my keyword table is tiny compared to the number of jobs so I suspect it would be more efficient. Preliminaries I usually try to separate the presentation from the logic. This allows
+for testing the logic without much thinking about the Webware. Thus,
+this page does not contain much about testing Webware pages but concentrates on testing the model. I've tried to automate the retrieval of Webware pages using cURL. This
+works quite good, but I don't yet have much experience with it. Please
+complement this page with your experiences. Define test classes Each class to be tested is accompanied by a test class (usually, I use the name "Test" + class name for it). This class has a method called setUp (not always) for each aspect to be tested a method called testAspect a method called tearDown (not always) The setUp method prepares data structures necessary for testing, the
+tearDown method disposes any open file handles and so on. Define a function returning the test suite Each module contains a function called suite(). This function creates
+and returns a test suite containing all testcases for the module: Create a test driver module In order to run all tests, I have a module test.py which looks like
+this: This module is rather low-tech; it would be easy to enhance it to load
+all available modules (maybe in a directory tree) and create the
+respective test suite. Call this module from every class that should be tested The last statement in every module is: Run the tests whenever you changed something This is very simple when using Emacs with Python mode. Just press
+Control-C twice, and watch the tests running by. You might want to
+tweak unittest.py so that it does not report successful tests - in
+this case Emacs just reports "no output" in the status line when all
+tests have run successfully - this is as unobstrusive as it gets ;-) If you do not use emacs, you have several options:
+* use the graphical client (GuiTestRunner) of unittest
+* open a console and run the tests with python test.py
+* use your editor's capabilities to start external programs When you use the console, you don't need to include the test module
+into each module, of course. Testing the interface with cURL If you want to test the pages as they are presented by Webware, you
+have to use a program which automates the retrieval of HTML
+pages. cURL (http://curl.haxx.se) does a good job. It can save and
+send cookies and has a good (command line) interface. You can e.g. request a page with: With the following line you can submit a query to search.python.org: The extraction and forwarding of cookies follows a similar pattern. You can use curl either from shell scripts - request a page, compare
+it with a "known good source" and report any differences - or you can
+integrate it into your testsuite using pycurl
+(http://pycurl.sourceforge.net). This module provides Curl objects
+that can be configured to retrieve webpages. Unfortunately, its
+documentation is rather poor. -- AlbertBrandl - 14 Mar 2002 It is also very useful to be able to automatically setup and tear down
+the WebKit environment + Servlets that will be used as part of a test
+suite. The regression testing framework that comes as part of
+WebwareExpRefactoring is very handy for this. It uses the builtin
+HTTPServer and thus can be run out of the box without requiring an
+external webserver to be installed and configured. This framework uses Steve Purcell's HTTPSession class to do the same
+thing you're doing with curl directly from Python. It's trivial to
+write new test cases using this approach. -- TavisRudd - 14 Mar 2002 What's the best way of dealing with highly stateful applications? For
+instance, websites with database backends, where most actions involve
+querying or modifying the database. Or sites you can't easily get to
+some functionality without going through a long path -- login,
+interactive feedback (even as simple as providing a list of options),
+etc.? I haven't done the testing I'd like, because it seems so
+difficult to set up tests in these environments... hints on doing it
+better? <br> -- IanBicking - 14 Mar 2002 If possible you should enclose the state information. I use classes
+for the interaction with the database, another class for workflow
+information (which workpiece will be sent to which author after which
+action) and so on. A controller cooperates with the workflow manager
+in order to determine the next person responsible to update a
+document. Since these classes only use a tiny subset of the webware framework
+(mostly response objects, sometimes transaction objects), it's very
+easy to write stubs for these. Python does not care if the signature
+of an object conforms to a certain interface ==> it happily accepts
+those stub objects in place of the real objects, as long as they
+provide the methods that are actually used. The final acceptance tests are done by hand. But
+WebwareExpRefactoring looks rather promising to me - I'll certainly
+have a look at it before I start testing the next release. -- AlbertBrandl - 15 Mar 2002 The combination of PyUnit and HTTPSession in WebwareExpRefactoring
+is a good fit for this sort of stuff. HTTPSession allows you to model
+a sequence of HTTP requests (including header and cookie management)
+and maintain state throughout. You can push cookies, manipulate GET
+and POST vars, and do practically everything else you can do with a
+web browser. If you're working with a database you could setup and
+tear down a test database as part of your unittests. The beauty of
+this framework is that you can control and manipulate everything from
+a single Python test module. There's no need to muck around with
+external POST files, etc. Have a look at
+http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/expwebware/Webware/WebKit/Test/ -- TavisRudd - 14 Mar 2002 Just an idea: Would it be possible to capture a real interaction with
+the webserver and use this for setting up acceptance tests that are
+then run with the above combination? I'm in search of a simple means
+to get the users create their acceptance tests. -- AlbertBrandl - 15 Mar 2002 Intriguing... I'm thinking it would be easiest to hack WebKit.cgi, so
+that it recorded all the requests and response (maybe in
+serially-named files, which would be easy to inspect). Then you could
+put together a very simple little adapter that would work off those
+files, resubmitting the requests and either comparing, or simply
+recording the new responses. A page-comparer would be a seperate development, hopefully robust
+against mere style changing. But even if it wasn't, it could check
+easily that no exceptions occurred, and maybe check that pages that
+shouldn't have changed didn't (for instance, if you have 50 pages,
+maybe a single change would only effect the output on a couple of
+them). Growing your testing suite would be difficult in this case -- you'd
+have to add something to this adapter that would replay all the
+beginning events, then let you append more. Also, you'd have to deal
+with changes to the interface -- for instance, where you change the
+structure of a form -- so that you would replay only X events (up to
+where the interface has changed) and then manually continue. But I really like this idea... it would make testing so much easier,
+even as a developer. I'm going to start on it right away. -- IanBicking - 15 Mar 2002 Puffin is a very cool Python web app testing framework, actively
+developed by Keyton Weissinger. Would be nice to have a look at it. -- KendallClark - 27 Apr 2002 This page describes how to use Apache's mod_rewrite module with
+WebKit_, and mod_webkit in particular. It starts with a general
+overview and then presents some recipes for solving specific
+problems. Related reading: mod_rewrite reference docs, mod_rewrite guide mod_rewrite is an Apache module that is used to remap URLs. Here's
+how the Apache manual describes it: This module uses a rule-based rewriting engine (based on a
+regular-expression parser) to rewrite requested URLs on the
+fly. It supports an unlimited number of rules and an unlimited
+number of attached rule conditions for each rule to provide a
+really flexible and powerful URL manipulation mechanism. The URL
+manipulations can depend on various tests, for instance server
+variables, environment variables, HTTP headers, time stamps and
+even external database lookups in various formats can be used to
+achieve a really granular URL matching. It is an incredibly flexible tool that can be used for a wide range of
+tasks. Here's some examples: change the file layout of your site without breaking bookmarked or
+search-engine indexed URLs make a certain directory or file visible in every subdirectory of
+your site without needing to symlink to it in every subdir or manage
+complex relative paths. use mod_webkit to serve files that appear to the user as if they are
+in Apache's top directory ('DOCUMENT_ROOT'). Without mod_rewrite,
+each URL would look like www.mysite.com/WK/MyServlet.py. With
+mod_rewrite, you can use a simpler URL like
+www.mysite.com/MyServlet.py. redirect to files on remote servers To use mod_rewrite you need to compile it with Apache and then enable
+it in your _httpd.conf_ configuration file. See the apache docs for
+compilation instructions. To enable it in your _httpd.conf_ file,
+uncomment or add the following lines to the DSO section in part 1: -- TavisRudd - 05 Mar 2002 To use ModWebKit, you'll usually have some lines like this in your
+http.conf: Then /WK/Servlet will run Servlet.py in the default context. The problem with this is that you now have an implementation dependent
+fragment "WK" (standing for "WebKit") in your application URLs. This
+is considered bad for various reasons. Ideally, the URL should only
+reflect the content and be human readable (you might want to read the
+passage about "The right URL for your link" from the Tutorial "The
+Care and Feeding of Hyperlinks" ). You cannot simply omit the
+Location "/WK" or replace it by "/" if you want everything to be
+served through WebKit (I don't know why, but it won't work). However,
+you could replace "WK" by a memorable and reasonable keyword pointing
+to the application itself, not the technology behind it. The more sophisticated solution is to map the URL using the URL
+manipulation capabilities of Apache provided by mod_rewrite. Perhaps
+you want to map different URLs to different contexts, or even to the
+root of the website: The L flag means "this the LAST rule, don't apply any more" and PT
+means "PASS-THROUGH the changed URI to the next handler." You can
+leave the L off, but the PT is essential if you are using mod_webkit.
+See the mod_rewrite reference guide for an explanation. To redirect all images to a static location, so that there's no
+overhead from calling Webware, you would put this rewrite directive
+before the above: For a virtual domain, do something like: This assumes /WK (not virtual) is where the AppServer is located,
+and that you've created a context BobsYourUncle for that virtual
+domain. What if you aren't using mod_webkit? Say you are using a CGI adapter
+at http://mainhost.com/cgi-bin/WebKit.cgi ? Then just replace /WK
+with =/cgi-bin/WebKit.cgi`` in all these examples. You can use
+[L] instead of [L,PT] if you aren't using mod_webkit. (Do you
+need [L,PT] if you are using mod_snake, etc?) -- IanBicking ... with changes by TavisRudd - 05 Mar 2002 If the image location is the same as the original URL, use a hyphen as
+the second argument. You don't need $1's ()s in this case: -- MikeOrr - 10 Dec 2001 I had two problems. The first was that I put my httpd.conf rewrite
+rules in a <Directory> such as / or the doc root. This gave either a
+repeating path that was HTTP Forbidden, or gave no response. The
+solution was to put the rules above the first <Directory>. The second problem was that if I put a slash in front of $1, the path
+could not be found. Finally, I was able to remove the ^ and $, which aren't needed to
+capture the whole path. My final solution was: I used Apache 1.3.22 (built from source) on Mandrake Linux 8.1. -- ChuckEsterbrook - 22 Jan 2002 I used .htaccess files to set the rewrite rule, and had the following
+rules as above: This caused the recursion mentioned above in another case, to solve
+this I went to /cgi/.htaccess and placed there: This solved it. Now I can get to actually try to build my application. -- BaruchEven - 17 Feb 2002 To clarify, if you want Webware to serve URLs in the top-level
+directory (/index.py) without a /WW prefix AND serve certain files
+statically: (The line numbers are not in the Apache file.) (1) and (2) rewrite /images, /pix/images and /pix/thumbs to their
+original URLs ("-") and use "L" to prevent rule 4 from executing. (3) rewrites URLs with the WebKit prefix to their original URLs, and
+uses "L" to prevent 4 from executing, which would wrongly rewrite
+/WW/index.py to /WW/WW/index.py (4) does most of the work, rewriting /index.py to /WW/index.py so that
+users don't have to type the /WW prefix. "PT" is necessary or the
+request won't be passed properly to WebKit. I don't know if it's possible to make WebKit serve directly from /
+using <Location /> AND serve some files statically. If it were
+possible, you'd need to supercede the "SetHandler webkit-handler",
+thus: but I don't know how you set the handler back to the default once
+you've set it to something else. -- MikeOrr - 16 Apr 2002 My server is hosting 3 unrelated URLs. Each URL resolves to a
+virtual host. One of these virtual hosts is used as the example. The Apache config for the Virtual Server includes both the Location
+directive as well as the RewriteRule directives. This allows for
+different configs for each of the three virtual hosts. The RewriteRules accomplish two things. One, the third rule adds the "/wk" to the start of the URL passed
+to Apache. This makes Webware happy. However, the first rule
+ensures that a "/wk" does not already exist in the URL, as two
+"/wk" values would be a problem. Two, the second rule sends request for images used by mywebcontext
+to the proper non-context directory. The "[L,PT]" on both the
+second and third rules allows only one of those rules to be processed. Given the server directory structure: The Webware context is: The Apache config is: -- TacticalJack - 04 Sep 2002 shareware Windows enough preferences to make it act the way you want the usual features: auto-indentation, syntax highlighting, autosave, etc. excellent tech support ability to re-open previously opened docs upon launch -- great for continuity of work ability to open files over FTP ChuckEsterbrook - 31 Oct 2001 charityware Various PSP syntax hilighting available. See: http://vim.sourceforge.net/scripts/script.php?script_id=576 Unpack the gvim60.zip and vim60rt.zip files into the same
+directory and then run install.exe. Don't have bad text editing experiences on Windows ever
+again! :-) Setting indentation to use 4 space indents can be done by
+adding the following lines into your .vimrc (or _vimrc on
+Windows): Or put this line in your files: Contributed by:
+PaulBoddie - 01 Nov 2001
+(modified 21 Nov 2001, 04 Dec 2001)
+MikeOrr - 20 Jun 2002 The original GPL app Various Unfortunately, it is difficult to deal with the Webware style of
+using tabs for indentation instead of spaces. You can set
+Options>Advanced>Emacs>Editing>Basics>Indent Tabs Mode... (turn it
+on). Unfortunately when you edit code from any other source of
+Python code it will insert tabs instead of spaces, causing great
+harm, since everyone else uses spaces, not tabs. Note: I've found that if you don't set the tab width to 4 (i.e.,
+leave it at 8) it will auto-detect tabs vs. spaces correctly. One of the nice features is editing files on your server over FTP,
+by opening /username@servername.com:/path/to/files/servlet.py,
+or even over SSH using TRAMP IanBicking - 02 Nov 2001 open source KDE/X-windows a "Python Class Parser" plugin is available. See http://www.kde.org/kate/3rdparty.html lots of preferences the usual features: auto-indentation, syntax highlighting, etc. excellent tech support ability to re-open previously opened docs upon launch -- great for continuity of work a filesystem-browsing side-panel with one click access to files if you are coming from UltraEdit for Windows, then you will probably like Kate ChuckEsterbrook - 10 Nov 2001 open source Windows and Linux/GTK nice syntax highlighting, including a good Python mode as well as an ASP mode that works pretty well for PSP files (many more modes available too) multiple documents open in tabbed windows lots of preferences configurable through text files Can run scripts as a mini-IDE with the output showing up in another panel Launches very quickly (at least on Windows) compared to most other text editors GeoffTalvola - 20 Jun 2002 Third-Party Pages discussing Webware for Python: This is a Webware for Python Wiki written by Ian Bicking. This wiki uses ReStructuredText for its markup. Restructured
+text is intended as a general-purpose markup for creating documents,
+with specific emphasis on documenting programming. As such it is the
+perfect fit for this site. ReStructuredText is also used by several
+other projects, so if you learn how to use it for this wiki you can
+transfer that knowledge other places. To learn how to use ReStructuredText, you may wish to look at the
+Quick Reference Note that WikiNames are not used in this software! Instead a _
+must be appended to a name to make it into a link, and use: for multi-word names. All names are made lower-case and spaces are
+replaced with hyphens (i.e., the link is to the page long-wiki-name.html). To play around with the markup to this wiki feel free to use the Wiki Sandbox. If you don't want to use textarea editing (which is no fun), this
+site uses External Editor to allow text to be edited with a
+standard text editor. The Zope Product aspect is not required (the
+equivalent is already built into this wiki), but the "helper
+application" is used to hook into the editor. This is
+the zopeedit.py application. To invoke the editor once you have
+set up the client, click the pen image ( If you are using a browser in the Mozilla family (Mozilla, Firefox,...) you can
+also use the cool Mozex extension, which lets you configure your favourite
+text editor (Emacs, Vim, Notepad,...) for textareas. For editing HTML pages directly in WYSIWYG fashion, this wiki uses Xinha.
+This should work out of the box with all modern web browsers supporting
+Javascript. You will even get a special button for Wiki internal links. Updates to this site are published to a New Pages and a Recent Changes
+RSS feed, which you can subscribe to in order to receive updates. This wiki was written specifically for the Webware for Python web site,
+though in most ways it is a general-purpose wiki.
+Of course, it runs on Webware and Python. You can download
+the source code for this Wiki here
+or check out the latest version from the GitHub repository with $ git clone https://github.com/WebwareForPython/w4py-olde-wiki.git Then follow the installation instructions in INSTALL.txt. See also: Wiki Features, Wiki ToDo, and Wiki Bugs. A style of Wiki writing that facilitates an ongoing discussion between
+interested parties. Each party owns their entries, which are signed
+and dated. See http://www.usemod.com/cgi-bin/mb.pl?ThreadMode for a
+fuller explanation. It is complemented by DocumentMode, which is like
+a synopsis of synthesis of a discussion. -- TavisRudd - 23 Nov 2001 We should strongly consider using the SourceForge task tracker to
+manage our To Do items since it is specifically designed for this and
+allows for sorting, assignments, etc. What makes more sense for the
+Wiki is the WishList where people can toss out ideas without
+formalities. -- ChuckEsterbrook - 02 Nov 2001 If the SourceForge task tracker is so great, why hasn't it been used
+for Webware since 2000? I'd argue there are big advantages to doing
+it here: ability to cross-reference other Topics/Discussions in the Wiki faster and more flexible to edit allows open discussion of the items in the list allows fast grouping / reorganization of items can use TWIKI's TOC feature What we need is a good layout for this list. -- TavisRudd - 03 Nov 2001 The SF tracker hasn't been used due to laziness. Editing a text file
+on my local drive is easier than just about anything. There are advantages to tracker, like the ability to query for open
+bugs, sort by date, sort by priority, etc. I believe the discussion
+can be open for tracker items. eg, you can add running comments.
+Also, I think the tracker can e-mail the assigned party and the
+reporter as updates are made. Basically, it's the classic set of pros and cons you always have
+between a Wiki and a structured web app. -- ChuckEsterbrook - 05 Nov 2001 These are the improvements and additional features that we plan to
+add to Webware at some stage in the future. We will add a method to the Response object that does what
+SettingPermanentCookies describes. -- GeoffTalvola - 06 Dec 2001 This documents the process that occurs when an HTTP request is
+made to Webware. Most of this process is internal to Webware, but
+knowing it can help you understand what's going on even if you
+don't modify any of that code. The process begins when a browser requests the web server to for a
+page. The browser tells the server what page it wants to receive, and
+passes any cookies that are marked for the server and any form
+variables (GET or POST). Because different web servers are supported, and under each server
+there several ways to interact with Webware, there are a variety of
+adapters that will handle the request at this point. With the
+exception of OneShot, they will generally package up the request and
+send it over a socket to the AppServer. The AppServer has been
+started ahead of time, and is waiting to respond. The AppServer is generally the subclass AsyncThreadedAppServer,
+however I will refer to it simply as AppServer. The AppServer listens for requests from an Adapter 1. When it
+receives a request, it puts it in a queue and the next available
+thread will handle the request. A fixed number of threads are started
+on launch, and if that pool of threads is exausted the request will
+block until another request has been finished. (@@ Correct?) The thread (which is an instance of the RequestHandler class) will
+wait until it has read all the data (in
+RequestHandler.handle_read), placing the data in
+RequestHandler.reqdata. Then the RequestHandler.handleRequest
+method is called. The request that was passed over the socket is then
+unmarshalled 2 (if the request was not properly packaged, you will
+get a marshalling error here -- that is what happens when you try to
+connect directly to the AppServer from your browser) RequestHandler handles STATUS and QUIT methods directly. (@@ how
+would these requests be made?) All other requests are handled by
+Application. AppServer keeps an instance of Application, and
+Application.dispatchRawRequest is called with the unmarshalled
+request 3. When you connect through OneShot.cgi you go through largely the same
+process, except that there is no persistance. The OneShot adapter
+starts a new AppServer (OneShotAppServer) for each request. This is
+inefficient but at times convenient. Application.dispatchRawRequest takes the dictionary that was passed
+over the socket, and creates an HTTPRequest. HTTPRequest.__init__
+parses the dictionary. It parses fields, cookies, and some internal
+values that are used by Application. (For instance, the fields are
+passed in as a raw, URL-encoded string, but are converted to a
+dictionary-like object) After the HTTPRequest is created, it is passed to
+Application.dispatchRequest. This creates a Transaction with
+Application.createTransactionForRequest. Transaction simply acts
+a container for these various pieces of a transaction (request,
+response, session, servlet, and application), and passes messages to
+them (through methods). Transaction is otherwise stateless and has no
+logic. It is not the parent of these objects -- in particular, the
+session, servlet, and application will typically outlive the
+transaction. A response is created with
+Application.createResponseInTransaction. An HTTPResponse is
+created, and again Transaction acts as a container. The Application asks for HTTPRequest.serverSidePath, which in turn
+calls Application.serverSidePathForRequest. This then tries to
+find the Servlet that corresponds to the URI asked for. Consider an example URI: Application keeps a cache of URLs and their matching files. If a
+cached filename matches, we use that. Otherwise: Remove the portion that relates to the adapter
+(/cgi-bin/OneShot.cgi). Inspect the first portion of the path (/Welcome): Does it match a Context? (Contexts are listed in
+WebKit/Configs/Application.config) If so, look in this context. If not, consider it to be in the
+default context (the default is defined in
+Application.conf). In our example it wouldn't match a
+context, and so we'd treat it as though it was in the default
+context (Examples). Look in the directory that matches the value of the context entry in
+Application.config. This directory is considered relative to the
+location of Application.py, i.e., the Webware/WebKit
+directory. In our example, Webware/WebKit/Examples Follow the path until you find a file. In our simple example, all
+that's left of the path is /Welcome. The file can have any extension (@@ I'm not really sure how this
+works) If you have ExtraPathInfo set to 1 in Application.config, then
+anything that is left of the path will be available to your servlet
+through the method request.extraURLPath() (@@ oh, I can't remember
+where -- there also appears to be some sort of attempt to match this
+remaining path information to a file) With the filename of the servlet, Application continues. When Application.dispatchRequest gets the resultant
+serverSidePath, it calls one of a couple methods: If the result is None, then the page was not found:
+Application.handleBadURL, which gives a 404 message. If the result is a directory, but the request didn't end in a slash:
+Application.handleDeficientDirectoryURL, which gives a redirect
+to the same location with a "/" appended to the URL. If the session ID is invalid (doesn't exist or has timed out,
+Application.isSessionIdProblematic):
+Application.handleInvalidSession, which creates a new session
+ID, sets the cookie, and passes to Application.handleGoodURL Otherwise (all good): Application.handleGoodURL Application.handleGoodURL calls
+Application.createServletInTransaction. Like the path lookup,
+this method first looks for a cached Servlet. If it's found, it
+checks the timestamp on the cache and the source file, invalidating
+the cache if necessary. If a cached Servlet wasn't found, or the cache was invalidated, it
+creates a new cache entry for the Servlet 4. Application.getServlet actually creates the Servlet. The cache
+actually keeps a queue of available instances of the Servlet, which
+are reused when possible. (@@ what's up with the factories here? I
+know what they do, but not how they get called) Once the Transaction has a Servlet to work with, it calls
+Transaction.awake, Transaction.respond, and finally
+Transaction.sleep. Transaction in turns calls these methods on
+both the Session and the Servlet. HTTPServlet.awake doesn't do anything, unless you override it in a
+subclass -- typically you would override it to set up resources and
+instance variables for the servlet, or to do actions based on the
+request. Session.awake sets its list access time and number of accesses
+when awake is called. HTTPServlet.respond is called with the transaction as its only
+argument. It calls a method based on the request type: 'GET', 'POST',
+'PUT', 'DELETE', 'OPTIONS', 'TRACE'. 'GET' calls
+HTTPServlet.respondToGet, 'POST' respondToPost, etc., all with the
+transaction as an argument. The actual Servlet must override these
+methods to give the desired behavior. Session.respond does nothing. Page (a subclass of HTTPServlet) has more interesting behavior. It is
+particularly directed towards generating HTML (HTTPServlet is entirely
+content-neutral), and consolidates a number of things. Page.awake initializes a number of variables that will not change for
+the entire transaction (but may change if the Page is reused for other
+transactions). Page.respondToGet and Page.respondToPost both call
+Page._respond, which looks for a field named '_action_' and
+dispatches based on that. '_action_' is translated by
+Page.methodNameForAction, and the result must be among the list
+returned by Page.actions (cached by Page._actionSet). If no
+'_action_' field is given, Page.writeHTML is run. Page can also generate the HEAD, TITLE, and other elements of the HTML
+page. You can override Page.writeBody or Page.writeContent to
+generate content, and methods like Page.title to generate other
+content. It's easiest just to look at Page.py to see these. Page.write calls HTTPResponse.write with its arguments.
+HTTPResponse.write holds these strings in a list until you are
+finished with the transaction. You can also stream the output by
+calling HTTPResponse.flush, which will start sending output directly
+-- once this has occurred, you can send no more headers (such as
+cookies, redirects, etc). After having set up the request, we need to back out all the way to
+the browser. After Application.dispatchRequest has called
+Application.handleGoodURL (which calls awake/respond/sleep), it
+will call HTTPResponse.deliver, which basically marks the response
+as committed (i.e., nothing more can be added). Then
+Application.returnInstance is called, which returns the Servlet
+instance back to the pool of cached servlets (to be reused for a later
+request). RequestHandler.handleRequest calls HTTPResponse.rawResponse,
+which returns a dictionary containing the keys 'headers' and
+'contents'. Headers is a list of header/value pairs. For example: RequestHandler.handleRequest then turns this into a normal
+CGI-style response, with header: value at the top, a blank line,
+and then 'contents'. It then deletes the transaction. Having waited patiently, RequestHandler will finally send the string
+contructed from the Response to the Adapter over the socket. The
+adapter will deal with it as appropriate. E.g., the CGI adapter
+prints the result to stdout. The user sees the page, and it is good. The AppServer writes the hostname and port to a file address.text.
+The Adapter reads this file to determine where it can connect to the
+AppServer. Marshalling takes simple Python values -- strings, lists, numbers,
+etc., and puts them into a string representation. The request is a dictionary with the keys 'format', 'time', 'input',
+and 'environ': The only current allowed value for 'format' is 'CGI'. A timestamp (seconds from the Unix Epoch). A dictionary that looks like what os.environ would look like were
+this actually a CGI call -- that is, with keys like REQUEST_METHOD,
+QUERY_STRING, etc. The request that the browser made. This would be something like
+GET /Examples/View?filename=Welcome.py (@@ POST example too?) The cache for the Servlet is used both for the file path lookup, and
+for the Servlet cache (i.e., two caches keyed by URL/PATH_INFO and by
+serverSidePath, but pointing to the same cached data). (@@ maybe some
+information on how the cache is stored) When I try to visit the URL of a page I just created, I get a HTTP 404
+error Side effects of ExtraPathInfo, such as hiding "page not found" errors. Setting the Location header fails (particularly with non-CGI
+adapters...) Sneak a peek on exactly what's going back and forth between the
+browser and the server. Your browser says 'bad marshal data' when trying WebKit for the first
+time. Microsoft Documents, Flash files, or other documents are not
+recognised by browser (or are served as text/html) when using Webware
+(Python) Things with Python that you might need to deal with. Webware files use /usr/env/python to invoke the Python interpreter,
+but this can cause Python 1.5.2 to be used, rather than Python 2 in
+many Linux distributions. Sometimes a library module imported into a servlet will fail to make it past
+AppServer's import mechanism. Mozilla: BrowsingAlternatePortsInMozilla
+Mozilla blocks many (all?) non-80 HTTP ports ReloadInMozilla
+Mozilla users may hit Reload and get faked out LogMessages
+Using logging to track down bugs WebwareAndCygwin
+Issues when running Webware on Cygwin Create an automated regression testing suite for WebKit. Project Moderator: WinstonWolff Jan 2005 - Winston - I have converted test for half of the kits in Webware. There is a script, AllTests.py, which runs all the tests within each package. I have also written a number of tests for PSP, which identify problems with indentation when files use returns instead of newlines. I've also fixed PSP for those test cases (test first design of course). While I was in there, I added two features which I needed--writing Python code at the file/module level, and at the class level from within a PSP file. Here are my first thoughts on organizing automated test cases. To make it easy for people to contribute automated tests, I think we need: Test discussion - A way to get feedback when you are thinking about writing a test. Although a test might be only a few lines of code, it's affect on Webware is significant. Writing a test implies that all of Webware must now comply with this test and the "proper" result may not be obvious for some cases. So people who are new might need help to know what to test for, especially taking into account other platforms. Simply discussing on the development list is probably sufficient, but the issue should be mentioned in the documentation. Easy to run tests - When writing a test, I want to know that I am not breaking things on other platforms that I cannot test on. So there should be a way to get feedback quickly after checking in a new test. Conversely, all the people who are developing with Webware are reluctant to download new versions and run tests if they break things and disrupt their own development. So I think Webware needs to be arranged so you can have two separate versions on your machine. One that you develop with, and a second that you can download often just to run the tests. This implies that we will not use disutils to install Webware into the Python standard places, and that we have a way to control the python path to determine which version of Webware is being used. Testing Configuration - With an automated testing system, we need a way to specify what tests run on my computer, e.g. I am on a Mac so don't run COMKit tests, or I have only PostgreSQL so don't run MySQL or MSSQL tests, or I have Apache with mod_kit or I use the cgi adapter. Testing daemon - Eventually, we might setup a "continuous testing daemon" that people can run on their machine which will check for new versions in CVS, download them to a separate place, run the tests, and email any failures to a mailing list. I've heard of one for Ant, perhaps we could use that, or write a simple one in Python. That would allow developers to get feedback on new tests from all platforms within minutes of committing a new test. So to accomplish this we need: Documentation describing the need to discuss a test on the webware-devel list, and describing how to add a test, run the tests, etc. alltests.py in each kit - We need a standard module that contains all the automated tests for that component. I have always called it alltests.py. It has a suite() function that returns the suite of unittest.TestCases for that component. Test Launcher - A script that will set the python path, choose the local machine's configuration, and then run all or just some tests. BuildBot http://buildbot.sourceforge.net/ has been suggested for this, and it looks good to Winston. This page is for discussion of the future of URI Mapping in Webware.
+Please feel free to edit inline, rather than in a discussion format. see also: ManagingTheURISpace The end product may or may not look like
+http://jaguar.sourceforge.net/webware/urldecode.html -- TerrelShumway - 26 Apr 2002 There are several ways to "interpret" a REQUEST_URI to get at a piece
+of data that should be returned to the client. In Webware it amounts
+to finding a piece of code, called a Servlet, that knows what to do
+with whatever bits of data have been dumped into a Request object to
+create an appropriately formed Response object representing the
+Resource. More than one Servlet is often involved in interpreting a
+request. direct filesystem mapping with ExtraPathInfo regex mapping, like mod_rewrite Ant-style FileSets/FileMappers http://jakarta.apache.org/ant/manual/index.html This may be a filesytem-centric strategy, but it provides a very
+simple pattern language that is suitable for many purposes, including
+Webware's current default strategy. It could be also be used to replace "ExtensionsToIgnore" and friends
+with a richer selection language. -- TerrelShumway - 29 Apr 2002 flat text file explicitly specifying a complete map XPath expressions XML file XML instance is a tree-shaped hierarchy of typed containers, which we
+can use to represent a mapping of URI space onto Webware
+entities. URISpace has the same characterisitics; it's tree-shaped,
+hierarchical, and we can use the typing of containers to hold metadata
+about parts of URI space or bits of config/metadata that can be passed
+to the Webware entity controlling that part. See, as a really nice
+example, Mark Nottingham's URISpace 1.0 W3C Note:
+http://www.w3.org/TR/urispace URISpace can be used to model a great variety of types of config file,
+including web or web app servers. Mark's note includes the following
+example: This approach has a few benefits: 1) XML aware editors and tools are
+growing in number and variety; 2) XML really does map nicely onto the
+problem domain (i.e,. mapping two hierarchical spaces to each other);
+3) the config parser is just another XML application, pretty trivial
+to code; 4) this vocabulary gives a lot of freedom and flexibility; 5)
+the concrete improvements to the actual URI dereferencing algorithm(s)
+is separable from the way we represent the mapping it instantiates; 6)
+URISpace vocabulary seems to fit nicely with the MVC
+container-controller principle in ManagingTheURISpace. The Java Servlet Spec uses a file $APP_HOME/__WEB_INF__/web.xml to
+configure a web application (Context in webware speak). Part of this
+file is dedicated to describing the servlets included in the app, and
+a mapping of which URI paths are handled by which servlet. (Again,
+very good MVC style.) An example:
+http://jakarta.apache.org/tomcat/tomcat-4.1-doc/appdev/web.xml.txt The URISpace XML file described above is a much more general extension
+of this. (Sun seems pretty sticky about publishing the spec, but you can get it
+here: http://www.jcp.org/aboutJava/communityprocess/final/jsr053/ ) -- TerrelShumway - 29 Apr 2002 The main purpose of a URI Mapper is to make all of these strategies
+possible and interchangable without a lot of pain. Furthermore,
+different strategies are appropriate for different paths in the URI
+space. Some ideas on things that I'd like to see Webware allow with URL
+decoding: I'd like to be able to join two directories -- one which contains
+servlets, and one with contains static pages or perhaps Cheetah
+templates. A nonprogrammer would then be insulated from documents
+which would be dangerous to edit (Python code). I'd like to properly represent abstract objects (like database
+objects). I can do this with extraPathInfo now, but the caching
+doesn't work as I'd want -- I have to recreate the servlet each time
+on awake(), when if the servlets were cached according to that
+extraPathInfo (or something like that), then I'd only have to set
+things up in __init__. I'd like to be able to modify settings based on the path of the URL.
+For instance, .html files in some paths might need to go through a
+processor that wraps them in a template -- in other paths they might
+be presented verbatim. In some paths, errors should be emailed to me,
+in other paths they should be displayed immediately. I'd like to be able to better be able to resolve conflicts between
+extensions. Specifically, I'd like to be able to say that .tmpl files
+override .py files. I'd like to do positional arguments before the servlet -- perhaps
+fragile, but also traditional and a bit more compact. Somewhat like mod_speling, I'd like to have case-insentive URLs and
+I'd like to have spell checking in the URLs -- including spell
+checking on non-filesystem arguments (positional URL arguments both
+prefixing and following the servlet name). I'd like to have enough introspection to create a sitemap directly,
+as well as indexes and the like. I'd like to be able to represent arbitrary URL mappings, ala
+mod_rewrite. Do the thing that Zope does, where when you should have a url like
+/path/to/something and it should be /path/to/something/, see if the
+servlet can handle that on its own (by putting a BASE HREF in the
+output), or if you should do a client redirect, or if you should just
+ignore it (for instance, if you are serving a non-href-aware type of
+document). Virtual domains. These are mostly like prefix contexts/variables. Many of these are possible with some mod_rewrite magic. I don't think that Application should support all of these -- quite
+the contrary. I'd like to see a system that gives all the hooks
+necessary to do these sorts of things. I'd like a less centralized
+algorithm -- much shorter than what is currently there. The one things that I don't particularly want is fully dynamic URL
+mapping -- I'm fine with indefinitely caching the results of a URL
+decoding. Those results won't necessarily be a filename, but they'll
+be something. -- IanBicking - 06 Nov 2001 Here's my understanding of the various environmental variables that
+can be passed with a request, and exactly how they relate to the
+actual URL: if set, then the URL should be constructed by joining DOCUMENT_ROOT and REQUEST_URI (without query string, of course) points to the adapter portion of the URL (after rewrite) the rest of the path after the adapter (after rewrite) the original URL requested (pre-rewrite ?) the file the original URL would have pointed to (?) -- or maybe just a different name for SCRIPT_NAME the path of the original (pre-rewrite) request the full URI (with hostname) of the original (pre-rewrite) request Information produced from this: the context of the request the servlet it points to the extra portion of the URL the url of the adapter the hostname the url of the servlet, with adapter and context the url of the servlet, with extraPathInfo as well the url of the adapter the url of the context -- IanBicking - 26 Jun 2002 You should consider security concerns if you don't use a cookie for
+the session ID, i.e., if you use a technique with URLs like
+/WK/_SID_=1239872987/SomeServlet_ or
+/WK/SomeServlet_?_SID_=124987254 If the ID is for a logged in session (i.e. the user has typed a
+password) and needs to be protected from unauthorized access, you have
+to be careful of any links on your page that point to other sites. If
+the user's browser is pointing at: and the user clicks a link on the page to www.attacker.com/whatever,
+then the browser will normally send an HTTP GET request to the
+attacker.com server containing an HTTP REFERER header which has your
+page's URL including the session ID. The attacker can then use the
+session ID from the referer header to hijack the user session. If
+you're running something like a web bbs where attackers can drop their
+own URL's into messages that they post, they can carry out the attack
+without you (the foo.bar owner) ever adding any links of your own to
+the attacker. The user doesn't even have to be made to click a link.
+if your page (because of you or the attacker) contains an: to display an image, the attack happens before the user has a chance
+to do anything about it. -- PaulRubin You can secure against this kind of attack by using a redirect script
+for all external links. I.e., you replace all links (href and img,
+perhaps script, style, and link) in all your output. So if you would
+have had a link like http://www.booger.com/green you'd use
+/WK/Redirect?site=www.booger.com/green where you install some
+simple redirect script on your server at /WK/Redirect Also, you'd get a certain amount of security if you didn't allow
+people to access a session from a new IP address. You could add the
+original IP address (from <tt>req.environ()['HOST_ADDR']</tt>) to the
+session, and check that it's consistent in your SitePage's awake()
+method. Dialup users who get disconnected will also get trapped by
+this, and they will curse you (just so you know what you are losing). Note -- normal dialup users who sit behind transparent proxies will
+run into this problem a lot. -- FariedNawaz - 12 Jul 2002 ] Followup Note: Transparent proxies can be partly cured with use of X-Forwarded-For header field stating the ip address of user of the proxy, but this field can be faked by attacker, so it is not way out of the problem :( Lastly, if you use SSL, browsers will typically not allow non-secure
+content to be loaded without a warning (like an IMG). I'm not sure if
+secure content from another (potentially malicious) server would be
+loaded, or what referers are passed from secure connections. A CSV is a comma-separated-value file. These are often used to
+represent a table of data with named columns and rows of records. For
+example: I use these in my sites to store configuration information. This could
+be: what columns of data to display for different objects a list of menu item titles, links and descriptions logs MiddleKit object models You might also obtain csv's by saving a spreadsheet as that format,
+dumping SQL data, downloading information from a web site, or
+exporting data from some other program. CSVs are easy to work with because they are text, can easily be read
+by your programs and can be edited with nice GUI programs. Do not torture yourself by editing csv's with a text editor except for
+the occasional tweak or search-and-replace. Instead use a spreadsheet
+which gives better presentation and navigation of the cells of data: language author website Python Guido Perl Larry Java James On Linux, I have the best luck with gnumeric (even though I am
+typically a KDE fan, which provides KSpread). On Windows, I use
+Excel. Webware provides MiscUtils.DataTable which has excellent doc strings
+and is very easy to use. In addition to fully supporting the typical
+csv format (regarding embedded commas, quotes and newlines) (after
+Webware 0.6.1), it will also strip whitespace around fields and
+support simple one line #comments which are quite useful in "config"
+csv files. If you want something lower level than DataTable, see
+MiscUtils.CSVParser which is the parsing guts for DataTable and can
+be used separately. -- ChuckEsterbrook - 31 Dec 2001 Design and implement a flexible framework for handling user
+authentication and authorization in Webware applications. TavisRudd TavisRudd RayLeyva DarrylVanDorp for the moment. 23 Nov 2001 -- formal Webware SubProject created 20 Nov 2001 -- discussion starts on WebwareDiscuss and WebwareUserManager topic created Oct 2001 -- listed as an idea on the AppIdeas page A flexible and seamless framework for handling authentication and authorization is essential for all the ideas listed on the AppIdeas page. It will also benefit users creating their own applications and serve as the foundation for the WebwareUserManager application. ... see the design notes and discussion below. performance overhead overdesign Webware 0.7 (??) It should provide the following layers: authentication: secure authentication mechanism; preferably works with or without
+cookies, and will fall back to semi-secure mechanisms when SSL
+and/or Javascript is not available. can reject non-secure authentication for all users, or only a
+class of users (e.g., people with admin privileges) keeps track of failed attempts to prevent dictionary attacks --
+also uses TarPit to hinder such attacks (as well as DoS?) flexible time-outs allows IP-based restrictions / filtering (for example root might
+only be allowed to login from localhost) auto-forwarding (a non-authenticated user tries to access
+something restricted, is sent to the login page, and then back to
+whatever they were trying to access) works with both dynamic and static content authorization (aka permissions, access-control) concept of both users and groups (completely separate from the OS!
+-- plus hooks to tie it with OS if wanted) ability for one user to belong to multiple groups ability for a group to belong to other groups flexible, action based, permissions ... eg. members of group X are
+allowed to view resource Y, but not edit/delete/etc. it. In the
+context of web publishing, actions can include the following:
+view, edit, delete, rollback, publish, hide, etc. an 'anonymous' or 'public' user for non-authentificated users a catch-all group that includes all defined users (like
+'everybody' on NT) a root user and root group multiple users and groups can have permissions to a particular
+resource, unlike Unix permissions. concept of 'ownership' is distinct from that of 'permissions'. management interfaces (as part of the WebwareUserManager application): a user-info interface that allows individual users to do change
+passwds, personal data, etc. a 'registration' interface that lets the user choose a username
+and requires an e-mail address and password a mechanism to help those who have lost their password. Specific
+password-recovery mechanisms are discussed below. web-based and command-line management interfaces that allows the
+'root' user or other authorized users and groups to quickly
+drill-down on users/groups and add/update/suspend/delete those
+users/groups. Users must re-enter their passwords before being able to change it
+or their email address. In general, the system should: have support for multiple data stores(flat file, BDB, various
+relational databases, etc.) have the ability to tie into existing databases without needing to
+migrate data (i.e. soft-code the names of the fields used) support passwords stored in multiple forms -- plaintext, crypt (as
+traditional in Unix /etc/passwd), md5, SHA1, or homogenous (as in
+.htpasswd for Apache) have a clean separation between the backend and the user-interface
+that permits quick customization of the UI. have interfaces that are seperate from Webware itself, as components
+of this may need to be implemented seperately from any Web
+environment (for instance, in a Tk interface, over XmlRPC by a
+non-Python application, etc). The key concepts are: user group -- comprised of 'users' and/or other 'groups' resource - an abstract concept of something in Webware (e.g. a
+page, a servlet, a wiki topic, etc.) that a user might use. action (e.g. view, edit, delete, publish, hide, rollback, etc.) permission -- is a 'user', or one of the 'groups' they belong to,
+allowed to perform an 'action' on a 'resource'. authentication -- the process of logging in authorization -- the process of checking to see if a 'user' has
+'permission' to perform an 'action' on a 'resource'. ownership -- who is allowed to change the permissions of
+particular resource. Important questions about the Authentication layer*: (please answer
+them in the discussion below) At what stage in the request-response cycle does authentication take
+place (Adapter, Application, or Servlet)? Doing it at the Adapter
+level means we can use the same mechanism to protect content that
+isn't served via WebKit. Doing it at the Application level means
+that we can also protect static content that is served via
+WebKit. Doing it at the Servlet level means that we can only
+protect servlets. Are we going to use an identifier in the session or a separate
+authentication cookie as is suggested in the two papers listed
+below? Assuming that we use sessions to store the authentication details,
+how do we make sessions work with/without cookies? Extend this to
+protecting static content via WebKit. How do we store authentication information in the user session? How do we guard against session hijacking? How are password's stored internally? plain or hashed? How do we interface with arbitrary stores of user information and
+passwords (flat-file, BSD-DB, ZODB, relational database, LDAP, etc.) When Webware makes the shift to a multi-application framework, does
+authentfication span applications? How do we make the authentication system fall-back to a semi-secure
+mechanism (e.g. md5 hashes via javascript) when SSL isn't available. How are we going to implement IP filtering or automatic IP-userID
+mapping? How are we going to block brute-force dictionary attacks? http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf -- a must read! http://www.skyhunter.com/marcs/ewalnut.html#SEC41 -- E, a
+capabilities language: relates to capabilities as a permission
+system (something to implement in Python? Not according to E -- but
+maybe in a casual manner) http://www.eros-os.org/essays/00Essays.html -- EROS: The Extremely
+Reliable Operating System, also uses capabilities. What *is* a
+Capability, Anyway? and Comparing ACLs and Capabilities address
+the ACL vs. Capability question, with a decidedly mathemathical
+bend. Makes the argument that the two are not orthogonal (i.e.,
+basically the same). I think permissions should be considered something of a different
+issue -- ACLs being traditional at this point, but not always
+appropriate. They are closely tied to the system's notion of a an
+object and the granularity of permissions. I myself have been thinking about a more OO way of doing
+permissions -- most of the ways seem to be based on static data
+structures with explicit algorithms: it's not possible, for
+instance, to say "anyone can do action X if someone with permission
+Y requested that they do it" -- you can make the request also grant
+the permission, but the algorithm itself is external to the
+security system. You can't, for instance, retroactively change
+those permissions around, because the actions (the request) have
+already passed. Anyway, I'm not sure where I'm going with that idea, but it might
+only be to say that figuring out permissions is distinct from
+managing users and their roles/groups, and probably more
+complicated. -- IanBicking - 20 Nov 2001 Zope has a robust but (for some applications) overly complex security
+system: http://www.zope.org/Documentation/ZDG/Security.stx . We're leaving out the Zope term "role" because it ambiguously overlaps
+with our term "group". We're also leaving out Zope's "Proxy Roles"
+(analagous to Set-UID programs, or allowing a user to acquire extra
+permissions in certain circumstances.) Our "ownership" is different than Zope's. What Zope calls ownership
+was added to Zope in 2000 to plug a security hole related to
+through-the-web editing and proxy roles. For us, 'owner' means the
+user(s) who may reassign the permissions of a particular resource. As far as user interface, Zope uses one screen with a table of
+permisions down the left and roles along the top, another screen for
+linking groups to roles, and the user screen for assigning users to
+groups. -- MikeOrr - 22 Nov 2001 Let's scrap the term 'role' completely, and only use the terms of
+users, groups, actions and permissions. Where 'actions' are things
+like view, edit, etc. and permissions are the authorization for
+particular users and groups to be able to perform those actions on an
+object. An application designer should be able to define custom
+actions. 'roles' implies a strict connection between 'groups' and
+particular 'actions', which is not desirable. The permissions system
+in Windows NT and Novell Netware are good models. -- TavisRudd - 20 Nov 2001 I don't know what Novell uses -- it seemed very fine-grained -- but I
+don't particularly like NT. The way permissions combined seemed very
+strange -- for instance, if you have Everyone: Deny All, then other
+more permissive permissions would be meaningless. This seems
+backwards, and overall very confusing. If you want to look at a
+particular ACL implementation -- ACLs are hardly novel, after all -- I
+think it should be possible to find something better than NT. -- IanBicking - 20 Nov 2001 Novell uses a modified version of LDAP to power it's NDS
+authentication system. Couldn't we utilize OpenLDAP as a basis? -- RayLeyva - 20 Nov 2001 With all this talk of users, groups, roles and so on, it's important
+not to forget applications where the concept of a user might be
+interesting, but where the relationships between users and actions are
+completely different (they could be determined by their position in an
+organisation, for example) and are irrelevant to the ownership of
+Webware resources (being relevant instead to the data manipulated by
+such resources). I have been thinking a bit about authentication and access in some
+recent experiments and I think it would be nice if one could have a
+layer where access checks are implemented separately from normal
+servlet code. In XmlForms I would consider adding an access
+section to each definition which can validate a user's credentials
+according to an arbitrary validation function, and should the
+validation fail, the user would be directed to another resource either
+to get authenticated or to be told what went wrong. These validation functions might well use a lot of
+application-specific logic, but they could benefit from simple,
+generic user support in Webware. One of the things about Zope which
+seemed either badly explained or badly designed was the tight binding
+between users and what Zope wanted users for. Clearly, Zope supports
+user sessions for itself, but then people mention that it doesn't
+support user sessions for Zope-based applications without add-on
+products being present to provide such sessions. -- PaulBoddie - 21 Nov 2001 Here are examples of group structures appropriate to different
+applications: (A) Unprivileged user, logged-in user, content manager. The last may
+be factored out if the app doesn't allow through-the-web managing.
+The first may be factored out if user identification isn't necessary
+for these actions: simply put an if-anonymous-user branch in each
+servlet. If you're left with only one group (logged-in user), maybe
+you won't use most of this kit/package. (B) Unprivileged user (may read some content and post to forums),
+privileged user (may read all content and post to forums), content
+manager (may add/edit articles; e.g., "pages"), app manager (may
+change the app's look or do more programmingish stuff). All this is
+separate from the anonymous user, which has his own default
+permissions. Only some sites will have a web management interface. Even fewer will
+allow non-managers to modify editorial content ("articles", as opposed
+to forum postings or user-submitted entries) through the web.
+WebwareUserManager should scale down to these sites and not require
+unnecessary complexity. Complexity should be added only at the point
+it's needed. -- MikeOrr - 22 Nov 2001 Remember, this discussion is about a UserManagement system that can be
+used for the various application ideas on the AppIdeas wiki page.
+Most of those would fall into the 'large, complex' category. We would
+really need to discuss the permissions system in the context of each
+of those applications. But as none of them exist let's focus on
+authentication for now. What are people using at the moment to do their authentication? Note: for authentication we only need to think in terms of users Here's a summary of the important questions: At what stage in the request-response cycle does authentication
+take place (Adapter, Application, or Servlet)?
+Doing it at the Adapter level means we can use the same mechanism to
+protect content that isn't served via WebKit. Doing it at the
+Application level means that we can also protect static content that
+is served via WebKit. Doing it at the Servlet level means that we can
+only protect servlets. are we going to use an identifier in the session or a separate
+authentication cookie as is suggested in the two papers I listed
+below? Assuming that we use sessions to store the authentication
+details, how do we make sessions work with/without cookies? Extend
+this to protecting static content via WebKit. How do we store authentication information in the user session? How do we guard against session hijacking? How are password's stored internally? plain or hashed? How do we interface with arbitrary stores of user information and
+passwords (flat-file, BSD-DB, ZODB, relational database, LDAP, etc.) When Webware makes the shift to a multi-application framework, does
+authentfication span applications? How do we make the authentication system fall-back to a
+semi-secure mechanism (e.g. md5 hashes via javascript) when SSL isn't
+available. Do we want to enable some sort of IP filtering or automatic
+IP-userID mapping? Are we going to block brute-force dictionary attacks with a failed
+login time-out mechanism? Are we going to worry about multi-machine failover support? -- TavisRudd - 21 Nov 2001 Here's some excellent overviews of the issues involved and the schemes available:
+* http://www.wwnet.net/~janc/auth.html
+* http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf -- TavisRudd - 22 Nov 2001 Tavis said WebwareUserManager (WUM) is meant for complex apps such as
+those on the AppIdeas page. But it would be better to have a system
+that scales up to those applications and also down to simpler
+applications that don't need all its features. Because even the
+simplest application (with one group) may want some features, such as
+a registration screen and password-recovery screen. And maybe this
+simple application will one day become more complex and require some
+features it didn't previously. Then you can simply activate the
+features rather than porting the application to WUM. One of the goals mentioned above was seamless reauthentication. That
+is, when the user hits an unauthorized page, the system prompts him to
+login, then retries the original request. Zope does this through an
+Unauthorized exception, which Zope automatically catches and does the
+login prompting and retrying. -- MikeOrr - 23 Nov 2001 PasswordRecovery Lost-password recovery mechanisms:* "mail me my password" button (to my registered address). "mail me a new password" button. challenge question and answer, preselected during registration. We
+can offer a single question, a choice of questions, or a
+user-provided question. To reassure paranoid users, the question
+should not relate to any info that could be (mis)used to identify
+the user or place them in a marketing category. "Pet's name" is
+safe; "favorite sports team" is questionable; "birthdate" and
+"mother's maiden name" is unacceptable. (Disadvantage: user may not
+want to come up with and memorize a question/answer.) mail user a randomized URL, valid only for 30 minutes, where she can
+choose a new password. (Tavis prefers this, and Mike thinks it may
+offer a fair compromise between security and convenience.) -- I am
+absolutely against mailing passwords. -- TavisRudd - 23 Nov 2001 -- MikeOrr - 22 Nov 2001 One of the sites I used also did this: send a new, generated password by postal mail to the
+user. Unfortunately, despite attempting to select "send by e-mail",
+the application decided to use postal mail instead, but I did get a
+letter a few days later with the password in it. :-) Is this a
+justification for the establishment of a PostKit component? ;-) -- PaulBoddie - 23 Nov 2001 Something which I just realised: I have been following this page's
+progress using the WebChanges page, but the categorisation and naming
+of what is being discussed seems wrong. As one of the AppIdeas which
+discusses a WebwareUserManager, it seems to suggest an application
+which provides easy administration of user details, rather than a
+framework for the identification and authentication of
+users. Shouldn't we set up a WishList entry concerning sessions and
+users, or at least refer to this as "WebwareUserHandling"? -- PaulBoddie - 23 Nov 2001 Paul, you're right. This core of this is a system component rather
+than an application. However, I'd like to build an application for
+managing users on top of the framework. -- TavisRudd - 23 Nov 2001 I think this is a new *Kit idea -- which isn't an application, but
+isn't part of WebKit, or even necessarily Webware. There should be a
+KitIdeas. -- IanBicking - 25 Nov 2001 Here's a good architectural doc from Sun:
+http://java.sun.com/security/jaas/doc/pam.html -- TavisRudd - 14 Jan 2002 Something more appropriate for Webware, at least in terms of how much
+effort we would want to put into this, is the "realm-based" security
+that you can get in various Java Servlet containers. For example, one
+can declare various realms in the configuration of a Servlet container
+(application server) which states where the user database is, or how
+the username and password details are validated. Then, and this is the
+bit we should change a little, one can declare which "roles" are
+required to access specific Web resources. Now, I personally don't believe that this Web-resource-based
+authorisation is the beginning and end of authorisation, since (as I
+note above) it can be meaningless for various kinds of application,
+and one may well end up checking roles or user identities in the
+application itself, so we could just specify an API which lets
+applications discover the roles associated with a user, or even just
+the user identity (both are found in the request object in the Java
+Servlet API). Whether or not we introduce a Web-resource-based access
+policies is an open question which hasn't really been answered above. The interesting part of the J2EE standard, though, is the way that one
+can either use the standard "pop-up dialogue window" or a "form-based"
+page to prompt for the user details in a Web application, should the
+user attempt to access a service which requires authentication. With
+this feature in Servlet containers, it's possible for authentication
+to occur transparently to the application - the user doesn't come
+close to a resource that is protected until he/she has the appropriate
+role(s). Anyway, perhaps this would be a start. -- PaulBoddie - 05 Jun 2002 A kit provided by Webware to handle user authentication and access
+control. Unfortunatly it looks like UserKit is no longer being
+activly developed. -- LukeHolden - 24 Dec 2002 The UserKit provided by Webware is a good example of a UserUtils
+class. -- LukeHolden - 24 Dec 2002 When using WebDAV, one ususally wants to distinguish between source
+resources (.psp, .py files) and logical resources (the generated
+HTML). To access the source resources, you probably want to use
+mod_dav and set up a separate location that webware will not handle. Put this into your httpd.conf (untested: please fix it if it is
+broken) (taken from http://httpd.apache.org/docs-2.0/mod/mod_dav.html): then point your WebDAV client to /source-dav/ -- TerrelShumway - 07 May 2002 Author: Bram Moolenaar (yes, it's VIMs author) License: GPL A-A-P is does everything what make does plus you can use python in
+it's recipes. Another great thing is that A-A-P uses signatures
+instead of timestamps to detect changed files. The examples expect that you are using a UNIX like OS. I did not invest too much time to write the following recipes, so I'm sure
+there is much room to improve/enhance them in many ways. Assuming the following directory structure of the the application
+"Example": Example/ (this is the working directory created by MakeAppWorkDir_.py) Application/ Lib/ (business logic, ...) Logic/ (presentation logic) Servlets/ (files delivered by WebKit, a context) Templates/ To compile new and changed templates living in Templates/ and move
+them to Servlets/ I use the following recipe: File main.aap in Example/Application/Servlets/: Since the file is called main.aap, you can compile your templates
+by simply changing your working directory to Servlets/ and typing
+"aap" at the command prompt. First we create a list of source files (TEMPLATES) and a list of
+target files (SERVLETS) using python commands enclosed with
+backticks. Since we use the python glob function, we don't have to
+care about a growing number of template files. The :rule command teaches A-A-P how to create servlets from
+cheetah templates. It calls cheetah as an external process, so A-A-P
+can use its return value to determine the success of the compilation. File main.aap in Example/Application/Servlets/: Installing the files on the test server is simply a matter of typing
+"aap test publish" at the command prompt in the Application/
+directory. Modified files will be compiled automatically by executing
+the A-A-P recipe in Servlets/. In practice I check more dependencies (for example successful
+execution of unit test) before the files are copied to a server and I
+do other things like changing file permissions on the server after the
+upload. Quite simple, isn't it? Its very easy to define dependencies with the make like syntax and
+python does the rest. A powerful combination in my opinion. Bram Molenaar wrote on the a-a-p-user mailing list that "A-A-P doesn't
+stand for something specific". -- AndiPoisel - 26 Feb 2003 Here are some notes on how to use Webware to gain maximum performance: use Webware with Apache! use mod_webkit, rather than the slower adaptors keep the processing that actually occurs in WebKit to a minimum by
+serving all static requests directly from Apache / Squid. Only
+serve dynamic content from WebKit. follow the Apache tuning guidelines
+(http://httpd.apache.org/docs/misc/perf-tuning.html) if you have a smp server(s), WebKit might scale better if you run
+one WebKit process per processor to overcome Python's global
+interpreter lock. This will require multiple ports and multiple
+mod_webkit entries in your httpd.conf file. You'll need extra RAM
+to do this. Use absolute paths rather than doing relative path calculations
+instead of doing them in Python code. If you have site global
+directories for your stylesheets, images, etc., but you're using
+mod_rewrite to send all requests to WebKit do something like this: This much easier and way more efficient than calculating these
+relative paths in your Servlet code. It allows you to do this: rather than this: use aggressive caching wherever possible: in your app code in your db access in your html generation no query strings wherever possible encourage client-side caching of static data; see ServingStaticFilesEfficiently. design your db schema and queries well design your application well use apache-bench and Steve Purcell's HTTPSession.py to profile your site and identify hotspots. you'll probably identify a small number of URIs that eat up most
+of your processing time. If you really need to (and I doubt you
+will), you can achieve significant performance gains by hardwiring
+each of them to a particular WebKit port / process and bypassing all
+the URI-to-servlet mapping that WebKit usually does. To do this,
+you'll either need to hack WebKit yourself or use the experimental
+refactoring of Webware that I've been working on (search for
+WebwareExpRefactoring on the Wiki). If you use
+WebwareExpRefactoring, these hard-wired servlets can be run in the
+same process as your normal servlets. keep the Python performance tips in mind while developing
+(http://manatee.mojam.com/~skip/python/fastpython.html) Some helpful strategies are
+* use shortcut namebinding like this: always convert globals to locals by using default args: ...
+# do something with currTime() -- TavisRudd - 30 Mar 2002 Verbose Output in the Server Log If you set Verbose = True in AppServer.config, then the starting time and duration of every request will be logged in the server output. If you like this output, but want to tweak it or have it only for certain servlet types, then you can disable it and instead create a custom base servlet class for printing such infos, like this: This page is called "WebCommerce" because "e-commerce" doesn't make a
+good WikiWord. This page is simply an index for the various e-commerce topics: WebDAV can be used to manage source resources (aka Webware entities: psp, cheetah templates, servlet source files, graphic assets, etc.) logical resources (i.e., Application objects; e.g., Purchase Order, Article, Author, Buyer, Album, Artist, etc.) using the REST (representation state transfer) Web Architecture. The application of DAV semantics to source resources is fairly generalizable, which means an out-of-the-box implementation in WebKit will work for just about any use case But the application of DAV semantics to logical resources is not very generalizable, so WebKit's DAV implementation needs to be as easily customizable and extensible as possible There is a core of DAV requirements that can be implemented independently of specific application semantics: One of the most important use cases for DAV in Webware is MS Web Folders used to provide basic, generic CMS facilities for Webware sites Another important use case is management of source resources Given the variety of applications of DAV semantics in the various use case contexts, it's important to have maximal flexibility in the way WebKit goes about ManagingTheURISpace. One obvious place to begin an implementation is to write a custom databinding between DAV HTTP Requests and XML entity bodies and Python structs (classes or dicts or some other custom data structure); SAX is likely to be the fastest solution, and the least memory consumptive, but it basically means writing a state machine, and that can get hairy, though the DAV XML isn't too bad. For DAV XML response construction, DOM makes good sense. -- initially, anyway, during prototyping, it won't matter very much; the important thing to get right is the in-memory representation of the XML entity bodies in DAV Requests (and Responses). DAVKit: http://www.colorstudy.net/software/webware/DAVKit-0.1.tar.gz (see http://www.colorstudy.net/software/webware/ in case that link
+doesn't keep up with new versions) http://www.webdav.org/
+http://webdav.de/ -- KendallClark - 27 Apr 2002 webware recipe: UseWebDAVtoEditSourceResources -- TerrelShumway - 07 May 2002 From the Webware ComponentIndex: WebKit provides Python classes for generating dynamic content from a
+web-based, server-side application. It is a significantly more
+powerful alternative to CGI scripts for application-oriented
+development, while still being nearly as easy to use as CGI. WebKit is
+analogous to NeXT/Apple's WebObjects and Sun's Servlets. You can find WebKit docs online at:
+http://webware.sourceforge.net/Webware/WebKit/Docs/ The AppServer in Webware is not a web server, but instead sits behind a web server, managing your Python servlets, server pages and other resources. The AppServer waits for connections from Adapters, which shuttle requests and responses between the web server and AppServer. Those Adapters allow Webware to be used with any web server, including Apache which is the most popular choice. Adapters come in different flavors including OneShot.cgi, WebKit.cgi,
+ModWebKit, ModPython and ModSnake. OneShot.cgi is the most
+convenient for development, because it loads everything from
+scratch. ModWebKit is the fastest and hence, the most popular choice
+for deployment. The WebKit Install Guide already contains the information you need to
+connect your web server and AppServer. You should read the one in your
+distribution located in Webware/WebKit/Docs/InstallGuide.html, or you
+can read the on-line copy from the last release. If you are using an adapter, you may often want to clean up the URL,
+which might look like http://somewhere/WK/ShowItem?item=5, but could
+be made to look like http://somwhere/item/5 using mod_rewrite. See
+ModRewriteRecipes for examples. Some specific web servers that you might want to look at include:
+* Apache: http://httpd.apache.org/
+* OpenSA: http://www.opensa.org/
+* Xitami: http://www.xitami.com/ -- (overhauled) ChuckEsterbrook - 30 Oct 2001 How about some more documentation about setting up the Web servers?
+This information could change frequently, but it would be helpful to
+people who are just starting out with Web application development. For example, with Apache one might set up the following entry (from
+memory) in srm.conf to get the WebKit.cgi program deployed at its
+own location: Then, it would be possible to visit the following URL to see the
+default page: And the Examples context would be accessible at... -- PaulBoddie - 08 Nov 2001 A lot of that is covered in ModRewriteRecipes, but maybe that should
+be more clear -- IanBicking - 08 Nov 2001 Xitami: Do the following, one time, to setup and configure the Xitami
+web server for use with Webware. Install as instructed by Xitami. Then
+create a cgi-bin directory, such as C:Xitamicgi-bin. Copy the
+WebKit.cgi file into the cgi-bin directory you just created. Change
+the line in WebKit.cgi to point back to your Webware directory. For
+example: WebwareDir = 'C:/Code/Webware' Do the following every time you want to start running
+Xitami/Webware. Start Xitami. Start the Webware AppServer. To test
+whether everything is working as it should, open up a browser and go
+to http://localhost/cgi-bin/WebKit.cgi/. Make sure you include the
+final slash ( / ) as Xitami does not appear to handle / redirection
+the same way as Apache. These instructions are based on Windows 98, but should apply to the
+other platforms supported by Xitami, of which there are many. -- PatrickOBrien - 15 Dec 2001 Xitami also has a LRWP module for interfacing with the likes of
+webware, but the interface is backwards -- webware needs to connect as
+a client, rather than listening as a server -- which means that
+requests need to be serialized (very bad for performance). I played
+with it a bit, but it did not seem to be worth the effort. -- TerrelShumway - 10 Apr 2002 If you have a public web site, and just want it to work, the simplest
+solution is to get a host that already specifically supports webware:
+e.g. Python-Hosting.US or Python-Hosting.com. If you have your own dedicated server, I'll share my hosting scripts
+if you ask me: projects-zhost@jdiworks.net -- TerrelShumway - 10 Jul 2004 See WebwareBenchmarks for some recent benchmarks comparing the different Apache Adapters for Webware. -- ChrisZwerschke - 18 Apr 2010 Here are some excellent notes from the W3C: Web pages should always include a DOCTYPE header with markup that conforms to the selected standard. Checking pages with a validation service is an excellent way to quickly verify your markup: See also: Automatic HTML Validation I had no problems to install and run Webware using Cygwin on Windows
+2000, but MakeAppWorkDir.py failed. A first thing to notice: you must use the Cygwin way to specify the
+directory even if invoking from the Windows command prompt. Don't
+say: because you will get an IOError. But say: This is understandable. Don't forget that your Cygwin Python version
+has sys.platform == 'cygwin'. The next problem is more difficult. Here is how it looks: When I
+started the file appserver.bat, I got the following error: This came because of a newline representation conflict. Although the
+config files in my main WebWare directory contain UNIX-style newlines,
+the .config files created by MakeAppWorkDir.py have Windows-style
+newlines. The work-around is to convert them manually: I don't know who is to blame for this bug. It seems to me that Python
+on Cygwin is able to read text files with UNIX-style newlines, but
+creates them with UNIX-style newlines. Although shutil.copyfile()
+opens them in binary mode. Strange... -- LucSaffre - 11 Sep 2002 Here are two example servlets showing how to validate forms with FormEncode. FormEncodeExample - an example included with FormEncode itself, using actions FormExample1 - similar, but without using actions FormExample2 - similar, but Schema defined in the form template In order to get a feeling for the performance of the different ways
+of connecting Apache to the Webware application server, I have recently
+done some benchmarking using the Colors example servlet. The test was done with Apache 2.2 32bit connecting to the Webware 1.1
+ThreadedAppServer, running under MS Windows 7 on an Intel Quad core CPU with 2.66GHz.
+Here is the result: As expected, mod_webkit and mod_scgi are giving the best performance,
+while WebKit.cgi and OneShot.cgi have the worst performance, since as
+classic CGI programs they are creating new processes for each request,
+each firing up a new Python interpreter. Interestingly, the performance of mod_wsgi with the WSGIAdapter is also
+not so bad. Note that the test was done using embedded mode without any optimization,
+and maybe the performance could be improved in daemon mode with proper configuration. Note that mod_webkit is a part of Webware for Python, while the other
+Apache modules are third party projects. -- ChrisZwerschke, 18 Apr 2010 Someone considering this should considering using/porting Roundup,
+http://roundup.sourceforge.net/ -- IanBicking - 25 Nov 2001 (moved from AppIdeas) an ecommerce shopping cart -- TavisRudd - 01 Nov 2001 eCommerce systems are funny, since they involve some simple
+functionality for buying stuff, and some complicated functionality for
+displaying and searching for items in the store -- which is actually a
+ContentManagementSystem, though often mistakenly called simply a
+"shopping cart". I have a crude little module that actually implements just the cart,
+if anyone wants it. -- IanBicking - 02 Nov 2001 This was always something that people were really interested in for
+Zope, but which was never addressed satisfactorily. I used to
+subscribe to the very low traffic ZCommerce list and every now and
+again one would see newcomers posting questions like "Where can I get
+ZCommerce?" and "Is there a shopping cart for Zope?" and they would
+always go away disappointed. -- PaulBoddie - 05 Nov 2001 Here's a review of OpenSource versus commercial shopping carts
+http://www.robval.com/linux/ecommerce/ -- TavisRudd Why is there the avoidance of serious discussion on commercial implications of Wiki and related material? An example would be for how we might implement it our (ASP) ecommerce software http://www.veracart.com
+please inform me if such a discussion is taking place already: y@mytechsupport.com (moved from AppIdeas) Cofax, http://www.cofax.org/ -- is the Java software that powers
+Knight-Ridder newspaper web sites, like http://inq.philly.com/. Very
+cool, professional stuff. But it's Java. It needs to be ported to Webware. Cofax uses a lot of 3rd party libraries, which have Python
+equivalents, and if you abstract out the servlet and connection and
+caching stuff (much of which is duplicated by Webware, or will be
+eventually), what you're left with is a pretty manageable port. One thing that could stand upgrading is the template system Cofax
+uses; I'd prefer to use NITF (the news industry's XML vocabulary) and
+XSLT, but Cheetah should be available too. I'm very interested in building these kinds of sites, since I pretend
+to be a Web publisher most of the time, so I'd be interested in
+working actively with folks on a port. KendallClark - 16 Nov 2001 Need help with your Webware project? Here is a directory of consultants/contractors with expertise in Webware.
+Some might even be amenable to permanent employment. Chuck Esterbrook Chuck.Esterbrook -AT- gmail.com Founder and developer of Webware. Implemented StockAlerts.com, PatientWire.com and others. Years of contracting/consulting with Webware and Python, complete to production. Andrey V Khavruchenko Developed accounting software using Webware. Extensive experience in Python and other scripting languages. Risk-free software development approach. Aaron Held Deployed a Webware based extranet reporting system for Fortune 100 clients to generate reports against 100 million record tables. Previously developed a large number of eCommerce and Manufacturing support internet applications. Ian Maurer Developed a home health care scheduling and documenting system using Webware. Experience working with serveral Python based technologies such as Django, Cheetah Templates, ZPT, and SQLObject. Currently developing e-commerce sites using IBM's WebSphere Commerce Suite as a Senior Consultant for Xteric Technology Group. Maksim F Ischenko Completed several projects using WebKit, including on-line data processing for the ticket reservation system. Have had an experience using and integrating Cheetah, XSLT and SQLObject along with WebKit. Several years of extensive development in Python, Tcl and other script languages and integration with C++. Fionn Behrens Python and Webware hosting, support, development and project consulting in Germany since the 90s. Completed several large projects using WebKit Own hosting service with Webware option Extensive knowledge with python, developer of the pSQL database wrapper Brad Bollenbach Independent consultant specializing in web development with Webware, Python, Cheetah and SQLObject. Developed an invoicing system for a consultancy firm entirely in Webware, Cheetah and SQLObject. Currently developing an online merchant proxy using Webware, Cheetah and SQLObject. Terrapin Technologies, Inc. Developed a long-distance telephone self-registration, self-care, and customer support application using Webware. Broad experience in software design with Python, Webware, C++, C, Perl, Java, Expect, HTML, and CGI. Expertise in architectures including: OOA/D, Message- and Transaction-Oriented Middleware, N-Tier Client-Server. PeaceWorks Computer Consulting PeaceWorks has developed several web applications using Webware, MiddleKit, PostgreSQL and MySQL for deployment on both Linux and Win32. PeaceWorks focusses primarily on non-profit organizations, but also does consulting and development for commercial businesses Oliver Bock Independent consultant and software developer located in Sydney, Australia. Developed a sophisticated market-research application using Webware with MySQL as the back end. Parker Wallace Company Contact: Ben Parker, ben@parkerwallace.com, http://parkerwallace.com We create fast usable websites and deliver powerful tools to manage them. Expertise includes e-commerce, internationalization, integrated supply-chain customer service, real-time data feeds, and much more ... but most importantly we help you make technology decisions that are correct for your business, your budget, and your schedule. Our largest public Webware site is http://www.tablethotels.com Enrique Arizon Benito Contact: Enrique Arizon Benito, enrique@heraldodeinternet.com Teleworking solutions. Development of custom web applications with Webware/Python (technical support, supply chain, process simulations and data-mining, ...), integration with mail, VoIP, databases, VPN, ... No more need for expensive offices in the city center and daily hours wasted in traffic jams and undergrounds. Contact me for free for a first evaluation of your needs. Spanish language support !!! Soluciones de Teletrabajo. Desarrollo de aplicaciones web a medida con Webware/Python (soporte técnico, suministros, simulación de procesos y minería de datos, ...), integración con correo, VoIP, bases de datos, Redes Privadas Virtuales, ... Se acabo la necesidad de costosas oficinas en el centro de la ciudad y horas diarias perdidas en atascos y metros. Contacteme gratuitamente para una primera evaluación de sus necesidades. ¡¡¡ Soporte en Español !!! Thomas E Jenkins Consultant in the Philadelphia PA area. Designed and implemented platform for providing integrated business services (project management, payroll, time logs, etc.) for Techead (http://techead.com) using Webware. Developed intranet for managing ISP services for Cavalier Telephone (http://cavtel.com) using Webware. Gil A L Pereira Consultant in Brazil. Expertise includes internationalization, real-time data feeds, e-commerce, b2b, servers (and network) managment and security. Our preferred tools are: Python, Webware, VB, MSSQL, PostgreSQL or MySQL, and deployment on Linux, OpenBSD or Win32. Samples of our public Webware sites are http://www.casadapizza.net (expensive one) and http://www.orquidariopaulista.com.br (really cheap one) Suporte em Português e Inglês. This would be interelated to nearly everything else. The main object would be a document. This contains the actual text
+(or file), some information how to render the text and, most
+important, some attribute information. An attribute would be a
+basic meta information about the document. Here is a simple example. We want store documents about
+software. Basic attributes would be: operating system vendor product (name of the software) modul (if it's not a trivial software) version information category (getting started, installation, tips and tricks,...) Some of these attributes are related to each other while others are
+not. If I have for example a product, I have already the vendor (Word
+-- > Microsoft). This leads to a hierachical structure: Every of these attributes can have several instances
+* operating system: Win98, Linux, ...
+* vendor: Microsoft, RedHat, ...
+* ... All of this should be configurable via the web interface. This is
+exactly where I've seen some Document Management Systems fail: the
+attributes are hardcoded or, even if you can change something, it is
+not possible to introduce a hierachy of attributes. documents can be browsed via the attributes or searched via a search engine. documents can be converted on the fly (XML/XSLT --> HTML) documents can be exported on the fly (XML/XSLT --> PDF) batch import/export of documents revision management check out of documents integrated workflow management? This might be enough for a start and some dicussion. Please feel free to add your ideas. -- StephanDiehl - 23 Nov 2001 Small point on the "exported on the fly". Currently the best way to
+go from XML style documents to PDF, is utilizing FO documents, and
+process them with FOP ( A java tool ). [ Purely my opinion RL ] I haven't found an XML:FO option to reportlab, they do support some
+XML style tags inline of their flowables ( specifically their
+Paragraph one ). I'm looking into it to see how best to extend that.
+If possible tie it into a cheetah template like structure ... and push
+out some PDF. Second small point "documents can converted". That implies utilizing
+XML as the basis of the document structure, and utilizing XSLT
+transformation via 4Suite, or some such. I would recommend possibly
+distributing the load for something like this via XmlRpc, and
+utilizing a Java / Xalan engine for the transformation. I'm looking
+into the same for the XML/XSLT to PDF translation utilizing FOP, and
+XmlRpc. It could allow for a more scalable system architecture, even though it
+would probably slow the responsiveness of the system. Having said all of the above. I find working with Cheetah / Webware /
+Python substantially faster ( for me! ), in development. I came from
+Taglibs / JSP ( Tomcat / WebLogic / JRun ) / XML ( XSP via Cocoon ) /
+Java. Personal preference is to stick with Cheetah / Webware template
+applications, spitting out reportlab PDF ... but hey they can't (
+currently ) support your request of XML to PDF, or XML to HTML, but I
+find it easier to work with Cheetah templates than XML / XSLT
+transformations. Personal preference only. -- RayLeyva - 21 Nov 2001 --- The reason I'd like to use XML is to be independent from any
+application in order to create the documents outside the management
+system and do whatever I want with them. I've played around with
+4suite. It's quite easy to use but seems to be a little slow. A non
+python xslt processor might give better results in that area. Another
+way to produce PDF is going via LaTeX. It looks quite promising but is
+really not usable for "on the fly" processing. Anyway, to be able to preprocess the files before sending to the
+browser is central. Another example that comes to mind is the usage of
+Wiki pages (see WebwareWiki): strcutured test --> html via a wiki
+parser. -- StephanDiehl - 22 Nov 2001 <br/> Couldn't we just use Cheetah to generate templated XML exports? It's
+what I was thinking to create the XML for the xalan based
+transformations. Just a thought. -- RayLeyva - 22 Nov 2001 If it works, why not? I wouldn't want to force to anybody anyway to
+use something specific. There should be some configurable filetype
+handler. So, in the case of XML, the admin should decide, if he is
+using a xslt processor, some css stylesheet, Cheetah, or whatever
+works best for the situation. -- StephanDiehl - 22 Nov 2001 Perhaps we can use the term "property" or "attribute" rather than
+"category". The word "category" normally means a group the document
+is "in". In other words, it's the value of a certain property, not
+the property itself. The category may also be expressed above the
+doucment; e.g., the directory the document is in. This encourages
+users to think of the document as being in a certain hierarchical
+location. But putting the category name in a property allows for
+easier sorting/searching (sort by any property, search for all
+documents matching a certain property value). Which is better depends
+on the application. But in any case, using the term category for
+property is confusing. -- MikeOrr - 22 Nov 2001 Agreed. This makes a lot of sense. I wasn't too happy about
+"category", but couldn't think of anything better. From now on I'll
+use attribute. -- StephanDiehl - 23 Nov 2001 Here are a bunch of wishlist items for a content management system
+(CMS): WebwareDocumentManagementSystemMSO1. -- MikeOrr - 15 Apr 2002 I think RDF would probably give a good framework for document
+classification. The backend storage has to be in native format -- not XML. If you
+demand that everything be converted to XML, you'll lose information in
+the process and not be able to retrieve it later -- especially
+annoying if you try to retrieve the original content and get something
+slightly different back. To do this, you might rank every sort of conversion and find the
+shortest path from the original document format to the target document
+format, probably caching various formats while you're at it. -- IanBicking - 15 Apr 2002 About XML transformations, performance and so on: 4Suite isn't the
+only option for XSLT - there's a wrapper around Xalan as well as
+libxslt (and libxml2), although those packages may not be compliant
+with the XML-SIG API. -- PaulBoddie - 16 Apr 2002 We're using the Apache stuff in a separate process (i.e.,
+os.system(...)) to generate PDFs from XML that we build using
+Cheetah. It's not the fastest in the world, but it's functional. I'm
+planning on updating it and bypassing the XML step, having the XSLT
+engine call directly into our object model, which saves the XML export
+and the subsequent XML parse steps. I'll report back on my experiences
+when I finish that :) (PS: I haven't read closely enough to determine how much actual
+forward motion there's been on the Webware CMS. Anyway, in case it's
+relevant, we (HFD ) are "most likely" going to release our content
+management product (built on Webware) under an Open Source license by
+the end of 2003. I say "most likely" because we're all generally
+agreed that it's what we want to do, but we have to iron out the
+details, and I don't want to make a concrete statement without having
+concrete under me :) (PPS: Chuck, Geoff, the Monkey's grown up a lot
+since you last saw him :) )) -- TrippLilley - 11 Nov 2002 At minimum, the basic functionality of a wiki minus the wiki syntax:
+write document online, edit online. I guess a preview mode should be
+essential, with a "make more changes" button (sometimes pressing the
+browser's Back button on TWiki's preview page loses all my changes). Alternate ways to edit the document are important, because textareas
+are inconvenient for large documents. FTP (a la Zope) is good because
+everybody has it, and WebDAV for those who have it. The first phase
+could just have a stub for import/export routines, to be filled in
+later.) Version history is not that important to me, but if it's easy enough
+to incorporate CVS, why not? That might suggest a web interface to
+CVS (CVSKit?), but that would be better done as a separate project.
+(PS.Remember that CVS alternatives are emerging, so we wouldn't want
+to write a CVS kit that precludes plugging in alternative back ends.) Most sites will want metadata stored with the document. Title,
+author, publishing date, category, etc. However, each site has
+different ideas about what these fields should be. You'd need a list
+of field names, types, and boolean required/optional. We'd need a
+form for editing these fields. Zope makes a form with the fields at
+the top and the textarea below, with a file upload control (actually a
+tab) for the textarea. Zope also has a web interface to modify the
+field names, but we could skip that and make the siteadmin put a
+Python list somewhere instead. For field types, we'd have to decide
+what variety to support. Strings at minimum, maybe also numbers and
+dates (using mx.DateTime). Zope supports many types, but maybe we
+don't want to go that far. Some sites will need a "hide this document" checkbox, or a date range
+for when to display, to avoid the public reading an unreleased or
+expired document. Sometimes documents need to be prepared the day
+before and published in the middle of the night when nobody is at
+around, to coincide with 8am in another time zone. Sites that edit documents in text files may want to edit the metadata
+with the documents. We could use a simple XML format. (Angle
+brackets changed to braces to prevent wiki misformatting.) External Python routines will need a way to get all the articles in a
+certain site section, query metadata fields individually, update
+fields and validate articles (e.g., all required metadata present,
+correct types, start date <= end date). Perhaps export it like this: with a template like: which would be plugged into a larger servlet. Of course, some sites will need distinct pools of articles for various
+portions of the site, with different display styles and different
+metadata. They can use separate CMS Kit instances for this. Since it's unlikely we'll arrive at one CMS Kit that pleases
+everybody, we should plan a naming scene that allows several to
+coexist.Especially, the generic name CMSKit should not be monopolized
+by one implementation that may turn out not to be the best. An easy example (Java, Python): The straight, Hello World example: Python: A larger example might be nice too -- IanBicking - 10 Dec 2001 I have been working on an experimental refactoring of Webware that
+addresses most of the issues currently on the WishList and ToDo
+list. It is near feature complete, but needs extensive testing. The CVS repository is available at
+http://sourceforge.net/cvs/?group_id=41909. Here's a link to the README file, which has alot more information. -- TavisRudd - 14 Jan 2002 JayLove has also been working on a 'mulitApplication' version of
+WebKit. It has slightly different aims than this, but is definitely
+worth looking at if you're interested in how Webware could and should
+be evolving. I've lost the link, so send Jay an email if you're
+interested. -- TavisRudd - 27 Feb 2002 There's a few of us working on it behind the scenes, but when/if the
+actual code merge takes place is up to Chuck. Personally, I feel that
+it's much easier to test the experimental code and tidy up the few
+holes remaining than it would be to implement all the items on the
+WishList/Todo using the Webware 0.6 codebase. That said, Chuck
+has an obligation as Webware's Benevolent Dictator to review all the
+changes in the experimental code, before we merge and cut a
+release. Either he has to do a manual code walkthrough/review or I and
+the other people working on experimental code have to make the
+automated test suite comprehensive enough to mitigate the need for a
+tedious manual review by Chuck. Either way, an _official_ merge is at
+least a few months away. Chuck wants to cut an official release in Feb. I agree with him that
+we should keep the official releases flowing with whatever we've got
+rather than waiting for the next big thing. Whether the release in
+Feb is called 0.7 is up to him. In the meantime, I'm going to continue testing the experimental code,
+encouraging people to join in on the testing, mirroring any new
+additions to the official codebase, and backporting the smaller
+changes in the experimental code to the official codebase wherever
+possible. Geoff has pointed out that the experimental code will only be ready
+for serious consideration once we have some of the larger existing
+applications running on it. With that in mind, here's what people
+can do to help out: download the cvs version of the code read the README and see if what I've written makes sense. follow the installation instructions and report any problems run the automated test suite on their platform ... reporting any problems or further documentation needed along the way create a small test application, or try porting their existing applications ... reporting any problems or further documentation needed along the way add more test cases to the automated test suite. This is very easy. manually review the code and/or search for any ToDo comments that begin with @@. tackle items on the ToDo/WishList on the Wiki site or in the experimental code's README file. Write documentation/howtos There's two mailing lists set up for the experimental code: one for
+reporting problems and discussing issues and another for automated
+CVS notifications. http://sourceforge.net/mail/?group_id=41909. Please report bugs
+and direct questions to expwebware-devel@lists.sf.net rather than to
+the regular webware email lists. Sending stuff to the regular lists will
+just confuse people who aren't aware of this refactoring effort. For those people who want to get their feet wet with the code, send
+me a message if you want CVS write access or send patches to the
+expwebware-devel email list. -- TavisRudd - 14 Jan 2002 (moved from AppIdeas) This is a follow up to a topic thread from the mailing list. Looking to create a Webware based Groupware application. It should
+include email, and calendaring functions. There's a million of them
+out there, but not for Webware ;D With this as a starting point I would like to eventually get to
+something like a WyattERP (http://www.wyatt-erp.com, it's hosted on
+SourceForge), except ASP and Python/WebWare based. -- RayLeyva - 17 Nov 2001 I think for a heavy-use application like WyattERP you need a pretty
+good interface -- I'm not sure a normal HTML/HTTP interface is it
+(though maybe if you used browser extensions heavily). For things
+like data entry and high-feedback interfaces, HTTP/HTML/ASP sucks :-( But maybe if certain pieces of the application use custom local
+program, it might work better (to reference the next idea). -- IanBicking - 19 Nov 2001 This idea ties in fairly closely with many of the other ideas listed
+above. The core of all these applications would the
+authenfication/authorization system. Maybe we should start with that.
+I haven't used UserKit so I'm not sure if it is up to scratch. The
+recent discussions about non-cookie based session management are
+related to this as well. Once a flexible authenfication/authorization system is in place, it
+would make sense to design a 'component/html-widget' system that
+allows multiple UI components to be used on a single page without
+namespace clashes, just as you can place multiple complex widgets on a
+GUI form and not have to worry about conflicts between them. Most
+html-based applications I've seen fail miserably in this respect. It
+should be possible to drop a calender 'component' into a page that
+already contains other components, or even other calendar components.
+ASP.net's server controls seem to do this quite well. Here's an
+Cheetah-based example of what I'm going on about: The widgets in the example above should manage their own state and
+automatically resolve namespace clashes (of query string/post vars)
+among themselves. -- TavisRudd - 20 Nov 2001 It also involves having some information about what the variables mean
+-- for instance, all of the widgets should preserve the
+queryString GET variable (that, say, defines the search results),
+but if you have a link on the page that deletes an item with a GET
+variable, then other widgets shouldn't preserve that variable in their
+generated URLs. Perhaps this is a matter of seperating variables that
+effect the view, and variables that indicate an action to be
+performed. Immediately redirecting after an action may be the best
+resolution, now that I think about it, using a RedirectNotify
+pattern. -- IanBicking - 20 Nov 2001 That's a great example of why it's better to use POST vars for
+performing actions like that. GET vars are stateful, POST vars are
+not. Once-off actions like deleting something shouldn't be encoded in
+the stateful queryString. Other widgets shouldn't have to know
+anything about the GET vars or SESSION vars they aren't using. They
+should just preserve them. This is also an example of where it is
+useful for Webware to preserve the GET vars during a POST operation. -- TavisRudd - 20 Nov 2001 Unfortunately, GET vs. POST is also a UI issue -- you can't do POST
+without a FORM element, so you can't do something like <a
+href="?deleteItem=%i">[delete]</a> with POST, even though that will
+probably be an action (POST-ish). Blame it on HTML. -- IanBicking - 20 Nov 2001 Then use a form instead of a simple link. It's more verbose on the
+HTML side, but to the servlet writer it isn't anything extra if
+they're using a well factored library. -- TavisRudd - 25 Nov 2001 You don't get the same interface when you use a form -- forms aren't
+well suited to having a large number of divergent options. For
+instance, when showing a table of 50 items, you don't want a submit
+button for each delete option. Unfortunately, good UI dictates you
+use links sometimes when POST would be more appropriate from the
+server-side perspective. -- IanBicking - 25 Nov 2001 You can use Javascript URIs to manipulate a form and get the best of
+both worlds. There's also other UI designs that solve this. Consider
+the handling of delete/block in HOTMAIL. Instead of having a delete
+link for each email they use a checkbox selection form and a single
+delete button. -- TavisRudd - 25 Nov 2001 Using Javascript to manipulate and fill forms is very fragile and,
+IMHO, very bad style. I've seen far, far too many broken shopping
+carts that relied on Javascript to manipulate the client-side forms
+for the benefit of the server-side programmer. The client in web
+programming is a given -- there are some things that can only be done
+(well) on the client side, but you should _never_ have the client side
+make up for problems of the server-side architecture. Using POST and
+GET to indicate functional differences in variables is imposing
+server-side needs where they aren't appropriate. I think
+RedirectNotify provides the safety of POST (or even better!) while
+being more general. Leave the POST/GET decision to the UI and
+graphical designer, and implement new ideas with new (orthogonal)
+techniques -- IanBicking - 25 Nov 2001 I agree about Javascript being fragile when you have non-programmers
+fiddling with the code, but I'm thinking in terms of simple UI
+'widgets' that the non-programmers can insert in the page with a
+single statement in Cheetah. The non-programmers would never touch
+the Javascript. With RedirectNotify I can't think of anyway that
+would work. Your argument implies that client-side form validation
+javascript is 'very bad style'. I disagree. -- TavisRudd - 26 Nov
+2001 Not the form validation, but I get the impression you are talking
+about something like: That's the kind of Javascript I find to be particularly fragile. When
+the Javascript is necessary to get the UI you want, then yeah, you
+have to use it, but that's something else. -- IanBicking - 26 Nov 2001 That's what I meant, but I should clarify that I've never had to use
+that style and don't think it would be necessary with the style of UI
+'widget' that I'm thinking of. Even if that style was neccessary for
+a particular 'widget' I don't see why it would be fragile, as the
+'widget' would autogenerate all the HTML and Javascript code related
+to it. There's nothing to be broken. In other contexts I'd agree
+with you, but not when it's autogenerated. -- TavisRudd - 26 Nov 2001 Actually, come to think of it, there are several ways of handling
+actions with just GET vars in the QUERY_STRING that don't require
+RedirectNotify. The simplest would be to just drop any GET var that
+contains the word 'action' when rebuilding the URI. That might cause
+problems if you were mixing these 'widgets/components' in with other
+systems, but you could get around that by adding a prefix specific to
+this UI system. Another approach would be to make each
+'widget/component' remember what action it performed last and to
+ignore requests for it to be repeated. And there's other ways ... What I'm getting at is that an interface to a 'component/widget'
+should be atomic: it shouldn't rely on external servlets. On the
+other hand, forwarding from one servlet to another might make sense in
+a complex transactional process like order processing. To flesh out the GUI analogy a bit more:
+* a collection of related servlets == an application
+* a servlet == a form, a dialogue, or the main application window
+* a 'component' == a widget (stock or custom) -- TavisRudd - 26 Nov 2001 If the widget is to be atomic, would it preclude items suchs as login
+boxes. This would contain 2 label, and 2 txtbox widgets, but is
+itself a widget. Couldn't FFK be expanded to encompass this? -- RayLeyva - 27 Nov 2001 What you need in a Host: You need Python installed, but you can do that yourself as long as you
+have a C compiler available. The real issue is being able to have long-running processes. Most
+commercial hosts don't allow, or otherwise hinder long-running
+processes -- in Webware's case, the AppServer. Some hosts kill
+processes that run too long, so you'll want to ask about that. You also want mod_rewrite installed, as that's fairly important for
+cleaning up the URL and if you wanted to serve the root page from
+Webware (see ModRewriteRecipes). You can control mod_rewrite from an
+.htaccess file. You'll be able to use the C-based CGI adapter, since you probably
+can't install mod_webkit. But that should be fine. That pretty much covers it. There isn't a list of hosts that support
+this. Most do not, but I imagine there's a healthy minority who
+probably would. If you find some, please post them here. Some of the
+places that host Zope (but not necessarily as their primary business)
+may very well support Webware, as they are similar (from the host's
+perspective). -- IanBicking - 15 Dec 2001 Hosts: Please list any hosts you have used Webware on, and any experiences
+you may have had. Prices are also useful. Superb.net: I've used their SPS server, ~$80/mo for a virtual private
+server (looks like a dedicated server, but is actually shared).
+Reliability isn't as good as I'd like, but I have complete control
+over the environment. Installing Webware is easy. -- IanBicking - 15
+Dec 2001 fionet.com: A german hoster. I am working regularly on their servers. They have Debian
+Linux and you can get python cgi and your own WebWare tree preinstalled as well as
+ssh access and mySQL. Its fine for complex projects and hosting prices
+that include a WebWare installation start at €25. They have no real home
+page but can be reached via mail to service[AT]fionet.com. --
+WikiGuest - 04 Dec 2004 cornerhost.com: hosts zunzun.com, excellent personal support, very
+Python-friendly, mod_webkit installed for Apache which gives
+zunzun.com quite snappy response times, several different hosting
+plans, $500/yr is the one I use as it is literally unlimited in
+bandwidth, CPU usage, or disk space. HIGHLY recommended. -- James
+Phillips 16 March 2003 Etria.com: My company (along with Brian Skahan). We offer Webware
+hosting via mod_webkit and Apache2. Things are not setup yet, but if
+someone wants the service we will juggle our priorities to get it
+done. See website for rates. -- TomVonSchwerdtner - 02 Apr 2003 Python-Hosting.com: One of the only hosts I've run across that
+explicitly offer Webware hosting. linode.com : Uses User Mode Linux. Root Access $20/mo gets you 1GB
+Disk Space, 64MB RAM, 25GB monthly transfer. Very nice setup. --
+Randall Smith - 19 July 2003 Python-Hosting.us: For the latest and greatest python support.
+A turnkey python and webware-specific solution -- TerrelShumway -
+02 Jan 2004 web-hosting-url.com: Sphera Virtual Dedicated Server (VDS). Roll-your-own setup, very reliable... and the price is right! -- Luc Chase - 1 July 2004 Quantact.com: http://www.quantact.com/
+User Mode Linux VPS 3G Disk 64M Ram 20G Bandwidth $14.99 very configurable. Discounts for Open Source Developers. -- Tim Doyle - October 2004 openhosting.com: VPS for $27/month. Run by the author of mod_python. I haven't been using it very long, but so far so good. -- Donovan Eastman - March 2005 http://www.anchor.net.au: 10MB account on shared Linux server for $A198/year. Good connectivity for Australian users. I was able to set up a cut down WebKit 0.8.1 in 3MB of its space. Allows CRON and ssh w/Python 2.2. -- OliverBock - August 2005 GrokThis.net: Not a VDS, its better. Supporting CGI, FastCGI, and mod_python at reasonable rates. Faster and more affordable than a VDS, with access to your own dedicated web service process and all of its configuration files. You can find the full definitive index of Webware mailing lists at SourceForge: The Archives are also available at SourceForge, but very slow: Probably the fastest and least-annoying archives to read online are here: The Webware project has three main email lists: webware-discuss for general discussion, usage questions, etc. webware-devel for more technical development discussions webware-announce for release announcements See also the Webware list archive. Webware for Python is a suite of Python packages and tools for developing
+object-oriented, web-based applications. The suite uses well known design
+patterns and includes a fast Application Server, Servlets, Python Server
+Pages (PSP), Object-Relational Mapping, Task Scheduling, Session Management,
+and many other features. Webware is very modular and easily extended. Webware for Python is well proven and platform-independent. It is compatible
+with multiple web servers, database servers and operating systems. The first bugfix release of Webware for Python 1.1 is now available (download as tarball or zip archive).
+See the WebKit release notes for details. A new version of the DBUtils plugin (DBUtils-1.1.tar.gz) is available for downloaded now. See the Release Notes for DBUtils for changes. The final release of Webware for Python 1.1. is now available (download as tarball or zip archive).
+Major new features: WSGI adapter, Sessions based on shelve or Memcached, MiddleKit supports SQLite now.
+The 1.1 branch runs on Python 2.4 to 2.7. See the overall and WebKit specific release notes for details. You can also download a new bugfix release 1.0.3 (tarball or zip) of the older 1.0 branch
+which supports all Python versions from 2.0 to 2.7. Published some new benchmarks for the perfomance of Webware with the various Apache connectors. The second bugfix release of Webware for Python 1.0 is now available (download as tarball or zip archive). See the WebKit release notes for details. This is the first bugfix release of Webware for Python 1.0 (download as tarball or zip archive). See the WebKit release notes for details. The software for the Webware Wiki has been updated and can be downloaded here. The historic 1.0 final release of Webware for Python can now be downloaded (tarball or zip archive). A lot of bugs have been fixed, and a few small improvements and changes have been made since the last release. See the WebKit, MiddleKit, TaskKit and CGIWrapper release notes for details. You can also download the new release 1.0 of the database connection utility DBUtils (tarball or zip archive). See the DBUtils release notes for details. A new release of Webware (Webware-0.9.4.tar.gz) and DBUtils (DBUtils-0.9.4.tar.gz) can be downloaded. Webware has got some new configuration settings, and some bugs have been fixed. Please have a look at the WebKit release notes and the Release Notes for DBUtils for details. Some minor problems have been fixed in LoginKit. LoginKit handles logins in Webware, using Component. The latest version LoginKit-0.2.tar.gz can be downloaded from here. DBUtils 0.9.3 has been released. You can either install it independently or as a Webware plug-in. DBUtils-0.9.3.tar.gz can be downloaded directly from here. User's Guide and Release Notes are available online and contained in the distribution as well. Webware for Python version 0.9.3 can be downloaded here. It includes a couple of fixes and
+improvements of WebKit and some cleanup of the overall Webware codebase. More details in the WebKit release notes. A new feature in this release is automatic redirection to custom error pages whenever an HTTP error or any other Python exception occurs in a servlet. There still had been some quirks with Python versions before 2.2 and after 2.4, which have been solved in this release. Webware now runs fine with the current Python 2.5, but is still backward compatible down to Python 2.0 (except for KidKit, because Kid requires Python 2.3 anyway). The plan is to keep this broad compatibility range in the upcoming Webare 1.0 release, postponing major refactoring and strengthening requirements until later versions. The software for the Webware Wiki has been updated and can be downloaded here. Component provides a subclass of WebKit.Page that makes it easier
+to reuse code from multiple sources that extend the capability of servlets.
+You can download Component-0.2.tar.gz from here.
+LoginKit handles logins in Webware, using Component.
+LoginKit-0.1.tar.gz can also be downloaded from here. DBUtils 0.9.2 has been released and can be downloaded from here. See the Release Notes for changes. Webware for Python version 0.9.2 can be downloaded here. It includes a couple of fixes and improvements of WebKit, MiddleKit, MiscUtils and WebUtils (see the respective release notes). A new feature in this release is the possibility to dump stack frames of all running threads to standard output by sending a SIGQUIT or SIGBREAK signal to the Application server using the threadframe module. DBUtils 0.9.1 has been released and can be downloaded from here. See the Release Notes for changes. Webware for Python version 0.9.1 has been released and is available for download now. It contains a couple of fixes and improvements of the WebKit, MiddleKit and KidKit components. The documentation is contained in the released package and is also available online. Finally, Webware for Python version 0.9 is available for download. It again includes numerous enhancements, additions and bug fixes
+over the previous release. See the documentation and release notes
+for more information. DBUtils 0.8.1 is now available as a separate package; please download from here. WSGIKit has a new site: http://wsgikit.org It also has a new tutorial on making a to-do application. ZPTKit version 0.1 released. ZPTKit helps make Zope Page Templates
+easy to use with Webware. For more on ZPTKit see here,
+download the package or view the source. Component version 0.1 released. Webware has switched its source-code repository from using Sourceforge's
+CVS service to using a Subversion hosted on the webwareforpython.org
+(aka w4py.org) server. Here are some instructions on how to access the repository.
+Newcomers to subversion may be interested in the Subversion book. You can now find the WSGI-enabled Webware implementation at
+svn.colorstudy.com/trunk/WSGIKit.
+It is now distutil-installable and includes documentation. An effort is in the works to port Webware to the new
+Web Server Gateway Interface (WSGI). The WSGI is an effort by
+the Python Web-SIG to create a standard
+interface for Python web applications, frameworks, and servers, and
+should allow Webware to be run in different environments, and alongside
+other Python web frameworks. Work can be found in a SVN repository
+at svn.w4py.org/WSGIKit/trunk. Thanks to Eric Radman for the new site design, and Jacob Hanson for the logo.
+We're in the process of moving content over from the old site,
+re-organizing, and updating it where necessary. Please bear with us. The Webware project has been generously provided with a virtual server and
+hosting package, and has registered webwareforpython.org. We will be moving the
+Wiki to this server and are in the process of redesigning the entire website in
+order to provide more useful and better-organized information -- stay tuned! Webware 0.8.1 released. It contains an important security fix,
+and all users should download and upgrade.
+See the WebKit 0.8.1 release notes for details. Webware 0.8 was released today.
+See the Webware 0.8 release notes for more info. Use Subversion ( http://subversion.tigris.org/ ) , when it is ready. I would say, "use CVS", but it will never be ready, because the
+backend storage model does not support sandboxes (many short-lived
+experimental branches) well. Why does Subversion work? secure: branching/merging are almost trivial. rejecting/accepting a
+patch is very easy. barrier to entry: actually lower than with web/email-based
+interface. a developer with the commitment/skill level to contribute useful
+patches will probably already have the tools installed the tools create a valid patch automatically (no fiddling with diff
+options) based on current development (not what the person
+downloaded a week ago when he started the project) other developers can immediately (and easily) apply the patch and
+evaluate it. the automated build/test procedure can see and verify the change
+very quickly, rather than waiting for a harried developer to
+download the patch from the clunky web interface and apply it to
+code that has changed four times in the week since the patch was
+created. (all of these points except the first also apply to CVS.) Combined with a Roundup discussion, this is a killer tool with very
+useful history features. Bonus: subversion uses WebDAV as its native network protocol, so you
+get a simple browser interface for free. -- TerrelShumway - 16 Apr 2002 Welcome to the Ministry of Propaganda. WebwareVsJSP WebwareVsPHP WebwareVsZope WebwareVsColdFusion WebwareVsModPython What does Webware provide? The strengths of server-side Java, without its complexity. The power of server-side Perl, without its ugly syntax. The Python of Zope, without its headaches. The flexibilty of CGI, without its overhead. The simplicity of PHP, without its limitations. The capababilities of ASP and ColdFusion_, without their
+price-tag and vendor lockin. THE POWER OF THE WEB, WITH THE EASE OF PYTHON Or without the marketing-speak, Webware provides: a fast, reliable, extensible, and scalable application server. coding in Python: a powerful object-orientated language. Python
+dramatically improves developer productivity and code readible over
+Java, C++, and other compiled languages. Furthermore, it's easier
+to learn than other interpreted languages. support for several development approaches:
+* using pure Python Servlets as in Java Servlets;
+* using embedded PSP script as in ASP, PHP, JSP, and embed-Perl;
+* using powerful templating languages such as Cheetah;
+* using XML+XSLT through Python;
+* and any combination of the above. full access to all Python modules. Webware doesn't require you jump
+through hoops to import relevant Python modules -- unlike Zope. an extensive object-orientated library of of add-ons and tools. automatic management of sessions, cookies, HTTP headers, etc. extensive support for XML, SOAP, XML-RPC, COM, and CORBA through Python. structured exception handling through Python. This makes Webware
+much suitable for large complex projects than environments without
+structured exception handling, such as PHP. * full object
+persistance and caching. This feature of the appserver a major
+advantage over solutions that are embedded in the webserver process,
+such as PHP where it costs over $2000US to purchase an add-on that
+only caches the byte-compiled code. database connection persistance and pooling. portability. It works anywhere Python works: Linux, Unixes, Windows,
+OS2, etc. web server portability. It works with Apache, IIS, and most others. web server independence. The application server runs in a separate
+process and can even run on separate machines. One webserver can
+communicate with multiple application servers, and vice-versa. It
+won't crash your web-server and your webserver won't crash
+it. Load-balancing is easy. protocol independence. Although the application server was developed
+with HTTP in mind, it can be extended to work with any TCP/IP based
+network protocol, even CPIP ;-). a powerful template framework that separates the page logic from its
+design/display and supports a reusable component-based design. interopability with other programming languages, such as C, C++ and
+Java, through Python's extension framework. full source code. All code is licensed under an 'Open Source'
+license, which allows for unrestricted use, modification, and
+redistribution. Unlike the GNU GPL, it allows the code to be used
+in commercial products. and most importantly, an active and supportive developer community! -- TavisRudd - 03 Nov 2001 Multi-language site with gettext: How to localize your site using gettext WebwareAndCheetah: How to project your servlets using Cheetah? RunningWebwareAsUserWebware: How to make Webware run under its own user account ('webware') FileStreamingAndContentDisposition: How to stream files and set the Content-Disposition Header PDFCreationWithReportLab: Basics of creating a reportlab PDF, under WebWare. SettingPermanentCookies: How to set a "permanent" cookie, as opposed to a cookie that expires when the browser is exited. ComputeOnceThenCache: How to compute something only once, then cache it for all future requests UseCSVs: How to use comma-separated-value files in your projects. SingletonClasses: How to write a class that can only be instantiated once MethodsInPSPSyntax: How to write additional methods in a PSP file using PSP syntax ListOfAvailableSettings: Introspect what settings are really supported -- TerrelShumway - 24 Apr 2002 UseWebDAVtoEditSourceResources - the magic chants for mod_dav RunWebkitAsApacheRoot: lose the /webkit/ prefix to your urls and avoid mod_rewrite RunWebKitBehindApache: With the newly-introduced HTTP serving for WebKit, put WebKit behind Apache for mixed resource serving magic HttpAuthentication: using standard HTTP authentication AutomaticHTMLValidation: how to set up webware to automatically validate the HTML your servlets and/or PSPs produce WebwareAndFormEncode: How to use FormEncode for validating forms in Webware servlets. FormAuthentication: Examples of how to do form based authentication. UsingAaap: Using the A-A-P program to compile cheetah templates and publish Webware applications. NewSyslogWIthSIGHUP: How to setup your application with SIGHUP to logfile management with newsyslog AjaxServlet: Example of Ajax servlet with OpenRico AjaxInWebware: a fairly simple way to implement some Ajax functionality into Webware servlets JavaScriptDependencyResolution: Load multiple JavaScript files in one request SimpleSOAPServlet: A Simple SOAP Servlet ServingStaticFilesEfficiently: Exploit client-side caching of static data Some of the ApplicationDesign topics border on being recipes. This page is for notes and discussion on how to secure all aspects of
+your Webware application. If you have links to other pages related to
+web-server and OS security issues please add them below. http://www.enteract.com/~lspitz/pubs.html (several papers on securing your OS --StephanDiehl) Open Webware/WebKit/Application.config with a text editor and change the 'AdminPassword'. (or remove the Admin context completely.) If your app requires client authentication, read Dos and Don’ts of Client Authentication on the Web to avoid many common web authentication pitfalls. The two things I love most about this system are: 1) it's simple, and 2) it doesn't require sessions (server state). -- TerrelShumway - 10 July 2004 You might also consider: periodically reseeding python's random number generator from a strong source (such as /dev/random or EGD), binding session Ids to client IP addresses (though this may annoy mobile DHCP sessions), after successful name+password authentication in an SSL session, drop the old session and generate a new one. Set the "secure" flag in the associated _SID_ cookie sent to the client, to reduce the risk of eavesdropping. -- KenLalonde - 28 Nov 2001 session ID in the URL (via GET variable or path) http://www.yourwindow.to/information-security/ (security glossary) http://www.counterpane.com/crypto-gram.html (monthly security newsletter by Bruce Schneier --StephanDiehl) -- TavisRudd - 22 Nov 2001
+-- ChuckEsterbrook - 10 Dec 2001 (moved from AppIdeas) This isn't an application, so much as a group of applications working
+together: a sourceforge/tigris like application built with Webware.
+SourceForge.net provides a valuable service to the Open Source
+community, but its implementation and user interface could be improved
+in many ways. -- TavisRudd - 31 Oct 2001 Please read InternetGroupware before starting this project. --
+TerrelShumway - 16 Apr 2002 needed sub-components (all internationalized!):
+* WebwareUserManager -- this is the most critical element and must be done first
+* WebwareCVSManager
+* WebwareFileReleaseManager
+* WebwareBugTracker / WebwareTaskManager / WebwareSupportRequestManager (use Roundup)
+* WebwarePatchManager
+* WebwareNewsManager (use IMAP/NNTP/RSS)
+* WebwareForum (use IMAP/NNTP/RSS) / WebwareWiki feature wishlist: better reporting the SourceForge make it possible to have sub-projects and sub-sub-projects (nesting to any level). Sub-projects inherit
+the properties and settings of parent project and are included in statistics / reports for the parent
+projects. integrated Wiki provide website creation/management tools as integrated part of the application if desired (see
+WebwareContentManagementSystem) make it database-independent NOTE A Webware SubProject has now been spawned from this topic. See
+UserHandlingProj for more on designing and implementing a flexible
+framework for handling user authentication and authorization in
+Webware applications. This page is home for discussion about
+WebwareUserManager, an application built on top of that framework to
+help manage users and groups. -- TavisRudd - 23 Nov 2001 I think permissions should be considered something of a different
+issue -- ACLs being traditional at this point, but not always
+appropriate. They are closely tied to the system's notion of a an
+object and the granularity of permissions. I myself have been thinking about a more OO way of doing
+permissions -- most of the ways seem to be based on static data
+structures with explicit algorithms: it's not possible, for
+instance, to say "anyone can do action X if someone with permission
+Y requested that they do it" -- you can make the request also grant
+the permission, but the algorithm itself is external to the
+security system. You can't, for instance, retroactively change
+those permissions around, because the actions (the request) have
+already passed. Anyway, I'm not sure where I'm going with that idea, but it might
+only be to say that figuring out permissions is distinct from
+managing users and their roles/groups, and probably more
+complicated. -- IanBicking - 20 Nov 2001 Zope has a robust but (for some applications) overly complex security
+system: http://www.zope.org/Documentation/ZDG/Security.stx . We're leaving out the Zope term "role" because it ambiguously overlaps
+with our term "group". We're also leaving out Zope's "Proxy Roles"
+(analagous to Set-UID programs, or allowing a user to acquire extra
+permissions in certain circumstances.) Our "ownership" is different than Zope's. What Zope calls ownership
+was added to Zope in 2000 to plug a security hole related to
+through-the-web editing and proxy roles. For us, 'owner' means the
+user(s) who may reassign the permissions of a particular resource. As far as user interface, Zope uses one screen with a table of
+permisions down the left and roles along the top, another screen for
+linking groups to roles, and the user screen for assigning users to
+groups. -- MikeOrr - 22 Nov 2001 Let's scrap the term 'role' completely, and only use the terms of
+users, groups, actions and permissions. Where 'actions' are things
+like view, edit, etc. and permissions are the authorization for
+particular users and groups to be able to perform those actions on an
+object. An application designer should be able to define custom
+actions. 'roles' implies a strict connection between 'groups' and
+particular 'actions', which is not desirable. The permissions system
+in Windows NT and Novell Netware are good models. -- TavisRudd - 20 Nov 2001 I don't know what Novell uses -- it seemed very fine-grained -- but I
+don't particularly like NT. The way permissions combined seemed very
+strange -- for instance, if you have Everyone: Deny All, then other
+more permissive permissions would be meaningless. This seems
+backwards, and overall very confusing. If you want to look at a
+particular ACL implementation -- ACLs are hardly novel, after all -- I
+think it should be possible to find something better than NT. -- IanBicking - 20 Nov 2001 Novell uses a modified version of LDAP to power it's NDS
+authentication system. Couldn't we utilize OpenLDAP as a basis? -- RayLeyva - 20 Nov 2001 With all this talk of users, groups, roles and so on, it's important
+not to forget applications where the concept of a user might be
+interesting, but where the relationships between users and actions are
+completely different (they could be determined by their position in an
+organisation, for example) and are irrelevant to the ownership of
+Webware resources (being relevant instead to the data manipulated by
+such resources). I have been thinking a bit about authentication and access in some
+recent experiments and I think it would be nice if one could have a
+layer where access checks are implemented separately from normal
+servlet code. In XmlForms I would consider adding an access
+section to each definition which can validate a user's credentials
+according to an arbitrary validation function, and should the
+validation fail, the user would be directed to another resource either
+to get authenticated or to be told what went wrong. These validation functions might well use a lot of
+application-specific logic, but they could benefit from simple,
+generic user support in Webware. One of the things about Zope which
+seemed either badly explained or badly designed was the tight binding
+between users and what Zope wanted users for. Clearly, Zope supports
+user sessions for itself, but then people mention that it doesn't
+support user sessions for Zope-based applications without add-on
+products being present to provide such sessions. -- PaulBoddie - 21 Nov 2001 Here are examples of group structures appropriate to different
+applications: (A) Unprivileged user, logged-in user, content manager. The last may
+be factored out if the app doesn't allow through-the-web managing.
+The first may be factored out if user identification isn't necessary
+for these actions: simply put an if-anonymous-user branch in each
+servlet. If you're left with only one group (logged-in user), maybe
+you won't use most of this kit/package. (B) Unprivileged user (may read some content and post to forums),
+privileged user (may read all content and post to forums), content
+manager (may add/edit articles; e.g., "pages"), app manager (may
+change the app's look or do more programmingish stuff). All this is
+separate from the anonymous user, which has his own default
+permissions. Only some sites will have a web management interface. Even fewer will
+allow non-managers to modify editorial content ("articles", as opposed
+to forum postings or user-submitted entries) through the web.
+WebwareUserManager should scale down to these sites and not require
+unnecessary complexity. Complexity should be added only at the point
+it's needed. -- MikeOrr - 22 Nov 2001 Remember, this discussion is about a UserManagement system that can be
+used for the various application ideas on the AppIdeas wiki page.
+Most of those would fall into the 'large, complex' category. We would
+really need to discuss the permissions system in the context of each
+of those applications. But as none of them exist let's focus on
+authentication for now. What are people using at the moment to do their authentication? Note: for authentication we only need to think in terms of users Here's a summary of the important questions: At what stage in the request-response cycle does authentication take
+place (Adapter, Application, or Servlet)? Doing it at the Adapter
+level means we can use the same mechanism to protect content that
+isn't served via WebKit. Doing it at the Application level means
+that we can also protect static content that is served via
+WebKit. Doing it at the Servlet level means that we can only
+protect servlets. are we going to use an identifier in the session or a separate
+authentication cookie as is suggested in the two papers I listed
+below? Assuming that we use sessions to store the authentication details,
+how do we make sessions work with/without cookies? Extend this to
+protecting static content via WebKit. How do we store authentication information in the user session? How do we guard against session hijacking? How are password's stored internally? plain or hashed? How do we interface with arbitrary stores of user information and
+passwords (flat-file, BSD-DB, ZODB, relational database, LDAP, etc.) When Webware makes the shift to a multi-application framework, does
+authentfication span applications? How do we make the authentication system fall-back to a semi-secure
+mechanism (e.g. md5 hashes via javascript) when SSL isn't available. Do we want to enable some sort of IP filtering or automatic
+IP-userID mapping? Are we going to block brute-force dictionary attacks with a failed
+login time-out mechanism? Are we going to worry about multi-machine failover support? -- TavisRudd - 21 Nov 2001 Here's some excellent overviews of the issues involved and the schemes available:
+* http://www.wwnet.net/~janc/auth.html
+* http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf -- TavisRudd - 22 Nov 2001 Tavis said WebwareUserManager (WUM) is meant for complex apps such as
+those on the AppIdeas page. But it would be better to have a system
+that scales up to those applications and also down to simpler
+applications that don't need all its features. Because even the
+simplest application (with one group) may want some features, such as
+a registration screen and password-recovery screen. And maybe this
+simple application will one day become more complex and require some
+features it didn't previously. Then you can simply activate the
+features rather than porting the application to WUM. One of the goals mentioned above was seamless reauthentication. That
+is, when the user hits an unauthorized page, the system prompts him to
+login, then retries the original request. Zope does this through an
+Unauthorized exception, which Zope automatically catches and does the
+login prompting and retrying. -- MikeOrr - 23 Nov 2001 PasswordRecovery Lost-password recovery mechanisms: "mail me my password" button (to my registered address). "mail me a new password" button. challenge question and answer, preselected during registration. We
+can offer a single question, a choice of questions, or a
+user-provided question. To reassure paranoid users, the question
+should not relate to any info that could be (mis)used to identify
+the user or place them in a marketing category. "Pet's name" is
+safe; "favorite sports team" is questionable; "birthdate" and
+"mother's maiden name" is unacceptable. (Disadvantage: user may not
+want to come up with and memorize a question/answer.) mail user a randomized URL, valid only for 30 minutes, where she can
+choose a new password. (Tavis prefers this, and Mike thinks it may
+offer a fair compromise between security and convenience.) -- I am
+absolutely against mailing passwords. -- TavisRudd - 23 Nov 2001 -- MikeOrr - 22 Nov 2001 One of the sites I used also did this: send a new, generated password by postal mail to the
+user. Unfortunately, despite attempting to select "send by e-mail",
+the application decided to use postal mail instead, but I did get a
+letter a few days later with the password in it. :-) Is this a
+justification for the establishment of a PostKit component? ;-) -- PaulBoddie - 23 Nov 2001 Something which I just realised: I have been following this page's
+progress using the WebChanges page, but the categorisation and naming
+of what is being discussed seems wrong. As one of the AppIdeas which
+discusses a WebwareUserManager, it seems to suggest an application
+which provides easy administration of user details, rather than a
+framework for the identification and authentication of
+users. Shouldn't we set up a WishList entry concerning sessions and
+users, or at least refer to this as "WebwareUserHandling"? -- PaulBoddie - 23 Nov 2001 Paul, you're right. This core of this is a system component rather
+than an application. However, I'd like to build an application for
+managing users on top of the framework. -- TavisRudd - 23 Nov 2001 (moved from AppIdeas) a Parnassus-like site, not just using WebKit's base features, but
+also using some of the other kits and extensions -- PaulBoddie - 06 Nov 2001 If we were to enable user comments via an integrated Wiki this could
+become immensely useful, and popular! This would allow the index of
+listed software to be matched up with user reviews and also more
+general discussions about specific 'problem domains'. It might even
+become THE Wiki for Python related discussion. Furthermore, if it was
+combined with WebwareListArchive serving the main Python lists, you'd
+have an even more useful site. Imagine, being able to easily link
+Wiki discussions and discussions on the list. The interface to
+WebwareListArchive could auto-link WikiNames. We could also provide
+some simple syntax for linking to postings from the Wiki topics.
+Finally, it would also be nice to have a quick syntax for linking to
+sections in the main Python docs. I guess it would compete with http://aspn.activestate.com/ASPN/Python/ -- TavisRudd - 07 Nov 2001 It could be neat if the archiver would notice an email with WIKI:
+SomeWikiName in the subject line, and then generate a Wiki page from
+that and all emails that reply. Later on someone could edit the page
+to turn it into something more compact. It would be even better if
+you could put that wiki note into the subject or body later, and it
+add all previous messages in that thread as well -- since you don't
+always know what threads will be interesting. The other situation is when someone asks a question that should be
+documented. A good reply will often make for a good basis for
+documentation. There you'd just want to mark a single email as being
+destined for the Wiki. -- IanBicking - 07 Nov 2001 Advantages of Webware: Considerably faster than CGI, because:
+* The interpreter is not started for each request
+* Servlet instances are cached
+* In general, a persistant application leaves more room for caching any sort of result
+* Resource access can be pooled, for instance, database connections Easier programming model:
+* Threading provides a simpler way of handling concurrent modification of resources (often CGI scripts simply ignore potential race conditions, leading to errors under high load)
+* Considerable amount of basic code is readily available in Webware: session support, cookie handling, etc. Disadvantages: Not possible to run on many shared commercial hosts, or generally in an environment that doesn't allow long-running processes Setup can be more complicated Because processes are not recreated for each request, errors or memory leaks might effect other requests Webware is similar to Java Servlets in many ways -- the basic
+architecture was modelled on Java Servlets. The primary difference is
+that Webware is written in (and for) Python, not Java. See python.org's Python-Java comparisons Advantages of Webware: Python is much more compact: WebwareExampleVsJavaServletExample Python is more flexible and transparent than Java. All implementations of Webware are fully open source! Places where they are equal Python is nearly as portable as Java Disadvantages: All implementations of Webware are, well, only one implementation Webware is not as portable to various web servers (Apache is used primarily, IIS is used by some, other servers are unknown -- though with a CGI adapter, most would still work) No large companies are backing Webware (This App now exists: http://wiki.colorstudy.net ) OtherWikisFeatures -- cool things found elsewhere that we should
+steal^H^H^H^H^Hbe inspired by. Zope has ZWiki, why don't we have a WikiKit? We could write one from
+scratch or make a stub interface to one of the existing Python wikis
+(Twiki is working well here; there's also PikiPiki, MoinMoin,
+PikiePikie, Pyle). Using an existing one would, of course, allow us
+to leverage its features and development, but it requires kludging
+since none of the existing wikis were intended to be embedded thus.
+We could also tackle some existing problems in the wiki world, such as
+providing an XML DTD as a common base format for converting one wiki
+server's syntax to another's, and scripts for importing wiki data
+to/from other server's data stores. MikeOrr - 07 Nov 2001 If we go ahead and make this, the backend should be completely
+independent of Webware. Only the interface should be tied to it. Also, I prefer the names WebwareWiki and WWiki to WikiKit, but
+that's probably because I dislike the *kit naming scheme as whole. -- TavisRudd - 05 Nov 2001 If you seperate out the concepts, it can be a more
+managable/reusable/aproachable project. For instance, there is the WikiSyntax, which is similar to
+StructuredText -- but it's only societally important to the Wiki,
+not technically. Some Wikis use straight HTML anyway. Then there's the editing interface. This is vaguely what I did in
+ComfyEdit , which is more notable to say that part isn't that hard
+either. Most Wikis that aren't built on object databases of some sort
+are actually just a set of text files that get edited. TWiki is
+clever enough to add RCS recording to that process as well -- but then
+it's just a bunch of .txt and .txt,v files. The third important part of a Wiki is the naming and linking
+mechanism. Personally, I like the notion of named links ala Wiki much
+better than HREFs for internal links anyway. The WikiName syntax can
+be awkard, but the essential flat namespace could be mapped to from
+different syntaxes -- that's more a text formating issue. Each of these really has a lot of value on its own. Easy text editing
+is nice (maybe an appropriate format for WebwareDocumentation even).
+Editing pages/files/objects is generally useful -- it could even be
+considered a subset of something that includes FTP and WebDav. The
+named links are also a very interesting linking idea (including
+backlinks); it provides an interesting alternative to hierarchical
+namespaces. The lesson of WikiWiki isn't so much one of cool technologies -- it's
+of putting together a set of useful ideas into something that is much
+more than its parts. There might be other combinations that lead to
+other cool stuff -- if the parts are all orthogonal, we might stumble
+on something great. -- IanBicking - 07 Nov 2001 Here's some ideas for possible enhancements to existing Wikis such as
+TWiki: firm up the StructuredText syntax (see
+http://structuredtext.sf.net) auto-syntax coloring of code listings (starting with Python, but
+extending to other langs) multi-topic search and replace tool make it possible to have non-web interfaces, along the same lines as
+WebDav. Think of a WikiMode for Emacs! (already kind of
+possible, seeing as Emacs has a web browser and speaks HTTP) make it possible for the Wiki to understand several different
+WikiSyntax styles provide conversion and import tools, like Mike suggested above make the web interface prettier and more functional: edit-box that spans 90% of window width rather than a fixed width
+_(Not possible, as far as I know)_ provide a more functional WebChanges page, or simply use a
+WebCVS script make the homepage of the Wiki prettier (TWiki's is rather ugly)
+_(Simply because I haven't changed the templates -- not really
+TWiki's fault)_ make it possible to roll-back changes (to an abritrary
+rev. number) _(You can do this in TWiki)_ make it possible to use Subversion rather than CVS. _(Why? What is
+Subversion?)_ extend TWiki's concept of 'webs' to allow nesting of 'webs' (like
+Python's package structure. ... blends the flat namespace of
+traditional Wiki's with a hierarchical structure like Zope's
+folders. use something other than basic HTTP authentification for logging in. allow tight integration with the other AppIdeas that we've floated.
+In particular: WebwareZope / WebwareSourceforge -- huge potential here! WebwareForum WebwareSlash make it possible to embed editable regions in otherwise
+write-protected wiki pages (There's another Wiki that does this, but
+I can't remember which one) make it possible to view/preview the contents of one WikiTopic
+inside another. This would be enabled via either a button on each
+topic or a user setting. The view/preview bit could be done
+in-place or via a separate frame (preferable). Think of it like the
+view pane in an email client. Each WikiName link would be followed
+by a * or some other symbol when this feature was active. Clicking
+on the * would display the contents of that WikiName in the view
+frame. make it possible to use Templates (like Cheetah's object-orientated
+ones) for individual WikiTopics. make it possible to drop in 'user interface components' such as
+ready built forms, surveys, etc. into a WikiTopic make it possible to store custom meta-informational fields about
+WikiTopics. -- TavisRudd - 07 Nov 2001 _(IanBicking)_ the last days there was a lot going on in AppIdeas. A lot of ideas
+are about full fledged application while others consist of building
+blocks for such application. There is an agreement that
+WebwareUserManager is one of the most important building blocks.
+Then there are a huge number of ideas that can be described as some
+sort of Content/Document/Knowledge Management System. It might be
+helpfull to define these ideas more rigorous as all of these terms are
+used as "buzzwords" by a lot of marketing strategies. I see these Applications (or modules) in a hierachical order: Content Management:
+* management of any data
+* import/export of data
+* authentication mechanism
+* version control
+* categorising data Document Management:
+* is a Content Management system
+* the content consists of human readable files/documents
+* has filter for import/export/view
+* has workflow rules included
+* has adapter for third party tools (for example text processing tools)
+* has integrated search engine Knowledge Management:
+* is a Document Management System
+* allows links
+* includes something like RDF, Topic Maps,...
+* includes expert system (?) Of the proposed Apps, I'd see the following classification: Content Management: Document Management: WebwareDocumentManagementSystem, WebwareForum, WebwareMail, WebwareSlash Knowledge Management: WebwareVault, WebwareWiki, WebwareSourceforge -- Stephan.Diehl - 22 Nov 2001 <br/> Which operating system is most secure? The answer is "not Windows". Webware runs on Windows just fine using either IIS or Apache 1.3. If
+you're already using Windows and comfortable with it then feel free to
+run Webware. However, keep in mind that by and large the majority of worms,
+viruses, trojan horses, etc. spread through and attack Microsoft
+products including Windows, IIS and Outlook. FreeBSD and Linux are generally good choices for secure systems. Of
+course, with any operating system, you must review and monitor your
+security measures. If not managed properly any system can be
+insecure. OpenBSD would seem like a good choice since security is its theme, but
+the last reports (summer 2001?) were that the threaded WebKit app
+server did not run properly on it. Below are a few references to these problems. Feel free to add. -- ChuckEsterbrook - 10 Dec 2001 So far the Oracle fortress is holding up, mostly because the
+attackers are trying to break in by exploiting potential holes in
+Windows NT and Oracle is running Oracle 9i Application Server on
+Unix (news - web sites), according t o Jarvis. ... "Microsoft (Corp.) (Nasdaq:MSFT - news) doesn't even use NT on
+their own Web site. They use Unix," he added. "It's rather
+ironic." Microsoft executives were unavailable for comment, a spokeswoman
+said on Thursday and Friday http://dailynews.yahoo.com/h/nm/20011209/wr/tech_oracle_unbreakable_dc_1.html -- ChuckEsterbrook - 10 Dec 2001 Above link appears broken. See
+http://www.counterpane.com/crypto-gram-0202.html for details on
+Oracle's claim to security fame. AndrewHallam - 29 Mar 2002. ...Nimda... is a mass-mailing worm that utilizes multiple methods
+to spread itself. The name of the virus came from the reversed
+spelling of "admin" . The worm sends itself out by email, searches
+for open network shares, attempts to copy itself to unpatched or
+already vulnerable Microsoft IIS web servers, an d is a virus
+infecting both local files and files on remote network
+shares. http://www.symantec.com/avcenter/venc/data/w32.nimda.a@@mm.html -- ChuckEsterbrook - 10 Dec 2001 In May [2001], hackers took control of at least 9,000 Microsoft
+IIS sites, according to Attrition.org, a site tracking Web site
+break-ins. http://www.zdnet.com/zdnn/stories/news/0,4586,2779525,00.html -- ChuckEsterbrook - 10 Dec 2001 "Code Blue virus exploits IIS hole" http://www.vnunet.com/News/1125322 -- ChuckEsterbrook - 10 Dec 2001 Comments on Microsoft security: http://news.com.com/2010-1072-831385.html
+http://news.com.com/2010-1078-818611.html -- ChuckEsterbrook - 29 Mar 2002 homepage: http://www.comdongin.com If you have a site that uses Webware add it to this list. If the site
+is for internal use only just add a description rather than a link.
+If you don't mind sharing your source code add a link to a tarball.
+Please sign and date your entries/comments and use horizontal rules
+to separate the entries. If your site uses Cheetah please also add a note on the
+Cheetah.WhoIsUsingCheetah page. Ideas for info to include: link basic description notes on the tools / architecture used time and effort involved was it a for-fun, non-profit, in-house, consulting or biz-start-up project? Note This is not an exhaustive list of all the places Webware is being used.
+Most of the people using it haven't added a note here yet. http://www.vorbis.com (PSP) http://foreclosures.lycos.com (Webware, Cheetah, Apache) http://www.electronicappraiser.com (Webware, Cheetah, Apache) Payment) service for the South African market http://memigo.com -- a filtering weblog http://instasong.com (Webware, Cheetah, Apache) Online Customized Songs (Mp3) http://www.encyclon.net/multiblast (Webware, Cheetah, Apache) - Bioinformatics Tool (only korean) Name: PatientWire (see http://www.patientwire.com/ and
+http://www.wolfbioscience.com/) Description: A Patient Relationship Management system for Eye Care
+Professionals (see the website for more info) Tools and Architecture: PostgreSQL, Webware, Cheetah, PSP, Linux,
+Apache (mod_ssl, mod_rewrite, mod_proxy, mod_webkit,
+mod_extract_forwarded, mod_gzip) Time and Effort: About 1.5 person years (ChuckEsterbrook,
+TavisRudd, GeoffTalvola, and several others). --TavisRudd - 01 Mar 2003 Name: Redsys Description: A content management system for our online database Tools and Architecture: HTMLgen, Linux, Apache Time and Effort: About one person year up to now The project was abandoned in July 2002 due to organizational changes. -- AlbertBrandl - 09 Nov 2001 The Monkey, a content management system (CMS) Provides through-the-web CMS including documents, indexes,
+categories, publishing to FTP and SSH, templating, user management,
+etc. Tools and Architecture: WebKit, UserKit, MiddleKit, Cheetah,
+MySQL, Apache, Linux Time and effort: Multiple people on the project since early fall
+2000. The Monkey is entirely funded and owned by http://HFD.com/ which
+rents the Monkey to clients. HFD also does original web design. HFD
+has funded some of the development of Webware via the Monkey
+project. -- ChuckEsterbrook - 09 Nov 2001 Name: Juhe Description: A membership management system for the Austrian Youth
+Hostel Association Tools and Architecture: WebKit, HTMLgen, ReportLab, PostgreSQL,
+mod_python, Apache, Linux Time and Effort: started in early spring 2001 using a CGI
+environment, ported to Webware in august 2001, went online on 29 Oct
+2001, 1 programmer, 1 webdesigner, various supporters in design
+phase about 50 users, will increase Not public accessible, sorry -- AndiPoisel - 11 Nov 2001 ongoing port of [[http://www.calrudd.com]] & its admin site from PHP
+to Python Description: An online portfolio for Calrudd Construction Tools and Architecture: WebKit_(from WebwareExpRefactoring),
+Cheetah, Apache, mod_rewrite, mod_ssl, PostgreSQL / psycopg,
+FreeBSD (plus Photoshop, ImageMagick and PIL) Time and Effort: online in various incarnations since
+1998. Currently in PHP. 1 programmer/designer, plus several
+photographers and writers. The port is not in production use yet. I'll update this when it's
+complete. -- TavisRudd - 23 Sept 2002 PixelWolf: our in-house content management system manages access to and metadata about www.calrudd.com's 2000+ photos
+and vector graphics, plus more than 11000 auto-derived versions of
+the photos (various sizes and resolutions for Web viewing) Tools and Architecture: WebKit (from WebwareExpRefactoring),
+Cheetah, Apache, mod_rewrite, mod_ssl, PostgreSQl / psycopg, SuSE
+Linux, rsync, ImageMagick, Python Image Library integrates with Windows XP desktops via samba and some symlink magic
+on the server side Time and Effort: operational since Feb 2002. 2 months for 1
+programmer/designer. The content comes from several professional
+photographers. -- TavisRudd - 19 Mar 2002 ongoing in-house development of an intranet/extranet for Parlance
+Corporation and its customers technologies: Windows 2000, Apache w/ mod_ssl, SQL Server 2000 via
+ADO, XML-RPC, 3rd-party charting software uses XML-RPC to receive periodic "heartbeat" status updates and
+other information from NameConnector</nop> (tm) systems in the field data received via XML-RPC is combined with other sources of data to
+generate reports with HTML+CSS various other management tools exposed via web site sorry, it's not a public site -- GeoffTalvola - 03 Dec 2001 2- and 3-dimensional interactive online data modeling web site 100% pure Python developed on NT, running on Linux virtual server Developed to change from GUI to Web application uses Cheetah and Webware home of psycopg (a Python DB module for PostgreSQL) and several other Open Source projects 'up and running in a single night' Communication Sciences' WebCalls Description: Web based interface to allow clients to access analyzed
+telecommunication data This is the start of a port of a complex Windows based application
+to the web. Initially prototyped as Java Servlets, moved to Python to speed
+development time. Sorry, You must be a Communication Sciences client to access the site. -- AaronHeld - 21 Jan 2002 Neosynapse All products use Webware for their user interface Products include a full-feature billing system for medium sized
+businesses, an IP allocation management system for large network
+operators, and a permissions management system. Uses: Webware, Apache, PostgreSQL, and other product-specific components User interface implemented entirely in PSP In production now, under active development -- DavidCasti - 17 Apr 2002 RemoteQuote Targetted at installers of remote car starters and security systems Contains database of wiring information for vehicles, remote car
+starters and security systems Uses a complex algorithm to quote installations of starters or
+security systems, producing a cost estimate listing all extra parts
+(relays,diodes,etc) required to do the job and detailing how to do
+the install Uses: WebKit, MiddleKit (with modifications), FormKit, Servlets,
+PSPs, Apache, MySQL -- JasonHildebrand - 20 Sep 2002 I (my company, Burke -Madrid, Spain-) am working in some applications,
+which are in beta now: One is an application generator. It uses Webware as the http
+front-end. It is being used in the Ministerio de Justicia for User
+Support. Others are written ad-hoc. They are related to Criminal records and
+Last wills tracking. -- MarcosSanchezProvencio - 27 Sep 2002 The dynamic parts and the administration tools of these websites use
+Webware / Cheetah / PostgreSQL / psycopg / Apache. All of them were
+developed and are running on Linux machines. I am currently porting
+the administration tool for www.dmvoe.at and www.edirect.at from
+PHP to the same combination adding an e-mail newsletter tool. The
+websites will follow. -- AndiPoisel - 11 Oct 2002 Name: Stockton-Taylor Music http://www.stocktontaylormusic.com<br/>
+Additional web address http://www.e-stm.com Descr: Web site for music publishing business. Choral and church
+music only. Searchable catalog of music. Ability to view samples
+of music and lyrics. Ability to hear selected audio samples.
+On-line ordering but no on-line payments. Tools: Linux (debian), Apache, MySql, Webware, WebKit, Python,
+PSP, Servlets, mod_rewrite, mod_python. Most pages are done in PSP.
+Some static pages. No use of CGI. Effort: One person, working most nights, for about three months. NOTE: As of 11 Nov 2002, the web site has been deployed in a
+severely limited version. Currently waiting on data entry by the
+users. As data becomes available, the full functionality of the web
+site will be turned on. UPDATE: 20 Nov 2002, will be enabling user registration. This
+feature will allow notification of activation of additional web site
+features to be sent to users. Stay tuned, I will post updates on
+this page. -- TacticalJack -- 20 Nov 2002 Tablet Hotels, Unique Hotels for Global Nomads For-profit service to reserve luxury and boutique hotels at great rates. Webware, Apache2, MySQL, qmail, Linux Serves hundreds of requests per second Converted from an original WebLogic JSP architecture to Webware servlets - new enhancements are much faster in Python! -- BenParker - 05 Sep 2003 My home turf home of the PyDiddy WikiWeb. totally done with Webware time and effort involved was much, as it was my first WW project it is for-fun Normal Mailorder is a very cool german record shop specialized in
+all kinds of alternative, independent music from pop to hiphop. I developed the online shop, which is of course done with solely
+with Webware. The database backend is abstracted by use of
+SQLObject , which saved my a** because MySQL proved to be too
+unstable and I had to switch to PostreSQL really fast. Thanks to
+SQLObject this required only the change of just a single line in the
+whole application. There was quite some time and effort involved in doing it, but it
+would not only have taken much longer to develop without Webware: a
+lot of things would have been near impossible for a single guy team. this was in part a commercial job. -- FrankBarknecht - 01 Dec 2003 Instantly generated detailed report in pdf format of a person's
+destiny as predicted by the ancient Chinese art of fortune telling
+known as BaZi or the Four Pillars of Destiny. Uses Webware, FunFormKit, Apache with mod_webkit, mod_rewrite,
+MySQL, ReportLab, PIL Time and effort: 1 BaZi master and 1 programmer took 1 year. The
+site is up now. -- PakCheongBoey -28 Feb 2004 Online scrabble game (french) with more than thousand players Uses Cheetah for template, TwistedMatrix for server All my static websites uses Cheetah for templates and DocUtils for text The config files of the server (courier, exim, proftpd, apache,
+bind) are generated with Cheetah source code available in GPL for people who ask (no documentation yet) -- WilliamDode - 17 Mar 2004 company website (in Czech only) Using WebKit and customized mod_python adapter to serve dynamic content
+while behaving like static site (to allow simple wget -R mirroring) also some internal agenda (mail server administration tool, simple CRM tool). Czech webzine on travelling (in Czech only) Using WebKit + CheetahTemplate to serve dynamic content from PostgreSQL (using psycopg) -- Veros - 7 Dec 2005 Dynamic "eDialogues" that provide marketing-specific applications to
+deliver online conversations and generated "warm", profiled leads All internool tools and live eDialogues built with Webware/Cheetah/SQLObject -- TracyRuggles - 15 Dec 2004 Since Feb 2004, the "Dangerous Materials Registry Information System" (DaMaRIS)
+is available for universities and companies via a spin-off company from the
+Heidelberg University (so far, only for the German market). The system is based on Webware for Python, Reportlab, Apache and PostgreSQL. Access to a demo-system can be provided. -- ChrisZwerschke - 09 Sep 2005 Corporative site for multimedia-publisher (CDROMs, DVDs, etc.) Webware for Python, Apache 1.3 (mod_webkit, mod_rewrite), Cheetah templates,
+PostgreSQL, PIL Main language - Russian, but a small English version is also available via http://www.iddk.ru/en/ Online multimedia shop (CDROMs, DVDs, Audio-CDs) Webware for Python, Apache 1.3 (mod_webkit, mod_rewrite), Cheetah templates,
+PostgreSQL, PIL Only Russian version is now available -- OlegMamontov - 05 Dec 2005 Corp. site for webhosting company Webware for Python, Apache 2, (mod_webkit, mod_rewrite), PgSQL, XML/XSLT Site is in German -- CorneliusBolten - 18 Jan 2007 On line newspaper Webware for python, Apache 1.3 (mod_webkit, mod_rewrite), WebKit, Kid templates, FirebirdSql Site is in Spanish -- Oscar Kuchuk -- May 2007 Web services for Omnis developers, plus embedded products for clients Webware for python, Apache 2 (mod_webkit, mod_rewrite), WebKit Original deployment was in 2004 using WebKit.cgi and Apache 1.x. Have installed Webware+our products on Redhat, Centos, and Mac OS X. -- Mark Phillips -- December 2007 Please list any bugs you find here.
+Feature requests should go on Wiki ToDo. This doesn't work, but it should: Some of the features of this wiki: Restructured Text is fully supported, and is extended in a way
+that is natural both for a wiki and for reST. Any links (like
+link_) that cannot be resolved internally in a document are
+considered to be wiki links to other pages on the system. Because Restructured Text is a full-featured markup language, it
+does not cut corners based on a limited initial domain. It is not
+wiki-specific, and does not produce output that is in any way
+wiki-specific or of inferior layout. It does not use WikiNames. WYSIWYG editing using Xinha. Includes a custom button to
+insert intra-wiki links. The wiki supports using an external editor to edit content, to
+avoid through-the-web editing (browser textareas are poor for
+editing). The wiki also supports the Universal Edit Button to edit content. Text and title searching. Incremental page searches
+when finding pages to link to. "Pages" can be of any type, including binary and image files
+(e.g., PDF). XML RSS feeds for new pages and recent changes
+available for updates of the site. All edits are versioned, and a history is kept. You can compare
+versions. Administrators can easily rollback changes, remove old versions
+or remove user accounts. This helps a lot in cleaning from spam. You can plug in your own captcha for creating user accounts.
+Currently there are captchas suited for Python programmers and
+musicians. Robots and stupid spammers will not be able to
+create user accounts, but for serious users it is easy. The wiki can be published to static files, with URLs that will be
+analogous to the in-wiki URLs. You can provide your own Cheetah
+template to control the page content. Pages are regenerated as
+their wiki equivalents are edited. Links to pages that do not
+exist are removed from the static published content; creating
+those pages will regenerate any pages that contained the
+previously dangling link. These pages can be statically published both locally, and to an
+SFTP or FTP account. Multiple domains can be served off a single installation; both
+aliases (e.g., http://wiki.webwareforpython.org and
+http://wiki.w4py.org), or entirely separate sites
+(http://wiki.cheetahtemplate.org). Configuration is done through a simple .ini file.
+Configuration can be global or domain-specific. One possibility
+would be to configure two domains to point to the same content,
+but for one domain to be marked read only. As this wiki becomes
+more configurable, this could allow for it to grow into more
+CMS-like features. All data is stored in simple text files; pages in .txt files,
+metadata in rfc822-style files, RSS in its own RSS file
+(as the canonical source of data), configuration in an .ini
+file. There is no binary data and no pickles (except for some
+indexing which is done in bdb files, which are not canonical sources
+of data -- you can delete and regenerate those files at any
+time). This makes upgrading easy -- you only need to update the
+code, not the data. Doesn't require any special tools, like RCS. It is self-contained
+(beyond the Webware requirement, and modules in the standard
+Python library). Though it has grown some other optional
+libraries. The logic for the wiki is separated from the logic for the
+interface. The wiki is written in independent modules which are
+not bound to Webware or any particular interface. Another way of relating wiki pages to each other. More on its
+page. This is the beginning of blog functionality. Comments are a certain kind of related page -- both individually
+addressable and editable, but are also shown inline in the
+commented-upon page. In this Wiki, you can use the reStructuredText markup language. For a quick introduction, please read: reStructuredText parser is a component of Docutils. For reference, please use the documentation available on the Docutils website: See also the HTML Wiki Sandbox for WYSIWYG fun. Contents List: first again second again third again fourth again link in a list or what? Are enumerated listrs broke? so what does this do eh? WikiWord wikiword wikiword wikiword!_ ormaybe (literally ormaybe__) something here and here but *can't combinum* does that work? many (most?) puncts? http://mysteryshopping.allseafood.org Header row, column 1
+(header rows optional) Header 2 Header 3 Header 4 body row 1, column 1 column 2 column 3 column 4 body row 2 Cells may span columns. body row 3 Cells may
+span rows. Table cells contain body elements. body row 4 img Name Admiral Morgan Cost 5 Side English hollow hostage 142 test buy socks wear socks wash socks Ouch, the text box has a proportional font in my browser! That makes it hard.
+how to creat my page bbs Renamed wiki-link RussellBallestrini is testing this software. I'm building a wiki/CMS using pylons. Close to finished. I use reSTructured Text as well. Some things I have in mind, though it's very unsure when and if ever: Templating (configurable on a per-domain basis). Probably with Cheetah.
+Choosable in some fashion as well, particularly a "printable" template. More Indexing (backlinks are indexed) More configuration options to control output and suppress links. Mail notification on changes (though we have RSS now) Some sort of categorization (maybe with more metadata; keyword
+metadata, or a more controlled form of categorization) InterWiki-links Locking of pages for editing Related terms starts the idea, but it needs a lot more flushing
+out; more on that page. Look at http://www.mnot.net/python/RSS.py , see if it should
+replace rsspersist.py Maybe something with Atom, like described in
+http://www.xml.com/pub/a/2004/04/14/atomwiki.html Make implicit user logins -- with cookie or by IP address -- by user
+preference. Allow for deleting and moving pages. I'm thinking this would be
+implemented with a new page type, application/x-redirect, and with
+deletes. A rename or move would be a copy, delete, replace with
+redirect. Give untitled links titles by fetching the target and looking for a
+<title>. Maybe part of restructured text parsing, and an
+addition to the WYSIWYG editor, or maybe a post-processing filter. Referrer tracking. Accept trackbacks (as comments?). Many RSS (or whatever format) files. One for each page (just append
+.rss). One for each user? One for site as well. Note the many features on this page:
+http://wikifeatures.wiki.taoriver.net/moin.cgi/IdeasToPlace and
+also:
+http://wikifeatures.wiki.taoriver.net/moin.cgi/FeatureSummaries
+specifically... Fading Links: links change color depending on how often they are
+followed. Rates not just popularity of a page, but popularity of a
+connection. Automatic Translation. Nice because it's easy. Email Address Protection. Or, more generally, pluggable output
+filters. In this case, a little Javascript "encryption". Fine Grained Addressing. This is like Purple Number.
+This could be added fairly easily to text/html content, but
+restructured text will be harder. Event Casting, i.e., some sort of remote event notification.
+Perhaps also trackback pings to all newly created links. Obscure links behind redirects, like described here. But only
+"unapproved" links -- i.e., new links. (Note, we are already
+telling search engines not to index the sandbox pages, which are
+most prone to these abuses.) IP tracking and banning might also go
+with this. Revert+ban (block IP, delete user account) would be
+convenient, but it isn't that bad yet. Boilerplate pages. There's a number of pages (growing) related to
+instructions about the Wiki. When the Wiki starts up, it should
+automatically install any new pages (pages that are listed in the
+boilerplate, but aren't on the site). Put templates, embedded content (e.g., navigation text), CSS into
+Wiki. Locking or edit conflict detection. Links to pages that don't exist would pass the content of the anchor,
+so that we have a decent idea of what the title should look like. Attachments. Archives would be spiffy too -- download the archive,
+or view the archive contents (maybe just a listing). Serve as a
+simple code repository. (Single Python scripts are already
+supported well enough.) Permissions. Restrict editing, creation, commenting based on roles,
+assign roles. Display entire page in RSS when a page is created. If no log
+message on an edit, include some sort of abbreviated diff. Machine Readable Pages. I.e., an XMLRPC interface. Should be
+easy, but I'm not sure how many interesting clients there are. Near Links. This might mean tracking the available pages in
+several wikis, and noting similarities in page names (either
+matches, or near matches, like Like Pages). Some notes/links on wikis and blogs:
+http://www.ourpla.net/cgi-bin/pikie.cgi?WikiWeblogs Anything related to the markup should be put into docutils -- I think
+wiki links are nearly all we need in the way of extensions; everything
+else can be done in docutils directly. (Though perhaps there should
+be a general directive, which simply passes parameters to the Wiki to
+be rendered at display time) Put whatever you want here. Possibility to generate static pages for fast and scalable read-only
+with any server. request granted Breadcrumb links (showing your recent path through the wiki) IB: What's the value over the back button? CG: Well, if you click back then click a link, it erases the forward history,
+so it's handy for that. I don't think I described it right -- it's more of
+a static-length page history with most recently visited pages at the top
+(and when you visit a page in that history, it pushes it back up to the top).
+It lets you do non-linear history traversal. :) It's not really THAT useful,
+but MoinMoin has it, and I find that it comes in handy when you want to go
+back and forth between two wikipages that aren't linked directly to each other.
+Plus, it's prominently visible, so you can quickly jump back 5 pages with ease! Petition the docutils guys to make their formatting warning messages include
+a piece (or the whole) line that generated the error. ;) Javascript function that clears the "click here to search..." field when you
+select it. (IB: it's supposed to do that now; works for me on Mozilla at least) ReST Quick-Reference link on editing page (popup window) Things I don't really have in mind, but maybe would be a good idea: Macros, like in MoinMoin -- would replace custom pages
+(like recent changes) User preferences Transclusion (including the contents of one page in another;
+maybe it would be important with binary pages) Internationalization (I have so little in-code text now, it would be
+both easy and uninteresting) Blog-like top-comment mode Workflow. Cool, but hard. Note Staged Commits. Winston Wolff is founder of Stratolab, which provides after-school computer courses in New York City This page is a repository for WishList and ToDo entries that have
+been completed. -- TavisRudd - 23 Feb 2002 Extension cascading - Added to the cvs in Dec. 2001 When dropping the extensions from filenames in requests the existing
+Webware will raise an exception if there are multiple files with
+different extensions for the requested basename. It should allow you
+to specify a list of extensions to cascade through and use the first
+one found. If you list .html before .py in your list of extensions
+and a request can map to 'file.html' or 'file.py', 'file.html' will be
+returned rather than raising an exception. -- TavisRudd - 31 Oct 2001 This might be represented by a nested list, like ['.tmpl', ['.py', '.psp'], 'html'], which would mean that .py and .psp would
+be at the same level of precedence (and an error would occur if there
+was abiguity)... unfortunately this isn't backward-compatible, but I
+don't think it matters much here. -- IanBicking - 06 Nov 2001 Email interface It would be nice if the AppServer/servlets could respond to email in
+addition to HTTP requests. This would have to be done through
+adapters, because most email configurations are probably more complex
+than HTTP servers (though most situations will use the same very
+simple interface). Typically it would be with a small program that
+gets the entire text of the message dumped to it -- such a program
+would be very easy to write, but I'm not sure how to map that to a
+request (maybe XMLRPCServlet is analagous). I assume the adapter will
+take the target servlet as a command-line argument, as I don't see any
+natural mapping between emails and servlets. The servlet would not
+have any meaninful output. -- IanBicking - 04 Jan 2002 I thought a MailKit might be nice. You could have a Request object
+(similar to HTTPRequest, I suppose) which is fed by some kind of agent
+- it's possible to implement a program which scans a POP3 mailbox, for
+example, even though most mail handling applications seem to ignore
+this entry-level (and very useful) approach. The Response object would
+be passed on to some SMTP agent. -- PaulBoddie - 14 Jan 2002 More on this: one could implement something comparable to WebKit.cgi
+or an adapter like mod_webkit which connects to AppServer and feeds
+requests to them in almost the same way. These new adapters could even
+be part of an SMTP server (there's one available for Python). Perhaps
+there would need to be another connection point for these other
+adapters in AppServer, and applications implementing servlets and
+pages for e-mail would obviously need to be aware of the limitations
+of the medium. -- PaulBoddie - 21 Feb 2002 I've made this. I haven't been able to test it properly due to
+unrelated MTA configuration issues. It's based on WebKit.cgi, and
+connects to the AppServer very similarly to XML-RPC, except it passes
+an RFC822-formatted message (the email) instead of the XML input.
+This is sufficient, except for the fact that it pretends the post data
+is of type text/xml instead of message/rfc822 -- adding a small hook
+to Webware is all that's necessary to fix this. It runs from a pipe
+so, for example, you can set it up by putting something like
+"|/usr/local/Webware/WebKit_/WebKitEmail_.py /path/to/servlet" in your
+.forward file. -- IanBicking - 21 Feb 2002 I had a look to see what implementations of mail protocols in servers
+there are for Python and I found a few interesting packages and
+applications. There's a POP3/SMTP mail server called "shicks" which
+I'd never seen before, although that might be too heavyweight for a
+lightweight Webware interface (and it's GPL'd). What the extra
+component needs to support is SMTP reception and sending (as far as I
+can tell), but WebKit should, unlike with HTTP requests, allow the
+creation of new "mail" requests which can get out to this new
+component. Question: should incoming and outgoing mails be handled by
+a single component? (Of course, one could change WebKit to allow new
+HTTP requests to be created by applications, but this would be for
+client-side connectivity to other sites - another fascinating area to
+experiment with.) -- PaulBoddie - 22 Feb 2002 I feel most email systems will be heterogeneous, similar to web
+servers (mine is). So, an adapter-like interface is easiest for when
+you want to direct just a few emails to Webware (global redirecting is
+still easy). For sending emails, smtplib works well right now. Email
+isn't really a request-response protocol, and I don't think there's
+any reason to make it look like that. -- IanBicking - 22 Feb 2002 This is informal wishlist of things people would like to see improved
+in Webware. It complements the ToDo list. Items that are completed
+or moved onto the ToDo list should be removed from this list (copy
+and paste the text to the WishesGranted page). Please sign and date
+your new wishes and comments on existing wishes. Use horizontal
+rules to separate wishes. This page is for extensions to the Webware core. Autonomous items
+like Kits belong on the AppIdeas page. Contents Note: I've been working on an experimental refactoring
+(WebwareExpRefactoring) of the Webware code that addresses many of
+the issues/wishes listed below. It is not a fork, but is also not
+'official' webware code yet. Help is needed testing it and assessing
+if it is ready for 'official' review by ChuckEsterbrook. Please
+follow see WebwareExpRefactoring for information on how you can help
+out. -- TavisRudd - 14 Jan 2002 These are a few objects/applications we should evaluate someday and see if they are worth porting to Webware. Horde -- (PHP) contains classes for dealing with preferences,
+compression, browser detection, connection tracking, MIME handling,
+and more." IMP -- Horde webmail client. IMAP/POP. -- MikeOrr - 04 Apr 2002 Currently, ExtraPathInfo is a site-wide setting in
+Configs/Application.py. But it should really be set on a per-servlet
+basis. The servlet knows best whether it was designed to use
+ExtraPathInfo, and it's usually only a few servlets per application
+that do. ExtraPathInfo can cause unexpected behavior with servlets
+that are not specifically designed for it; e.g., see ExtraPathInfo. I propose a new HTTPServlet method, .wantExtraPathInfo_(), returning
+false. Servlets or intermediate classes can override it if they want
+to provide this feature. Thus, the first servlet found determines the
+setting. If it's false and there is extra path info, return a 404 Not
+Found error, because we've reached the wrong servlet (perhaps an
+index.py WebKit implicitly inserted into the URL). -- MikeOrr - 03 Jan 2002 It's been a planned feature of Webware for some time, but hasn't
+actually been there: for Webware to use locally-developed subclasses
+of major classes, like Application, HTTPRequest, etc. Not only would
+this help make Webware easier to upgrade -- since any local
+enhancements would be clearly seperated -- but it would also make
+experimentation much easier. Non-core-Webware-developers could easily
+make and distribute enhancements, and even core developers might
+appreciate being able to distrubute controversial enhancements. As these enhancements get wider use, we could see how they work in
+real situations with a greater variety of developers (and styles).
+Some will probably fall by the wayside, but others might show gretter
+than expected popularity. -- IanBicking - 03 Dec 2001 In the meantime:
+On Tuesday 11 December 2001 12:28, Geoffrey Talvola Chuck has written some nifty code to make it easy to replace class
+methods after the fact. It's in MiscUtils/MixIn.py.
+You can put an __init__.py file into your context directory. This
+gets imported when the appserver starts up, and is therefore a
+convenient place to put in code that needs to modify the core
+Webware classes. Something like this should work as the contents
+of your __init__.py: Until Webware has a better way to support custom classes, this is
+probably the cleanest way to get what you want. -- TavisRudd - 11 Dec 2001 self.request().field() (in HTTPRequest.py) currently returns
+either a string or a list. However, it causes major inconvenience.
+If there is only one value for the field, it's a string. If there are
+multiple values, it's a list of strings. This behavior is inherited
+from Python's cgi.py module. However, it's inconvenient for servlet
+writers. It requires the careful programmer to have to check the type
+of _every_ field, even those that should always be one or the other,
+or risk having the servlet crash if unexpected input is received. How about adding a .fieldMulti() method that always returns a list? A
+single value would be coerced to [value], and a missing value to
+[]. This would allow servlet writers to skip the list test. Additionally, how about changing .field() and .fields() to never
+return a list. Instead, choose one of the values arbitrarily and
+return it as a string. This would also allow servlet writers to skip
+the list test, in cases where they want only one value and don't care
+which one it is. If changing .field/s() as above is too backwards-incompatable a
+change, how about leaving .field/s() alone and adding .fieldSingle()
+with this behavior? -- MikeOrr - 30 Nov 2001 Consider proposing improvements in multiple value handling to Andrew
+Kuchling for Python's cgi module. Then other projects can get the
+benefit of such improvements. Of course, Webware will still have its
+own distinct approach for accessing field data, but we would at least
+be able to take advantage of common work done in this area. -- PaulBoddie - 03 Dec 2001 This isn't a substantial change, but rather a cleanup of semantics in
+the interface. IMHO, the interface in cgi doesn't have
+particularly clean semantics anyway, and tacking yet another thing
+onto it won't help, and will just introduce yet another backwards
+compatibility issue. Personally, while I always use a function to make multi-select-fields
+into lists, it's almost always a bug if I get a list when I didn't
+expect it, and I'd rather have it raise an error than throw values
+away. -- IanBicking - 03 Dec 2001 After making 2 enhancements to WebKit, then we will probably be able
+to debug our persistent web apps in a graphic environment like
+WingIDE. Provide a SequentialAppServer that is not multi-threaded. Provide for reloading of any modified modules prior to serving a request. In fact, this will be my highest priority after the 0.6 release, as it
+could faciliate faster development of both Webware and the sites built
+with it. -- ChuckEsterbrook - 03 Nov 2001 This should be fairly straightforward using the
+WebwareExpRefactoring code I've been working on. I've already
+factored all the threaded stuff out of the AppServerClass so all
+that we would need to do is write a simple socket connection
+dispatch class that doesn't use threads. Is this even possible?? Are you thinking of this in terms of a
+non-persistent AppServer that works like OneShot? -- TavisRudd - 05 Nov 2001 UPDATE: I've completed step 1 from above. SessionStore still has a
+worker thread for managing session expiries, but I think it shouldn't
+be a problem. I've done some more looking into step 2 and don't think
+it is possible without restarting the AppServer completely. Modules
+can be reloaded, but anything that has been imported from them will
+not be reloaded. In terms of debugging, a complete restart would make
+sense any way. -- TavisRudd - 23 Nov 2001 N/C
+-- TavisRudd - 31 Oct 2001 Both databases are as close as one can get to Open Source/Proprietary
+twins. They feature multi-versioning concurrency control, which means
+that readers never block writers and writers never block
+readers. Performance under concurrent access to data is far superior
+to databases that use table or row level locking. That both support
+transactions with rollback, triggers, etc. and are fully ACID
+compliant means great safety and an easier sale into corporate IT
+shops. Refer to http://www.zend.com/zend/art/databases.php for example
+benchmarks. -- EdmundLian - 31 Oct 2001 Webware would be more immediately useful if it had out-of-box support
+for simultaneous access to established databases like Oracle, DB2,
+PostgreSQL, etc. using pooled, persistent connections. This would
+allow Webware to be used as a front-end to traditionally designed (as
+in non-object-oriented, non-MiddleKit-style) schema. I know there's a
+patch for this out there, but it does not seem to have made its way
+into the CVS, nor has it solidified into some kind of plug-in or kit. -- EdmundLian - 1 Nov 2001 (MiscUtils.DBPool provides something like this -- IanBicking - 02 Nov
+2001) Sort of... to be truly useful, pooling needs to be part of
+Application.py as proposed by Ken LaLonde here. Some prefer it to be
+a SitePage method, so we need to figure out how to accomodate both
+camps. -- EdmundLian - 05 Nov 2001 Would it be possible to integrate other database pooling systems into
+Webware? Here's one I found on Parnassus! -- PaulBoddie - 08 Nov
+2001 Another system to consider for integration with Webware is SQL
+Relay. From the website: SQL Relay is a persistent database
+connection pooling, proxying and load balancing system for Unix and
+Linux supporting ODBC, Oracle, MySQL, mSQL, PostgreSQL, Sybase, MS SQL
+Server, IBM DB2, Interbase, Lago and SQLite with APIs for C, C++,
+Perl, Perl-DBD, Python, Python-DB, Zope, PHP, Ruby, Ruby-DBD and Java,
+command line clients, a GUI configuration tool and extensive
+documentation. The APIs support advanced database operations such as
+bind variables, multi-row fetches, client side result set caching and
+suspended transactions. It is ideal for speeding up database-driven
+web-based applications, accessing databases from unsupported
+platforms, migrating between databases, distributing access to
+replicated databases and throttling database access. -- DanielClark - 31 Jul 2002 There should be a standard module for doing browser detection.
+There's probably good PHP code to copy for this. It should have
+facilities for adding your own semantic browser-capability tests (for
+instance, you should be able to ask, "can the user's browser do X?"
+instead of just "what browser is it", and then you can add the test
+for what browser does what elsewhere (and grow that definition with
+increased testing) -- IanBicking - 06 Nov 2001 (a more extended discussion of this issue as it arises from the
+WebwareStyleGuidelines. In the meantime, check out CopingWithTabs) (extended ideas on enhancing Webware's URL decoding) It would be nice if Webware could dynamically serve files over FTP,
+analagous to HTTP (i.e., fully dynamic). More powerful with UrlDecoding (since FTP doesn't have GET variables). Medusa
+implements this protocol, so maybe some of the code can be ripped from
+there. -- IanBicking - 06 Nov 2001 Interface should look like FTP, since the interface is fairly
+equivalent. I think FTP is a better (simpler) place to start. --
+IanBicking - 06 Nov 2001 A flexible and seamless framework for handling authentication and
+authorization is essential for all the ideas listed on the AppIdeas
+page. It will also benefit users creating their own applications and
+serve as the foundation for the WebwareUserManager application. See
+UserHandlingProj for more discussion. It would be nice if you could cache servlet output. This should also
+include caching by parameters -- for instance, cache pages that have
+identicle GET variables, or add on session ID, or add on browser type,
+etc. Essentially the cache should be based on two items: the servlet
+URI and a key that would be generated by the Servlet (or
+ServletFactory?) -- that key could include whatever information is
+important. Of course, with customizable times and the potential to expire these
+caches prematurely. This is also a feature request for Cheetah , and for caching of
+parts of a page it would be essential. However, caching the entire
+output should be well suited to Webware itself. If this caching
+mechanism was generalized and exposed, servlets could cache portions
+in an ad hoc manner as well. -- IanBicking - 27 Nov 2001 Webware should compress non-streamed output (and maybe streamed
+output) with gzip when possible (which is at least 95% of the time
+with current browsers). This shouldn't compress already-compressed
+formats, like most image files. This is a large saving for most sites
+where bandwidth is at a greater premium than processing power. The
+implementation of this should be quite easy. -- IanBicking - 27 Nov 2001 This result is easily achieved outside of WebWare by using Apache's mod_deflate
+module. This is very quick to install, Google it to find directions.
+This presumes you're using Python Server Pages / an AppServer.
+-- Kevin J. Rice justanyone - 4 October 2006 My favorite proposal to improve Webware's scalability is: Allow multiple app servers to run on different machines. Each of
+these app servers would be given a unique "instance name" specified
+in a config file. Session IDs would have the instance name encoded
+into the session ID, for example an instance called Instance1 would
+create session IDs such as "Instance1_433048934". Add options for the adapters to distribute requests to multiple
+appserver back-ends. Requests without a session ID would be
+distributed randomly (or in round-robin fashion) to the different
+appservers. Requests with a session ID would be directed to the
+particular appserver instance that originated the session, as
+determined by extracting the instance name from the session ID.
+This would be fairly easy to implement in the Python-based adapters
+-- i.e. the CGI adapter and the mod_python adapter. If the strategy
+proves useful with those adapters, then it could be extended into
+the mod_webkit adapter which would require more work. The advantage of this strategy is that all session data is localized
+to a particular appserver. This is potentially much more scalable
+than having to dedicate a central database server to store session
+data. Now you can have as many front-end web servers as you want,
+load-balanced using any off-the-shelf load balancing solution. And
+you can have as many back-end Webware appservers as you need. -- GeoffTalvola - 03 Dec 2001 UPDATE: In release 0.7 I've implemented a setting SessionPrefix
+which can be used to add a machine-specific prefix to the session ID.
+This can potentially be combined with some mod_rewrite magic to
+achieve load balancing with session affinity in a slightly different
+way than I've described above, but so far it's just an idea; I haven't
+tried it. When I have the time I'll actually test the technique and
+write up a description of how to do it. If anyone's curious to try
+out the technique in the meantime, let me know and I'll try to
+describe what I had in mind. -- GeoffTalvola - 14 Mar 2002 CGI adapters capable of opening and pooling socket connections back to
+the application server should do so. Currently the WebKit.cgi,
+WKCgi.exe, and the ISAPI version open a new socket for each
+connection. In the case where the webserver is outside of the firewall and the
+application server is inside this will give a measurable performance
+increase. -- AaronHeld - 12 Jan 2002 So I read about this WebWare thing. Looks cool. So I download the
+tarball, read the README, run the install.py, and expect magic to
+happen. Hmm, nothing. Okay, so I skim the online docs. I don't see
+it until my second read of the Overview: Most people start with
+WebKit, which is the Webware application server A few clicks later
+and I finally land in the install guide for WebKit. My suggestion is that, coming into this in complete ignorance, you
+might want to provide a RoadMap straight up, front and center. As
+in: install the thing, run the app server, which web server do you
+have? for web server foo, do this, and go! Just a little nag, and maybe it's just me. I like to see the thing go
+before I decide it's worth spending my time delving into docs (which
+admittedly wasn't long, but I'm a fast reader), and as simple as it
+finally was for me to get the thing running, it should be in my face
+from the get-go. -- LesOrchard - 25 Jan 2002 I want to be able to trap when a browser cancels a transaction,
+without having to dig into ThreadedAppServer.TASASStreamOut and
+otherwise hack around :) Basically, I have (potentially) long-running processes that acquire an
+RLock, but I need to release that RLock if the process cancels. Right
+now, I have no way to interrupt that process... the servlet continues
+chugging in the background, even though the browser's stopped
+listening. I've seen the issue addressed in a mailing list posting, but couldn't
+find anything more concrete than Geoff's suggestion about trapping the
+EPIPE exception in TASASStreamOut.flush. -- TrippLilley - 01 Mar 2002 MiddleKit's deletion code currently is very slow in the way that it
+checks for other objects that point to an object being deleted.
+Basically, it pulls in entire tables instead of doing smarter SQL
+SELECTs to bring in only rows that point to the object being deleted. -- GeoffTalvola - 04 Mar 2002 PSP currently only allows you to define a single method in a PSP file
+using PSP syntax. If you need to define additional methods, you have
+to use the psp:method tag which only supports Python syntax. It would
+be nice if you could define multiple methods using PSP syntax. There is a hackish way to do this using Python 2.1's nested scopes,
+but it is a bit limited. See MethodsInPSPSyntax for details. -- GeoffTalvola - 14 Mar 2002 When you install Webware with "python install.py" it automatically
+generates some class hierarchy documentation and some summary
+documentation of all the classes. But it doesn't include enough
+information to be truly useful -- most notably, it doesn't include
+docstrings. We should improve this, perhaps by using pydoc in the
+install script. It was reported on the Webware mailing list that pydoc doesn't
+actually work on Webware's source files for some reason. -- GeoffTalvola - 10 Apr 2002 A serverSidePath_() function that works during servlet construction
+(i.e., in __init__). -- EdmundLian - 12 Sep 2002 Packaging enhancements would be very welcome, especially for those of us who
+would love to see a Debian package. -- ChadWalstrom - 6 Jan 2005 There are numerous advantages to using distutils: users don't need to maintain a local copy of all the modules. the core modules can be used easily by other frameworks it can be packaged for Debian and other OS distros. installs on Windows now make use of the GUI installer. distutils offers many advanced packaging facilities! distutils is the Pythonic way to do installs. -- TavisRudd - 31 Oct 2001 WSGIKit is an implementation of Webware, decomposed into WSGI
+middleware. It currently lives in a subversion repository at
+http://svn.w4py.org/WSGIKit/trunk -- but it will probably be
+moved someplace else in the future, maybe get a real page and
+everything. Discussion about WSGIKit should take place on
+webware-devel@lists.sourceforge.net XMLForms is an experimental work (at least for now) which attempts to
+define a robust framework for the presentation and processing of
+form-based information. It attempts to unify the handling of inputs from HTML forms (amongst
+other things) with the presentation of such forms, and it supports
+hierarchical data structures. The input descriptions, known as models, are written in XML and define the structure of the input expected for a servlet. The output descriptions, known as templates, are based on the input descriptions, but include presentation information amongst the structural elements. Templates do not embed programming language fragments or support arbitrary programming language features. This reduces complexity and encourages other activities... To be sure that users can supply enough information to a servlet to satisfy the model, the template can be compared to the model for that servlet to see if all the model information is being presented. Since the template descriptions are restricted, this activity is easier than for other template systems. Validation/verification is done with the model, and error presentation is supported automatically in the templates. Just define the areas where error messages occur, and include some content for those messages, and they will appear automatically in the output. Each resource or servlet can include templates for different audiences, known as editions - therefore localisation is possible to a certain extent. The author of XMLForms is PaulBoddie and details of where to find it,
+along with other potentially interesting things, can be found from his
+Wiki page. -- PaulBoddie - 01 Nov 2001 XML-RPC: Remote Procedure Calls using XML over HTTP. Webware supports
+these through the XMLRPCServlet. See WebKit/Examples/XMLRPCExample.py
+in the Webware distribution. -- TrippLilley - 11 Nov 2002 Pro: Pythonic. You just do a few extra opens and suddenly your Python
+data structure is persistent and automatically fetches itself from
+disk as necessary, and uncaches itself after a period of non-use.
+The "root object" is a dictionary containing any pickleable objects,
+which can themselves be lists or dictionaries, etc. Supports versioning and transactions. -- Edmund Lian 31 Dec 2001 Seems to be able to support heavy, primarily read-oriented loads
+since many Zope sites rely entirely on it. -- Edmund Lian 31 Dec 2001 Con: No central daemon to manage multiple unrelated connections. ZODB is a modify-by-appending database. Rather than updating
+records in place, ZODB adds a new record and "forgets" the old one.
+This is good for safety, journal record and undoing, but it's bad
+for fields that update frequently, especially counter values (e.g.,
+how many times a web page has been visited). Back up your database frequently. The default store puts everything
+in one file. Python specific. One needs to use Python to query or otherwise
+manipulate ZODB. There are a myriad of ways that one can manipulate
+data in a SQL database. -- CliffordIlkay - 18 Feb 2004 -- MikeOrr - 31 Dec 2001 Zope is an open source application server for building content managements, intranets, portals, and custom applications. The Zope community consists of hundreds of companies and thousands of developers all over the world, working on building the platform and Zope applications. Zope is written in Python, a highly-productive, object-oriented scripting language. look at Zope web page at http://www.zope.org/ ZPTKit is a toolkit for Webware. It builds upon several other pieces of code: Webware. This should be compatible at least to 0.8.1 and probably earlier. This is also compatible with Paste (both paste.webkit and paste.wareweb). Zope Page Templates. Right now there's several implementations of ZPT that have been either reimplemented from the spec or split from Zope. ZPTKit uses the package from http://zpt.sourceforge.net -- in the future it may move to a package derived from the Zope 3 tree. Component. This is a subclass of WebKit.Page, and adds extra features for integrating new features into your servlet (like a template manager). You must subclass your servlets from Component.CPage to use ZPTKit. For more information, consult the ZPTKit homepage.
\n' % line)
-
-if __name__ == '__main__':
- import sys
- if not sys.argv[1:]:
- print "Usage: %s file1 file2" % sys.argv[0]
- print "or to test: %s test" % sys.argv[0]
- elif sys.argv[1] == 'test' and not sys.argv[2:]:
- import doctest
- doctest.testmod()
- else:
- print diffFiles(sys.argv[1], sys.argv[2])
-
diff --git a/lib/import_atom.py b/lib/import_atom.py
deleted file mode 100644
index c11056a..0000000
--- a/lib/import_atom.py
+++ /dev/null
@@ -1,104 +0,0 @@
-import feedparser
-from common import htmlEncode, canonicalName, guessURLName
-from datetime import datetime, timedelta
-import time
-
-def import_atom(wiki, atom_file, logger):
- feed = feedparser.parse(atom_file)
- logger('Importing feed titled %s'
- % (feed.feed.get('link', '(unknown)'), htmlEncode(feed.feed.title)))
- redirects = []
- for entry in feed.entries:
- import_entry(wiki, entry, logger, redirects)
- return redirects
-
-def import_entry(wiki, entry, logger, redirects):
- title = entry.title
- name = guessURLName(title)
- base = ''
- while wiki.exists(name + str(base)):
- if not base:
- base = 1
- else:
- base += 1
- name = name + str(base)
- page = wiki.page(name)
- page.title = title
- if entry.get('modified_parsed'):
- modified_date = datetime(*entry.modified_parsed[:7])
- page.createdDate = modified_date
- id = entry.get('id')
- if id:
- try:
- page.atomID = id
- except ValueError, e:
- logger("Could not set ATOM ID to %r for page %s; error: %s"
- % (id, name, e))
- ## @@: currently summary is not persistent
- # summary = entry.summary
- contents = entry.content
- page.mimeType = str(contents[0]['type'])
- page.text = '\n'.join([c['value'] for c in contents])
- page.pageClass = 'posting'
- page.creationDate = time.mktime(convert_date(entry.issued).timetuple())
- author = entry.get('author_detail')
- if author and author.get('name'):
- author_name = author['name']
- if author.get('name'):
- page.authorName = author.name
- if author.get('email'):
- page.authorEmail = author.email
- if author.get('url') and author.url.lower().strip() != 'http://':
- page.authorURL = author.url
- if author.get('email'):
- author_name = '%s <%s>' % (author_name, author['email'])
- page.lastChangeUser = author_name
- parents = [link for link in entry.links if link.rel == 'parent']
- if parents:
- page.pageClass = 'comment'
- for parent in parents:
- parentPage = wiki.pageByAtomID(parent.href)
- if not parentPage:
- logger('Parent page not found: %r' % parent.href)
- print "Not found: %r" % parent.href
- continue
- page.connections += [(parentPage, 'comment')]
- logger('%s a comment on %s'
- % (title, parentPage.name))
- print "%s -> %s" % (title, parentPage.name)
- page.save()
- logger("Saved page %s as %s"
- % (title, name))
- print "Imported %s" % name
- if entry.has_key('link'):
- redirects.append((entry.link, page.link))
-
-def convert_date(isodate):
- date, time_part = isodate.split('T', 1)
- year, month, day = date.split('-', 2)
- if '-' in time_part:
- pos = time_part.find('-')
- elif '+' in time_part:
- pos = time_part.find('+')
- else:
- pos = -1
- if pos != -1:
- time_part, tz = time_part[:pos], time_part[pos:]
- else:
- tz = ''
- hour, minute, second = time_part.split(':', 2)
- result = datetime(
- int(year), int(month), int(day),
- int(hour), int(minute), int(second))
- if tz:
- plus_minus = tz[0]
- if plus_minus == '-':
- mult = -1
- else:
- mult = 1
- hour_offset, minute_offset = tz[1:].split(':', 1)
- delta = timedelta(hours=mult*int(minute_offset),
- minutes=mult*int(hour_offset))
- result += delta
- return result
-
diff --git a/lib/menubar.py b/lib/menubar.py
deleted file mode 100644
index be531be..0000000
--- a/lib/menubar.py
+++ /dev/null
@@ -1,123 +0,0 @@
-"""
-Functions to generate HTML for the menubar.
-"""
-
-__all__ = ['Literal', 'Separator', 'menubarHTML']
-
-class Literal:
- pass
-
-class Separator:
- pass
-
-class Namespace:
-
- def __init__(self, prefix):
- self.prefix = prefix
- self.id = 1
-
- def get(self):
- this, self.id = self.id, self.id+1
- return '%s_%i' % (self.prefix, this)
-
-def menubarHTML(lst, namespace="menu"):
- """
- Converts a list into the HTML for use with the menubar.js
- code (see also http://www.brainjar.com/dhtml/menubar/)
-
- Each link is a tuple of (title, href). Or, href can itself be a
- list of tuples. So a nested list looks like::
-
- [('File', [('Open', '...'), ...])]
-
- If you want to include a non-menu element (particularly in the
- top-level bar) use a title of Literal (which is a symbol in this
- module).
-
- This returns a tuple of (menubar, subelements). The menubar
- should be put wherever you want it to show up on the page, while
- the subelements should go directly after or just before
- .
-
- Pages that use this should also include ``menubar.js`` and the
- necessary CSS.
- """
-
- menubar = []
- subToRender = {}
- subelements = []
- ns = Namespace(namespace)
-
- menubar.append('')
-
- while subToRender:
- (parentTitle, name), args = subToRender.popitem()
- subelements.append('\n' % parentTitle)
- subelements.append(
- '\n')
-
- return ''.join(menubar), ''.join(subelements)
-
-__test__ = {
- 'simple': r"""
- >>> def t(v):
- ... a, b = menubarHTML(v)
- ... print a.strip(), '\n', b.strip()
- >>> t([('File', 'open.html')])
-
- >>> t([('File', [('Open', 'open.html'), ('Close', 'close.html')])])
-
-
- """,
- }
-
-if __name__ == '__main__':
- import doctest
- doctest.testmod()
-
diff --git a/lib/pooledtemplate.py b/lib/pooledtemplate.py
deleted file mode 100644
index ac63d47..0000000
--- a/lib/pooledtemplate.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-Pooled Cheetah templates
-"""
-
-from Cheetah.Template import Template as CheetahTemplate
-
-class Template(object):
-
- _keywords = ['cacheSize', 'searchList']
- cacheSize = 10
- searchList = []
-
- def __init__(self, *args, **kw):
- for name in self._keywords:
- if kw.has_key(name):
- setattr(self, name, kw[name])
- del kw[name]
- self.args = args
- self.kw = kw
- self.pool = []
-
- def eval(self, **namespace):
- tmpl = None
- try:
- tmpl, tmplNamespace = self.getTemplate()
- tmplNamespace.clear()
- tmplNamespace.update(namespace)
- result = str(tmpl)
- finally:
- if tmpl:
- self.returnTemplate(tmpl, tmplNamespace)
- return result
-
- def getTemplate(self):
- try:
- return self.pool.pop()
- except IndexError:
- return self.newTemplate()
-
- def newTemplate(self):
- namespace = {}
- kw = self.kw.copy()
- kw['searchList'] = self.searchList + [namespace]
- tmpl = CheetahTemplate(*self.args, **kw)
- return tmpl, namespace
-
- def returnTemplate(self, tmpl, namespace):
- if len(self.pool) >= self.cacheSize:
- # Throw it away
- return
- self.pool.append((tmpl, namespace))
diff --git a/lib/propertymeta.py b/lib/propertymeta.py
deleted file mode 100644
index a811f05..0000000
--- a/lib/propertymeta.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""
-propertymeta.py
-Ian Bicking Test
-
-' % (
- self.thumbnailLinkForMimeType(mimeType),
- htmlEncode(self.title))
- else:
- body = htmlEncode(self.title)
- hrefAttrs = ''
- if self.comments:
- hrefAttrs += ' title="%s"' % htmlEncode(self.comments)
- return '%s' % (
- self.link,
- hrefAttrs,
- body)
-
- def _commentText(self):
- if self.comments:
- return '\n
List of Available Settings
+
+find -name \*.py |xargs grep setting\(
+find -name \*.py |xargs perl -ne 'if (/setting\((.*?)\)/) {print "$1\n";}' |sort -u|grep ^\'
+find -name \*.py |xargs grep -c setting\(
+Log Analyzers
+
+
+
+
+
+
+
+LoggingKit
+
+What is it?
+Where can I find it?
+How do I use it?
+
+
+from LoggingKit import Logger
+
+class LogTest:
+ def __init__(self):
+ self.logHandler = Logger.logHandler(self.__class__, 'TestCategory')
+
+ def doSomething(self):
+ self.logHandler.info('The weather seems to be fine')
+ self.logHandler.trace('entered doSomething method')
+
+
+{'LogListener': [{'listenerName': 'InfoFile',
+ 'listenerType': 'LoggingKit.FileListener#DirectToFileListener',
+ 'listenerConfig': {'format': '%(date)s %(time)s - %(level)s - %(category)s %(msg)s\n',
+ 'destination': '/var/log/TestCategoryInfo.log',
+ },
+ 'filter': [{'type': 'category',
+ 'criteria': ['TestCategory',]}]
+ {'type': 'level',
+ 'criteria': ['INFO',]}]
+ },
+ ]
+}
+LoginKit
+
+
+
+Log Messages
+
+
+
+
+
+MakeAppWorkDir
+
+/Webware
+ /WebKit
+ ...
+
+/MyWebSite
+ /Cache
+ /Configs
+ /Contexts - I added this directory
+ /SomeContext
+ /SomeOtherContext
+ /ErrorMsgs
+ /Lib - I added this directory
+ __init__.py - can be empty
+ SitePage.py - SitePage class
+ /Logs
+ /Sessions
+ /Static - I added this directory for static content to be served directly by Apache
+ __init__.py - should contain: __all__ = ['Lib']
+ AppServer.bat
+ Launch.py
+ ...
+from Lib.SitePage import SitePage
+
+
+
+#!/bin/sh
+
+/usr/bin/env python Launch.py ThreadedAppServer $* >> /usr/local/Webware/daemon/log/webkit 2>&1 &
+echo $! > /usr/local/Webware/daemon/webkit.pid
+Managing the URI Space
+
+
+
+Resources
+
+Software
+Methods In PSP Syntax
+
+<%@ page imports="__future__:nested_scopes" %>
+<%@ page imports="new:instancemethod" %>
+
+<%def writeFoo(self):%>
+Foo
+<%end%>
+<%self.writeFoo = instancemethod(writeFoo, self, self.__class__)%>
+
+<p>Before calling self.writeFoo()
+<p><%self.writeFoo()%>
+<p>After calling self.writeFoo()
+MiddleKit
+
+The Object Model Approach
+
+
+
+
+
+
+
+MiddleKit does not force you to use a "funky" schema
+Updating live databases
+Making an application-wide store for your WebKit application
+MiddleKit is still active
+MiddleKit and WebKit
+
+Getting Set up
+In my own projects, I put the store in a module so it
+becomes a singleton. Something along these lines:
+
+Store.py
+-----------------------------------------------
+import os
+from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore
+
+# set user and password, maybe from config file
+
+# Create the store
+store = MySQLObjectStore(user, password)
+
+# Read the model
+dn = os.path.dirname
+_filename = os.path.join(dn(dn(dn(__file__))), 'Core/UsedCars.mkmodel')
+assert os.path.exists(_filename)
+store.readModelFileNamed(_filename)
+-----------------------------------------------
+
+Then in SitePage.py:
+
+from Store import store
+
+class SitePage(Page):
+ def __init__(self):
+ self.store = store
+
+So that my page subclasses can conveniently say "self.store"
+when they need it.
+
+Since module level code is executed only once, regardless of
+the number of imports, you get one store for the whole
+application.
+
+You can use this same "module" technique for any other
+application-wide singletons you like.
+
+
+BTW You now have to consider the implications of using these
+objects from multiple threads. Something you'd have to think
+about even if you weren't using MK.
+
+One alternative to the above approach is to create one store
+per user. That works well if your users don't share objects
+and you take care to remove the reference to the store when
+a user's session expires.
+
+I'm using the first approach, but plan on moving to the
+second at some point in the future.
+
+Another approach might be to put locks around certain
+operations.
+Saving Middle Objects in the session
+
+
+from pickle import Pickler as _Pickler
+from pickle import Unpickler as _Unpickler
+from MiddleKit.Run.MiddleObject import MiddleObject
+_store = None
+
+class Pickler(_Pickler):
+ def persistent_id( self, object ):
+ if isinstance( object, MiddleObject ) and object.isInStore():
+ return object.sqlObjRef()
+ else:
+ return None
+
+class Unpickler( _Unpickler ):
+ def __init__(self,file,store=None):
+ _Unpickler.__init__(self,file)
+ if not store:
+ store = _store
+ assert store
+ self._store = store
+
+ def persistent_load( self, pid ):
+ return self._store.fetchObjRef(long(pid))
+
+ def setStore(store):
+ global _store
+ _store = store
+
+ def dump(object, file, bin = 0):
+ Pickler(file, bin).dump(object)
+
+ def load(file,store=None):
+ return Unpickler(file,store).load()
+Setting it up
+def contextInitialize(application, path):
+ # I have a "Global" module in which I store
+ # a ref to my global store in this module
+ import Global
+
+ # initialize your store
+ from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore
+ Global.store = MySQLObjectStore(user=os.environ['DBUSER'])
+ modeldir = os.path.join( os.environ['APPDIR'], 'Lib/Middle' , os.environ['MIDDLEMODEL'])
+ Global.store.readModelFileNamed(modeldir)
+
+ # now that you have your global store, set the new encoder/decoder
+ import MiddlePickle
+ MiddlePickle.setStore(store)
+ application.sessions().setEncoderDecoder(MiddlePickle.dump,
+ MiddlePickle.load)
+Mike Orr
+
+MIME types
+
+
+
+Misc. Tips
+
+
+
+MiscUtils
+
+Model Two plus One
+
+Introduction
+Design
+
+
+--- ParseEventHandler.py 2002-10-24 15:24:18.000000000 -0700
++++ ParseEventHandler_lholden.py 2003-01-07 20:26:22.000000000 -0800
+@@ -328,11 +328,19 @@
+ self._writer.println()
+
+ if not AwakeCreated:
+ self._writer.println('def awake(self,trans):')
+ self._writer.pushIndent()
+- self._writer.println('self.__class__.__bases__[0]'+'.awake(self, trans)\n')
++## self._writer.println('self.__class__.__bases__[0]'+'.awake(self, trans)\n')
++ self._writer.println('for baseclass in self.__class__.__bases__:')
++ self._writer.pushIndent()
++ self._writer.println('if hasattr(baseclass, "awake"):')
++ self._writer.pushIndent()
++ self._writer.println('baseclass.awake(self, trans)')
++ self._writer.println('break\n')
++ self._writer.popIndent()
++ self._writer.popIndent()
+ ##commented out for new awake version per conversation w/ chuck
+ ## self._writer.println('if "init" in dir(self) and type(self.init) == type(self.__init__):\n')
+ ## self._writer.pushIndent()
+ ## self._writer.println('self.init()\n')
+ ## self._writer.popIndent()
++ public_html/
+|-+ SiteContext/
+| |-+ action/
+| | |- Login.py
+| | |- Logout.py
+| |-+ layout/
+| | |- BaseLayout.py
+| | |- SiteLayout.py
+| |-+ navigation/
+| | |- nav.psp
+| |- login.psp
+| |- Main.psp
+|-+ lib/
+| |-+ Security/
+| | |-+ GeneratedPy/
+| | | |- ...
+| | |-+ GeneratedSQL/
+| | | |- ...
+| | |-+ Security.mkmodel/
+| | | |- Classes.csv
+| | | |- Samples.csv
+| | | |- Settings.config
+| | |- ...
+| | |- SecureMixIn.py
+| | |- User.py
+| | |- UserFactory.py
+| | |- ...
+|-+ WebKit/
+| |-+ Cache/
+| |-+ Configs/
+| |- ...
+
+
+Section from apache.conf to handle WebKit:
+Alias /ext /home/alterself/public_html/ExternalContext
+RewriteRule ^/ext(.*) - [L]
+RewriteRule ^/ExternalContext(.*) /ext$1 [R]
+RewriteRule ^/wk/(.*) /$1 [R]
+RewriteRule ^/(.*) /wk/$1 [L,PT]
+<Location /wk>
+ WKServer localhost 8087
+ SetHandler webkit-handler
+</Location>
+Security classes
+lib/Security
+lib/Security/Security.mkmodel
+Classes.csv
+| *Class* | *Attribute* | *Type* | *isRequired* | *Min* | *Max* | *Extras* |
+| User | | | | | | |
+| | name | string | 1| 1| 100| |
+| | passwd | string | 1| 1| 100| |
+| | roles | list of <nop>UserRoles | 0| | | |
+| | preferences | list of <nop>UserPreferences | 0| | | |
+| Role | | | | | | |
+| | name | string | 1| 1| 100| |
+| | permissions | list of <nop>RolePermissions | 0| | | |
+| | info | string | 0| 1| 255| |
+| Permission | | | | | | |
+| | name | string | 1| 1| 100| |
+| | info | string | 0| 1| 255| |
+| Preference | | | | | | |
+| | name | string | 1| 1| 100| |
+| | info | string | 0| 1| 255| |
+| RolePermissions | | | | | | |
+| | role | Role | 1| | | |
+| | detail | Permission | 1| | | |
+| UserRoles | | | | | | |
+| | user | User | 1| | | |
+| | detail | Role | 1| | | |
+| UserPreferences | | | | | | |
+| | user | User | 1| | | |
+| | detail | Preference | 1| | | |
+| | data | string | 1| 1| 255| |
+Samples.csv
+| *User objects* | |
+| *name* | *password* |
+| guest | guest |
+| root | testpass |
+| | |
+| *Role objects* | |
+| *name* | *info* |
+| guest | |
+| user | Registered user |
+| | |
+| *name* | *info* |
+| registered | user is registered |
+| | |
+| *RolePermissions objects* | |
+| *role* | *detail* |
+| 2| 1|
+| | |
+| *UserRoles objects* | |
+| *user* | *detail* |
+| 2| 2|
+Settings.config
+{
+ 'Package': 'Security',
+ #'SQLLog': { 'File': 'Auth-sql.log' },
+}
+python (PATH-TO-WEBWARE)/MiddleKit/Design/Generate.py Security
+cd lib/Security/GeneratedSQL
+mysql -u root -p < Create.sql
+mysql -u root -p < InsertSamples.sql
+User.py
+# User.py
+from GeneratedPy.GenUser import GenUser
+
+class User(GenUser):
+
+ def __init__(self):
+ GenUser.__init__(self)
+
+
+ def hasPermissions(self, reqPermissionNames):
+ permissionCount = 0
+ reqPermissionCount = len(reqPermissionNames)
+ for role in self.roles():
+ for permission in role.detail().permissions():
+ for reqPermissionName in reqPermissionNames:
+ if reqPermissionName == permission.detail().name():
+ permissionCount += 1
+
+ if reqPermissionCount == permissionCount:
+ return True
+ else:
+ return False
+SecureMixIn.py
+# SecureMixIn.py
+from User import User
+from UserFactory import UserFactory
+
+class SecureMixIn:
+ def preLayer(self):
+ auth = False
+ if self.session().isExpired():
+ # Message for login screen about expired session here
+ pass
+ else:
+ if self.session().hasValue('username'):
+ username = self.session().value('username')
+ userfactory = UserFactory()
+ user = userfactory.userFromName("root")
+
+ if user.hasPermissions(self.reqPermissions()):
+ auth = True
+
+ if not auth:
+ self.forward("/login")
+ pass
+
+ def reqPermissions(self):
+ return ["registered",]
+UserFactory.py
+# UserFactory.py
+from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore
+
+class UserFactory:
+ def __init__(self):
+ self.store = MySQLObjectStore(user='(USER)', passwd='(PASSWD)')
+ self.store.readModelFileNamed('../lib/Security/Security')
+
+ def userFromName(self, username):
+ userResults = self.store.fetchObjectsOfClass('User', clauses="WHERE name = '"+ username +"'")
+ if len(userResults) < 1:
+ return 0
+ else:
+ return userResults[0]
+SiteContext/layout/BaseLayout.py
+# BaseLayout.py
+from WebKit.Page import Page
+
+class BaseLayout(Page):
+ def awake(self, transaction):
+ Page.awake(self, transaction)
+
+ def preLayer(self):
+ pass
+
+ def postLayer(self):
+ pass
+
+ def writeHTML(self):
+ self.preLayer()
+ # There is a space in the body tag because for some reason
+ # without it, TWiki or Mozilla messes up how the Model2+1 page
+ # is rendered. You are welcome to remove this space of course =)
+ self.writeln('''
+ <html>
+ <head>
+ <title>''' + self.title() + '''</title>
+ </head>
+ < body>''')
+ self.writeHTMLBody()
+ self.writeln('''
+ </body>
+ </html>''')
+ self.postLayer()
+
+ def writeScreen(self):
+ """ Write screen contents inside the page"""
+ self.writeln('<h1>No page content defined</h1>')
+
+ def writeHTMLBody(self):
+ self.writeScreen()
+SiteContext/layout/SiteLayout.py
+# SiteLayout.py
+from BaseLayout import *
+
+class SiteLayout(BaseLayout):
+
+ def writeHTMLBody(self):
+ self.includeURL("navigation/nav")
+ self.writeScreen()
+SiteContext/Main.psp
+<%-- Main.psp --%>
+<%@ page method = "writeScreen" %>
+<%@ page imports = "layout:SiteLayout:SiteLayout, Security.SecureMixIn:SecureMixIn" %>
+<%@ page extends = "SecureMixIn,SiteLayout" %>
+<%@ page indentType="braces" %>
+<psp:method name="title">return "Test page"</psp:method>
+
+Great! We are authorized!
+SiteContext/login.psp
+<%-- login.psp --%>
+<%@ page imports = "action.Login:Login" %>
+<%@ page indentType="braces" %>
+<% loginForm = self.request().arg('loginForm', Login.formTmpl()) %>
+
+<% if self.session().hasValue('username'): { %>
+Username already exists: <%= self.session().value('username') %><br><br>
+<% } %>
+
+Please login <br>
+
+<form method="post" action="/action/Login">
+<table>
+ <tr>
+ <td valign="top">username: </td>
+ <td valign="top"><%if loginForm['username']['error']:{%><%=loginForm['username']['error']%><br><%}%>
+ <input type="text" name="username" value="<%= loginForm['username']['data']%>"></td>
+ </tr>
+ <tr>
+ <td valign="top">password: </td>
+ <td valign="top"><%if loginForm['passwd']['error']:{%><%=loginForm['passwd']['error']%><br><%}%>
+ <input type="text" name="passwd" value="<%= loginForm['passwd']['data']%>"></td>
+ </tr>
+</table>
+<input name="_action_login" type="submit" value="Login"><br>
+</form>
+SiteContext/action/Login.py
+
+
+# Login.py
+from WebKit.Page import Page
+from Security.UserFactory import UserFactory
+import string
+
+class Login(Page):
+ """
+ This class handles login requests
+ """
+ def formTmpl():
+ return {'username':{'data':'',
+ 'error':''},
+ 'passwd': {'data':'',
+ 'error':''},
+ 'valid': True}
+ formTmpl=staticmethod(formTmpl)
+
+
+ def actions(self):
+ return ['login',]
+
+ def isValidStr(self, string):
+ return string != ""
+
+ def isValidUsername(self, username):
+ return self.isValidStr(username)
+
+ def isValidPasswd(self, passwd):
+ return self.isValidStr(passwd)
+
+ def login(self):
+ form = self.formTmpl()
+ req = self.request()
+ ses = self.session()
+
+ acceptLogin = False
+
+ # Populate our form dictionary with data
+ if req.hasField('username'):
+ form['username']['data'] = string.strip(req.field('username'))
+ if req.hasField('passwd'):
+ form['passwd']['data'] = string.strip(req.field('passwd'))
+
+ # Validate the contents of our form dictionary
+ if not self.isValidUsername(form['username']['data']):
+ form['valid'] = False
+ form['username']['error'] = "Not a valid username"
+
+ if not self.isValidPasswd(form['passwd']['data']):
+ form['valid'] = False
+ form['passwd']['error'] = "Not a valid password"
+
+ # If the contents is valid, perform login
+ if form['valid']:
+ user = UserFactory().userFromName(form['username']['data'])
+ if user:
+ if user.passwd() == form['passwd']['data']:
+ acceptLogin = True
+ else:
+ form['passwd']['error'] = "Wrong password"
+ else:
+ form['username']['error'] = "Unknown username"
+
+ if acceptLogin:
+ ses.setValue('username', username)
+ self.forward("../test")
+ else:
+ req.setArg('loginForm', form)
+ self.forward("../login")
+SiteContext/action/Logout.py
+# Logout.py
+from WebKit.Page import Page
+
+class Logout(Page):
+ def writeHTML(self):
+ self.session().invalidate()
+ self.forward("../test")
+Conclusion
+Changelog
+Model-View-Controller
+
+Introduction
+Requirements
+Design
+The Basket
+# file basket.py
+import UserList
+import fileinput
+
+NoSuchApple = "No such apple!"
+BasketEmpty = "The basket is empty"
+BasketFull = "The basket is full"
+
+
+class Basket (UserList.UserList):
+
+ def __init__(self, filename):
+ """initialize UserList data structures, set maximum size"""
+ UserList.UserList.__init__(self)
+ self.max_size = 10
+ self.filename = filename
+ self.load()
+
+ def putAppleInside(self, a_type):
+ """add an apple if the basket is not full"""
+ if len(self) == self.max_size:
+ raise BasketFull
+ self.append(a_type)
+
+ def removeApple(self, a_type):
+ """remove an apple of a certain kind, if available"""
+ if not self:
+ raise BasketEmpty
+ if a_type in self:
+ self.remove(a_type)
+ else:
+ raise NoSuchApple
+
+ def empty(self):
+ num = len(self)
+ if not num:
+ raise BasketEmpty
+ self.data = []
+ self.save()
+ return num
+
+ def load(self):
+ """load data from text file."""
+ self.data = []
+ for line in fileinput.input(self.filename):
+ self.append(line[:-1])
+
+ def save(self):
+ """save data to text file"""
+ f = open(self.filename, "w")
+ for line in self:
+ f.write(line + "\n")
+ f.close()
+Testing the Basket
+def testBasket():
+ testfile = "basket.txt"
+ b = Basket(testfile)
+
+ # test putAppleInside
+ b.putAppleInside("jonagold")
+ assert len(b) == 1
+ assert b[0] == "jonagold"
+ b.putAppleInside("granny smith")
+ assert len(b) == 2
+ assert b[0] == "jonagold"
+ assert b[1] == "granny smith"
+
+ # test empty
+ b.empty()
+ assert len(b) == 0
+
+ # lots of tests removed...
+
+if __name__ == "__main__":
+ testBasket()
+How to Execute the Tests
+python basket.py
+The Basket Controller
+# file basket_ctl.py
+import basket
+
+class BasketController:
+
+ def __init__(self, a_basket):
+ self.basket = a_basket
+
+ def awake(self):
+ """reload the basket - necessary if someone else has
+ put apples inside in the meantime"""
+ self.basket.load()
+
+ def addApple(self, name):
+ """add an apple and return a string describing if it
+ was successful"""
+ if not name:
+ return "You did not provide a name"
+ try:
+ self.basket.putAppleInside(name)
+ self.basket.save()
+ return "Added apple"
+ except basket.BasketFull:
+ return "The basket is full!"
+
+ def emptyBasket(self):
+ try:
+ apples = self.basket.empty()
+ return "The basket contained " + \
+ `apples` + " apples. It's empty now."
+ except basket.BasketEmpty:
+ return "The basket is already empty!"
+The Basket Page
+# file BasketPage.py
+from WebKit.Page import Page
+
+import basket
+import basket_ctl
+
+class BasketPage (Page):
+ """Usually, you derive your pages from a customized
+ SitePage. I won't do this here."""
+
+ def __init__(self):
+ """create model and controller objects"""
+ Page.__init__(self)
+ self.basket = basket.Basket("basket.txt")
+ self.ctl = basket_ctl.BasketController(self.basket)
+
+ def awake(self, transaction):
+ """make sure everything is up to date"""
+ Page.awake(self, transaction)
+ self.ctl.awake()
+
+ def writeBody(self):
+ """present the contents of the basket and some
+ means to control it"""
+ self.writeln("<ul>")
+ for item in self.basket:
+ self.writeln("<li>%s</li>" % item)
+ self.writeln("</ul>")
+ self.writeln("<form>")
+ self.writeln('<input type=text name="apple">')
+ self.writeln('<input type=submit name="_action_" value="Add apple">')
+ self.writeln('<input type=submit name="_action_" value="Empty basket">')
+
+ def addApple(self):
+ """extract the kind of apple and forward the request to the
+ controller which tells you what to respond"""
+ req = self.request()
+ self.show_response(self.ctl.addApple(req.field("apple")))
+
+ def emptyBasket(self):
+ """forward the request to empty the basket to the
+ controller"""
+ self.show_response(self.ctl.emptyBasket())
+
+ def show_response(self, response):
+ """a helper method to present the response"""
+ self.writeln(response)
+ self.writeln('<p><a href="BasketPage">Back to overview</a></p>')
+
+ # these are the usual, webware specific methods for
+ # easing the use of forms
+
+ def actions(self):
+ """return the list of all actions that can be called"""
+ return Page.actions(self) + ["addApple", "emptyBasket"]
+
+ def methodNameForAction(self, name):
+ """return the names for the actions"""
+ return {"Add apple": "addApple",
+ "Empty basket": "emptyBasket"}[name]
+mod_rewrite Recipes
+
+Overview
+
+
+
+
+LoadModule rewrite_module modules/mod_rewrite.so
+AddModule mod_rewrite.c
+Recipes
+Hiding the /WK part of an URL when using mod_webkit
+<Location /WK>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+</Location>
+RewriteEngine On
+RewriteRule ^(.*) /WK/LocalSite/$1 [L,PT] ## NOTE: put no space between L and PT!
+RewriteRule ^(.*\.(jpg|gif|png))$ /images/$1 [L,PT]
+<VirtualHost bobsyouruncle.com>
+ ...
+ RewriteEngine On
+ RewriteRule ^(.*)$ /WK/BobsYourUncle/$1 [L,PT]
+</VirtualHost>
+RewriteRule ^.*\.(jpg|gif|png)$ - [L,PT]
+
+...
+DocumentRoot "/usr/local/apache/htdocs"
+
+RewriteEngine On
+RewriteRule (.*) /webkit/ContextName$1 [L,PT]
+
+<Directory />
+...
+
+RewriteEngine On
+RewriteRule ^(.*\.(jpg|jpeg|gif|png)) /images/$1 [L,PT]
+RewriteRule ^(.*) /cgi/OneShot.cgi/MyContext/$1 [L,PT]
+RewriteEngine Off
+
+1 RewriteRule ^/images(.*) - [L]
+2 RewriteRule ^/pix/(images|thumbs)/(.*) - [L]
+3 RewriteRule ^/WW($|/.*) - [L]
+4 RewriteRule ^(.*) /WW/$1 [L,PT]
+<Location /WW>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+</Location>
+<Location /images>
+ SetHandler apache-core-default-handler
+</Location>
+Hiding the /WK using ModPython in a Virtual Host
+/pub
+ /httpd
+ /myweb
+ /mywebcontext
+ /mywebimages
+'Contexts': {'mywebcontext':'/pub/httpd/myweb/mywebcontext',
+ 'default':'mywebcontext'}
+<VirtualHost *>
+ DocumentRoot /pub/httpd/myweb
+ ServerName www.e-myweb.com
+ #
+ # Added For WebWare / WebKit Support
+ #
+ RewriteEngine On
+ RewriteRule ^/wk/(.*) /$1 [R]
+ RewriteRule ^/images/(.*) /mywebimages/$1 [L,PT]
+ RewriteRule ^/(.*) /wk/$1 [L,PT]
+ <Location /wk>
+ SetHandler python-program
+ # add the directory that contains ModPythonAdapter.py
+ PythonPath "sys.path+['/usr/local/Webware/WebKit']"
+ PythonOption AppWorkDir /pub/httpd/myweb
+ PythonHandler ModPythonAdapter
+ PythonDebug On
+ </Location>
+</VirtualHost>
+Making WebKit serve the root
+<Location />
+ WKServer localhost
+ SetHandler webkit-handler
+</Location>
+<Location /_>
+ WKServer localhost
+ SetHandler webkit-handler
+</Location>
+RewriteEngine On
+RewriteRule $^ /_/ [L,NS]
+Mixed serving of static files and dynamic pages
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} -F
+RewriteRule ^(.*) - [L]
+Options -Indexes
+Moduleimportfails
+
+mod_webkit
+
+Multi-language site with gettext
+
+__builtin__.__dict__['LANGUAGES'] = Languages()
+import gettext
+import os
+
+class Languages:
+
+ def __init__(self):
+ self._langs = {}
+ self.load()
+
+ def __getitem__(self, lang):
+ return self._langs[lang]
+
+ def load(self):
+ localeDir = 'yourLocaleDirectory'
+ dirItems = os.listdir(localeDir)
+ for dirItem in dirItems:
+ if os.path.isdir(os.path.join(localeDir, dirItem)):
+ try:
+ self._langs[dirItem] = gettext.translation('yourDomain', localeDir, [dirItem])
+ except Exception, e:
+ print 'Error: Loading language: "%s"' % dirItem
+
+ def gettextFunc(self, lang):
+ if self._langs.has_key(lang):
+ return self._langs[lang].gettext
+ else:
+ return lambda x: x
+
+
+awake(self, transaction):
+ ...
+ self._ = LANGUAGES.gettextFunc('languageCode')
+ ...
+someMethod(self):
+ _ = self._
+_ = lambda x: x
+
+def awake(self, trans):
+ self._ = LANGUAGES.gettextFunc('languageCode')
+ languagecontext.register(self._)
+
+def sleep(self, trans):
+ languagecontext.deregister()
+
+# then in languagecontext:
+import threading
+
+class LanguageContextTracker:
+
+ def __init__(self):
+ self._threads = {}
+
+ def curName(self):
+ return threading.currentThread().getName()
+
+ def register(self, obj):
+ self._threads[self.curName()] = obj
+
+ def deregister(self):
+ try:
+ del self._threads[self.curName()]
+ except KeyError:
+ pass
+
+ def language(self):
+ return self._threads[self.curName()]
+
+TheLanguageContextTracker = LanguageContextTracker()
+register = TheLanguageContextTracker.register
+deregister = TheLanguageContextTracker.deregister
+language = TheLanguageContextTracker.language
+
+
+
+import threading
+import gettext
+
+class LanguageContextTracker:
+
+ def __init__(self):
+ self._threads = {}
+
+ def curName(self):
+ return threading.currentThread().getName()
+
+ def register(self, obj):
+ self._threads[self.curName()] = obj
+
+
+ def deregister(self):
+ try:
+ del self._threads[self.curName()]
+ except KeyError:
+ pass
+
+ def language(self):
+ return self._threads[self.curName()]
+
+TheLanguageContextTracker = LanguageContextTracker()
+register = TheLanguageContextTracker.register
+deregister = TheLanguageContextTracker.deregister
+language = TheLanguageContextTracker.language
+
+_translations = {}
+
+def initialize():
+ "Load translations and put them to a dictionary. ADD ERROR HANDLING HERE"
+ _translations['fi'] = gettext.translation("mydomain","C:/mywebkitproject/locale",["fi"])
+ _translations['en'] = gettext.translation("mydomain","C:/mywebkitproject/locale",["en"])
+
+
+def mygettext(s):
+ "Look up the preferred translation and return the translated string"
+ try:
+ return _translations[language()].lgettext(s)
+ except:
+ return s
+def awake(self,trans):
+ if trans.request().hasField("lang"):
+ trans.session().setValue("lang",trans.request().field("lang"))
+ LangUtil.register(trans.session().value('lang','en'))
+ SiteTemplate.awake(self,trans) # I've got a Cheetah template at the top of my Page-class hierarcy
+
+
+def sleep(self,trans):
+ SiteTemplate.sleep(self,trans)
+ LangUtil.deregister()
+# Initialize translations and have _ point to LangUtil.mygettext
+import LangUtil
+LangUtil.initialize()
+__builtins__.__dict__['_'] = LangUtil.mygettext
+__builtins__.__dict__['__langutil__'] = LangUtil
+__langutil__.register(trans.session().value('lang','en'))
+class LangUtil:
+ def __init__(self):
+ self.data = threading.local()
+
+ def register(self, obj):
+ self.data.lang = obj
+
+ def language(self):
+ return self.data.lang
+Multiple Python Versions
+
+Make an Alternative Version
+Install into a Different Place
+MySQL
+
+General:
+
+
+
+
+Configuration Tips:
+Switching to UTF-8
+[mysqld]
+character-set-server=utf8
+collation-server=utf8_general_ci
+default-character-set=utf8
+skip-character-set-client-handshake
+SHOW VARIABLES LIKE 'char%'
+character_set_client utf8
+character_set_connection utf8
+character_set_database latin1
+character_set_filesystem binary
+character_set_results utf8
+character_set_server utf8
+character_set_system utf8
+character_sets_dir /usr/share/mysql/charsets/
+/etc/init.d/mysql restart
+MySQL Third Party
+
+
+
+
+
+anewtestpage
+
+Newsyslog with SIGHUP
+
+$ APPSERVER/AppServer >> Logs/webware.log 2>&1 command.
+import signal
+
+def setupSignal():
+ def handlerHUP(signum, frame):
+ sys.exit(3)
+
+ signal.signal( signal.SIGHUP, handlerHUP)
+def main(args):
+ global webwarePath, appWorkPath
+
+ setupSignal()
+
+ bla bla ...
+/$APPSERVER/Logs/webware.log webware:webware 644 5 1000 * Z /$APPSERVER/appserverpid.txt
+$ ls -l /$APPSERVER/Logs/webware.log*
+-rw-r--r-- 1 webware webware 75 4 25 10:59 /$APPSERVER/Logs/webware.log
+-rw-r--r-- 1 webware webware 187907 4 25 10:59 /$APPSERVER/Logs/webware.log.0.gz
+Non-Web Interfaces
+
+OneShot
+
+#!/usr/bin/env python
+
+# If the Webware installation is located somewhere else,
+# then set the WebwareDir variable to point to it.
+# For example, WebwareDir = '/Servers/Webware'
+WebwareDir = '/home/jdhildeb/projects/Webware'
+
+# If you used the MakeAppWorkDir.py script to make a separate
+# application working directory, specify it here.
+AppWorkDir = '/home/jdhildeb/projects/mobile/app'
+
+
+try:
+ import os, sys
+
+ # my mod starts here
+
+ file = os.popen("cd %s ; find . -name '*.py' -newer timestamp" % ( AppWorkDir ) , "r" )
+ output = file.readlines()
+ file.close()
+ if len(output) > 0:
+ os.system("cd %s; kill `cat appserverpid.txt` ; touch timestamp; sleep 1 ; ./AppServer &>appserver.log &" % AppWorkDir )
+
+ # my mod ends here
+
+ if WebwareDir:
+ sys.path.insert(1, WebwareDir)
+ else:
+ WebwareDir = os.path.dirname(os.getcwd())
+ webKitDir = os.path.join(WebwareDir, 'WebKit')
+ if AppWorkDir is None:
+ AppWorkDir = webKitDir
+ else:
+ sys.path.insert(1, AppWorkDir)
+
+ import WebKit.CGIAdapter
+ WebKit.CGIAdapter.main(AppWorkDir)
+except:
+ import string, sys, traceback
+ from time import asctime, localtime, time
+
+ sys.stderr.write('[%s] [error] WebKit: Error in adapter\n' % asctime(localtime(time())))
+ sys.stderr.write('Error while executing script\n')
+ traceback.print_exc(file=sys.stderr)
+
+ output = apply(traceback.format_exception, sys.exc_info())
+ output = string.join(output, '')
+ output = string.replace(output, '&', '&')
+ output = string.replace(output, '<', '<')
+ output = string.replace(output, '>', '>')
+ output = string.replace(output, '"', '"')
+ sys.stdout.write('''Content-type: text/html
+ <html><body>
+ <p>ERROR
+ <p><pre>%s</pre>
+ </body></html>\n''' % output)
+Other Wikis Features
+
+Paul Boddie
+
+
+
+Pauljimenez
+
+
+PDF Creation with ReportLab
+
+
+from WebKit.Page import Page
+from reportlab.pdfgen import canvas
+from reportlab.lib.units import cm
+
+class hello(Page):
+ def writeHTML(self):
+ c = canvas.Canvas(None)
+ c.drawString(9*cm, 27*cm, 'Hello, World!')
+ r = c.getpdfdata()
+ self.response().setHeader('Content-Type', 'application/pdf')
+ self.response().setHeader('Content-Length', str(len(r)))
+ self.response().setHeader('Content-Disposition', 'inline; filename="hello.pdf"')
+ self.write(r)
+
+from reportlab.platypus import BaseDocTemplate, SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
+from reportlab.lib.styles import getSampleStyleSheet
+from reportlab.lib.pagesizes import letter
+from reportlab.lib.units import inch
+from reportlab.lib import colors
+from WebKit.Page import Page
+from time import *
+from cStringIO import StringIO
+
+GRID_STYLE = TableStyle(
+ [('GRID', (0,0), (-1,-1), 0.25, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
+ )
+BOX_STYLE = TableStyle(
+ [('BOX', (0,0), (-1,-1), 0.50, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
+ )
+LABELED_GRID_STYLE = TableStyle(
+ [('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
+ ('BOX', (0,0), (-1,-1), 2, colors.black),
+ ('LINEBELOW', (0,0), (-1,0), 2, colors.black),
+ ('LINEAFTER', (0,0), (0,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
+ )
+COLORED_GRID_STYLE = TableStyle(
+ [('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
+ ('BOX', (0,0), (-1,-1), 2, colors.red),
+ ('LINEBELOW', (0,0), (-1,0), 2, colors.black),
+ ('LINEAFTER', (0,0), (0,-1), 2, colors.black),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
+ )
+LIST_STYLE = TableStyle(
+ [('LINEABOVE', (0,0), (-1,0), 2, colors.green),
+ ('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
+ ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green),
+ ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
+ )
+
+PAGE_HEIGHT = letter[1]
+PAGE_WIDTH = letter[0]
+styles = getSampleStyleSheet()
+Address = 'Place address information here.'
+dbDtFormat = '%Y-%m-%d %H:%M:%S'
+theDate = strftime( dbDtFormat, localtime() )
+
+class hello( Page ):
+ def myFirstPage( self, canvas, doc ):
+ canvas.saveState()
+ # Header
+ canvas.setFont( 'Times-Bold', 16 )
+ canvas.drawCentredString( PAGE_WIDTH/2.0, PAGE_HEIGHT-( 0.25*inch ), 'COMPANY NAME' )
+ canvas.line( 0.5*inch, PAGE_HEIGHT-( 0.35*inch ), PAGE_WIDTH-( 0.5*inch ), PAGE_HEIGHT-( 0.35*inch ) )
+ canvas.setFont( 'Times-Bold', 8 )
+ canvas.drawCentredString( PAGE_WIDTH/2.0, PAGE_HEIGHT-( 0.45*inch ), Address )
+ # Footer
+ canvas.setFont( 'Times-Roman', 9 )
+ canvas.drawString( 0.5*inch, 0.75*inch, 'First Page' )
+ canvas.restoreState()
+
+ def myLaterPages( self, canvas, doc ):
+ canvas.saveState()
+ # Header
+ canvas.setFont( 'Times-Roman', 9 )
+ canvas.drawString( 0.5*inch, PAGE_HEIGHT-( 0.5*inch ), 'Page %d' % doc.page )
+ canvas.drawRightString( PAGE_WIDTH-( 0.5*inch ), PAGE_HEIGHT-( 0.5*inch ), theDate )
+ # Footer.
+ canvas.drawString( inch, 0.5*inch, 'Page %d' % ( doc.page ) )
+ canvas.restoreState()
+
+ def writeHTML( self ):
+ response = self.response()
+ request = self.request()
+ trans = self.transaction()
+ app = self.application()
+
+ # Generate the PDF
+ buffer = StringIO()
+ doc = SimpleDocTemplate( buffer, pagesize = letter, leftMargin = 0.5*inch, rightMargin = 0.5*inch, bottomMargin = 1.5*inch )
+ Story = [ Spacer( 1, 0.15*inch ) ]
+ # Add the table
+ colwidths = ( PAGE_WIDTH-( 2*inch ), 1*inch )
+ rowheights = ( 16, 16, 16, 16 )
+ data = (
+ ( 'Line #1:', '$' ),
+ ( 'Line #2:', '$' ),
+ ( 'Line #3:', '$' ),
+ ( 'Line #4:', '$' )
+ )
+ t = Table( data, colwidths, rowheights )
+ t.setStyle( GRID_STYLE )
+ Story.append( t )
+ Story.append( Spacer( 1, 0.15*inch ) )
+ doc.build( Story, onFirstPage=self.myFirstPage, onLaterPages=self.myLaterPages )
+ pdf = buffer.getvalue()
+ buffer.close()
+ # Set the response headers
+ response.setHeader( 'Content-type', 'application/pdf' )
+ response.setHeader( 'Content-length', str( len( pdf ) ) )
+ response.setHeader( 'Content-disposition', 'inline; filename="hello.pdf"' )
+ # Send it back to the user
+ self.write( pdf )
+Postgres OO Features
+
+
+
+
+
+
+
+PostgreSQL
+
+
+
+
+
+
+
+
+
+Python DBAPI and RDBMs
+
+
+
+
+
+Python Issues
+
+
+
+Python Propaganda
+
+Python's pickle and shelve Modules
+
+
+
+
+
+
+class _UserPrefs(PersistMixin):
+ def __init__(self, userID):
+ self._userID = userID
+ self._prefs = self.load({}) # PersistMixin defines .load()
+ def setPref(self, pref, value):
+ self._prefs[pref] = value
+ self.changed() # This tells the Mixin that we need a commit
+ def pref(self, pref, default=None):
+ return self._prefs.get(pref, default)
+
+ ## these next two methods are required by PersistMixin
+ def data(self):
+ ## this is what will get pickled -- it goes with self.load() in __init__
+ return self._prefs
+ def filename(self):
+ ## this is where the data will be pickled to
+ return '/var/userdata/user%i.pickle' % self._userID
+
+UserPrefs = ParamFactory(_UserPrefs)
+## UserPrefs is then the way to get a _UserPref object
+Python Version Conflicts
+
+Problem
+Solution
+import os, sys, string
+
+def changeAll(dir, replaceFirst):
+ for filename in os.listdir(dir):
+ filename = os.path.join(dir, filename)
+ if filename[-3:] == ".py":
+ l = open(filename).readline()
+ if l[:2] == "#!" and string.find(l, "python") != -1:
+ fixup(filename, replaceFirst)
+ if os.path.isdir(filename):
+ changeAll(filename)
+
+def fixup(filename, replaceFirst):
+ f = open(filename)
+ l = f.readline()
+ rest = f.read()
+ f.close()
+ assert l[:2] == "#!"
+ newl = "#!" + replaceFirst
+ os.unlink(filename)
+ f = open(filename, "w")
+ f.write(newl)
+ f.write(rest)
+ f.close()
+ print "updated %s" % filename
+
+if __name__ == "__main__":
+ if not sys.argv[1:]:
+ print "Usage: fixup '/path/to/python' [directories]"
+ sys.exit()
+ replacer = sys.argv[1]
+ rootDirs = sys.argv[2:] or ["."]
+ for dir in rootDir:
+ changeAll(dir)
+#!/usr/bin/python1.5
+$ python2.1 Launch.py ThreadedAppServer
+
+
+export PATH=/usr/local/bin:${PATH}
+
+
+#!/bin/sh
+
+PATH=/usr/local/bin:${PATH} ./AppServer
+
+
+Python vs. PHP
+
+Introduction
+Common Ground
+
+
+Compared as Languages
+What does PHP have that Python doesn't?
+
+
+What does Python have that PHP doesn't?
+
+
+Compared as Web Development Frameworks
+
+
+
+
+
+
+
+
+
+
+
+
+
+Python Application Servers
+Python Template Engines
+Community Support
+
+
+
+
+Commercial Support
+Documentation
+Core Documentation
+Online Tutorials
+Books
+Conclusion
+Frequently Asked Questions
+
+
+
+
+
+
+
+
+General Discussion
+
+
+Python Web Stats
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Real-time Charts
+
+
+JpGraph
+ChartDirector
+Comment
+DISLIN
+Comment
+MayaVi
+Comment
+ReportLab
+Redirect Fails
+
+response.setHeader('Location', 'Home/')
+response.sendRedirect('Home/')
+Redirect Notify
+
+Register
+
+
+
+Reload In Mozilla
+
+Roll Your Own System with a Flat File
+
+
+
+
+
+
+Running Webware as User Webware
+
+
+
+# run as root:
+$LAUNCH >> $LOG 2>&1 &
+# run as a user named 'webware':
+#su -c "$LAUNCH" webware >> $LOG 2>&1 &;
+chown 'webwareuser':'webwaregroup' * -R
+Run WebKit As Apache Root
+
+Making Webkit the Document Root in Apache
+Changes to apache's httpd.conf
+In Section 2: Main
+
+
+<Directory /var/www/BOGUS>
+<Location />
+ WKServer localhost 8086
+ SetHandler webkit-handler
+</Location>
+In Section 3: Virtual Hosts? (OPTIONAL)
+
+
+NameVirtualHost *
+<VirtualHost *>
+ ServerName www.myserver.com
+ <Location />
+ WKServer localhost 8087
+ SetHandler webkit-handler
+ </Location>
+</VirtualHost>
+
+
+<Location /WK>
+WKServer localhost 8086
+SetHandler webkit-handler
+</Location>
+Action py-serverpages /WK/
+AddType py-serverpages .py
+
+
+<VirtualHost *>
+ ServerAdmin webadmin@someplace.com
+ DocumentRoot "/virhost"
+ ServerName virhost.com
+ ErrorLog logs/virhost.com-error_log
+ CustomLog logs/virhost.com-access_log common
+
+ <Location /WK/virhost>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+ </Location>
+
+ Action py-serverpages /WK/virhost/
+ AddType py-serverpages .py
+</VirtualHost>
+...
+ScriptAlias /mywebware /path/to/webkit.cgi
+RewriteEngine On
+# Only rewrite "/index.html", which also rewrites "/"
+# in the default apache configuration:
+RewriteRule ^/index.html /mywebware/ [L,PT]
+...
+Run WebKit Behind Apache
+
+ProxyPass /sub/name/ http://localhost:7000
+http://www.yourdomain.net/sub/name/something
+http://localhost:7000/something
+SAP-DB
+
+
+
+
+
+Serving static files efficiently
+
+RewriteRule ^/@[^/]+/(.+)$ /$1 [L,E=VERSIONED_FILE:1]
+Header add "Expires" "Thu, 10 Nov 2016 23:30:00 GMT" env=VERSIONED_FILE
+Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE
+# Apache serves image data out of /images.
+# Put this in an app-wide config file:
+images_url = '/@0/images'
+...
+self.write('<img src="%s/foo.gif" ... >' % images_url)
+# Let's assume the URL /static contains
+# static file data served by Apache 2.
+static_dir = '/var/www/myapp/static' # filesystem path to /static
+
+def static_url(thing):
+ path = '%s/%s' % (static_dir, thing)
+ modtime = os.path.getmtime(path)
+ return '/@%x/static/%s' % (modtime, thing)
+
+# Use it like so:
+self.write('<script type="text/javascript" src="%s"></script>' % static_url('foo.js'))
+Sessionprefix
+
+
+Setting Permanent Cookies
+
+from WebKit.Cookie import Cookie
+
+class SitePage:
+ # ...
+
+ def setPermanentCookie(self, key, value):
+ cookie = Cookie(key, value)
+ t = time.gmtime(time.time())
+ t = (t[0] + 10,) + t[1:]
+ t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t)
+ cookie.setExpires(t)
+ self.response().addCookie(cookie)
+
+ def deletePermanentCookie(self, key):
+ cookie = Cookie(key, '')
+ t = time.gmtime(time.time())
+ t = (t[0] - 10,) + t[1:]
+ t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t)
+ cookie.setExpires(t)
+ self.response().addCookie(cookie)</pre>
+if not trans.request().hasCookie(yourCookieKey):
+ self.setPermanentCookie(yourCookieKey, yourValue)
+cookie.setMaxAge(0)
+
+
+Simple SOAP Servlet
+
+from WebKit.RPCServlet import RPCServlet
+import SOAPpy
+
+class SOAPServlet(RPCServlet):
+ def __init__(self):
+ RPCServlet.__init__(self)
+ self.encoding = 'UTF-8'
+
+ def respondToPost(self, transaction):
+ rawData = transaction.request().rawInput(rewind=True).read()
+ p, header, body, attrs = SOAPpy.parseSOAPRPC(rawData, 1, 1, 1)
+ methodName, args, kwargs, ns = p._name, p._aslist, p._asdict, p._ns
+
+ result = self.call(methodName, *args())
+
+ result = {"Result": result}
+ resp = SOAPpy.buildSOAP(kw={'%sResponse' % methodName: result}, encoding=self.encoding)
+
+ self.sendOK('text/xml', resp, transaction, self.encoding)
+class soap(SOAPServlet):
+ def exposedMethods(self): return ['hello']
+
+ def hello(self, name):
+ return "Hello %s" % name
+>>> import SOAPpy
+>>> server = SOAPpy.SOAPProxy("http://localhost/soap")
+>>> print server.hello('whitekid')
+Hello whitekid
+>>>
+Singleton Classes
+
+class MySingleton:
+ def __init__(self):
+ # Do something
+ pass
+ def __call__(self):
+ return self
+Site Context
+
+SitePage
+
+from WebKit.Page import Page
+
+class SitePage(Page):
+ pass
+from SitePage import SitePage
+
+class MyPage(SitePage):
+
+ def writeContent(self):
+ self.writeln('Hello')
+def fragment(self, name):
+ filename = '%s/../Fragments/%s.htmlf' % (__file__, name)
+ return open(filename).read()
+from Lib.SitePage import SitePage
+SourceForge
+
+Spaces Not Tabs
+
+Steve Waterbury
+
+Submitting Patches
+
+Subproject
+
+Subprojects
+
+Project Index
+
+
+
+
+
+Starting a Project
+Project Summary
+
+
+Managing a Project
+
+
+Subversion Repository Access
+
+% svn checkout svn://svn.w4py.org/Webware/trunk Webware
+
+% svn checkout http://svn.w4py.org/Webware/trunk Webware
+% svn ls http://svn.w4py.org
+% svn ls http://svn.w4py.org/Webware/tags
+orphaned page
+
+
+Tabs Not Spaces
+
+
+Terrel Shumway
+
+Testexample
+An example query with Many-Many tables
+class Job (SQLObject):
+ ...
+ keywords = RelatedJoin ('Keyword')
+class Keyword(SQLObject):
+ name = StringCol(alternateID=True,notNone=True,length=50)
+ jobs = RelatedJoin('Job')
+ _defaultOrder = 'name'
+
+
+
+
+SELECT ..
+FROM job, job_keyword
+WHERE job.id = job_keyword.job_id and (job_keyword.keyword_id = k1 or
+ job_keyword.keyword_id = k2 or
+ job_keyword.keyword_id = k3)
+sqlstring.append ("job.id = job_keyword.job_id and (%s)"%
+ ' OR '.join(["job_keyword.keyword_id = %d"%Keyword.byName(k).id
+ for k in input_keys]]))
+tables.append ('job_keyword')
+qhold = []
+for i,k in enumerate(input_keys):
+ tabl = 'jk%d'%i
+ qhold.append ("job.id = %s.job_id and %s.keyword_id = %d"%
+ (tabl, tabl, Keyword.byName(k).id))
+ tables.append ('job_keyword AS %s'%tabl)
+q.append ("(%s)"%' AND '.join(qhold))
+Q = ' AND '.join(sqlstring)
+items = Job.select(Q,clauseTables=tables)
+
+
+
+
+Testing Framework
+
+
+
+def suite():
+ suite = unittest.TestSuite_()
+ for test in [TestWorkList,
+ TestPersistentWorkList]:
+ suite.addTest(unittest.makeSuite(test, "test"))
+ return suite
+import file
+import work
+import piece
+
+# etc...
+
+def main():
+ suite = unittest.TestSuite_()
+ for module in [file, work, piece]: # etc
+ suite.addTest(module.suite())
+ unittest.TextTestRunner_(verbosity=0).run(suite)
+
+if __name__ == "__main__":
+ main()
+if __name__ == "__main__":
+ import test
+ test.main()
+curl http://www.python.org
+curl -d "qt=html&submit=Webware" http://search.python.org/query.html
+Test Subject
+
+Overview
+
+
+
+
+LoadModule rewrite_module modules/mod_rewrite.so
+AddModule mod_rewrite.c
+Recipes
+Hiding the /WK part of an URL when using mod_webkit
+<Location /WK>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+</Location>
+RewriteEngine On
+RewriteRule ^(.*) /WK/LocalSite_/$1 [L,PT] ## NOTE: put no space between L and PT!
+RewriteRule ^(.*\.(jpg|gif|png))$ /images/$1 [L,PT]
+<VirtualHost bobsyouruncle.com>
+...
+RewriteEngine On
+RewriteRule ^(.*)$ /WK/BobsYourUncle/$1 [L,PT]
+</VirtualHost>
+</pre>
+RewriteRule ^.*\.(jpg|gif|png)$ - [L,PT]
+...
+DocumentRoot_ "/usr/local/apache/htdocs"
+
+RewriteEngine_ On
+RewriteRule (.*) /webkit/ContextName_$1 [L,PT]
+
+<Directory />
+...
+RewriteEngine_ On
+RewriteRule ^(.*\.(jpg|jpeg|gif|png)) /images/$1 [L,PT]
+RewriteRule ^(.*) /cgi/OneShot_.cgi/MyContext_/$1 [L,PT]
+RewriteEngine_ Off
+1 RewriteRule ^/images(.*) - [L]
+2 RewriteRule ^/pix/(images|thumbs)/(.*) - [L]
+3 RewriteRule ^/WW($|/.*) - [L]
+4 RewriteRule ^(.*) /WW/$1 [L,PT]
+<Location /WW>
+ WKServer localhost 8086
+ SetHandler_ webkit-handler
+</Location>
+<Location /images>
+ SetHandler_ apache-core-default-handler
+</Location>
+Hiding the /WK using ModPython in a Virtual Host
+/pub
+ /httpd
+ /myweb
+ /mywebcontext
+ /mywebimages
+'Contexts': {'mywebcontext':'/pub/httpd/myweb/mywebcontext',
+ 'default':'mywebcontext'}
+<VirtualHost_ *>
+ DocumentRoot_ /pub/httpd/myweb
+ ServerName_ www.e-myweb.com
+ #
+ # Added For WebWare / WebKit_ Support
+ #
+ RewriteEngine_ On
+ RewriteRule ^/wk/(.*) /$1 [R]
+ RewriteRule ^/images/(.*) /mywebimages/$1 [L,PT]
+ RewriteRule ^/(.*) /wk/$1 [L,PT]
+ <Location /wk>
+ SetHandler_ python-program
+ # add the directory that contains ModPythonAdapter_.py
+ PythonPath_ "sys.path+['/usr/local/Webware/WebKit_']"
+ PythonOption_ AppWorkDir_ /pub/httpd/myweb
+ PythonHandler_ ModPythonAdapter_
+ PythonDebug_ On
+ </Location>
+</VirtualHost_>
+Text Editors
+
+UltraEdit
+
+
+
+
+
+
+gvim
+
+
+
+
+
+
+set expandtab
+set ts=4
+set shiftwidth=4
+# vim: shiftwidth=4 tabstop=4 expandtab
+Emacs
+
+
+Kate
+
+
+
+
+
+
+SciTE
+
+
+
+
+
+
+Third-Party Pages
+
+This Wiki
+
+Markup
+`Long wiki name`_
+External Editor
+
). This is highly
+recommended -- client installation is easy, available for multiple
+platforms, and the editing experience is far superior.WYSIWYG Editor
+Other Notes
+Acquiring the Wiki
+
+
+Thread-mode
+
+To-Do
+
+
+
+
+
+
+User Visible Changes
+Transaction Anatomy
+
+Incoming
+The First Connection
+AppServer
+
+
+
+
+
+
+OneShot
+Application
+Creating a Request
+Creating a Transaction
+Creating a Response
+Finding the Servlet file
+http://www.server.com/cgi-bin/OneShot_.cgi/Welcome
+
+
+
+
+Dispatching on the Result of serverSidePath
+
+
+Creating a Servlet
+
+
+Waking the Transaction
+Responding
+Page
+Writing a page
+The Return Path
+Application
+Response
+[('Content-Type', 'text/html'),
+ ('Set-Cookie', 'foo=bar')
+]
+Adapter
+Finished
+
+
+
+Tripplilley
+
+
+Troubleshooting FAQ
+
+
+
+
+Unit Testing Proj
+
+
+Project Summary
+
+
+
+
+Status
+Project Goals
+
+
+
+
+URI Mapping
+
+Motivation
+<?xml version="1.0" ?>
+<urispace xmlns='http://www.w3.org/2000/urispace'
+ xmlns:urispace='http://www.w3.org/2000/urispace'
+ xmlns:conf='http://www.example.org/server-config'
+ xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
+ <conf:docroot>/usr/httpd/html</conf:docroot>
+ <!-- default doc root -->
+ <path match="images">
+ <conf:ttl>3600</conf:ttl>
+ <!-- set a cache-control: max-age -->
+ <conf:auth>basic</conf:auth>
+ <!-- authenticate users -->
+ <conf:authDB>/usr/httpd/users.db</conf:authDB>
+ <scheme match="https">
+ <conf:authenticate>off</conf:authenticate>
+ <!-- unless they're using SSL -->
+ </scheme>
+ </path>
+ <path match="cgi-bin">
+ <!-- cgi directory -->
+ <conf:docroot>/usr/httpd/cgi-bin</conf:docroot>
+ <conf:execCGI />
+ </path>
+ <path match="local">
+ <!-- deny non-local address -->
+ <conf:denyAccess />
+ <conf:match type="clientaddress" value="#allowedList">
+ <!-- point to a container -->
+ <conf:denyAccess urispace:op="clear" />
+ </conf:match>
+ </path>
+ <rdf:Bag ID="allowedList">
+ <!-- define a Bag for access ctrl -->
+ <rdf:li>192.168.1.0/24</rdf:li>
+ <rdf:li>172.16.0.0/16</rdf:li>
+ <rdf:li>10.0.0.0/12</rdf:li>
+ </rdf:Bag>
+</urispace>
+Web.xml
+Requirements
+URL Decoding
+
+
+URL Environmental Variables
+
+
+
+
+
+URL Session ID Security
+
+http://foo.bar/cgi-bin/WebKit_.cgi/_SID_=3678268432/MyServlet
+<IMG src=http://www.attacker.com/whatever>
+Use CSVs
+
+language,author,website
+Python,Guido,http://www.python.org/
+Perl,Larry,http://www.perl.org/
+Java,James,http://java.sun.com/
+
+
+GUIs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Python
+User Handling Proj
+
+Project Summary
+Project Moderator
+Project Members
+
+
+Current Status
+
+
+
+
+Problem
+Solution
+Risks
+
+
+Delivery Target
+Design Notes (in DocumentMode)
+
+
+
+
+
+
+
+
+
+
+Background Reading (in DocumentMode)
+
+
+General Discussion (in ThreadMode)
+
+
+
+
+
+
+
+
+UserKit
+
+UserUtils
+
+Use WebDAV to Edit Source Resources
+
+DavLockDB_ /tmp/DavLock_
+
+<Location /source-dav/>
+# get mod_dav to handle this
+Dav On
+
+AuthType Basic
+AuthName DAV
+AuthUserFile user.passwd
+
+<LimitExcept_ GET HEAD OPTIONS>
+ require user admin
+</LimitExcept_>
+
+</Location>
+Using A-A-P
+
+A-A-P
+
+
+Using A-A-P with Webware
+
+
+
+
+
+
+Compiling Cheetah Templates to Servlets
+TEMPLATES = `glob("../Templates/*.tmpl")`
+
+SERVLETS`` `' '.join([os.path.split(tmpl)[1] for tmpl in var2list(aap_sufreplace('.tmpl', '.py', TEMPLATES))])`
+
+:rule %.py : ../Templates/%.tmpl
+ :sys cheetah compile $source
+ :move ../Templates/$target .
+
+all : $SERVLETS
+Publishing the Application
+FILES = `glob("Lib/*.py")`
+ `glob("Logic/*.py")`
+ `glob("Servlets/*.py")`
+
+compile:
+ :execute Servlets/main.aap
+
+dist: compile
+ :attr {publish = scp://user@distserver/path/to/application/%file%} $FILES
+
+test: compile
+ :attr {publish = scp://user@testserver/path/to/application/%file%} $FILES
+What does A-A-P mean?
+Using Webware Effectively
+
+
+
+<VirtualHost_ *>
+ ServerName www.calrudd.com
+ ServerAlias calrudd.com
+ DocumentRoot "/var/www/www.calrudd.com/public_html"
+ <Location /WK>
+ WKServer localhost 8086
+ SetHandler webkit-handler
+ </Location>
+
+ RewriteEngine on
+ RewriteRule .*/GlobalImages_/(.*)$ /GlobalImages_/$1 [L,PT]
+ RewriteRule .*/spacer.gif$ /GlobalImages_/spacer.gif [L, PT]
+ RewriteRule .*/GlobalStylesheets/(.*)$ /GlobalStylesheets/$1 [L, PT]
+ RewriteRule .*/GlobalJavascript/(.*)$ /GlobalJavascript/$1 [L, PT]
+ RewriteRule .*/ImageStore/(.*)$ /ImageStore/$1 [PT]
+</VirtualHost>
+<img src="/GlobalImages_/xx.jpg" width="XX" height="XX" />
+<img src="${pathToRoot_}GlobalImage_/xx.jpg" width="XX" height="XX" />
+
+
+
+
+
+
+def __init__(self):
+ self._lock = lock = Lock()
+ self._lock_acquire = lock.acquire
+ self._lock_release = lock.release
+
+def doSomething(self):
+ self._lock_acquire()
+ ...
+ self._lock_release()
+
+
+
+
+Verbose Output
+
+from time import time, localtime
+from WebKit.Page import Page
+
+class VerbosePage(Page):
+
+ def awake(self, transaction):
+ Page.awake(self, transaction)
+ request = self.request()
+ requestTime = localtime(request.time())[:6]
+ print '%5d %4d-%02d-%02d %02d:%02d:%02d %s' % (
+ (request.requestID(),) + requestTime + (request.uri(),))
+
+ def sleep(self, transaction):
+ request = self.request()
+ duration = round((time() - request.time())*1000)
+ print '%5d %14.0f msec %s\n' % (
+ request.requestID(), duration, request.uri())
+ Page.sleep(self, transaction)
+Web Commerce
+
+WebDAV
+
+Preliminary Points
+
+
+
+
+Webware Code
+Resources
+WebKit
+
+Web Server Integration
+
+
+ScriptAlias_ /webkit /home/httpd/cgi-bin/WebKit.cgi
+http://localhost/webkit
+http://localhost/webkit/Examples
+
+
+Web Style Guidelines
+
+
diff --git a/webwareandcygwin.html b/webwareandcygwin.html
new file mode 100644
index 0000000..4e20a4b
--- /dev/null
+++ b/webwareandcygwin.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
Webware And Cygwin
+
+python MakeAppWorkDir_.py t:\data\luc\webware\app2
+python MakeAppWorkDir_.py /cygdrive/t/data/luc/webware/app2
+ConfigurationError_: Invalid configuration file,
+/cygdrive/t/data/luc/webware/app2/Configs/Application.config
+
+Exiting AppServer
+T:\data\luc\webware\app2\Configs>bash
+(this is /cygdrive/t/DATA/LUC/HOME/.bashrc running)
+bash-2.05b$ dos2unix *.config
+AppServer_.config: done.
+Application.config: done.
+CGIAdapter.config: done.
+FCGIAdapter.config: done.
+ModPythonAdapter_.config: done.
+OneShotAdapter_.config: done.
+bash-2.05b$
+Webware and FormEncode
+
+
+
+
+
+Webware Benchmarks
+
+
+Webware Bug Tracker
+
+Webware Cart
+
+Implications?
+Webware Cofax
+
+Webware Consultants/Contractors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Webware Redesign
+
+
+Webware Document Management System
+
+Basic Ideas
+
+
+operating system
+
+vendor
+ |---product
+ |---module
+ |---version
+
+information category
+Usage
+
+
+Webware Document Management System MSO1
+
+{article}
+ {system-metadata}
+ {publish}Y{/publish} {!-- Legal values: 'Y', 'N' --}
+ {start}2002-04-15{/start} {!-- Date visible to public, empty=now --}
+ {end}2002-04-15{/end} {!-- Date expires, empty=never --}
+ {/system-metadata}
+ {user-metadata} {!-- Fields specified by siteadmin --}
+ {title}The Adventures of Huckleberry Finn{/title}
+ {author}Mark Twain{/author}
+ {email}abd@def.ghi{/email}
+ {visible-publishdate}2002-04-15{/visible-publish-date}
+ {dateline}Raleigh, NC{/dateline}
+ {teaser}A story about an ornery dude.{/teaser}
+ {/user-metadata}
+ {body} {![CDATA[
+This is my article. In HTML format, wiki format, or whatever is
+acceptable for this site section.
+ ]]} {/body} {!-- Strip leading/trailing whitespace, add final newline. --}
+{/article}
+class MyDisplay_(Display):
+ def __init__(self, article):
+ self.article = article # An Article instance.
+ def html:
+ searchList = [article]
+ t = Template(file=TEMPLATE_FILE, searchList=searchList)
+ html = str(t)
+ return html
+#set $umd = $user_metadata
+### sectionImage is a user method to convert section name to image URL.
+<IMG SRC="$sectionImage($umd.section)">
+<H1>$umd.title</H1>
+$body
+Webware Example vs. Java Servlet Example
+
+import java.io.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+public class HelloWorld_ extends HttpServlet_ {
+
+ public void doGet(HttpServletRequest_ req, HttpServletResponse_ res)
+ throws ServletException_, IOException {
+
+ res.setContentType_("text/html");
+ PrintWriter_ out = res.getWriter();
+
+ out.println("<HTML>");
+ out.println("<HEAD><TITLE>Hello World</TITLE></HEAD>");
+ out.println("<BODY>");
+ out.println("<BIG>Hello World</BIG>");
+ out.println("</BODY></HTML>");
+ }
+}
+from WebKit.Page import Page
+
+class HelloWorld_(Page):
+ def writeHTML(self):
+ self.write('''
+ <HTML>
+ <HEAD><TITLE>Hello World</TITLE></HEAD>
+ <BODY>
+ <BIG>Hello World</BIG>
+ </BODY></HTML>
+ ''')
+Webware Exp. Refactoring
+
+Help Needed
+
+
+
+
+
+
+
+
+Webware Groupware
+
+#from SiteTemplate_ import SiteTemplate_
+#from GroupwareWidgets_ import GroupwareWidgets_
+#extends SiteTemplate_, GroupwareWidgets_
+Surrounding text
+$calendarWidget(trans)
+Surrounding text
+$emailWidget(trans)
+Surrounding text
+$forumWidget(trans)
+<form action="deleteItem" method="POST" name="delForm">
+<input type="hidden" name="itemID">
+</form>
+<script language="JavaScript_"><--
+function delItem(itemID) {
+ document.forms.delForm.itemID.value = itemID;
+ document.forms.delForm.submit();
+}
+//--></script>
+
+to delete: <a href="javascript:delItem(1)">[delete]</a>
+Webware Hosting
+
+
+Webware List Archive
+
+Webware Lists
+
+
+
+Webware for Python
+
+
+News
+2013-01-18 - Webware for Python 1.1.1
+2011-08-14 - DBUtils 1.1
+2011-08-03 - Webware for Python 1.1
+2010-04-18 - Apache connector benchmark
+2009-06-07 - Webware for Python 1.0.2
+2009-02-06 - Webware for Python 1.0.1
+2009-01-03 - WebwareWiki 0.2
+2008-11-29 - Webware and DBUtils 1.0
+2007-07-07 - Webware and DBUtils 0.9.4
+2007-06-29 - LoginKit 0.2
+2007-05-21 - DBUtils 0.9.3
+2007-04-26 - Webware 0.9.3
+2006-09-28 - WebwareWiki 0.1
+2006-09-26 - Component 0.2 and LoginKit 0.1
+2006-09-22 - DBUtils 0.9.2
+2006-09-18 - Webware 0.9.2
+2006-05-08 - DBUtils 0.9.1
+2006-04-23 - Webware 0.9.1
+2005-11-13 - Webware 0.9
+2005-09-13 - DBUtils 0.8.1
+2005-04-09 - WSGIKit Site & Tutorial
+2005-03-18 - ZPTKit 0.1
+2005-03-16 - Component 0.1
+2005-03-14 - Webware switches to Subversion
+2004-11-17 - WSGI Webware renamed to WSGIKit
+2004-10-05 - Webware WSGIKit
+2004-06-19 - Website re-organization in progress
+2004-04-05 - New server and domain
+2003-08-01 - Webware 0.8.1 released
+2003-02-09 - Webware 0.8.0 released
+Webware Patch Manager
+
+
+
+Webware Propaganda
+
+Comparisons
+
+
+
+
+
+
+Webware Recipes
+
+
+
+Webware Security
+
+Securing your server (the machine and OS)
+
+
+Securing your web server (Apache, IIS, etc.)
+
+Securing your installation of Webware
+
+
+Securing your Webware application
+
+
+
+
+
+
+
+
+General notes and links
+
+
+Webware Sourceforge
+
+
+
+Webware User Manager
+
+
+
+
+
+
+
+
+
+
+Webware Vaults
+
+Webware vs. CGI
+
+
+
+
+
+Webware vs. Java Servlets
+
+
+
+
+
+
+
+Webware Wiki
+
+
+
+
+
+
+
+
+
+Webwar EX Management
+
+
+
+Which Operating System
+
+
+
+
+
+
+
+
+
+
+
+
+Whitekid
+
+Who Is Using Webware?
+
+
+
+The list of Webware users
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wiki Bugs
+
+Indirect Wiki Names
+Maybe you should look at this_
+
+.. _this: `another wiki page`_
+Wiki Features
+
+
+
+
+
+
+
+
+
+
+
+
+
+Wiki Markup
+
+Wiki Sandbox
+
+
+
+
+
+
+
+This is a verbatim block.
+
+
+
+
+
+inline experiments
+another head
+howbout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A List
+
+
+Wiki ToDo
+
+Ideas for future Improvement
+
+
+
+
+Requested Features
+
+
+More ideas
+
+
+Winstonwolff
+Winston Wolff
+Wishes Granted
+
+
+
+Wishlist
+
+
+
+Experimental Refactoring
+Miscellaneous
+Move ExtraPathInfo setting into servlets
+Automatic use of local subclasses of major components
+###############################################
+from MiscUtils.MixIn import MixIn
+from WebKit.Session import Session
+
+class SessionMixIn_:
+ def expiring(self):
+ # your implementation goes here
+
+# Now inject the methods from SessionMixIn into Session
+MixIn(Session, SessionMixin)
+###############################################
+Single vs. multiple CGI fields
+Make WebKit WingIDE-friendly
+
+
+
+
+Roll COMKit's functionality directly into the AppServer
+MiddleKit support for PostgreSQL_ and Oracle
+Out-of-box support for pooled, persistent DB connections
+BrowserDetection
+SpacesNotTabs
+UrlDecoding
+FTP Serving
+WebDAV
+SeamlessUserHandling
+OutputCaching
+GzipCompression
+ScalabilityEnhancements
+
+
+Persistant Connections from adapters
+RoadMap
+Add Browser Cancel Hooks
+Faster Deletion in MiddleKit
+Define Multiple Methods in PSP
+Auto-Generate Better Docs With pydoc
+serverSidePath()
+Packaging Enhancements
+distutils Packaging
+
+
+WSGIKit
+
+XMLForms
+
+Executive Summary
+Technical Summary
+
+
+XMLRPC
+
+ZODB
+
+
+
+
+
+Zope
+
+ZPTKit
+
+
+
+