Pagoda Development Blog
07/15/2007 - Ian / Brian
Do have to use that awful template language? - One of the things that bugs us is when a CMS forces you to use a template language that you don't like, and/or forces you to edit your templates on the web through a little text box. Pagoda will use Genshi by default, but you can use any template system supported by the Turbogears / Buffet plugin system. In Pagoda, your templates can be stored on the file-system and edited with your favorite tools, they can be uploaded directly to the server, or you can (if you really prefer it) edit them through-the-web using a nicely-sized text box. We had originally hoped that template edits could somehow be revisioned, by caching the template in the database and only updating the cache when the template is published... but we quickly realized that if a template Included another template, we'd have to keep track of that one too... and so on and so on... until it becomes a rather formidable challenge to really support this feature. Perhaps one day... but not now!
07/01/2007 - Brian / Ian
The World Series - Leave it up to Americans to call something the "world series" and only invite their own teams. Yes, I know that there's a team from Canada that gets invited, but you get the idea. In a sadly similar vein, the first internet engineers put internationalization on the back-burner, so to speak. Go to a japanese or russian or icelandic website, or any website from a country that normally doesn't use the latin character set that we're so familiar with. Look at the URLs for those sites. Notice anything? They're all in english! Or at least, they look like they are, or at least you think you can pronounce them. That's because only certain characters (and they're all from our character set) are allowed in a URL. Any other characters have to be escaped. No matter where you are in the world, if you want to use the internet you're forced to learn the character set of ... not the first or second most common language... but the third most common language in the world (TIME Almanac 2001). Anyways, because of this, and because of the difficulties of implementing this, we have decided that URLs will not be translatable in Pagoda. At some point in the future we may revisit this challenge and decide otherwise. For now, we just need to get a release out!
Workflow Mechanism - This is one of those discussions we keep redoing because it's hard to remember all the ramifications and remember why we chose a certain solution. We've made a module pagoda.workflow which contains integer constants for our workflow states: DRAFT, PENDING, PUBLISHED, DELETED. When you create a new Page (for example), the page gets its status set to DRAFT. Once you edit the Page and submit it for publishing, the status gets set to PENDING. When you go into the control panel and publish the pending changes, the status then gets set to PUBLISHED. You can probably guess what would have to happen for the status to get set to DELETED. One challenge we faced was what should happen when the user 'restores' an earlier version of a Page? Should all the revisions between then and now disappear? Should there be an additional table which has records that point towards the 'active' Page? We found that we can avoid this extra table AND have more humane functionality by doing the following: When the user selects to 'restore' an earlier version of a Page, a new revision is created using the values of the desired revision. The revision comment for this new revision would automatically be set to something like "Page(id=17, url='recipes') restored to version 28 from Sept 20, 2007". The humane aspects to this are that no data mysteriously vanishes, and that 'restoring' a revision is just as undo-able as anything else (since it's its own revision).
Workflow Implementation - After much beard-stroking, we decided that once you delete a content item, let's say you delete a Page, you have to publish that deletion before you can go ahead and create something else (an Event, perhaps) at the same URL. There are two reasons for this. First (and primary) reason is that it will be confusing if we allowed users to create something with the same URL as something else in the content tree. Looking at the content tree the user would see two things with the same name, and it would be confusing and require extra work to figure out which one is the Page pending deletion and which one is the Event pending creation. The second reason is that this requirement allows us to easily determine the correct content type for an item at a particular URL. We just grab the most recent Node that matches the URL, and then grab the matching Revision and look at its content_type.
Content Types - Every CMS has some kind of content type registry. We decided to use entrypoints to handle Pagoda's content types. Plugins can support multiple content types, and all they have to do to register them is modify their setup.py so the setup call includes something like this:
setup(
...
entrypoints = """
[pagoda.content_types]
event = pagoda.plugins.calendar:Event
calendar = pagoda.plugins.calendar:Calendar
ical = pagoda.plugins.calendar:CalendarFeed
""",
...
)
This has an interesting side-effect, which is that multiple plugins can register as handlers for the same content type. The plugins can then be turned on and off in your Pagoda configuration, which (in our example) would allow you to easily switch back and forth between using Event or using SuperEvent. Also we have the option later on of letting multiple plugins do *something* when content items are requested.
06/25/2007 - Ian / Brian
Yes, we're still alive - We haven't slowed down development this past month, in fact we've been working overtime and have neglected this blog a bit. Over the past month we totally refactored our design, added a test framework, came up with a most elegant way to handle multi-lingual revisioned content, wrote a documentation generator, and got our first patch into SQLAlchemy.
If the shoes don't fit, make new shoes - Generally when you start a project you have certain goals in mind and some notions of how to split the code up into segments. Then you actually write the code, discover that your goals are now a little different, and that the pieces you've split the code into aren't fitting together as well as they were when you started. That's a great time to start from scratch, and that's what we did. We have a much simpler design that supports all the same features in less code and more importantly code that's easier to understand and maintain. Try it with one of your projects, you'll find it only takes a couple of days to totally rewrite your project, now that you really know what your project is.
Turning Databases into Gold - Ah the power of SQLAlchemy. There's a huge looming problem that .. uh... looms .. over every CMS out there. Internationalization. The ability to support content in multiple languages. The Drupal project has a good summary of why this is so hard... and they're not even doing revisions! It took us a few weeks of tooling around with SQLAlchemy, but we've come up with a way to do revisioned, multi-lingual content without any unnecessary duplicates stored in the database (the common solution has this problem). So just what is our golden nugget of database design? You'll have to wait until the developer screencast comes out... right now it's too difficult to explain without being able to show.
Documentaton is Key - Does anyone have a good name for a documentation generator? We sewed together bits of epydoc, genshi, pydoc, and pygments to get a really beautiful set of documentation out of our docstrings. We're ready to release this as its own project, it just needs a name. Here's some ideas we've had... "The Doctor" ... "Documenta" ... "Doctation" ... "Doctasia" ... sort of hoping to have "doc" in there somewhere. Post your ideas to the pagoda-talk google group.
05/29/2007 - Brian / Ian
Plugin Interaction - We decided that Attachments and Permissions should be separate plugins that do their thing based on which content is active. I know. I just said "based on which content is active". It's that kind of abstraction that starts to get developers into trouble. We think this one is called-for however. Each plugin that contains a content type will have a sqlalchemy model class for that content type that inherits from 'Content', which will somehow ensure that content instances have globally unique ids. The 'Revision' class will also inherit from 'Content', so content type classes can just inherit from that and get the best of both worlds. Since each content instance will have a globally unique id, plugins like Attachments and Permissions can just see which content is active (i.e. which one's being edited or viewed) and serve up the appropriate information.
05/13/2007 - Ian / Brian
Where's The Updates? - Because the frequency of posts have gone down, you might think we've been slacking off. Actually, we've been working even harder to get an alpha of Pagoda ready for the world. This blog was just meant as a little helper for us to remember why we made certain design decisions. This way when we ask ourselves "wait, why do we have three different get_page methods?" -- we can look up the reasons instead of just trying to remember them. We haven't been making many posts because most of our design decisions are done, and we're finally getting to the fun part where we "flesh out" the code. We want to be able to give a release date, but because we keep running into design decisions here and there we get slowed down and lose days to stuff like..
I just know someone will run into this - We wasted all of today tracking down why the code was working in Ian's linux account but not in Brian's. In the end (after so many wasted hours..) we just deleted Brian's python libraries and easy_install-ed everything back into place. Poof! Problem solved. Some mystical confluence of development versions of this and that must have thrown a wrench into TurboGears.
05/07/2007 - Brian / Ian
YUI ToscaWidget - So we're using ToscaWidgets everywhere we can, to find javascript dependencies and prevent duplication of csslinks and jslinks. One thing we need is a widget to bring in the YUI javascript libraries. There's already something called TGYUI, which is YUI packaged up as a TurboGears widget. We need it as a ToscaWidget, so we're sort of copying-and-pasting from that project and making a twYUI widget that will live in pagoda.widgets One discussion to remember (the reason we write these posts are to remember the rationale behind our decisions) is that Yahoo hosts YUI for us, but should we use their hosting or save the files locally? If we just output links to the YUI on Yahoo's servers, it will save Pagoda users some bandwidth. However, that would eliminate the potential for Pagoda to be used locally, without the internet.
05/01/2007 - Brian / Ian
trac attack - Ok so we finally started using trac like it was meant to be used. We're documenting our discussions in appropriate tickets, using milestones and components and everything. One of the things we picked up from Producing Open Source Software is to try our best to keep all discussion public. While we want to avoid the too-many-chefs problem, we want to be as transparent as possible. To be transparent, we try to document our design decisions on this blog, and now that we're using trac for real we'll be creating tickets for design issues and putting lots of comments on them so it's clear what all went into a decision.
[NOT] Parsing Templates - The system we started designing kept track of templates and what containers were in those templates. Then, when a template was needed to render a page, we would ask all the plugins for contents specific to these containers on the current page. This was messy, because it involved parsing the templates which was messy, and then a bunch of other mess. We decided to switch to a callback-style system, where a render_container function is called from within the templates, that will look something like render_container(id='newsbox', plugin='news', max_items=7). This way we don't have to keep track of which containers are in a template, and we don't have to keep track of which plugin goes with which container. This also puts the burden of most of the template-language-specific code on the template itself, which means the Pagoda code is cleaner. We did end up needing however, a simple set of template engine plugins for Pagoda, which currently only support a safe_xml function, to mark strings as safe so that kid/genshi/whatever doesn't escape the html returned by the plugins. We're calling them 'engines' instead of 'plugins' so nobody confuses these with Pagoda 'plugins'. In the future these engines will also include something to prevent people from inserting code-blocks into their templates, wherein they could put code that would delete other people's sites in the same Pagoda instance.
04/29/2007 - Ian / Brian
Hacker's Plague - Brian and I have hacker's plague. We cannot just design and code something that works and leave it be. Our brains refuse to move on to the next task until the design and the code are wonderfully pythonic, until it will accommodate all the plugins and overrides that we can imagine developers will want, and until it's fast and memory efficient and thread-safe and... you name it. Four days ago we started work on a final touch to the Page model. Every page on a Pagoda site is represented by a Page object. Pages are revisioned, and pages can have parents. Using SQLAlchemy we wanted to add a latest_parent property to all Page objects that would find the latest revision of the parent page... and we wanted some way of generalizing the code to get the latest revision of something, so that plugin developers wouldn't need to write this code for each plugin. We came up with half a dozen working solutions, but either they generated unnecessary nested sql SELECTs (that did more work than necessary), or the solutions weren't general enough to apply to other models. Eventually of course (thank you SQLAlchemy!) we found a good solution that met all the requirements, but it took four days to get there. That's the crux of hacker's plague.
04/25/2007 - Ian
Where to put the Sites? - So we envision three kinds of people using Pagoda. You've got:
- Your app superstar who wrote a great app but needs to put up a couple pages and so tosses PagodaCMS into the mix. This person probably wants to use his/her existing templates and images/css/js/etc...
- Your web designer who wants to host 10 sites in a Pagoda instance. This person probably wants to use site-specific templates and static files for each site.
- Your hosting provider who wants multiple sites to all use the same templates and static files.
So we decided that by default pagoda will assume that you are person 1, and that you're looking to host just one site. It will use the existing static and templates directories, and the default database. Now, if you go ahead and use tg-admin pagoda site create to create another site, you'll be asked for a site name, a domain glob (ex: "*.pagodacms.*"), and a dburi (defaulting to whatever's in your project's config). You'll also be asked if you want to use your app's existing templates and static directories, in which case an additional 'attachments' directory will be created in your app root to store attachments. If you don't want to use your existing stuff, a sites directory will be created in your app (if it's not there already), containing a directory with the same name as you selected for the site name, which in turn will have a templates dir, a static dir, etc. Also the sites directory will get a sites.cfg file to hold the dburi and domain glob for each of the sites that you've added. This will allow us to upgrade in the future to all the high-end ideas we are holding secret for now.
Plugin Controllers - We decided that plugins will have urls like /admin/plugins/events/edit/17 (ex: for using the 'events' plugin to 'edit' event 17). When testing plugins developers will use those urls. For the main Pagoda 'edit' interface, an iframe will hold the active plugin controller. Javascript in the 'edit' page will re-size the iframe to fit the contents. For every plugin controller, the resulting html will be run through a genshi "match template" and links will be changed to have a target=_parent attribute. That way the 'edit' page will get refreshed when links in the plugin's controllers are clicked. But what about when a plugin controller has some fancy schmancy javascript that changes location.href or plays with the page history or something? Now we have to get devious. We're going to have some javascript in the 'edit' page set an onload handler on the iframe so that when the iframe changes pagse, it checks to make sure the parent url matches the iframe's url (minus the /plugins part of course), and if it doesn't match then it will redirect the parent to the right place. There will be some flicker as the iframe loads and then the main page reloads, but this is a rare case (good code doesn't often change location.href) and we feel it's an acceptable trade-off.
04/20/2007 - Ian / Brian
Dragons Fly Home - When you've been tilting at windmills for a week, it's nice to find something easier to tilt at. While we had imagined that from pagoda.site import models would import a site-specific model, it turns out that it's much easier to just write a run_in_site decorator and apply that to the PagodaController's default controllers. After reading through nearly all of the SQLAlchemy code and definitely all the TurboGears database code, we found that all we really had to do in that decorator was write two mystical lines of code to be able to switch out the database for the current thread. We'll be explaining those mystical lines of code (and all our other code!) once we get the demo up and running and we have a blog running under Pagoda.
04/16/2007 - Brian / Ian
Up the Dragons! - Still working on the 4/13/2007 Dragons issue, been working on it and are still working on it. At first we were overwriting getattr in models/site.py so that requesting models.site.pages.Page would point the caller towards a Page in the currently requested site (like cherrypy.request always magically points to the current request)... but we ran into some limitations there, and the import syntax was ugly and not general enough. We looked at ihooks, imputils, and overwriting __import__, before noticing Python PEP 302 - Import Hooks. We wrote a "meta hook" and put it in pagoda/site/__init__.py so that if you import pagoda.site.models.something.SomeThing it will get the thing from the appropriate site. All import methods seem to be working, including the imp module. Now we just have to test to make sure this thing is working from all angles.
Deployment - We've been envisioning two general ways that people will be able to use PagodaCMS. The first is a all-in-one graphical installer that installs Pagoda and TurboGears and a sample site that starts up right away, no editing config files or anything... just like the Plone installer works. We want to let people play with Pagoda without having to know anything about it in order to install it. The other use-case is of course we want people to be able to easy_install Pagoda, change the root controller in their TG app to subclass PagodaController, and use tg-admin pagoda create site or something like that to create their Pagoda sites. Implementation details coming soon...
04/13/2007 - Ian / Brian
Caution, There be Dragons Here - One big challenge is how to get multiple databases (one per site) to play nicely in TurboGears. Noticing how CherryPy does a neat little trick so that cherrypy.request always points towards the current thread's request, we are planning on making a clever little bit of code to live at pagoda.models.sites that will do some magic to maintain a session/engine/metadata for each site and so when you try to access (for instance) pagoda.models.sites.Page, you get a version of the Page module that's linked to the appropriate session/engine/metadata. We'll be using the amazing "imp" module, and possibly the "new" module. Both are available in python 2.3, so we still haven't done anything that locks us into 2.4 or 2.5. We are still aiming for Pagoda to work under 2.3
The Big Picture - Each site will have its own database to store its own usernames and passwords and the like. Thus, there's no need to use the controlling app's identity for anything. The hyper-admin (the person with shell access) will edit Project/project/pagoda/instance.cfg and input information about sites like so:
[[sites]] [[[TeslaMuseum]]] domains = "www.teslamuseum.*:80 www.teslathemagnificent.*:80" dburi = "sqlite:///teslamuseum.sqlite" [[[TeslaStore]]] domains = "store.teslamuseum.com:443" dburi = "sqlite:///teslastore.sqlite"
Next the hyper-admin will use tg-admin pagoda create-admin site="TeslaMuseum" (and again with TeslaStore) to create admin users for the sites.
04/11/2007 - Brain / Ian
Identity - Revisited the 4/04/2007 discussion on identity. TurboGears will be totally changing the current identity framework and splitting it up into separate "authorization" and "authentication" components for TurboGears 2.0. Because of this, it's likely that User.adduser() will change to something completely different. Sooo... we are thinking we'll put in a small abstraction layer so that we can handle whatever future the TurboGears identity stuff might have. Ian's going to make a pagoda.identity module that has functions like add_user(username, realname, password), add_group(name), add_user_to_group(user, group), add_permission(name), grant_permission(username, groupname, permissionname), and so on. When the TurboGears identity changes, we'll be able to put a compatibility layer into this module and if we've done it right then we won't have to change any other code.
Containers! - The big thing in pagoda is Containers. You put them in your templates and they look like this::
<div pagoda:container="news" pagoda:plugin="rss">
Example News Content
</div>
So this would tell the rss plugin to display a newsfeed. But where does the feed come from? Which categories? How far back? How many entries? We want to support those options like this:
<div pagoda:container="news" pagoda:plugin="rss"
pagoda:rss-feed="http://www.example.com/rss"
pagoda:rss-update-time="1 hour"
pagoda:rss-max-items="10">
Doesn't Really Matter What I Put Here
</div>
So what if a certain page wants to do it differently, like maybe this is a site-wide rss feed but the homepage should only display 3 items instead of 10? Each page in edit mode will have an icon provided by the container's plugin that pops up an overlay with a form to let users override the options from the template. These options get saved in the database.
Multiple Sites, One Instance - Brian and Ian had some golden-nugget insights today. One of our design goals was to support multiple sites inside one TurboGears/Pagoda instance. To do this, we originally had thought that we would have one database holding all of the sites, but to make it easier to do backups and ensure security we decided to require multiple databases. Right now we're not using anything that sqlite doesn't support, so hopefully requiring multiple databases won't present a big obstacle for anyone. As for the per-site templates, config, and static files, we have to store those somewhere... it's a fair requirement to say that the TurboGears process will need permissions to write to the controlling app's directories, but it wouldn't be fair to require write access to the python lib dir where pagoda lives... so we decided that if you have a controlling app called TestProject with a python project called testproject inside, when pagoda starts up it will make sure there's a TestProject/testproject/pagoda directory. Inside there, we'll make a sites directory that will look something like this (assuming two sites have been configured)...
- pagoda
- sites
- teslamuseum
- static
- templates
- config
- veganvictories
- static
- templates
- config
- teslamuseum
- sites
This makes it easy to backup sites individually and to set file permissions so that if each site owner gets shell access they won't be able to mess with other sites.
Templates - When pagoda starts up we'll want to scan the templates directories for all those sites and and save a list of the available templates, so that when users add pages they can select which template to use for that page. There's multiple difficulties in this. First, how do we figure out which files in the templates directory are templates? Certainly .pyc files (compiled kid files) are NOT templates. The __init__.py file is likewise NOT a template. Also there might be a README file sitting in there, who knows! To solve this problem we decided to attempt to render every file (except *.py and *.pyc files) in the templates directory using the controlling app's default template engine and then scan the resulting mess with ElementTree to locate the containers. We can do this without passing in anything from the database because these templates won't be expecting any context variables, they just get filled in magically by Pagoda at page generation time. All templates that don't throw an exception during this step will be displayed in a "Templates" section of the control panel, where users can give templates a common name and specify which ones should be options when adding a new page.
4/10/2007 - Ian / Brian
Using Multiple Databases - We've been designing PagodaCMS so that one instance can host multiple websites. At some point in the future we'll probably want to let the "hyperadmin" (the person who can add/delete sites and install plugins) specify that a certain site should be in a database of its own. There was talk of this on the TurboGears mailing list, which looks like the way we'll want to do it.
To Widget, or not to Widget - Brian has been considering making ToscaWidgets to hold the YUI and Ext javascript libraries... but it's difficult to try to generalize it. The idea with those libraries is that you can make <script> tags to import just the pieces that your web app will use, and save on download time, so somehow a YUI or Ext widget would have to allow for that... but how to do it? It can be done in ToscaWidgets, but it takes some time to figure out the correct pythonic way to do it. Another part of the discussion is that widgets are something you make to reuse, and since we're only going to use them on the admin section, why make it into a widget? For the time being, we decided to just put YUI and Ext into the Pagoda static directory, and just put some <script> tags in the template to import the parts we need.
Unit-Testing Widgets - There's a great thread on the TurboGears mailing list about unit-testing widgets, which we may want to do at some point for the ToscaWidgets we've been developing (I'm thinking of the PrettyPicker and twHumaneTinyMCE widgets at the moment).
4/09/2007 - Brian / Ian
The MAGIC of %(top_level_dir)s - This was one of those times that you spend 4 hours completely befuddled as to why something isn't working. We were using turbogears.config.update_config() to grab some pagoda config files, which it turns out was changing the value of %(top_level_dir)s as seen by the controlling app. I ended up writing my own version that let's us specify more defaults than just %(top_level_dir)s, so we can have %(pagoda.top_level_dir)s and %(pagoda.static_dir) or whatever we'd like. This new update_config() is in pagoda.utils, along with a LRU Cache Decorator and a function that lets you register static directories.
Interfaces - So python doesn't have Interfaces. This much we know. So how have people been making plugin systems? There seems to be three general approaches. The first (let's call it the "BaseClass" approach) is to just have a base class for a plugin called BasePlugin or something like that. BasePlugin would have definitions for all the methods and members that a plugin can have. This is easy enough to code, but it masks whether a particular plugin actually implements a particular method or not, and often that's vital information to have. The second approach (let's call it the "Faked Interface" approach) is the one taken by trac and some other rather great software. Basically the first line of code in a class is a line like implements(IAdminPageProvider), which remembers this particular class as something that extends a particular interface. It lets us ask "which classes implement a particular interface?" This is an important question to ask, but it can be answered just as easily using entrypoints in setuptools. Also this kind of practice doesn't seem very pythonic to me personally. The third approach (how about "Minimalistic" approach) is to write documentation on a plugin's interface, and that's as far as it goes. Any class loaded as a plugin will be queried for tons of stuff, for example panels in the control panel or the name of the static_dir for a particular plugin. The "query" mechanism will just be hasattr(plugin, "method_we_are_looking_for").
URLs in Admin Pages - Right now any of your controller classes can subclass PagodaController (instead of RootController) and you'll magically get a content management system with yourcontroller/admin as the path. Of course this mount point can be overridden in the config files. If you get into the admin section and click on a page to edit it, the url will now be something like yourcontroller/admin/pages/edit/12. The details on how this will be handled still need to be sketched out. Should code go in the PagodaController or in the plugin? Plugins definitely need to have controllers, for handling "edit page"-type validation of form submission... but also something has to query all plugins for things to put in the various slots.
4/04/2007 - Ian / Brian / Chris
Versioned Plugins/Widgets - Ian moved stuff around so Plugins and Widgets now have trunk/tags/branches
Template Parsing - We'll need to be able to parse a template file and retrieve any Pagoda containers in it. Chris wrote two versions of some code to do this, one using Genshi and one using ElementTree. He liked the Genshi version better because it was more low-level, but decided to use the ElementTree version because it ran faster and was easier to use. Also python 2.5 will come with a version of ElementTree written in c so we'll get a speed boost there.
Identity Models - After a pretty short discussion, Brian and Ian reached consensus that Pagoda will try to use the controlling app's identity models if available, and otherwise will use a default identity model that will come with pagoda. Ian coded this into the PagodaAdminController.
Admin Interface - Brian wrestled with Genshi inheritance while working on the Admin Interface.
Testing - Chris integrated the nose tests from 3/28/2007 into the pagoda code.
4/03/2007 - Brian / Ian
Table Creation - At some point we need to create the pages/sites/etc tables in the database. tg-admin sql create won't create the pages, because it has no knowledge of the Pagoda model. We *could* tell people to edit their model.py file and put from pagoda.model import * or something, AND tell people to add the db_module directive to their egg-info/sqlobject.txt file (or rather, whatever the sqlalchemy equivalent is)... but that's so much work to ask people to do! We ideally want them to easy_install Pagoda, add a couple lines to their project's controller.py file, and be off and running with their content management system. On the other hand, it would be nice if people had the ability to drop their site and start from scratch using a tg-admin command... Then again, sqlalchemy has some bug and so tg-admin sql drop doesn't work yet with sqlalchemy. SO... THEREFORE... our solution is to automatically create tables in the PagodaController's init() method... AND to make a custom tg-admin pagoda sql create (and drop) command that will create (or drop) the Pagoda tables on demand.
Control Panel - Ian and Brian flushed out the beginnings of the code for the Control Panel. Work in progress is in the PagodaAdminController class in controllers/admin.py
3/28/2007 - Chris / Brian / Ian
Nose Tests - Chris and Ian worked to get nosetests working with sqlalchemy. They used the recipe from http://paste.turbogears.org/paste/949 and http://paste.turbogears.org/paste/950 -- but the recipe needed a "session.clear()" at the end of the tearDown method.
Plugin Framework - Ian looked at several ways to do a plugin system and thinks we should use setuptools as the plugin system. This is the way that TurboGears does it's widgets for instance, so you can just do "easy_install Lightbox" and the Lightbox widget will automatically appear in your Toolbox.
Calendar - Brian (of the Inkscape Illuminati) made some mockups of what the calendar plugin should look like.
3/22/2007 - Ian / Brian / Chris
Dynamic Content - This meeting was entirely about how to integrate plugins into our "Admin" page (currently in mockups as 'edit.html'). Ian originally thought that containers for dynamic content could have two icons above them: a wrench icon that lets you configure which *things* to display in the container, and then another icon that opens up some huge popups that let you add/remove/manage those *things*. All decided that this was too much to cram in a popup. Ian's other idea was to add a drop-down to the "Edit" tab (like we have for the "Control Panel" tab), and then from the drop-down you could select "Pages" or "Events" or "Photos" or whatever else the plugins allowed for. All thought this was fine, although it added another click in the process, which we are trying to minimize. Brian suggested adding more boxes underneath the PageTree to be able to manage these other things, and Ian amended that by saying that to preserve vertical space the boxes should be partially collapsable. If in the future we have just soo many plugins in regular use that people are having to scroll down all the time, we'll change over to Ian's idea of the drop-down from the "Edit" tab.
3/21/2007 - Brian / Ian
Caching - When the Page Dispatcher is called, we don't want to do a bunch of calls to the database, so we want to have a @cache decorator somewhere. But where? We could put it in the Page Dispatcher (default method of PagodaController) or in the model. Brian suggests putting it in the PageDispatcher, Ian has no argument against that.
NavOrder - When we're re-ordering child pages, it would be nice if we didn't have to update ALL of the child pages with new nav_orders. Ian originally suggested having nav_orders that were floating-point decimal numbers, so we could always move something between any two children and just find a nav_order value that's between those. This seemed genius, but then Brian came up with an even better idea... and now we can't remember what that was. We had talked about doing children as linked lists or something, but for some reason we decided not to take that route. We had also talked about just sticking a comma-separated list of child IDs in the parent somewhere, which would be easy enough to update... and decided not to do that either because then there's TWO places where we have parent/child info, and it's more work to update both any time we need to move a child to another parent.. In the end Brian and Ian decided to try the "float thing" again. Update: Brian remembered the super big reason why we didn't use the comma-separated list of child IDs. It's because if you re-ordered some child, the only changes would be the one happening in the comma-separated list in the parent, so the parent would get a new revision, instead of the child getting the new revision which is what makes the most sense.
THREE DIFFERENT GET_PAGE METHODS! - One might wonder why Brian has "get_child_latest", "get_child_active", and "get_child_revision" all in the model, when they seem like just slightly different versions of each other. Couldn't he just make one called "get_child" that assumes 'active' unless a revision="latest" or revision=<some-number> argument is passed. Well, wonder no more! First, he just likes the code better. Second, we can just add a convenience function later if we want to have that simplicity, but we can't go from just one method to three at a later date if we wanted to maintain backwards compatability. So we can always add that method later, if we decide to.
Plugins - Ian started thinking about plugins. His initial thought was that calendars/photo-galleries/etc would be separate TG apps, and the plugins would be a way you could *display* events/photos/etc in a container. This is nicely extensible, but then you have to go to all sorts of different url's to manage your site. Not to mention TG 1.0 doesn't have a real way for multiple apps to coexist. Plus, Pagoda already has all this great translation and revisioning code, it would be a shame not to use it.
