Introduction: Just What is Molly?

HTML, XML, XHTML & MAML

When Tim Berners-Lee created HTML (HyperText Markup Language) in the early 1990s he based it on the concept of tag markup used by SGML (Standard Generalized Markup Language) and so we all became familiar with the practice of structuring (and formatting in the pre-CSS days) text with tags like <h1> and <p>. HTML was rather loosely defined and the various browsers' developers each created their own versions of it and added their own tags, so HTML evolved, though somewhat haphazardly. Before long two things became clear; one, that HTML was inconsistent and difficult for software to parse and interpret correctly, and two, it was neither extensible, nor sufficient for all of the tasks web developers wished to do. To address these two deficiencies the W3C (World Wide Web Consortium), headed by Berners-Lee, created XML and XHTML. XML stands for eXtensible Markup Language. XML is basically a set of rules for creating markup languages that use tags like HTML. By following these rules any software developer can create a markup language to serve whatever purposes they might have. XHTML is a reformulation of HTML that follows the rules of XML. This slightly stricter version of HTML has supplanted HTML as of version 4.01, and provides web developers several benefits including a cleaner separation of structure and presentation than HTML and the ability to be used and parsed together with other XML-based markup languages.

Molly uses an XML-based markup language named MAML (Molly Active Markup Language) mixed together with XHTML to allow web site developers to easily add sophisticated server-side functionality to their sites without having to learn complex programming languages like PHP, Perl, Java, ASP, or .NET. Instead, web developers use the XHTML tags they already know together with new MAML tags that Molly uses to generate more XHTML dynamically based on things such as database lookups which would normally require a fair amount of custom programming.

History of Molly

In the mid 1990s one of us (Vullo) began building web-based applications for dental education. These included online learning and electronic dental records. Even then, it was clear that web sites needed pages which were dynamically generated from information stored in databases. At the time the database he used was Apple's FileMaker Pro and the tool for embedding that information into a web page was Blue World's Lasso.

Several years later Vullo was called upon to create a new distance education and medical record system. This was to be a more ambitious project and after prototyping with Lasso it became clear that a more robust and flexible system would be required. Fortune smiled and PHP 4 was released about this time and with its maturity was rapidly developing a reputation as an outstanding server-side web development language. PHP also supported access to multiple database systems including the then up-and-coming MySQL.

While the typical PHP approach to building web applications at the time was to simply build HTML pages and embed snippets of stand-alone PHP code wherever dynamic content was required, Vullo decided to take a different approach. He recognized that such a development style quickly becomes difficult to support and scales poorly with large sites. Instead he decided to build an organized code library, encapsulating oft-needed functionality into generic functions. Then, instead of embedding chunks of code into a web page, a single line function call would be used instead, much more like an HTML tag than an embedded program. Thus was born (the as-of-yet unnamed) Molly version 1. Soon after this one of us (Hoey) gave birth to a daughter and named her Molly. Vullo named the software after Molly as a gift to her mother.

Soon thereafter the W3C released XML and XHTML and it became clear that the next logical step was to eliminate the PHP entirely from the web pages and replace the function calls with XML tags designed to be usable by HTML authors without any need of programming or understanding PHP. This change necessitated a new architecture for Molly, and so Molly II was developed.

How Molly is different from other Systems

Over the years Molly has been compared with various other systems such as ColdFusion, Mambo, Joomla, and Ruby on Rails. While each of these systems has its strengths, and share some abilities with Molly, they are also different enough in design, philosophy, architecture, and implementation for us to confidently continue to develop and use Molly. Here then is a brief comparison of Molly's similarities and differences to these other tools:

ColdFusion

Adobe's ColdFusion is perhaps most like Molly in that it uses a tag syntax. There are a number of notable differences however, not the least of which is that ColdFusion is expensive ($1,299 - $7,499 as this was written) and proprietary, whereas Molly is free and open source. While ColdFusion uses tags as its syntax (CFML - ColdFusion Markup Language) it is not XML compliant and so would break any standards-based XML parser. ColdFusion is also, despite its tag syntax, considered a programming language and in fact has an ECMAScript-like (but not compatible) syntax as well.

Mambo & Joomla

Mambo & Joomla are two versions of the same content management system which forked in the midst of considerable controversy and angst among its developers. While there are a lot of devoted users of the products, it is perhaps because they have invested so much energy in building sites with the products and choosing sides in the split. Both products are built with PHP, are ostensibly open source, and take the same approach as Molly I (which we abandoned for the simpler to use XML syntax and cleaner architecture of Molly II and beyond). Mambo and Joomla support only MySQL whereas Molly supports MySQL, Oracle, PostgreSQL, ODBC, SQLite, LDAP and many others.

Ruby on Rails

Perhaps the single most backward-named computer software, Ruby on Rails is actually an object framework (Rails) built in a programming language named Ruby. It has the advantage of being free and open source and was hailed for a while as "the next big thing" in web development. But in the authors' opinion Ruby on Rails has two fatal flaws: First is a rather steep learning curve and the necessity to adopt the Rails system whereby structure happening automatically based on a naming scheme. Second, and perhaps more important, is that many developers have experienced scalability issues for large or busy sites.

How Molly Works

Molly is a web server-side technology, which is to say that all of the work that Molly does happens on the web server. The user never sees any of Molly's MAML tags or underlying programming. Contrast this with client-side technologies like JavaScript where all the user needs to do is "View Source" in their browser and they can see all of the code. When a user does that on a page processed by Molly, all they see is the XHTML that Molly generates.

So, being a server-side technology, Molly has to be installed on a web server. How to do that is explained in Appendix I: Installing Molly.

Molly uses special new tags to add server-side functionality to your web pages. Because XHTML is a language built in XML it allows us to add other XML languages' tags to our code. Molly implements one such language — Molly Active Markup Language, or MAML. MAML tags look and are written just like XHTML tags with one difference; they include the "maml:" prefix that tells Molly (and other XML processors) that those tags are not XHTML. That way MAML can have tags with the same name as XHTML tags and there is no problem. For example, XHTML has the <form> tag and Molly has the <maml:form> tag. These can both be used in the same document without any problem. At this point you may be wondering why Molly would have her own form tag and not just call it something else. Well, Molly's forms look and act just like XHTML's forms, except that Molly's automatically connect to the database and process the data when the user clicks the submit button. With an XHTML form the webmaster would have to write all of the necessary server-side programs necessary to do that.

So Molly is at her heart an XML parser, but she's really much more. She's what we call an "abstraction layer." Molly's MAML tags are abstractions that hide all the underlying programming from you so that you can focus on building your web site and not on the details of programming the functionality that makes your site dynamic. Just like XHTML and CSS together form an abstraction layer that hides the complexity of delivering content over the internet and rendering it into the pixels you see on your computer screen.

Molly is written in a programming language called PHP. (Programming languages themselves are abstraction layers above the binary code that computer processors actually read and write.) Molly is designed to be extensible and so if you know PHP and wish to add new functionality that is not only possible, but fairly easy to do thanks to over a decade of experience in designing and building her architecture. Part III: Under the Hood explains that architecture and the rules we follow when programming Molly.

How Molly Helps

Molly helps by eliminating most of the need to write custom server-side code necessary to build dynamic web sites and web applications (WebApps). Molly does this in a way that, while giving the web developer the advantages of a content management system and skinned environment, does not interfere with their ability to handcraft their HTML and CSS however they like. Molly does not impose design or architecture paradigms or constraints. While we provide some default pages and skins, it is just as easy to retro-fit Molly into your own web site's design and architecture and eliminate embedded PHP or add new functionality without writing any.


Part I: Learning Molly & MAML

If you already know how to build web pages in XHTML, then you are well on your way to learning how to use MAML, since MAML uses simple tags very much like those with which you are already familiar. The first thing we will cover is the basic structure of a MAML page. We will demonstrate how MAML is translated by Molly into XHTML. After that we will explain how to to use a few MAML tags, and how they are transformed by Molly into XHTML.

A Basic MAML page's boilerplate code is virtually identical to a normal XHTML page. So to see the difference, let's build "Hello World" in XHTML and in MAML.

