▲
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):
<maml:fetch table="molly_test" key="name" criteria="true" sort = "name" order = "ascending">
<table style="width:22em; background-color: #f4f5fb;padding:1em;">
<tr>
<th><maml:sort sort="name">Name</maml:sort></th>
<th><maml:sort sort="eyecolor">Eye Color</maml:sort></th>
<th><maml:sort sort="age">Age</maml:sort></th>
</tr>
<maml:row>
<tr>
<td><maml:field name="name"/></td>
<td><maml:field name="eyecolor"/></td>
<td><maml:field name="age"/></td>
</tr>
</maml:row>
<tr>
<th colspan="3" style="text-align:right;">Total Records: <maml:numrows/></th>
</tr>
</table>
</maml:fetch>
- 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.
- 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.
- Our first table row will have the column headers.
- 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.
- As above we've added sorting, by eye color in this case.
- And here we're sorting by age.
- End of the header row of the table.
- 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.
- Because we're building an HTML table we'll repeat a
<tr>
table row.
- 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.
- Likewise this table data cell will be filled with eye color.
- And this with data from the table's age field.
- End of the table row.
- And end of the record (and the portion of the code which will repeat for every record found).
- We're adding one last row to our table…
- Which will display the number of rows Molly found in the database (and rendered above).
- 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:
- ActiveLinkColor
- AttachmentsDirectory
- BackgroundColor
- BackgroundImage
- BoxFrameColor
- ConfigTable
- CurrentYear
- FileRoot
- FirstName
- FormDark
- FormHandler
- FormLight
- FormMedium
- HTMLRoot
- HoverLinkColor
- LastName
- LinkColor
- LoggedIn
- LoginName
- MenuColor
- MenuLinkColor
- MenuRolloverBackgroundColor
- MenuRolloverTextColor
- MenuSelectedBackgroundColor
- MenuSelectedTextColor
- MenuTextColor
- PageColor
- QueryString
- Salutation
- SelectedTabColor
- SidebarHeadingBackgroundColor
- SidebarHeadingTextColor
- SidebarLinkColor
- SiteLogo
- SiteName
- Skin
- SkinDirectory
- SkinScripts
- SystemFileRoot
- SystemRoot
- TabColorHover
- TextColor
- UnselectedTabColor
- UserID
- VisitedLinkColor
- WebCronLastRun
- WindoidFieldColor
- WindoidFrameColor
- copyright
- javascript
- stylesheet
- tagline
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:
- Database
- Login/Users
- User Group Permissions
- if/else
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
- Wiki (MVC) Approach
- Skins
- Location
- Creating
- main.css
- main.js.htmf
- Templates
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.
- Test your HTML template with the W3C Validator.
- Once it checks out, save the template as a .maml file.
- Replace the doctype and HTML tags with Molly versions (XHTML 1.1).
- Test your template in Molly to make sure the XHTML is well formatted.
- Create a new skin by duplicating an existing skin.
- Delete the existing CSS file in the skin directory and the images in the images folder.
- Copy your stylesheet into the skin directory and rename it “main.css”.
- Copy your images into the images folder.
- Change the skin in the molly.ini file.
- 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:
forms
scripts
skins
index.maml
- you are here!
- .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):
AddType application/x-httpd-php .maml .html .php .tplt .ini .inc .txt .css .mss .php4 .php3 .phtml
DirectoryIndex index.maml index.html index.php
php_flag register_globals off
SetEnv MOLLY_INI molly.ini
php_value short_open_tag off
php_value auto_prepend_file /Users/rvullo/Sites/mx/system/loader.php
php_value auto_append_file /Users/rvullo/Sites/mx/system/parser.php
# 404: Not Found
ErrorDocument 404 /~rvullo/mx/error/404.maml
IndexIgnore .htaccess
Options -Indexes
php_value upload_max_filesize 30M
Line-by-line Explanation:
- Tells Apache to process .maml, .html, .css, etc. files with PHP so that Molly can parse them.
- Tells Apache that files named "index.maml" should be loaded instead of listing the contents of a directory.
- #
- 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.
- #
- 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.
- #
- 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.
- #
- Tells PHP to automatically include Molly's loader code at the beginning of every processed file.
- Tells PHP to automatically include Molly's parser code at the end of every processed file.
- #
- #
- 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.
- #
- Tells Apache not to serve .htaccess files.
- #
- Turns off Apache's automatic directory listings.
- #
- 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
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
tags
html.override.inc
maml.ajax.inc
maml.constructs.inc
maml.database.inc
maml.format.inc
maml.security.inc
maml.tokens.inc
maml.xml.inc
database
scripts - now located in
utilities as of June 11th, 2018
- 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
block_insert.html
class.upload.php
drag_drop_upload.html
insert.html
login.html
logout.html
register.html
replace.html
search.html
upload.html
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
- 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:
- Sets up a series of global variables with relevant file paths:
- $gv_CurrentFile
- $gv_CurrentFilePathInfo
- $gv_CurrentFileExtension
- $gv_MollySystemDirectory
- $gv_MollySystemDirectory
- Reads the values from Molly's .ini file into the $gv_SiteGlobals array.
- Checks the .ini
Secure
setting and redirects to the same page using https if appropriate
- Checks to see if the current file's extension is ".php" and if so skips the rest of the loading process.
- 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.
- Initializes the global variable $gv_IDNum which is used to generate unique DOM IDs by various maml tags.
- Initializes the global $gv_Config array with some default values. These are used as tokens for values in the various skins.
- 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.
- Load any persistant tokens from the global $_SESSION array.
- 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).
- Set up a global variable so Molly can check if someone is logged in
- Start Buffering PHP's output so that the current file can be captured and parsed before outputting it.
- 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."
- Using SSH connect to your server and log in. Change directories to your web directory.
- If you do an ls you should see the .tar.gz file that you just uploaded.
- 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
- Configure Database
- Install Database Tables
- Edit
molly.ini
file
- Edit
.htaccess
file
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.
- On newer PHP systems "mysqli" should be used instead of "mysql" as the database type.
- In line 34, change "jpeg" to "png" if necessary depending on your operating system.
- In line 41, change the email address to your own email address.
- In lines 48, 49, and 50, change the file paths to the correct paths to your directories.
- In lines 62, 63, 68, 69, 92, 93, 147, and 148, change the information to your database and username.
- In lines 64, 70, 94, and 149, change the password to your own password.
- In line 72, make the session name something unique.
-
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
- PHP hashing algorithms: https://secure.php.net/manual/en/function.hash-algos.php
- PHP HMAC/PBKDF2 algorithms: https://secure.php.net/manual/en/function.hash-hmac-algos.php
- Note that PBKDF2 uses HMAC, this is not well-documented
- PHP's developers have designed functions specifically for hashing passwords.
- Currently defaults to the bcrypt/blowfish algorithm, but Argon2i was added in PHP 7.2
- Both of these can be set manually set to be used, or
auto
can be used to use the PHP default and automatically rehash as it get changed
- NIST currently recommends
pbkdf2_sha256
or pbkdf2_sha512
- Note that HMAC/PBKDF2 algorithms changed a lot with PHP 7.2 & versions before that have algorithms unsuitable for usage with HMAC hashing. On PHP <7.2 you should use an algorithm on both lists algorithm lists linked above
- Note that SHA512/224, SHA512/256, & SHA-3 were introduced in PHP 7.1
- Finally, just because an algorithm is on one of those lists, it doesn't mean it's a good algorithm. It only means PHP has an implementation of it built-in. Usage of
auto
, pbkdf2_sha256
, pbkdf2_sha512
are recommended
PasswordEncryption setting in molly.ini
- none = cleartext - convenient for lazy development but should NEVER be used in production
- auto = use PHP's functions to automatically hash, verify, and rehash passwords with PHP's recommended defaults. This will override the
OldPasswordHandling
to "rehash" for any PHP password_hash
algorithm, but only for those (currently bcrypt/blowfish & Argon2i).
- bcrypt/blowfish = blowfish using password_hash
- argon2i = Argon2i algorithm, only available on PHP 7.2 and higher
- (algo) can be any valid PHP hashing algorithm, for example, sha256, sha512, and md5 (md5 not recommended, but still in use unfortunately). Please check the above link to see allowed algorithms, and please check which are in your version of PHP.
- hmac_(algo) can be any valid HMAC-compatible PHP hashing algorithm, for example, sha256 & sha512. Please check the above link to see allowed algorithms, and please check which are in your version of PHP.
- pbkdf2 hash using PBKDF2 with SHA256 algorithm, 64,000 iterations, 32 byte salt
- integrate will attempt all hashing methods available; will disable the
OldPasswordHandling
option and defer to the system being integrated with for management (since it can't reliably make passwords compatible with the other side).
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
- 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:
- 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
- All Molly file extensions (
.maml
, etc.) will need to be configured to be handled by PHP-FPM in addition to .php
.
- May need to manually specify the
www-data
user (or whatever your particular operating system uses) in the www.conf
file.
- Might need to add all Molly file extensions to the
security.limitextensions
property in www.conf
file.
- The loader and parser will need to be given in
php.ini
since the .htaccess
file is not processed by Nginx.
- Access to
/system
should be denied.
.maml
will need to be added to the list of indexes.
- If your config is named anything other than
molly.ini
then a PHP environment variable will need to be set (can be done with fastcgi_param
).
IIS on Windows Server
- All Molly file extensions (
.maml
, etc.) will need to be configured to be handled by FastCgiModule in addition to *.php
.
- The loader and parser will need to be given in
php.ini
since the .htaccess
file is not processed by IIS.
- Access to
/system
should be denied.
.maml
will need to be added to the list of indexes.
- If your config is named anything other than
molly.ini
then a PHP environment variable will need to be set.
- Even though Windows uses backslashes (
\
) for file paths, forward slashes (/
) should be used for the Molly FileRoot
path in molly.ini
.
- Molly has been run with the PHP & MySQL in the "Microsoft Web Platform Installer" but has not been successfully run under SQL Server.
XAMPP on Windows
- Even though Windows uses backslashes (
\
) for file paths, forward slashes (/
) should be used for the Molly FileRoot
path in molly.ini
.
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:
- target
- interval
- url
- trigger
- id
- class
- style
- permitted
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:
- blocknum
- wiki
- blockid
- id
- class
- style
- permitted
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:
- title
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- to
- subject
- replyTo
- id
- class
- style
- permitted
Prototype: <maml:email to = "" subject = "" replyTo = "" id = "" class = "" style = "visibility:visible;" permitted = ""></maml:email>
Sends an email upon <maml:form>
submission.
- The to attribute (required) denotes the address to send the email to.
- The subject attribute (required) denotes the subject of the email. Tokens are allowed.
- The replyTo attribute (optional) denotes the address that receivers should send their replies to.
- The tag contents are the body of the email, and can contain tokens as well.
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:
- name
- id
- class
- style
- permitted
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:
- query
- table
- fields
- sort
- order
- criteria
- maxentries
- start
- key
- detail
- host
- database
- user
- password
- id
- class
- style
- permitted
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:
- <maml:row> tag’s maxentries attribute
- <maml:previous>
- <maml:next>
- <maml:pages>
- <maml:detail>
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:
- name
- id
- class
- style
- permitted
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:
- path
- extension
- dateformat
- id
- class
- style
- permitted
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:
filename | fileroot | filesize | permissions | filetype | modtime | extension | fmd5 |
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:
- name
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- keyvalue
- table
- keyfield
- criteria
- response
- mode
- action
- name
- schema
- ownership
- owner
- linkname
- linkfield
- id
- class
- style
- permitted
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:
- condition
- id
- class
- style
- permitted
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:
- "non-downloadable" images (i.e. no right-click saving)
- optionally add watermark and/or custom message to the image
- hides actual server location of the image from the user
Path: /home/interper/system22/core/tags/maml.format.inc
Start Line: #248
maml:include
Attributes:
- src
- parse
- debug
- id
- class
- style
- permitted
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:
- type
- style
- name
- value
- size
- selected
- id
- onclick
- onfocus
- onblur
- onchange
- onselect
- onreset
- hash
- match
- allowInEmail
- class
- permitted
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:
- type: Defines the type of input the user can enter. Options include text, password, radio, checkbox, button, hidden, and more. Hidden inputs will be moved to the session information and re-injected in the submission. This means it will not appear in the page HTML & allows for carrying over information the user shouldn't see.
- name: Defines the name of the input field, which is submitted along with the form data.
- value: Specifies the initial value of the input field. Required for radio buttons and checkboxes to determine their labels.
- size: If type is text or password, this specifies the maximum number of characters allowed in that field. Otherwise, this attribute specifies the initial display size of the input field.
- id: Defines the ID of the input field, which is used to reference that input and its contents.
- allowInEmail: denotes that the value of this input can be used as a token in a
<maml:email>
tag as the given string, prepended by "f_"
. This option does not work on password inputs.
- hash: Hash the input with the default password hashing method in
molly.ini
- match: Requires the value of this field to match the specified field
Example:
Input example here.
Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #742
maml:item
Attributes:
- href
- target
- selected
- id
- class
- style
- permitted
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:
- response
- fail
- title
- logout
- id
- class
- style
- permitted
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:
- type
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- count
- button
- label
- id
- class
- style
- permitted
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.)
- count: Positive integer. How many times to automatically replicate the contents on the client side (default is 1).
- button: Where to position the button(s) that the user clicks to replicate the contents. May be one or more of the following:
none, before, after, beforeeach, aftereach
- label: What text to label the button(s) with. Default is "Add Another"
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:
- id
- onclick
- ondblclick
- onmousedown
- onmousemove
- onmouseout
- onmouseover
- onmouseup
- onkeydown
- onkeypress
- onkeyup
- disabled
- label
- selected
- value
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- denied
- id
- class
- style
- permitted
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:
- href
- id
- class
- style
- permitted
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:
- empty
- maxentries
- id
- class
- style
- permitted
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:
- name
- size
- disabled
- multiple
- tabindex
- onclick
- onfocus
- onblur
- onchange
- onselect
- onreset
- id
- class
- style
- permitted
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:
- name
- id
- class
- style
- permitted
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:
- sort
- order
- id
- class
- style
- permitted
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:
- name
- id
- cols
- rows
- class
- style
- onclick
- onfocus
- onblur
- onchange
- onselect
- onreset
- permitted
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.
- name: Specifies the name of the textarea; submitted alongside form data when used in maml:forms.
- id: Specifies the ID of the textarea, and used to reference this input.
- class: Required for use.
- style: Specifies visibility and other style attributes for the textarea.
- cols: Specifies the number of columns the textarea will display. By default, set to 75.
- rows: Specifies the number of rows the textarea will display. By default, set to 10.
Example:
Path: /home/interper/system22/core/tags/maml.database.inc
Start Line: #1327
maml:timeformat
Attributes:
- format
- id
- class
- style
- permitted
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:
- name
- value
- id
- class
- style
- permitted
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:
- title
- showMsg
- hideMsg
- id
- class
- style
- permitted
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:
- frameColor: colorspec
- frameLeft: thickness (i.e. 2px)
- frameRight: thickness
- frameTop: thickness
- frameBottom: thickness
- fieldColor: colorspec
- titleAlign [left|center|right]
- titleStyle: any style spec valid for text
- backLink: URL, relative or absolute
- titleColor: color of windoid title
- titleFontFamily: font family of title
- titleFontSize: size of title
- titleFontWeight: [bold|bolder|normal], etc.
- borderStyle: [solid|dotted|dashed], etc.
- background: add an image to windoid background
- dragable: can user move the windoid around the page?
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.
- m_appendjavascript ( $pv_Scripts, $pv_ScriptName, [$pv_AllowDuplicates = ] )
- m_appendquerystring ( $pv_Key, $pv_Value )
- m_appendstylesheet ( $pv_Styles, [$pv_StyleName = ], [$pv_AllowDuplicates = 1] )
- m_arraylastkey ( $pv_TheArray )
- m_autoinclude ( $pv_PathToIncludesDirectory, $pv_FileExtension )
- m_captchabasepath ( )
- m_captchacode ( )
- m_captchadone ( )
- m_captchaimgurl ( )
- m_captchasendimg ( )
- m_captchasendwav ( )
- m_captchawavurl ( )
- m_checkhashbrute ( $pv_password, $pv_hashed )
- m_checkhashdefault ( $pv_password, $pv_hashed, $pv_algorithm )
- m_checklogin ( $pv_LoginName, $pv_Password )
- m_clean_array ( $pv_input )
- m_clean_params_for_query ( $pv_QueryComponent )
- m_dategmt ( $p_UnixTimeStamp )
- m_debug ( $pv_TheDebugString )
- m_directorylist ( $pv_Directory, [$pv_DateFormat = d F Y H:i:s] )
- m_doinsert ( $pv_Query, [$pv_HostName = ], [$pv_User = ], [$pv_Password = ], [$pv_Database = ], [$pv_DatabaseApp = ] )
- m_dologin ( $pv_UserID )
- m_doquery ( $pv_Query, [$pv_HostName = ], [$pv_User = ], [$pv_Password = ], [$pv_Database = ], [$pv_DatabaseApp = ] )
- m_echomodulesdocs ( )
- m_fetchdbhash ( $pv_Query, [$pv_HostName = ], [$pv_User = ], [$pv_Password = ], [$pv_Database = ], [$pv_DatabaseApp = ] )
- m_fetchrecordsarray ( $p_TableName, $p_Fields, $p_Query, [$p_OrderBy = ], [$p_Limit = ] )
- m_getdocument ( $pv_URL )
- m_hasharraytoxml ( $pv_HashArray, [$pv_XSLT = default] )
- m_hashpassword ( $pv_password, [$pv_algorithm = ], [$pv_salt = ], [$pv_iterations = ] )
- m_implementedtags ( )
- m_interpolate ( $template, $hash, [$prefix = ] )
- m_ismollytag ( $pv_TheTagName )
- m_loadmodules ( )
- m_makeunixtimestampgmt ( $hh, $mm, $ss, $m, $d, $y )
- m_nakedhtml ( $pv_URL )
- m_parentdir ( $pv_Path )
- m_pbkdf2 ( $password )
- m_pbkdf2_verify ( $password, $hash )
- m_permissions ( $pv_FilePathName )
- m_permitted ( $pv_GroupList )
- m_pullparse ( $pv_XMLString )
- m_testdomi ( )
- m_timegmt ( $p_UnixTimeStamp )
- m_validateagainstdb ( $pv_LoginName, $pv_Password )
- m_validateagainstldap ( $pv_LoginName, $pv_Password )
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...
- Your code can send headers without fear
- You can manipulate the buffered text
- You can capture your own buffers (ie. between tags you're writing)
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:
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%;" />
More sophisticated effects can be achieved by starting with a colored template image such as this:
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%;" />
GIF Template
Here is a greyscale test image:
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%;" />
More sophisticated effects can be achieved by starting with a colored template image such as this:
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%;" />
JPEG Template
Here is a greyscale test image:
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%;" />
More sophisticated effects can be achieved by starting with a colored template image such as this:
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%;" />
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
- We have updated the options in
molly.ini
. Please consult the new version.
- The setting
FormHandler
and the string sanitization section are of particular note
- Backup your site before doing anything, including your database
- The folders
system/scripts/
and system/forms/
have been moved to utilities/scripts/
and utilities/forms/
, respectively
- Forms will now time out if the session expires. This is part of a security measure detailed below.
- If this is proves to be an issue for you, please change your PHP session configuration or use a
<maml:ajax>
tag that loads a Molly page in the background to keep the session alive
- This change will break any custom code that uses Molly's forms system
- As the MySQL extension for PHP has been removed, the extension MySQLi is needed instead in
molly.ini
- Please see the documentation for all module- & tag-related information
Molly Changes
New features
- Molly is now PHP7-compatible!
- Skin improvements
- Molly now has a pseudo-skin called
_default
that provides fallbacks for missing templates when newer functionality is added
- Available skins use HTML5 Semantic elements
- Available skins are now responsive
- New, modern default skin called
mx2
- New/improved tags
-
<maml:captcha>
now works
-
<maml:ajax>
for loading asynchronous content; and continuously if desired
-
<maml:fmd5>
, <maml:sha1>
for hashes of files from <maml:filelist>
-
<maml:menu>
has more types
-
<maml:input>
tags with the attribute input="hidden"
will be ingested into the session and not even shown on the page (prevents unwanted alteration by the user)
-
<maml:email>
tag for sending emails on form submission
- All elements (except
<maml:block>
and <maml:calendar>
for backward compatibility reasons) now have a permitted
attribute that can be used to protect page content like <maml:protect>
does. This also got a cool security upgrade, details below
- External link checker (for links in the database only) available to logged-in admins & the W3C Link Checker at
admin/linkcheck.maml
molly.ini
can now be given an absolute path in .htaccess
- Molly will slightly optimize the page load by moving & grouping the following where it thinks it safely can:
- CSS in the
<head>
- JavaScript at the bottom of the
<body>
- This can be disabled with the new
Optimize
setting in the Site
section of molly.ini
if it causes issues
- Molly will automatically add some encoding information to the top of the
<head>
- The group-checking
m_permitted()
function can now accept multiple groups as well as a guest
value for users who are not logged in
Release testing
Molly has been tested successfully against the following (note that only Apache is supported):
- Debian 8, 9
- CentOS 6, 7
- Windows Server 2016
- Apache
- Nginx w/ PHP-FPM
- XAMPP (on Windows Server)
- IIS (Windows Server)
- MariaDB 10 (roughly MySQL 8)
- PHP 5.3, 5.6, 7.2
The following did not work in testing, although your mileage may vary:
- Microsoft SQL Server
- Oracle SQL Server
- PostgreSQL
- SQLite
Library updates
- jQuery updated to 3.3.1
- jQuery Migrate 1.4.1 was installed for backward compatibility
- TinyMCE updated to 4.7.13
- ADOdb updated to 5.20.12
- FancyBox is no longer open source and as such as been replaced with Featherlight (http://noelboss.github.io/featherlight/)
Security updates
- Tags removed from the page (such as through the
permitted
attribute) will get the parser to completely remove that element & its children from the page instead of a visual removal
- In testing this was also able to stop PHP within these tags from running, however, this may or may not work depending on your server configuration
- Forms have been moved out of the
system/
folder so that system/
can be moved out of the web root without breaking anything
- Forms have been tied to the user's session with strict checking on validation
- Other data-handling improvements
- Removed multiple
eval()
statements, additional security measures the rest
- General cleaning for SQL Injection-related attack types
- Using ADOdb's driver-based sanitization function now (see update notes above for require changes to
molly.ini
)
- All user input is automatically sanitized in the
$_GET
, $_POST
, & $_REQUEST
variables
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
- Documentation improvements
- Code cleaning & optimization
- Better code practices, reducing potential PHP Errors & Warnings
- Improvements to installer script
- Updated some code that has been deprecated in PHP, such as
ereg_replace
- The
.htaccess
setting MOLLY_INI
can take absolute paths now
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.
- Create scatterplots, line graphs, bar charts, histograms, & pie charts
- Tag structure allows for use with getting data from
<maml:fetch>
& <maml:row>
- Compatible with regular SVG elements
Module: Calendar
- New
timeline-horizontal
and timeline-horizontal-grouped
views
- ICS exporting of events at
modules/calendar/export.maml
- If you want your calendar protected, you might want to delete that file or add an
.htaccess
to prevent access
- Updated to match new functionality in Molly
- Minor visual improvements
Module: Schedule
- Now works! You can use
<maml:schedule>
and <maml:a>
to show content & links (respectively) for only a given period of time
- New: Use the
schedule.ini
configuration file to do webcron jobs
Module: Quiz
- Show all quizzes on the page at once
- Allow quizzes to be taken by logged-out users
- Including scoring not being logged in the database
- New config option to dis/allow guest access
- More question types: checkboxes & radio buttons can both be used in the same quiz
- Improved scoring feedback
- Minor visual improvements
Module: MollyPoint
- Now has a YouTube backend! Uploading large files to servers no longer needed
- Significantly improved load time for slide list
- More flexible
- Visual improvements
- Updating to match new functionality in Molly
©2000-2018 by Ronald P. Vullo, Ph.D. • All Rights Reserved