(Why "Hello World?" Well this is a computer book, and it's the law. ;o)

Basic HTML Document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Hello World HTML</title>
</head>
<body>
    <p>Hello World!</p>
</body>
</html>

Basic MAML Document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:maml="http://interpersonalnet.com/maml/">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Hello World MAML</title>
</head>
<body>
    <p>Hello World!</p>
</body>
</html>
 

See the difference? Yeah, it's hard. The only difference is on the third line. Here, this time I'll highlight it so you can see it easier:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:maml="http://interpersonalnet.com/maml/">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Hello World MAML</title>
</head>
<body>
    <p>Hello World!</p>
</body>
</html>
 

It's not much, but it's very important. What does that snippet of extra code do? Well, it does a little bit of XML magic and adds the "maml" namespace to the document so that Molly's XML parser can process the file and do all the cool things that make Molly what she is. That namespace declaration is why Molly's tags all begin with "maml:" like this:

<maml:box>Some text with a box around it.</maml:box>

This use of namespaces also makes the MAML XML syntax compliant with XML and XHTML standards, so any XML editor or processing software should have no problem with Molly's MAML code.

OK, so let's take our simple example a little further. We'll add the <maml:box> tag to our Hello World MAML file like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:maml="http://interpersonalnet.com/maml/">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Hello World MAML</title>
</head>
<body>
    <p>Hello World!</p>
    <maml:box>Some text with a box around it.</maml:box>
</body>
</html>

Now if we save this as a file with a .maml extension like hello_world.maml and view it in a browser from a Molly Server it will look something like this:

	

Hello World!

Some text with a box around it.

 

(Remember, Molly is a "server-side" technology and so MAML pages have to be viewed in a web browser via HTTP from a server where Molly is installed. If you view the page from your local hard drive via the browser's file:// URL the browser will not understand the MAML code.)

Molly processes the MAML file and generates an HTML document similar to the following in its place:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv = "content-type" content = "text/html; charset=utf-8">
    <title>Hello World MAML</title>
    <link rel="stylesheet" type="text/css" href="/~rvullo/decor/default.css" />
    <style type="text/css">
        // Styles go here
    </style>
    <script type="text/javascript">
        // Scripts go here
    </script>
</head>
<body>
    <p>Hello World!</p>
    <div class="box">
        <p class="title"></p>
        <p class="contents">
            <span class="boxbottom">
                Some text with a box around it.
            </span>
        </p>
    </div>
</body>
</html>

Now at this point you don't really have to worry about the specific code that Molly generated, this is simply to illustrate the point that Molly translates the MAML tags into HTML and automates a lot of things like adding CSS and JavaScript when necessary. Note that while Molly requires XHTML as input, current skins generate the newer (but not XML compliant) HTML5 as output. You may incorporate any HTML5 tags into your MAML pages as long as they are "well formed." This means that Every open tag requires a close tag, and all tag attributes must be enclosed in double quotes. As a simple example, HTML5 allows the <br> tag, however in a MAML file that must be typed as <br /> so that the XML parser can process it. Likewise, while HTML5 allows <a href=page.maml>Link to Page</a>, Molly requires that to be expressed as <a href="page.maml">Link to Page</a> with the href attribute's value properly enclosed in double quotes.

Getting Started with Database Access

Molly makes it very easy to fetch information from a database and put it on a web page formatted however you want. Here is a simple example of that:

Notice that clicking the column titles sorts the results.

Here is the MAML and HTML source code to generate the above list (line numbers added for explanation reference):

  1. <maml:fetch table="molly_test" key="name" criteria="true" sort = "name" order = "ascending">
  2. <table style="width:22em; background-color: #f4f5fb;padding:1em;">
  3. <tr>
  4. <th><maml:sort sort="name">Name</maml:sort></th>
  5. <th><maml:sort sort="eyecolor">Eye Color</maml:sort></th>
  6. <th><maml:sort sort="age">Age</maml:sort></th>
  7. </tr>
  8. <maml:row>
  9. <tr>
  10. <td><maml:field name="name"/></td>
  11. <td><maml:field name="eyecolor"/></td>
  12. <td><maml:field name="age"/></td>
  13. </tr>
  14. </maml:row>
  15. <tr>
  16. <th colspan="3" style="text-align:right;">Total Records: <maml:numrows/></th>
  17. </tr>
  18. </table>
  19. </maml:fetch>
  1. The <maml:fetch> tag is used to set up a query to retrieve information from the database. The table attribute specifies which table to search. The key attribute specifies the key field for the table. The criteria attribute specifies the search. In this case we set criteria="true" so Molly fetches all of the records in the table. You could be more specific with something like criteria="age > 10" and fewer records would be found. Finally sort = "name" and order = "ascending" tell Molly to return the results sorted by the name field.
  2. This is an ordinary <table> tag. Because you mix MAML tags freely with HTML you can format your output however you wish. You could use an ordered list instead of a table if you like.
  3. Our first table row will have the column headers.
  4. This is a simple HTML <th>Name</th> one would normally use to create a table header with the addition of a <maml:sort sort="name"> tag. This simple tag tells Molly to create all the necessary code to allow users to click on the column head and retrieve a sorted list based on the field name specified in the sort attribute.
  5. As above we've added sorting, by eye color in this case.
  6. And here we're sorting by age.
  7. End of the header row of the table.
  8. Everything between the <maml:row> and </maml:row> tag pair will be repeated for every record found by the query specified in the <maml:fetch> tag.
  9. Because we're building an HTML table we'll repeat a <tr> table row.
  10. This HTML <td> contains the <maml:field name="name"/> tag which will be replaced by the results of the search for each row. The name attribute specifies the database field name.
  11. Likewise this table data cell will be filled with eye color.
  12. And this with data from the table's age field.
  13. End of the table row.
  14. And end of the record (and the portion of the code which will repeat for every record found).
  15. We're adding one last row to our table…
  16. Which will display the number of rows Molly found in the database (and rendered above).
  17. And we have to close all of our open tags.

Tokens

Tokens are place holders for values which Molly will substitute before sending the page to the browser. (Programmers can think of these as variables.) Some tokens are pre-defined as part of Molly's configuration to make it easy, for example, to create a path to an image which will work from any of the site's pages:

<img src="*[HTMLRoot]*decor/icons/gradcap-7.png" />

Other tokens can be set by the webmaster using the <maml:token> and <maml:persist> tags.

Pre-Defined Tokens

Configuration Tokens (Set in the site's .ini file and molly_config database table)

*[HTMLRoot]* —> HTML Path to the root of the site
*[SkinDirectory]* —> Path to skins directory, relative to *[HTMLRoot]*
*[Skin]* —> Current Skin
*[SystemRoot]* —> HTML Path to Molly's system directory
*[FileRoot]* —> Unix path to the root of the site
*[QueryString]* —> Query String (if any) sent by browser when requesting this page†
*[SkinScripts]* —> JavaScripts Molly will be inserted into the page's head†
*[ConfigTable]* —> The name of the table from which the site's configuration settings have been read.

†In general webmasters should not access these, they are for internal system use only

Tag-Specific Tokens

Some tags as part of their functionality create pre-defined tokens as well. For example, the <maml:login> tag will create tokens when a user logs into the site:

Molly Page Tokens relevant to login

*[LoggedIn]* —> Set to 1 (true) if the user is logged in, 0 (false) if not
*[LastName]* —> logged in user's last name
*[FirstName]* —> logged in user's first name
*[Salutation]* —> logged in user's salutation (i.e. Miss, Mrs, Mr, Dr, etc.)
*[UserID]* —> logged in user's unique user id (integer)
*[LoginName]* —> logged in user's unique username/alias

Currently Defined Tokens

The following is a dynamically generated list of all the currently defined tokens for this page view in the Molly installation you are using to read this page:


If you wish to see the current values for all of the tokens on your site, you can use the following code on a .maml page:

<div>
   ActiveLinkColor: *[ActiveLinkColor]*<br />
   AttachmentsDirectory: *[AttachmentsDirectory]*<br />
   BackgroundColor: *[BackgroundColor]*<br />
   BackgroundImage: *[BackgroundImage]*<br />
   BoxFrameColor: *[BoxFrameColor]*<br />
   ConfigTable: *[ConfigTable]*<br />
   CurrentYear: *[CurrentYear]*<br />
   FileRoot: *[FileRoot]*<br />
   FirstName: *[FirstName]*<br />
   FormDark: *[FormDark]*<br />
   FormHandler: *[FormHandler]*<br />
   FormLight: *[FormLight]*<br />
   FormMedium: *[FormMedium]*<br />
   HTMLRoot: *[HTMLRoot]*<br />
   HoverLinkColor: *[HoverLinkColor]*<br />
   LastName: *[LastName]*<br />
   LinkColor: *[LinkColor]*<br />
   LoggedIn: *[LoggedIn]*<br />
   LoginName: *[LoginName]*<br />
   MenuColor: *[MenuColor]*<br />
   MenuLinkColor: *[MenuLinkColor]*<br />
   MenuRolloverBackgroundColor: *[MenuRolloverBackgroundColor]*<br />
   MenuRolloverTextColor: *[MenuRolloverTextColor]*<br />
   MenuSelectedBackgroundColor: *[MenuSelectedBackgroundColor]*<br />
   MenuSelectedTextColor: *[MenuSelectedTextColor]*<br />
   MenuTextColor: *[MenuTextColor]*<br />
   PageColor: *[PageColor]*<br />
   QueryString: *[QueryString]*<br />
   Salutation: *[Salutation]*<br />
   SelectedTabColor: *[SelectedTabColor]*<br />
   SidebarHeadingBackgroundColor: *[SidebarHeadingBackgroundColor]*<br />
   SidebarHeadingTextColor: *[SidebarHeadingTextColor]*<br />
   SidebarLinkColor: *[SidebarLinkColor]*<br />
   SiteLogo: *[SiteLogo]*<br />
   SiteName: *[SiteName]*<br />
   Skin: *[Skin]*<br />
   SkinDirectory: *[SkinDirectory]*<br />
   SkinScripts: *[SkinScripts]*<br />
   SystemFileRoot: *[SystemFileRoot]*<br />
   SystemRoot: *[SystemRoot]*<br />
   TabColorHover: *[TabColorHover]*<br />
   TextColor: *[TextColor]*<br />
   UnselectedTabColor: *[UnselectedTabColor]*<br />
   UserID: *[UserID]*<br />
   VisitedLinkColor: *[VisitedLinkColor]*<br />
   WebCronLastRun: *[WebCronLastRun]*<br />
   WindoidFieldColor: *[WindoidFieldColor]*<br />
   WindoidFrameColor: *[WindoidFrameColor]*<br />
   copyright: *[copyright]*<br />
   javascript: *[javascript]*<br />
   stylesheet: *[stylesheet]*<br />
   tagline: *[tagline]*<br />
</div>

Skins

Molly uses a "skins" system to facilitate changing the look and feel of your web site, as well as allowing you to change the way many MAML tags render their output. All of the presentation code and images for Molly are contained in the decor directory in the root of your Molly site. Skins are contained within this decor directory (though the path to skins may be changed in your molly.ini file).

Anatomy of a Skin

Within the skins directory can be one or more directories, the name of which is the name of the skin. The name of the skin currently being used by this particular Molly installation is "hex." Skin directories have a specific structure and set of files. There are two required files:

And three required directories:

main.css File

This is the stylesheet that will be linked to every .maml page on your site. The file will be processed to replace tokens with their appropriate values. Most of these (the presentational ones) are set in the molly_config table of the database. Tokens which provide appropriate paths are set in the molly.ini file.

main.js.htmf File

This is a snippet of HTML (.htmf stands for hypertext markup fragment) which will be inserted into the <head></head> section of each page. This is where you should insert any JavaScript code which you would like to have on every page of your site.

images Directory

This directory contains any images used in the design of your site, such as background images, logos, etc. Images which are content (i.e. photos of your business, etc.) do not belong here. They should be outside of the decor directory which is reserved for presentational aspects of the site. Images in this directory may be templates for Molly's colorizing code and if so should be named with ".tplt" before the ".jpg" or ".png" file extension to make this clear.

scripts Directory

This directory contains all JavaScripts required by the skin. At a minimum it should have a main.js file which will be included in every page.

templates Directory

This directory contains all of the template files used by Molly to generate the resulting HTML of most of the MAML tags. In general when creating your own skins you can just copy the default templates directory with all of its template files and you will be fine. Most of the changes you are likely to want to maje can be accomplished by editing the main.js.htmf file. If, however, you wish to actually change the HTML (structure) that Molly generates, then these are the files you would edit.

Template files follow a naming convention and it is important that you understand and respect this convention if you are editing them or creating new ones for new MAML tags you might be creating (in a module, for example, or if you are working on extending Molly). Template files must have a ".tplt" file extension. Template files are named for the tag they are used by and in general since most tags have a begin tag and an end tag there is a separate file for each of these. So, for example, the <maml:windoid> tag uses the windoid.begin.tplt template file and the </maml:windoid> tag uses the windoid.end.tplt template file.

This system provides quite a bit of flexibility in modifying how Molly renders a page without editing the underlying PHP code. If you wish to do this, it is best that you create your own skin to do so. The easiest way to do that is to duplicate an existing skin, give it a new name, then edit your molly.ini file to use the new skin.

Skins and the molly_config Database Table

Skins will usually provide one or more SQL files for populating the molly_config table with appropriate values for tokens in the main.css file.

Currently Installed Skins

The following is a dynamically generated list (using <maml:filelist path="*[FileRoot]**[SkinDirectory]*" filetype="dir">) of the skins which are installed the decor/skins/ directory of this Molly installation:

Other Stuff I need to write about:

Users and Permissions

In your database, there should be a molly_login table and a molly_username table. This is where the information for each user who can log into your Molly site gets stored. The user_id number in the molly_login table must match with the user_id number of the corresponding user in the molly_username table.

The molly_permissions table is where you can set the editing permissions for each user. Again, the user_id number in this table must match with the corresponding user's user_id number. The group_name column lists the permissions each user has. The options are admin, editor, and config. These are the values that can be entered into the permitted attribute in the <maml:block> tag so that only certain users can edit that block. The permitted column shows whether or not the user has that permission; "1" means permitted, "0" means not permitted. The group_type column has three options: shared, independent, and exclusive. Shared means that users in that group can grant and revoke permissions for each other. Independent means that there are also multiple users in that group but they cannot grant or revoke permissions for each other. Exclusive means that there can only be one user in that group.


Part II: Building Sites with Molly

Page Approach vs. Wiki Approach

When building a site with Molly, there are two ways to approach it. The first way is to create multiple pages by creating multiple maml files and linking between the pages. The <maml:block> tag can be used to fill in the content of each page so that it can be edited dynamically from the web. The second approach is to use one maml file with multiple wiki pages from the database. Again, the <maml:block> tag can be used to set which wiki page will be displayed on each page, but only one wiki block can be placed on each maml page.

Creating a New Skin

Files for skins can be found in the decor directory. When building your website with Molly, you may want to create some of your own skins in addition to the default skins. If you already have an HTML webpage with a stylesheet that you would like to make into a skin, here are the steps to do so.

  1. Test your HTML template with the W3C Validator.
  2. Once it checks out, save the template as a .maml file.
  3. Replace the doctype and HTML tags with Molly versions (XHTML 1.1).
  4. Test your template in Molly to make sure the XHTML is well formatted.
  5. Create a new skin by duplicating an existing skin.
  6. Delete the existing CSS file in the skin directory and the images in the images folder.
  7. Copy your stylesheet into the skin directory and rename it “main.css”.
  8. Copy your images into the images folder.
  9. Change the skin in the molly.ini file.
  10. Fix any image paths in your HTML file or stylesheet.

Now that you’ve created your new skin, you can make the style more intelligent by replacing hard coded colors in your stylesheet with Molly tokens, which come from the molly_config table in your database. You can also colorize your images.

To make your pages more intelligent, you can change your template to a .htmf file and move it into the snippets folder. Use the maml:include tag to include snippets in your webpages. You can also use maml:block tags optionally surrounded by maml:windoid tags to make content editable via the web.


Part III: Under the Hood

Main Directory Structure

The default directory structure of a Molly installation is as follows:

folder open
Molly Distribution
vertical line
node
.htaccess
node
folder closed
system
node
folder closed
utilities
vertical line
node
folder closedforms
vertical line
node
folder closedscripts
node
folder closed
modules
node
folder closed
decor
vertical line
node
folder closedskins
node
folder closed
docs
vertical line
node
folder closedindex.maml - you are here!
node
folder closed
error
node
index.maml
vertical line
etc.
.htaccess
The .htaccess file is Apache's way of setting configurations on a per-directory basis. If your web server does not support .htaccess you will need to enable these configurations in other ways (i.e. via httpd.conf and php.ini). Here is a typical .htaccess file (line numbers added for documentation):
  1. AddType application/x-httpd-php .maml .html .php .tplt .ini .inc .txt .css .mss .php4 .php3 .phtml
  2. DirectoryIndex index.maml index.html index.php
  3.  
  4. php_flag register_globals off
  5.  
  6. SetEnv MOLLY_INI molly.ini
  7.  
  8. php_value short_open_tag off
  9.  
  10. php_value auto_prepend_file /Users/rvullo/Sites/mx/system/loader.php
  11. php_value auto_append_file /Users/rvullo/Sites/mx/system/parser.php
  12.  
  13. # 404: Not Found
  14. ErrorDocument 404 /~rvullo/mx/error/404.maml
  15.  
  16. IndexIgnore .htaccess
  17.  
  18. Options -Indexes
  19.  
  20. php_value upload_max_filesize 30M
Line-by-line Explanation:
  1. Tells Apache to process .maml, .html, .css, etc. files with PHP so that Molly can parse them.
  2. Tells Apache that files named "index.maml" should be loaded instead of listing the contents of a directory.
  3. #
  4. Tells PHP to turn off automatic global generation. This is the default configuration for most PHP installations now. It is included in Molly's .htaccess file just to be sure for security reasons.
  5. #
  6. This is where you tell Molly which .ini file to read for configuration information. By default this is simply molly.ini but if you have multiple Molly installations it is helpful to name each site's .ini file such that you can keep track of them.
  7. #
  8. Some PHP installations have short open tags enabled such that blocks of PHP code can begin with <? instead of <?php. This, however, breaks when you have an <?xml declaration.
  9. #
  10. Tells PHP to automatically include Molly's loader code at the beginning of every processed file.
  11. Tells PHP to automatically include Molly's parser code at the end of every processed file.
  12. #
  13. #
  14. Tells Apache to send Molly's not found page instead of the default apache message. This can be used to automatically redirect users or simply to customize the look of the error page.
  15. #
  16. Tells Apache not to serve .htaccess files.
  17. #
  18. Turns off Apache's automatic directory listings.
  19. #
  20. If your site allows file uploading this directive tells PHP how large of a file to allow.
system
This directory contains the bulk of the code that makes Molly work. See the next section for details.
utilities
This is the directory where the forms and scripts directories are located. These subdirectories control how Molly's forms function separately from HTML forms, and define JavaScript code to be used by Molly.
modules
This is where add-on modules for Molly are located. For details on configuring and using modules see the Modules Reference appendix.
decor
The decor directory is where all files related to the site's look are kept, primarily in the skins subdirectory.
skins
Molly supports the notion of skins for easily varying the look and feel of your site. See the Skins section for details on how they work.
docs
Molly's documentation, primarily this page that you are reading right now. The docs directory is not required on a production site and may be safely removed.
error
Directory for error pages, notably the 404 Not Found page specified in Molly's .htaccess file (above).
index.maml
Default Molly site home page.

System Folder Directory Structure

folder open
system
vertical line
node
molly.ini
node
loader.php
node
parser.php
node
folder closed
core
vertical line
node
folder closed05_utility.inc
vertical line
node
folder closed10_date_time.inc
vertical line
node
folder closed15_file.inc
vertical line
node
folder closed20_database.inc
vertical line
node
folder closed25_sessions.inc
vertical line
node
folder closed27_security.inc
vertical line
node
folder closed30_XML.inc
vertical line
node
folder closed40_tags.inc
vertical line
node
folder closed70_modules.inc
vertical line
node
folder closed80_environment.inc
vertical line
node
folder closed90_parser.inc
vertical line
node
folder closedtags
vertical line
vertical line
last node
folder closedhtml.override.inc
vertical line
vertical line
last node
folder closedmaml.ajax.inc
vertical line
vertical line
last node
folder closedmaml.constructs.inc
vertical line
vertical line
last node
folder closedmaml.database.inc
vertical line
vertical line
last node
folder closedmaml.format.inc
vertical line
vertical line
last node
folder closedmaml.security.inc
vertical line
vertical line
last node
folder closedmaml.tokens.inc
vertical line
vertical line
last node
folder closedmaml.xml.inc
vertical line
node
folder closeddatabase
node
folder closed
scripts - now located in utilities as of June 11th, 2018
vertical line
etc.
molly.ini
This file contains Molly's configuration settings including paths, database usernames and passwords, etc. May be named anything.ini to make multiple Molly installations more manageable.
loader.php
This script is automatically run at the beginning of a page load (because of the PHP auto_prepend_file directive in Molly's .htaccess file. It reads the molly.ini file (above) and sets up everything for Molly.
parser.php
This script is automatically run at the end of a page load (because of the PHP auto_append_file directive in Molly's .htaccess file. It does the actual parsing of Molly's MAML tags and substitution of tokens.
core
This directory contains all of the include (.inc) files that provide the bulk of Molly's functionality. All .inc files within this directory (and the tags directory with in it) are automatically included by loader.php (and 40_tags.inc). The include files directly inside the core are named with numerical prefixes because they must be loaded in order due to dependencies.
05_utility.inc
Utility functions for Molly programmers.
10_date_time.inc
Code for converting and manipulating dates, time, time zones, etc.
15_file.inc
File reading functions.
20_database.inc
Database abstraction functionality.
25_sessions.inc
Callback functions for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers.
27_security.inc
Molly's user login system.
30_XML.inc
XML, DOM, and XSLT processing (no functionality yet).
40_tags.inc
Contains prototype tag and template classes, page building utility functions, and loads all of the tag class definitions from the tags directory.
70_modules.inc
Loads code for modules. Modules are optional add-ons to Molly. Over time some modules may migrate into Molly's core.

Modules to be loaded are specified in the molly.ini file for the site in the [Modules] section. This file automatically includes any .inc files found within each module's directory.

Each modules directory contains a documentation file named docs.htmf which is automatically be included into the Modules appendix of this document.
80_environment.inc
Utility code that Molly uses to determine the current environment - for example if a tag has been implemented.
90_parser.inc
The actual parser code called by parser.php.
tags
This directory contains the include files with all of Molly's MAML tag class definitions. They are in separate files based on functionality for easier coding and support.
html.override.inc
Molly processes a few HTML tags in order to simplify developing MAML pages. These include the <html>, <head>, and <style> tags.
maml.ajax.inc
Under development: hopefully simple way to implement AJAX.
maml.constructs.inc
Under development: rudimentary branching functionality.
maml.database.inc
Database access tags such as <maml:fetch> and <maml:form> and all of their associated tags.
maml.format.inc
Tags which implement user interface components such as menus and windoids.
maml.security.inc
Tags for implementing user login and user/group specific viewing permissions.
maml.tokens.inc
Tags which allow webmasters to create tokens either on a per-page-view basis or a per-session basis.
maml.xml.inc
Contains the class definition for the <maml:nothing> tag. This tag acts as a root XML element for MAML files which generate non-web-page results for use in AJAX and web services. If we re-implement Molly's SOAP functionality, that would go here.
database
Contains the ADODB database abstraction code employed by Molly. (Longtime Molly developers should note that this open source solution replaces Molly's original database abstraction code which had been developed specifically for Molly.)
scripts
Moved to the utilities structure as of June 11th, 2018.

Utilities Directory Structure

folder open
utilities
node
forms
vertical line
node
folder closedblock_insert.html
vertical line
node
folder closedclass.upload.php
vertical line
node
folder closeddrag_drop_upload.html
vertical line
node
folder closedinsert.html
vertical line
node
folder closedlogin.html
vertical line
node
folder closedlogout.html
vertical line
node
folder closedregister.html
vertical line
node
folder closedreplace.html
vertical line
node
folder closedsearch.html
vertical line
node
folder closedupload.html
node
scripts
vertical line
node
folder closedtinymce
vertical line
node
folder closedfiledrag.js
vertical line
node
folder closedjquery-1.8.2.js
vertical line
node
folder closedjquery-3.3.1.min.js
vertical line
node
folder closedjquery-migrate-1.4.1.min.js
vertical line
node
folder closedmaml.multiple.js.inc
vertical line
etc.
forms
Contains all files relevant to the maml:form tag and its functionality in communicating with the database.
block_insert.html
class.upload.php
This file is a PHP script that uploads files for your website and can manipulate images with ease. It can convert, resize, and alter images and add several features to your thumbnails and image galleries. In conjunction with forms, it looks for files uploaded with maml:form and saves the file in memory for later manipulation.
drag_drop_upload.html
insert.html
login.html
logout.html
register.html
replace.html
search.html
upload.html
scripts
Contains all scripts that are used by Molly across the site.
tinymce
filedrag.js
jquery-1.8.2.js
jquery-3.3.1.min.js
jquery-migrate-1.4.1.min.js
maml.multiple.js.inc

Files to be parsed by Molly

Extension Action
.php Process as normal php, no Molly functionality
.inc PHP include files
.html Process similarly to Molly 1, that is, set all the globals and include basic function and object libraries and module libraries, then procede to pass the file through the php parser. (Note that for normal HTML files this does essentially nothing, but that it allows you to minimally incorporate some of Molly's functionality if you understand the underlying architecture.)
.maml Set all the globals and include all the libraries and modules, then parse the file through Molly's MAML XML parser.
.tplt template files
.ini prevent being served to the web
.txt (Maybe turn into a nice formatted web page?)
.mss & .css Treated like a template file - that is, all *[tokens]* are replaced with config values

Molly's Parsing Engine

Previous versions of molly used the SAX (Simple API for XML) parser using xml_parser_create(). SAX was available in PHP 4 and compatible with PHP 5. It functions as a stream parser with an event-driven API. It is a push parser; the user defines a number of callback methods that will be called when the parsing events occur. The SAX interface does not deal in elements, but in events that correspond to tags. SAX parsing is unidirectional, which means that previously parsed data cannot be reread without restarting the parsing operation. Molly's new engine uses the new XMLReader object. This is a wrapper on Libxml2, which is a widely used XML C parser. Like SAX, XMLReader is also a stream based parser. But unlike SAX, XMLReader is a pull parser, meaning it only parses what is asked for by the application. It acts as a cursor moving forward on the document stream and stopping at each node on the way. XMLReader has a very small memory footprint and is faster than SAX because there is no need for it to process callbacks you do not care about.

Embedded JavaScript

Molly is an XML Parser, so whenever she sees a < she thinks a tag is beginning. Now obviously this isn't the case in a block of JavaScript. Browsers have learned to ignore code inside <script></script> tags, but technically that's not correct. The solution is to place the JavaScript code inside a CDATA block which tells the XML parser not to parse that text.

<script language="javascript" type="text/javascript">
<![CDATA[
 
    // JavaScript code goes here
 
]]>
</script>

Details of the Molly Framework's Processing Steps

Setting things Up

Molly works by running code both before the page is loaded and after the page is loaded. The code that runs before the page sets up the environment and the code after the page parses the page and replaces the various maml tags with the result of their processing. So, we need to configure the web server to automatically load the appropriate scripts at the beginning and end of page loads. The two key scripts are system/loader.php (loads the Molly environment) and system/parser.php (parses the maml tags). This is traditionally done via the Apache web server's .htaccess mechanism (as detailed at the beginning of this appendix) but there are other approaches which can be used, depending on the server's configuration. The directives normally used in the .htaccess file can be incorporated directly into Apache's httpd.conf file, for example. The php.ini file can also be used for configuration. The main advantage of the .htaccess file approach is that it allows multiple distinct Molly installations to be running on a single server. But this too can be managed via the php.ini approach with a little extra code. For example, if in your php.ini file you incorporate something like the following two lines:

auto_prepend_file = /home/username/molly_system_dispach/prepend.php
auto_append_file = /home/username/molly_system_dispach/append.php

Then the prepend.php and append.php files can do, in a centralized place, what the .htaccess files do in a decentralized way. This would also work for non-Apache web server environments.

Sample molly_system_dispach/prepend.php File

<?php
	$pos = strpos($_SERVER["HTTP_HOST"], 'learntech');
	if (!($pos === false)) {
		$gv_Site = 'learntech';
	}
	 
	$pos = strpos($_SERVER["HTTP_HOST"], 'diyvr');
	if (!($pos === false)) {
		$gv_Site = 'diyvr';
	}
	 
	$pos = strpos($_SERVER["HTTP_HOST"], 'vrweb');
	if (!($pos === false)) {
		$gv_Site = 'vrweb';
	}
 
	switch ($gv_Site) {
		case 'learntech':
			$_SERVER['MOLLY_INI'] = 'learntech.ini';
			include '/home/learntech/system/loader.php';
		break;
		 
		case 'diyvr':
			$_SERVER['MOLLY_INI'] = 'diyvr.ini';
			include '/home/diyvr/diyvr/system/loader.php';
		break;
		 
		case 'vrweb':
			$_SERVER['MOLLY_INI'] = 'vrweb.ini';
			include '/home/vrweb/vrweb/system/loader.php';
		break;
	}
?>
 

Sample molly_system_dispach/append.php File

<?php
    switch ($gv_Site) {
        case 'learntech':
                include '/home/learntech/system/parser.php';
            break;
 
        case 'diyvr':
                include '/home/diyvr/system/parser.php';
            break;
 
        case 'vrweb':
                include '/home/vrweb/system/parser.php';
            break;
    }
?>
 

So, as you can see, what the Molly startup mechanism does, whether via .htaccess or php.ini is to specify the filename of the .ini file that contains the configuration values for that particular installation of Molly and includes Molly's loader.php script. Then at the end of the page load, include Molly's parser.php script.

Inside the loader.php Script

Molly's Loader performs the following steps:

  1. Sets up a series of global variables with relevant file paths:
    • $gv_CurrentFile
    • $gv_CurrentFilePathInfo
    • $gv_CurrentFileExtension
    • $gv_MollySystemDirectory
    • $gv_MollySystemDirectory
  2. Reads the values from Molly's .ini file into the $gv_SiteGlobals array.
  3. Checks the .ini Secure setting and redirects to the same page using https if appropriate
  4. Checks to see if the current file's extension is ".php" and if so skips the rest of the loading process.
  5. Defines three core functions: m_Interpolate(), m_ParentDir(), and m_AutoInclude(). m_Interpolate() processes Molly's tokens and m_AutoInclude() will include all of the files of a particular file extension in a specified directory - this facilitates including all of the other necessary files for Molly.
  6. Initializes the global variable $gv_IDNum which is used to generate unique DOM IDs by various maml tags.
  7. Initializes the global $gv_Config array with some default values. These are used as tokens for values in the various skins.
  8. Using the m_AutoInclude() function, load all the core include files:
    • 05_utility.inc
    • 10_date_time.inc
    • 15_file.inc
    • 20_database.inc
    • 25_sessions.inc
    • 27_security.inc
    • 30_XML.inc
    • 40_tags.inc
    • 70_modules.inc
    • 80_environment.inc
    • 90_parser.inc
    These files are prefixed with numbers to force their include order, as there are dependencies. Additionally, some of these scripts themselves autoinclude other scripts - notably 20_database.inc and 40_tags.inc as well as 70_modules.inc which implements Molly's system for allowing expansion without touching the core. Modules can even implement new tags.
  9. Load any persistant tokens from the global $_SESSION array.
  10. Fully populate the $gv_Config array from the database table specified in the .ini file (possible now that the core functionality, including database access has now been included).
  11. Set up a global variable so Molly can check if someone is logged in
  12. Start Buffering PHP's output so that the current file can be captured and parsed before outputting it.
  13. Set appropriate headers for files based on their extension (i.e. CSS files get Content-type: text/css, and files like .ini are redirected to a 404 not found page so that Molly doesn't serve them to the web.

At this point PHP reads and outputs the requested file, which Molly is caching rather than outputting so that the subsequently appended parser.php can process it.

Inside the parser.php Script

The first thing the parser.php script does is check to see if the current file has a .php extension, and if so, exits. Next it puts the cached file (i.e. the unprocessed current .maml page) into a global variable and empties the cache, but leaves caching on so as to capture the processed page. (Also, some maml tags will use the PHP caching mechanism themselves to process the contents between their begin and end tags.)

If the current file has a .maml extension, then the captured page is sent to the m_PullParse() function to parse the page and process all of the maml tags, otherwise it is just echoed back into the just emptied cache.

Finally, the cache is once again captured into a variable, the cache cleared and caching turned off. All of the existing tokens in the newly captured cached page are replaced with their values and any non-existent tokens are deleted.

The m_PullParse() Function: The Heart of Molly

The m_PullParse() function is the entirety of the system/core/90_parser.inc file which is the last file included by Molly before the page is processed. It is the essence of the Molly tag-to-code engine.


Appendix I: Installing Molly

Environment

Molly is designed to be deployed on a Unix or Linux system running the Apache web server and PHP 5. Molly users have successfully installed and used Molly on Windows servers running Apache, however the core developers do not currently test or support that environment. For the curious, the main development platform for core development is currently Mac OS X (version 10.6.2 - Snow Leopard), PHP 5 (version 5.3.0), Apache 2 (version 2.2.13), and MySQL Server (version 5.1.43). The main Molly site (molly.rit.edu) is deployed on production server running Linux (version), Apache 2 (version) PHP 5 (version), and MySQL (version). Molly should run fine on any recent version of the above software as we try to avoid specific late version dependencies. Additionally, Molly requires the modules php-gd & php-xml.

Downloading and Uploading Molly

The latest version of Molly can always be downloaded from molly.rit.edu. Molly is distributed as a GZipped Tar file. Upload the Molly archive to your server's web directory. On many Unix and Linux systems this directory will be named "public_html" or "www" and on most Macintosh servers it is named "Sites."

  1. Using SSH connect to your server and log in. Change directories to your web directory.
  2. If you do an ls you should see the .tar.gz file that you just uploaded.
  3. Uncompress the molly archive file into a tar (Unix "tape archive") file, then untar the resulting .tar file.:
    gunzip molly.tar.gz
    tar -xf molly.tar

At this point you should have a directory named molly inside your web directory. (The Unix tar program preserves all of the directories' and files' privileges, so they are all set for you.)

Configuration

Once you have Molly's files installed on your server there are a few things you need to do to configure Molly:

Configure Apache

Modifying /private/etc/apache2/httpd.conf

Turn on Environment Variables module.

Find and uncomment (add if they're missing) the following two lines:

  #LoadModule env_module         libexec/httpd/mod_env.so
  #AddModule mod_env.c

Allow .htaccess files in the main web directory to modify configurations.

Around line 400 (inside the <Directory "/Library/WebServer/Documents"> on OS X) is the following directive:

  AllowOverride None

Change it to:

  AllowOverride All

(Actually, "AllowOverride FileInfo Options Indexes" should be sufficient.)

Modifying User .conf file(s)

MacOS X has an individual .conf file for each user on the system. In the /private/etc/apache2/users will be a .conf file for each user on the system. Edit the user.conf file for each user who will be using Molly as follows.

Change:

  AllowOverride None

to:

  AllowOverride FileInfo Options

Configure Database

Molly Account and Database

If you are the database administrator you will need to log in and create the database and username that Molly will use to access the database. If you do not have administrative privileges, skip to the next section and use the username and database assigned to you.

Replace MySQL stuff with updated screen captures.

Log in as the root user:

  [Ronald-Vullos-Computer:~/Sites] rvullo% mysql -uroot -p
  Enter password:
  Welcome to the MySQL monitor.  Commands end with ; or \g.
  Your MySQL connection id is 355 to server version: 4.0.17-standard

  Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

  mysql>

Let's assume you want to create a database named "mollydb" and that Molly will be connecting to this system as user "mollyuser" with the password "mollypass." As root user you would create the database and assign the access privileges as such:

  mysql> CREATE DATABASE mollydb;
  Query OK, 1 row affected (0.07 sec)

  mysql> GRANT ALL PRIVILEGES ON mollydb.* TO mollyuser@localhost IDENTIFIED BY 'mollypass';
  Query OK, 0 rows affected (0.71 sec)

  mysql>

Root administrative tasks are finished, so log out:

  mysql> exit
  Bye
  [Ronald-Vullos-Computer:~/Sites] rvullo%

Install Database Tables

Creating Molly's Tables and Loading Default Data

The easiest way to do this is to first change directories into the directory containing the SQL files provided with Molly:

  [Ronald-Vullos-Computer:~/Sites] rvullo% cd system/database/sql
  [Ronald-Vullos-Computer:system/database/sql] rvullo%
   

Next you will log into MySQL using the username and password that Molly will use (either created above, or provided by your database administrator or ISP). Once logged in you will select Molly's database and into it load first the schema which will create all the tables (mysql.sql), then the data to populate those tables (data.sql):

  [Ronald-Vullos-Computer:system/database/sql] rvullo% mysql -umollyuser -p
  Enter password:
  Welcome to the MySQL monitor.  Commands end with ; or \g.
  Your MySQL connection id is 357 to server version: 4.0.17-standard

  Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

  mysql> USE mollydb;
  Database changed
  mysql> SOURCE mysql.sql;
  Query OK, 0 rows affected (0.43 sec)

  Query OK, 0 rows affected (0.06 sec)

       Several identical lines deleted for brevity

  Query OK, 0 rows affected (0.00 sec)

  mysql> SOURCE data.sql;
  Query OK, 1 row affected (0.02 sec)

  Query OK, 1 row affected (0.00 sec)

       Many identical lines deleted for brevity

  Query OK, 1 row affected (0.00 sec)

  mysql> exit
  Bye
  [Ronald-Vullos-Computer:system/database/sql] rvullo%

At this point Molly's database should be all set. The next step will be to set the appropriate values in the molly.ini file.

Edit molly.ini file

Go to the system directory within Molly and open up the molly.ini file. There are several things that you will need to change in this file.

  1. On newer PHP systems "mysqli" should be used instead of "mysql" as the database type.
  2. In line 34, change "jpeg" to "png" if necessary depending on your operating system.
  3. In line 41, change the email address to your own email address.
  4. In lines 48, 49, and 50, change the file paths to the correct paths to your directories.
  5. In lines 62, 63, 68, 69, 92, 93, 147, and 148, change the information to your database and username.
  6. In lines 64, 70, 94, and 149, change the password to your own password.
  7. In line 72, make the session name something unique.
  8. In the "Login" section additional databases and LDAP servers can be configured. Note that Molly will only write new logins and reset passwords for the first database given. (TODO: Using LoginOldPasswordHandling with rehash or reset will copy the user to the first given database.)

Edit .htaccess file

The .htaccess file is located inside the main Molly directory. All you need to do with this file is to change "mollyuser" to your username in lines 13 and 14.

Security Considerations

This list SHOULD NOT be considered a comprehensive list.

System directory & molly.ini

For added security the system/ directory and molly.ini can be moved outside your public_html/ directory

On Apache this should no longer be necessary as the .htaccess file in the system/ directory will deny all access as of Molly 2018-7-15

File Uploading

(TO-DO)

Password Hashing

PasswordEncryption setting in molly.ini

Other

The OldPasswordHandling setting in molly.ini can allow you to migrate hashing methods. This SHOULD NEVER be used in response to being hacked. If your database is compromised you should delete all user passwords to force users to reset them.

LDAP accounts are stored in the database with the cleartext password "LDAP" - Molly does not store passwords for LDAP accounts in the database.

Testing Molly

  1. Start by accessing your Molly site from your browser. The URL should be something like: http://www.yourserver.com/~yourid/molly/ and should look like this:

    molly screen capture
  2. If the site looks like the one in the illustration above, then you're good to go. If you see a sort-of broken looking site with lots of pink, then there is a problem with your database, since Molly loads much of the user interface design information from a database table named 'molly_config.' You may also see error messages. Often this is a result of not having the correct mysql username and password settings in the molly.ini file. If what you see is nothing even close to a web site, then you probably have errors in your .htaccess file. (Molly's installer created both of these files for you, but you may edit them if need be.)

Molly on other systems

While Molly is not regularly tested on anything other than Apache, we have previously tested other systems. Here are some configuration things we had to do as of the last testing. Please note that these instructions are for reference only and are not officialy supported.

Nginx with PHP-FPM

IIS on Windows Server

XAMPP on Windows



Appendix II: MAML Tag Reference

The following documentation is generated automatically from the class definitions used by Molly to implement MAML tags. All MAML & MAGE tags have optional id and style attributes. If you do not provide an ID, Molly will automatically generate a unique ID where appropriate. Default attribute values are shown. Some attribute defaults are set in the system/molly.ini file.

maml:ajax

Attributes:

Prototype: <maml:ajax target = "" interval = "0" url = "" trigger = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:ajax>

Creates an AJAX script to continually load data from the url. The returned data will get put in the #target element. If a trigger is specified then the AJAX refresh won't start until the #trigger element is clicked. The interval specifies the time (in seconds) to reload automatically. If interval not set, or set to zero, then the AJAX will only run once.
Path: /home/interper/system22/core/tags/maml.ajax.inc
Start Line: #3


maml:block

Attributes:

Prototype: <maml:block blocknum = "" wiki = "" blockid = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:block>

Description: Allows the webmaster to designate an area of the page as editable online by members of the permitted group. When those members are logged in they will see an editor in place of the normally static content.

Molly automatically creates the link between blocks and the records in the molly_blocks table (field: block_id) however it should be noted that these links are somewhat fragile as they are based on the file name (and path relative to HTMLRoot), and the position (order) of the block on the page if there are more than one block. Because of this, there is an optional blocknum attribute that can be used if the order of blocks on a page must be changed after the records in the database table exist. Alternatively the database record can be edited if neccessary to reflect a relocated block or renamed/moved page.

If the blockid is set, this allows the same block to be used on multiple pages rather than tying it to a particular page.

The block tag can also be used to create a wiki. Setting the wiki attribute returns that wiki page from the database and activates automatic wiki linking via putting {{link name}} in the block’s text. You can only have one wiki block per MAML page. To create a new wiki page, create a wiki link then follow it. Use of the wiki attribute overrides use of the blockid attribute.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #1161


maml:box

Attributes:

Prototype: <maml:box title = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:box>

Description: Creates a round-cornered box around whatever it encloses based on the box.top.tplt and box.bottom.tplt template files, the default.css styles, and values in the config database table. The optional title attribute is displayed at the top of the box. The box itself is created from two image files (decor/rounded-left-template.png and decor/rounded-right-template.png) which are colorized using Molly’s image colorizing code. It would be nice to add a color attribute to allow override of the default color BoxFrameColor from the config database table.

Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #150


maml:captcha

Attributes:

Prototype: <maml:captcha id = "" class = "" style = "visibility:visible;" permitted = ""></maml:captcha>

Adds captcha protection to Molly forms.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #662


maml:desist

Attributes:

Prototype: <maml:desist name = "data" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:desist>

Description: destroys a token created by a <maml:persist> tag and removes its data from memory.

Path: /home/interper/system22/core/tags/maml.tokens.inc
Start Line: #25


maml:else

Attributes:

Prototype: <maml:else id = "" class = "" style = "visibility:visible;" permitted = ""></maml:else>

Description: The <maml:else> works within the <maml:if> tag to allow one to conditionally display portions of the page.

<maml:if condition="'*[token]*' == 'value'">
    <p>True Stuff (inside an if).</p>
    <maml:else>
        <p>False Stuff (inside an else).</p>
    </maml:else>
</maml:if>
 
Path: /home/interper/system22/core/tags/maml.constructs.inc
Start Line: #102


maml:email

Attributes:

Prototype: <maml:email to = "" subject = "" replyTo = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:email>

Sends an email upon <maml:form> submission.

Not all tokens can be used in the subject & body. Form data is be default not allowed. Form data can be used if and only if the allowInEmail attribute is set & <maml:email> is located after all desired inputs. This is a safety measure to prevent accidental spam/abuse. Additionally, the token *[f_Page]* can be used for the page the form was submitted from.

It is strongly advised to use this with <maml:captcha> or <maml:protect>.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #684


maml:extension

Attributes:

Prototype: <maml:extension name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:extension>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the extension of the file for each file found.

Alternatively you may use the *[extension]* token, for example if you wish to include the extension in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #326


maml:fetch

Attributes:

Prototype: <maml:fetch query = "" table = "" fields = "*" sort = "" order = "ascending" criteria = "" maxentries = "" start = "1" key = "" detail = "" host = "" database = "" user = "" password = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:fetch>

Description: Used to retrieve results from a database. Together with the <maml:row> and <maml:field> tags this allows you to retrieve and display data from Molly’s database.

Example:

<maml:fetch table="molly_test" key="name" criteria="true" detail="form.maml" format="off">
    <table style="width:20em;">
        <tr>
            <th><maml:sort sort="name">Name</maml:sort></th>
            <th><maml:sort sort="eyecolor" order="descending">Eye Color</maml:sort></th>
            <th><maml:sort sort="age">Age</maml:sort></th>
        </tr>
        <maml:row maxentries="3">
        <tr>
            <td><maml:field name="name"/></td>
            <td><maml:detail href="advancedLists.maml"> <maml:field name="eyecolor"/></maml:detail></td>
            <td><maml:detail href="advancedForm.maml"> <maml:field name="age"/></maml:detail></td>
        </tr>
        </maml:row>
    </table>
  Total Records: <maml:numrows/><br/>
  <maml:previous>Previous <maml:numrows/> Records </maml:previous>
  | <maml:pages delimiter="-" /> |
  <maml:next>Next <maml:numrows/> Records </maml:next><br/>
</maml:fetch>
 

Results:

Name Eye Color Age
Genny Brown 6
Heather Blue 28
Michelle brown 12
Ron Brown 48
Tim brown 22
Total Records:
Previous Records | | Next Records

Things which are known to be not working:

Example from select_tag_test.maml - be sure to update these docs as functionality is added.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #17


maml:field

Attributes:

Prototype: <maml:field name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:field>

Description: Used in conjunction with <maml:fetch> tag to retrieve results from a database.

Replaces itself with the data retrieved from that field by the <maml:fetch> tag’s query.

See <maml:fetch> tag for example usage.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #260


maml:filelist

Attributes:

Prototype: <maml:filelist path = "FilePath" extension = "" dateformat = "d F Y H:i:s" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:filelist>

<maml:filelist path="/unix/" extension="jpg" filetype="dir | file" dateformat="d F Y H:i:s">

The maml:filelist tag, together with the associated maml:filename, maml:fileroot, maml:filesize, maml:permissions, maml:filetype, maml:modtime, and maml:extension tags allow you to gather and use information about all of the files in a specified directory. This can be to display a simple list, like the example below, or to build a file picker widget like the one used in the config administration tool. The maml:filelist tag pair repeats its contents for each file found in the directory. Using the extension attribute you can limit the results to one or more file types.

Example:

<table>
    <tr>
        <th>filename</th>
        <th>fileroot</th>
        <th>filesize</th>
        <th>permissions</th>
        <th>filetype</th>
        <th>modtime</th>
        <th>extension</th>
        <th>fmd5</th>
    </tr>
    <maml:filelist path="/home/interper/molly/decor/skins/hex/images/" extension="gif jpg">
        <tr>
            <th><maml:filename /></th>
            <td><maml:fileroot /></td>
            <td><maml:filesize /></td>
            <td><maml:permissions /></td>
            <td><maml:filetype /></td>
            <td><maml:modtime /></td>
            <td><maml:extension /></td>
            <td><maml:fmd5 /></td>
        </tr>
    </maml:filelist>
</table>
 

Results:

filenamefilerootfilesizepermissionsfiletypemodtimeextensionfmd5
img01.tplt.jpg img01.tplt 22804 -rwxr-xr-x file 20 January 2012 08:41:27 jpg 205e395db585dedab3c6c12422111d27
img03.tplt.jpg img03.tplt 8372 -rwxr-xr-x file 09 October 2014 13:27:26 jpg e7c86bfef9850ff1b8d614ce35856a06
mxhead-dark.tplt.jpg mxhead-dark.tplt 42069 -rw-r--r-- file 10 October 2014 10:39:34 jpg b82a050f3400dd02a6246d32694d4ad7
mxhead-light.tplt.jpg mxhead-light.tplt 59228 -rwxr-xr-x file 10 October 2014 10:35:12 jpg ae41ec7d7ec40c7960d2318676c451fa
mxhead.tplt.jpg mxhead.tplt 44546 -rw-r--r-- file 10 October 2014 10:38:28 jpg 90e23346a3da304281e7125a8aa22113
test_pattern_green.gif test_pattern_green 23881 -rw-r--r-- file 19 January 2012 09:25:11 gif 053d9b94bc96ae204d37676498ccda7e
tinymolly.jpg tinymolly 5543 -rwxr-xr-x file 03 February 2011 14:12:37 jpg cad47d5ce2943b34103ca8e68e80d495
Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #108


maml:filename

Attributes:

Prototype: <maml:filename name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:filename>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the name of the file for each file found.

Alternatively you may use the *[filename]* token, for example if you wish to include the filename in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #230


maml:fileroot

Attributes:

Prototype: <maml:fileroot name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:fileroot>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the name of the file sans the extension for each file found.

Alternatively you may use the *[fileroot]* token, for example if you wish to include the fileroot in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #246


maml:filesize

Attributes:

Prototype: <maml:filesize name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:filesize>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the filesize of the file for each file found.

Alternatively you may use the *[filesize]* token, for example if you wish to include the filesize in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #262


maml:filetype

Attributes:

Prototype: <maml:filetype name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:filetype>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the filetype of the file for each file found.

Alternatively you may use the *[filetype]* token, for example if you wish to include the filetype in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #294


maml:fmd5

Attributes:

Prototype: <maml:fmd5 name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:fmd5>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the md5 hash of the file for each file found.

Alternatively you may use the *[fmd5]* token, for example if you wish to include the extension in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #342


maml:form

Attributes:

Prototype: <maml:form keyvalue = "" table = "" keyfield = "" criteria = "" response = "" mode = "" action = "" name = "" schema = "" ownership = "none" owner = "" linkname = "" linkfield = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:form>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #347


maml:if

Attributes:

Prototype: <maml:if condition = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:if>

Description: The <maml:if> tag allows one to conditionally display portions of the page.

<maml:if condition="'*[token]*' == 'value'">
    <p>True Stuff (inside an if).</p>
    <maml:else>
        <p>False Stuff (inside an else).</p>
    </maml:else>
</maml:if>
 
Path: /home/interper/system22/core/tags/maml.constructs.inc
Start Line: #7


maml:img

Attributes:

Prototype: <maml:img id = "" class = "" style = "visibility:visible;" permitted = ""></maml:img>

Description: This is a placeholder for a future <maml:image /> tag with the following features:

Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #248


maml:include

Attributes:

Prototype: <maml:include src = "" parse = "maml" debug = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:include>

Description: Reads the file specified in the src attribute and inserts it into the document. If parse="maml" then it is processed as a MAML file as it is being inserted. If parse="html" then only the portion of the file between the <body> and </body> tags is included. If src is a URL instead of a filespec, then this tag will fetch the file from the specified server. parse="xml" is not yet implemented.

Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #186


maml:input

Attributes:

Prototype: <maml:input type = "" style = "" name = "" value = "" size = "" selected = "" id = "" onclick = "" onfocus = "" onblur = "" onchange = "" onselect = "" onreset = "" hash = "" match = "" allowInEmail = "" class = "" permitted = ""></maml:input>

Description: The input tag is to be used within a maml:form tag and acts similarly to HTML form input types. The key difference of the maml:input tag is that it works alongside the maml:form tag to check input before being inserted into the database, and does so without the user needing to write any code.

Attributes:

Example:
    Input example here.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #742


maml:item

Attributes:

Prototype: <maml:item href = "" target = "_self" selected = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:item>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #105


maml:login

Attributes:

Prototype: <maml:login response = "/" fail = "Invalid Login" title = "" logout = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:login>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.security.inc
Start Line: #4


maml:menu

Attributes:

Prototype: <maml:menu type = "tabs" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:menu>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #72


maml:modtime

Attributes:

Prototype: <maml:modtime name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:modtime>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the modtime of the file for each file found.

Alternatively you may use the *[modtime]* token, for example if you wish to include the modtime in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #310


maml:multiple

Attributes:

Prototype: <maml:multiple count = "1" button = "after" label = "Add Another" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:multiple>

Description: This tag allows the user to replicate whatever it encloses using client-side (JavaScript/DOM) code. This is particularly useful inside a <maml:form> to allow the user to create several records at once when submitting a form. (This is actually what it is designed for, but we opened it up to be used outside fo forms to see what other uses we might find for it.)

Button value placements:

before Before the section to be duplicated.
after After the section to be duplicated.
beforeeach At the beginning of the section to be duplicated. (Gets duplicated along with section.)
aftereach At the end of the section to be duplicated. (Gets duplicated along with section.)
Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #274


maml:nothing

Attributes:

Prototype: <maml:nothing id = "" class = "" style = "visibility:visible;" permitted = ""></maml:nothing>

Description: The <maml:nothing tag allows you to insert tags to make the parser happy, such as declaring the maml namespace for files that only produce snippets for inclusion or AJAX calls.

Path: /home/interper/system22/core/tags/maml.xml.inc
Start Line: #6


maml:numrows

Attributes:

Prototype: <maml:numrows id = "" class = "" style = "visibility:visible;" permitted = ""></maml:numrows>

Description: Used in conjunction with <maml:fetch> tag to display the number of records returned.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #321


maml:option

Attributes:

Prototype: <maml:option id = "" onclick = "" ondblclick = "" onmousedown = "" onmousemove = "" onmouseout = "" onmouseover = "" onmouseup = "" onkeydown = "" onkeypress = "" onkeyup = "" disabled = "" label = "" selected = "" value = "" class = "" style = "visibility:visible;" permitted = ""></maml:option>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #1053


maml:permissions

Attributes:

Prototype: <maml:permissions name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:permissions>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the unix-style permissions string of the file for each file found.

Alternatively you may use the *[permissions]* token, for example if you wish to include the permissions in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #278


maml:persist

Attributes:

Prototype: <maml:persist name = "data" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:persist>

Description: Creates a token and stores the contents of the tags in that token. Tokens persist until the browser session ends or times out, or until a <maml:desist> tag is encountered.

Path: /home/interper/system22/core/tags/maml.tokens.inc
Start Line: #4


maml:protect

Attributes:

Prototype: <maml:protect denied = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:protect>

Description: The <maml:protect> tag allows one to restrict access to/viewability of a portion of page based on the user’s membership in a group specified in the permitted attribute. (These groups are represented in the molly_permissions table in the database.) The special group all allows all logged in users access, but not users who have not logged in (it represents, therefore, “all registered users”). Another special group guest allows only users who are not logged in to see the content.

The optional denied attribute specifies a string to be used to replace the protected section of the page. in the absence of the denied attribute the area will be left empty. Due to how Molly parses the file, even PHP code within the tag should be prevented from executing if the user is not permitted.

Path: /home/interper/system22/core/tags/maml.security.inc
Start Line: #60


maml:redirect

Attributes:

Prototype: <maml:redirect href = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:redirect>

Description: The <maml:redirect> leaves the current page and goes to the page specified in the href attribute. (Technically, it sends a location header to the browser telling it to request the new page.)

Path: /home/interper/system22/core/tags/maml.constructs.inc
Start Line: #147


maml:row

Attributes:

Prototype: <maml:row empty = "" maxentries = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:row>

Description: Used in conjunction with <maml:fetch> tag to retrieve results from a database.

The <maml:row> tag duplicates its contents for each row (record) found by the <maml:fetch> tag’s query.

See <maml:fetch> tag for example usage.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #211


maml:select

Attributes:

Prototype: <maml:select name = "" size = "" disabled = "" multiple = "" tabindex = "" onclick = "" onfocus = "" onblur = "" onchange = "" onselect = "" onreset = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:select>

prototype_tag is a generic class for MAML tags so that they can inherit methods such as id(). Undocumented tag classes inherit this documentation.
Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #961


maml:sha1

Attributes:

Prototype: <maml:sha1 name = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:sha1>

Description: Used in conjunction with <maml:filelist> tag to retrieve a list of files from a directory.

Replaces itself with the md5 hash of the file for each file found.

Alternatively you may use the *[fmd5]* token, for example if you wish to include the extension in an attribute of another tag.

See <maml:filelist> tag for example usage.

Path: /home/interper/system22/core/tags/maml.file.inc
Start Line: #359


maml:sort

Attributes:

Prototype: <maml:sort sort = "" order = "ascending" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:sort>

Description: Used in conjunction with <maml:fetch> tag to retrieve results from a database.

Creates links which allow the user to sort the results of the <maml:fetch> tag’s query.

See <maml:fetch> tag for example usage.

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #276


maml:textarea

Attributes:

Prototype: <maml:textarea name = "" id = "" cols = "75" rows = "10" class = "" style = "" onclick = "" onfocus = "" onblur = "" onchange = "" onselect = "" onreset = "" permitted = ""></maml:textarea>

Description: The maml:textarea tag is used to specify a textbox for multiline plaintext editing on the user's end. Textareas can be used within the maml:form tag, but are not required for use.

Example:

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #1327


maml:timeformat

Attributes:

Prototype: <maml:timeformat format = "unix" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:timeformat>

Description: Converts a timestamp (as from a database) to either unix or javascript timestamp format (i.e. second or miliseconds since January 1, 1970 00:00:00 UTC).

Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #1446


maml:token

Attributes:

Prototype: <maml:token name = "" value = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:token>

Description: The <maml:token> tag allows you to set the value of a token for use later on the page. The token specified by the name attribute is set to the contents of the tag. You may also use the optional value attribute which will override the contents of the tag and so should be used with he empty tag syntax for clarity.

<maml:token name="Fred">My Name is Fred.</maml:token>
Token *<span>[Fred]</span>* is <strong> *[Fred]* </strong>

or

<maml:token name="Fred" value="My Name is Fred" />
Token *<span>[Fred]</span>* is <strong> *[Fred]* </strong>
Path: /home/interper/system22/core/tags/maml.tokens.inc
Start Line: #42


maml:windoid

Attributes:

Prototype: <maml:windoid title = "" showMsg = "" hideMsg = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:windoid>

Description: Creates a window-like box around whatever it encloses based on the windoid.begin.tplt and windoid.end.tplt template files, the default.css styles, and values in the config database table. The title attribute is displayed in the titlebar of the windoid.

In previous versions of Molly this tag had additional attributes, some of which we may wish to re-create or re-think:

Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #2



Appendix III: Modules Reference

Modules are optional add-ons to Molly. Over time some modules may migrate into Molly's core.

Modules to be loaded are specified in the molly.ini file for the site in the [Modules] section. There each module is listed with the path to the module's directory. Molly will then automatically include any .inc files found within that directory. Most modules will only require one .inc file and by convention that .inc file will be named the same as the module. So, for example, the Calendar module included with Molly is located in the calendar directory and has one .inc file named calendar.inc

Example [Modules] Section of molly.ini

[Modules]		; path relative to [Paths]HTMLRoot + [Paths]ModulesDir
ZipCodes = "zipcodes/"
Calendar = "calendar/"

Modules should also include a documentation file named docs.htmf which is automatically included into this Modules appendix. (htmf stands for HyperText Markup Fragment and indicates that this file should include valid, well formed HTML and text, but is not a complete HTML document. Its contents are to be inserted into a complete HTML document.)

Simplest Example Module docs.htmf Document:

<h3>Module Name</h3>
<p>Docs go here.</p>

Site Specific Code

If you have site-specific PHP code you would like to include on all of your pages, creating a module for that site is the safest and most convenient way to accomplish that.

Installed Modules Documentation

The following documentation is generated automatically from information provided by the modules' authors.

../mp/docs.htmf

File Not Found


Appendix IV: Programmers' Documentation

This appendix is available for PHP programmers interested in understanding how Molly works "under the hood" but is primarily here for the active developers of the core Molly code. Please note that there is no need to understand or program PHP to use Molly. If you do not wish to extend or improve Molly, or write modules for Molly using PHP, then you can safely ignore this appendix.

Molly is open source, so if you make improvements or extensions to Molly please share them with us so that we can roll them into the next release.

File Types & Molly

Molly's Default behavior is to consider the extension of the files and processes them as follows:

Extension Action
.php Process as normal php, no Molly functionality
.inc PHP include files
.html Process similarly to Molly 1, that is, set all the globals and include basic function and object libraries and module libraries, then procede to pass the file through the php parser. (Note that for normal HTML files this does essentially nothing, but that it allows you to minimally incorporate some of Molly's functionality if you understand the underlying architecture.)
.maml Set all the globals and include all the libraries and modules, then parse the file through Molly's MAML XML parser.
.tplt template files
.ini prevent being served to the web
.txt (Maybe turn into a nice formatted web page?)
.css Treated like a template file - that is, all *[tokens]* are replaced with config values

Molly Naming Conventions

When developing code (including modules) please adhere to the following naming conventions:

Prefix Example Meaning
$gv_ $gv_VarName Global Variable (will be found in $GLOBALS)
$fv_ $fv_VarName Variable with scope local to a function
$go_ $go_ObjectName Global Object
$fo_ $fo_ObjectName Object with scope local to a function
$pv_ $pv_ParemeterName Parameter passed to a function (function scope)
$po_ $po_ParameterObject Object passed to a function (function scope) or class
$t_ $GET['t_Name'] Tokens passed via the query string
maml_ maml_TagName Class name for MAML Tag
molly_ molly_ClassName Class name for non-tag objects used in Molly
molly_ molly_TableName Database Table specific to Molly
m_ m_FunctionName Function that is part of Molly
MOLLY_ MOLLY_CONST_NAME Molly Constants

Inline Documentation

When defining a maml_ class to create a new tag programmers must take care to set the $doctext to explain the use of the tag. This will allow Molly to generate user documentation which is accurate and current with the code and better able to withstand versioning. We will use PHP's Nowdoc syntax (http://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.nowdoc) to accomplish this.

Molly System Constants

Constant Value
MOLLY_SYSTEM_DIRECTORY Unix Path to Molly's system directory (core functionality)
MOLLY_INI File Name of This Site's .ini file (allows multiple sites to share the same codebase by having different .ini files)

Molly's Internal Functions

m_AppendJavaScript($pv_Scripts, $pv_ScriptName, $pv_AllowDuplicates) [system/core/40_tags.inc]
Adds $pv_Scripts to the JavaScript section in the head of the page. $pv_ScriptName is tracked to know which scripts have already been added. $pv_AllowDuplicates is FALSE by default.
m_AppendQueryString($pv_Key, $pv_Value) [system/core/40_tags.inc]
Allows Molly to dynamically create the querystring for links such as <maml:sort>
m_AppendStylesheet($pv_Styles, $pv_StyleName, $pv_AllowDuplicates) [system/core/40_tags.inc]
Adds $pv_Styles to the stylesheet in the head of the page $pv_StyleName is tracked to know which styles have already been added. $pv_AllowDuplicates is TRUE by default.
m_ArrayLastKey($pv_TheArray) [system/core/80_environment.inc]
Returns the last key of an array
m_AutoInclude($pv_PathToIncludesDirectory, $pv_FileExtension) [system/loader.php]
Includes all the files in a directory with the specified extension
m_captchabasepath() [system/core/captcha/captcha-settings.php]
Returns the path to the captcha library directory.
m_captchacode() [system/core/captcha/captcha.inc]
Generates the random captcha code.
function m_captchadone() [system/core/captcha/captcha.inc]
Unsets the session variable that holds the current captcha code being tested against.
m_captchaimgurl() [system/core/captcha/captcha.inc]
Returns the global $captchaimg.
m_captchasendimg() [system/core/captcha/captcha.inc]
Generates an image of the current captcha code (generating a new code if one does not exist in the $_SESSION array). Randomly tilts, sizes, etc. the characters and generates noise to foil OCR systems.
m_captchasendwav() [system/core/captcha/captcha.inc]
Generates an spoken reading of the current captcha code (generating a new code if one does not exist in the $_SESSION array) formatted as a wav audio file.
m_captchawavurl() [system/core/captcha/captcha.inc]
Returns the global $captchawav.
m_CheckLogin($pv_LoginName, $pv_Password) [system/core/27_security.inc]
returns user_id for successful login, FALSE for failed login.
m_clean_params_for_query($pv_QueryComponent) [system/core/27_security.inc]
Used to clean user-provided data before building a query with it. Strips off semicolon and anything following it, then escapes other characters that are problematic in a database query.
m_DateGMT($p_UnixTimeStamp) [system/core/10_date_time.inc]
Converts a Unix timestamp to a formatted date, adjusted by user's timezone
m_debug($pv_TheDebugString) [system/core/05_utility.inc]
Caches debug string for output after parsing completes. Saves debug string(s) in a session variable until they can be displayed on the next MAML page since HTML pages are often used for invisible (to the user) forms processing. (see system/parser.php)
m_DirectoryList($pv_Directory, $pv_DateFormat="d F Y H:i:s") [system/core/15_file.inc]
Creates an array of hashes for each file in a specified directory. The hashes contain 'name', 'root', 'size', 'perm', 'type', 'time', 'extension' elements.
m_DoInsert($pv_Query, $pv_HostName='', $pv_User='', $pv_Password='', $pv_Database='', $pv_DatabaseApp='') [system/core/20_database.inc]
Does an insert query and returns an Insert ID.
m_DoLogin($pv_UserID) [system/core/27_security.inc]
Creates session variables to make a user logged in
m_DoQuery($pv_Query, $pv_HostName='', $pv_User='', $pv_Password='', $pv_Database='', $pv_DatabaseApp='') [system/core/20_database.inc]
Does a query and returns record set of the results.
m_EchoModulesDocs() [system/core/70_modules.inc]
Checks for the existence of docs.htmf file in each module directory (as specified in the molly.ini [Modules] section) and echos it (or a file not found message). This function is used in the docs page.
m_FetchDBHash($pv_Query, $pv_HostName='', $pv_User='', $pv_Password='', $pv_Database='', $pv_DatabaseApp='') [system/core/20_database.inc]
Does a query and returns an array of associative arrays (array of hashes)
m_FetchRecordsArray($p_TableName, $p_Fields, $p_Query, $p_OrderBy="", $p_Limit="") [system/core/20_database.inc]
Wrapper function for m_FetchDBHash that builds the SQL from appropriate pieces.
m_GetDocument ($pv_URL) [system/core/15_file.inc]
Returns the text of a file specified by $pv_URL
m_HashArrayToXML() [system/core/30_XML.inc] /* Not Currently Used */
m_ImplementedTags() [system/core/80_environment.inc]
returns an array of class names for implemented tags.
m_Interpolate($template, $hash, $prefix = '*[', $postfix = ']*') [system/loader.php]
Replaces placeholders in a template string with values from a hash
m_IsMollyTag($pv_TheTagName) [system/core/80_environment.inc]
Checks a string to see if it's a defined Molly tag classname
m_LoadModules() [system/core/70_modules.inc]
Loads modules specified in the molly.ini file for the site in the [Modules] section.
m_MakeUnixTimestampGMT($hh=0, $mm=0, $ss=0, $m, $d, $y) [system/core/10_date_time.inc]
Creates a unix timestamp from hour, minute, second, year, month, day
m_NakedHTML ($pv_URL) [system/core/15_file.inc]
Returns the text from between the <body></body> tags of an HTML document specified in $pv_URL
m_ParentDir($pv_Path) [system/loader.php]
Returns the parent directory pasth of the unix file path passed to it.
m_Permissions($pv_FilePathName) [system/core/15_file.inc]
Returns the the unix style permissions (as in ls -l) for the fully qualified file name passed as $pv_FilePathName.
m_permitted($pv_Group) [system/core/27_security.inc]
Checks to see if the currently logged in user is a member of the permissions group. Returns boolean.
m_PullParse() [system/core/90_parser.inc]
This is the actual XML parsing function that does Molly's magic. Starting with this version of Molly the parsing is done using the XMLReader extension (http://us.php.net/manual/en/intro.xmlreader.php). Previous versions used the expat XML Parser (http://us.php.net/manual/en/intro.xml.php).
m_session_close() [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers.
m_session_destroy($pv_Key) [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers.function m_session_destroy deletes the associated record in the session table, thus ending the session.
m_session_gc($maxlifetime) [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers. This is the function which collects garbage by deleting expired sessions from the sessions database. The probability that PHP will run this function is set in the php.ini directive "session.gc_probability."
m_session_open($pv_SavePath, $pv_SessionName) [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers. This function collects garbage by deleting expired sessions from the sessions database whenever a new session is opened.
m_session_read($pv_Key) [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers. This function reads the user's session data from the sessions database updates their session record to reset their session expiration time.
m_sess_write() [system/core/25_sessions.inc]
Callback function for session_set_save_handler() allowing Molly to store sessions and associated information in a database table. Among other things this allows us to easily determine who's logged in, and to share sessions across multiple web servers. This function stores the session data for a particular user. If the user already exists in the table, it updates the user's session value and expiry time. PHP serializes the session data and sends it in as a string.
m_SessionQuery($pv_Query) [system/core/25_sessions.inc]
Sends a query to the session database as configured in the molly.ini file - such that multiple servers can share the same session.
m_StringToStyleClassName($pv_TheString) [modules/calendar/calendar.inc]
Converts a string to a valid CSS class name. (Part of Calendar module.)
m_testDOMi() [system/core/30_XML.inc] /* Not Currently Used */
m_TimeGMT($p_UnixTimeStamp) [system/core/10_date_time.inc]
Converts a Unix timestamp to a formatted time, adjusted by user's timezone
m_ValidateAgainstDB($pv_LoginName, $pv_Password) [system/core/27_security.inc]
Returns a user_id for successful login, FALSE for failed login using login database and hash algorithm specified in the molly.ini file.
m_ValidateAgainstLDAP($pv_LoginName, $pv_Password) [system/core/27_security.inc]
Returns a user_id for successful login, FALSE for failed login using the LDAP/Active Directory server specified in the molly.ini file. If the suser does not exist in the Molly login/username tables a record is created for the newly ldap-validated user.

The following list of Molly's internal functions is generated automatically for this particular installation.

Output Buffering & Molly

Output Control

The output buffering functions allow you to control when output is actually sent from the web server to the browser. By default, output is sent as soon as it's generated.

Output buffering can be useful in several different situations, especially if you need to send headers to the browser after your script has begun outputting data. The Output Control functions do not affect headers sent using header() or setcookie(), only functions such as print(), echo, var_dump() and plain HTML between blocks of PHP code are buffered.

Start Buffering

Use the ob_start(); function to turn output buffering on. From this point, no output will be sent to the browser until you turn off buffering.

End Buffering

To end buffering and send all the accumulated text on its way to the browser, call the ob_end_flush(); function.

Simple Example

<?php
ob_start();
echo "Hello\n";
 
setcookie("cookiename", "cookiedata");
 
ob_end_flush();
 
?>

This simple example allows you to send headers - with setcookie() - "after" you've had output from the echo.

Output Buffering in Molly

"OK, but why does this matter to me?"

When you're programming in Molly, you need to know that before any output is sent, output buffering is turned on. And the last thing Molly does after parsing a page is turn output buffering off and send the whole page.

So...

Let's...

...build a tag that uses output buffering: the <maml:smiley> tag.

The smiley tag will find any smileys like :) ;) and :( in its enclosed text and convert them into graphical smileys like :) ;) and :(

To save you some work, I've written a function that does the actual conversion for you, so all we have to worry about is the output buffering. The function is defined thusly:

<?php
    function smileys2Graphics($p_TheText) {
 
        global $g_HTMLRoot;
        $p_TheText =  str_replace (':)','<img src="' . $g_HTMLRoot . 'decor/icons/smile.gif" alt=":)">',$p_TheText);
        $p_TheText =  str_replace (':-)','<img src="' . $g_HTMLRoot . 'decor/icons/smile.gif" alt=":-)">',$p_TheText);
        $p_TheText =  str_replace (':(','<img src="' . $g_HTMLRoot . 'decor/icons/pout.gif" alt=":(">',$p_TheText);
        $p_TheText =  str_replace (':-(','<img src="' . $g_HTMLRoot . 'decor/icons/pout.gif" alt=":-(">',$p_TheText);
        $p_TheText =  str_replace (':P','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":P">',$p_TheText);
        $p_TheText =  str_replace (':p','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":p">',$p_TheText);
        $p_TheText =  str_replace (':b','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":b">',$p_TheText);
        $p_TheText =  str_replace (':-P','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":-P">',$p_TheText);
        $p_TheText =  str_replace (':-p','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":-p">',$p_TheText);
        $p_TheText =  str_replace (':-b','<img src="' . $g_HTMLRoot . 'decor/icons/yuck.gif" alt=":-b">',$p_TheText);
        $p_TheText =  str_replace (';)','<img src="' . $g_HTMLRoot . 'decor/icons/wink.gif" alt=";)">',$p_TheText);
        $p_TheText =  str_replace (';-)','<img src="' . $g_HTMLRoot . 'decor/icons/wink.gif" alt=";-)">',$p_TheText);
        $p_TheText =  str_replace ('[])','<img src="' . $g_HTMLRoot . 'decor/icons/coffee.gif" alt="[])">',$p_TheText);
        $p_TheText =  str_replace (':.','<img src="' . $g_HTMLRoot . 'decor/icons/oh.gif" alt=":.">',$p_TheText);
        $p_TheText =  str_replace (':-.','<img src="' . $g_HTMLRoot . 'decor/icons/oh.gif" alt=":-.">',$p_TheText);
        return $p_TheText;
    } //smileys2Graphics
?>

Skeleton Tag

Here is the skeleton of our tag code. (All MAML tags start this way.)

class maml_smiley extends prototype_tag {
    protected $doctext = <<<'DOCBLOCK'
Convert typed smileys to graphics
DOCBLOCK;
 
    public function begin(){ //called at the <maml:smiley> tag
 
    }
 
    public function end(){ //called at the </maml:smiley> tag
 
    }
}
 

The Plan

We only want to convert smileys between our tags, so what we want to do is save the text that's been buffered so far and flush the buffer when our begin tag is encountered. When we get to the end tag, the only thing in the buffer is what's between our tags. At that point we can save that, flush the buffer again, and return the saved text to the buffer. Finally we can process the text between our tags and add it to the restored buffer.

Save and Flush

class maml_smiley extends prototype_tag {
    protected $doctext = <<<'DOCBLOCK'
Convert typed smileys to graphics
DOCBLOCK;
    protected $OutputCache='';
 
    public function begin(){ //called at the <maml:smiley> tag
        $this->OutputCache = ob_get_contents();
        // Fetch and save all the output cached thus far and... 
        ob_clean(); // ...clean the output buffer
    }
 
    public function end(){ //called at the </maml:smiley> tag
 
    }
}
 

Process and Restore

class maml_smiley extends prototype_tag {
    protected $doctext = <<<'DOCBLOCK'
Convert typed smileys to graphics
DOCBLOCK;
    protected $OutputCache='';
 
    public function begin(){ //called at the <maml:smiley> tag
        $this->OutputCache = ob_get_contents();
        // Fetch and save all the output cached thus far and... 
        ob_clean(); // ...clean the output buffer
    }
 
    public function end(){ //called at the </maml:smiley> tag
    // Fetch all the data between the <maml:smiley> and
    //</maml:smiley> tags after it's been parsed
        $v_SmileyText = ob_get_contents();
 
         // clean the output buffer                                    
        ob_clean();
        // return the previously saved cached output to the output buffer
        echo $this->OutputCache;
        //Process our text
        $v_GraphicText=smileys2Graphics($v_SmileyText);
        // add the converted text to the page buffer
        echo $v_GraphicText;
    }
}
 

Molly is Smart

Because we do this all the time in Molly, the prototype_tag class which we extended to create our tag has output buffering methods and variables built in to simplify this process:

class maml_smiley extends prototype_tag {
    protected $doctext = <<<'DOCBLOCK'
Convert typed smileys to graphics
DOCBLOCK;
 
    public function begin(){ //called at the <maml:smiley> tag
        $this->startCache();
    }
 
    public function end(){ //called at the </maml:smiley> tag
        $this->finishCache();
        $v_GraphicText=smileys2Graphics($this->tagCache);
        // add the converted text to the page buffer
        echo $v_GraphicText;
    }
}
 

Appendix V: Colorizing Images

colorize.php

The colorize.php script allows template images to be colorized on-the-fly, making it possible to do more sophisticated web designs, yet still control the overall color scheme via Molly's config module.Transparency is supported for PNG and GIF templates. Output format is PNG.

This page is just a quick demo of how the script works.

PNG Template

Here is a greyscale test image:

Test Pattern

By using colorize.php in either an <img> tag or a CSS style we can render that same image in color. In the example below, we are using this HTML:
<img src="../decor/colorize.php?template=test_pattern.png&color=00FF00" alt="Test Pattern" style="width:95%;" />

Test Pattern

More sophisticated effects can be achieved by starting with a colored template image such as this:

Test Pattern

and then adding an additional color (in this case red):
<img src="../decor/colorize.php?template=test_pattern_green.png&color=FF0000" alt="Test Pattern" style="width:95%;" />

Test Pattern

GIF Template

Here is a greyscale test image:

Test Pattern

By using colorize.php in either an <img> tag or a CSS style we can render that same image in color. In the example below, we are using this HTML:
<img src="../decor/colorize.php?template=test_pattern.gif&color=00FF00" alt="Test Pattern" style="width:95%;" />

Test Pattern

More sophisticated effects can be achieved by starting with a colored template image such as this:

Test Pattern

and then adding an additional color (in this case red):
<img src="../decor/colorize.php?template=test_pattern_green.gif&color=FF0000" alt="Test Pattern" style="width:95%;" />

Test Pattern

JPEG Template

Here is a greyscale test image:

Test Pattern

By using colorize.php in either an <img> tag or a CSS style we can render that same image in color. In the example below, we are using this HTML:
<img src="../decor/colorize.php?template=test_pattern.jpg&color=00FF00" alt="Test Pattern" style="width:95%;" />

Test Pattern

More sophisticated effects can be achieved by starting with a colored template image such as this:

Test Pattern

and then adding an additional color (in this case red):
<img src="../decor/colorize.php?template=test_pattern_green.jpg&color=FF0000" alt="Test Pattern" style="width:95%;" />

Test Pattern

Appendix VI: Change Log

Version 18 Revision 7.15 Release

This is an big and exciting release for Molly! This release includes weeks of work improving both security and functionality.

All Molly users are highly advised to update as soon as possible.

Preparing to update your site & other notes on new, different behaviors

Molly Changes

New features

Release testing

Molly has been tested successfully against the following (note that only Apache is supported):

The following did not work in testing, although your mileage may vary:

Library updates

Security updates

While previous versions of Molly are vulnerable to some SQL-based attacks, the information needed to successfully do these attacks is heavily dependant on the webmaster not displaying or otherwise giving out database-related information, such as item IDs. All Molly users are highly encouraged to update their instances as soon as possible. The difficulty of these exploits ranges from "really easy, if you know the ID" to "would require some good hacking skills to exploit." On the worse end of the exploits allows for full modification of everything in the database, given adequate knowledge by the attacker.

Other updates

Module Changes

New Module: MAGE

(pronounced "Maggie")

The MAGE Graphics Module gives functionality to create SVG-based charts in a manner compatible with <maml:fetch>-based database queries.

Module: Calendar

Module: Schedule

Module: Quiz

Module: MollyPoint