Converting Old Modules
----------------------------------
Introduction
----------------------------------
Three years ago when starting phpWebSite 1.0, the thought was that it
should be totally completely backward compatible. In the beginning of
development, it was, but eventually the features in phpWebSite outgrew
the previous version.
A decision had to be made. Do we want to limit the features of 1.x to
allow full compatibility? I tried to balance them instead. Old modules
will work in 1.x, but they require some modification.
I have tried to keep the amount of changes to a minimum: I have to
make them in all my modules as well.
This document was written while converting PhotoAlbum. This module may
be rewritten down the road, but for now if just needs to _work_. While
working, I will go over what changes were made to make it functional
again.
Installing the module
----------------------------------
Boost has been written to install modules written before 1.x. So you
should be able to get your module recognized in phpWebSite, you will
just need to get it working again.
One change you should make to your module before installing it is to
update the permissions file. Before, permissions were kept in a file
named module_rights.txt. It is in the conf/ directory. For PhotoAlbum,
it looks like this:
add_album::Add Album
edit_album::Album Settings
add_photo::Add Photo
edit_photo::Edit Photo
delete_photo::Delete Photo
delete_album::Delete Album
We are going to create a new file named permission.php and save it in
the conf/ directory. Here is what it looks like:
You will notice that I commented out item_permissions. We will get to
that later. For now, keep it out or comment that line out. Modules
from the old phpWebSite did not use item permissions.
Once you create the permissions file, you can delete the
module_rights.txt file.
What we see
----------------------------------
Layout has changed a great deal from 0.x.
1) it doesn't make the developer use GLOBALS
2) it doesn't need a layout.php config file
3) it has a default setting for putting content in the BODY tag
4) the Default theme is no longer required
5) there are no _boxes_ anymore
As a result of these changes, Layout is faster, easier to use, and
avoids some of the pitfalls of the old software (i.e. missing boxes,
extra boxes, etc.)
In order to get your module showing up again, you need to open up your
layout.php file in the module's conf directory.
In PhotoAlbum, I find out that the content variable is "CNT_photoalbum"
and the transfer variable is "body".
With this information I can search for the CNT_photoalbum string.
After a quick search I found:
$GLOBALS['CNT_photoalbum']['content'] = "
$title
$content";
$GLOBALS['CNT_photoalbum']['title'] = $_SESSION['translate']->it("Photo Album") . ": " . $_SESSION['PHPWS_AlbumManager']->album->getLabel();
$GLOBALS['CNT_photoalbum']['content'] .= $this->_view();
This is bad because the html formatting is in the code but we'll
ignore it for now. There is also a $_SESSION['translate'] in
there. We'll come back to that as well.
For now let's fix it to work with layout.
$template['TITLE'] = $_SESSION['translate']->it("Photo Album") . ": " . $_SESSION['PHPWS_AlbumManager']->album->getLabel();
$template['CONTENT'] = "$title
$content";
$template['CONTENT'] .= $this->_view();
$finalContent = PHPWS_Template::process($template, 'layout', 'box.tpl');
Layout::add($finalContent);
The 'template' lines are just substitutions for the previous code.
The finalContent portion needs a little explanation. Remember change
number five (no boxes). The old phpWebSite expected these box
templates to display the info. Since these are not used anymore, we
use a substitution, Layout's own box.tpl. It is basic but functional.
If you really wanted to, you can create your own box template and use
it instead.
Now we pass the complete text to Layout via the add function. Look
back at the information from the layout.php config file. Notice that
the transfer variable is "body". This is fortunate as Layout uses that
as the default tag to print content. Body is always the main content
area of a theme. So I can use:
Layout::add($finalContent);
If PhotoAlbum had a side box or a transfer variable that was NOT body,
I would use the following instead:
Layout::add($finalContent, 'photoalbum', 'CNT_photoalbum');
Notice we don't use the transfer variable at all. We can recycle the
content variable as the place holder. The content variable name in 1.x
just makes sure that the data ends up in the same place. So if had
some data going to our CNT_photoalbum two times we could use:
Layout::add($finalContent, 'photoalbum', 'CNT_photoalbum');
Layout::add($someOtherContent, 'photoalbum', 'CNT_photoalbum');
Using the same word makes sure that it appears in the same place. As
such,
Layout::add($finalContent);
Layout::add($someOtherContent);
would append the content in the "body" tag.
Now I just search for CNT_photoalbum in the index.php file and all the
classes, create the template array, pass it through the template class
to Layout's default box.tpl and put the result into the 'add'
function.
Once you are done with this process you should start seeing
your basic module screens appearing again.
Translation
-----------------------------------
Previous to 1.x, phpWebSite used its own translation class. This was
clunky. 1.x uses 'po' files (see Language.txt for more information).
Since the language module is no longer used, you should remove the old
functions calls. You module will still function, but no one outside
your native tongue will be able to use your module.
Prepare for a search and replace. We need to change:
$_SESSION['translate']->it('Hello World');
to
dgettext('module_name', 'Hello World');
The quickest way to fix this is to replace
$_SESSION["translate"]->it("
to
dgettext('module_name', '
Make sure to try other searches such as:
$_SESSION['translate']->it(" to dgettext('module_name', "
$_SESSION["translate"]->it(' to dgettext('module_name', '
$_SESSION["translate"]->it(" to dgettext('module_name', "
Notice the quote difference. It is very important.
After you have changed all these translations, you will need to
perform one last search and replace. The old translation system used
[var1], [var2], etc. to fill in values into the string. PO files don't
do that. So search for "[var" and add the 'sprintf' function.
Here is an example:
$message = dgettext('module_name', "The Photo Album [var1] was
successfully saved.","" . $this->getLabel() .
"") . "
\n";
change this to:
$message = sprintf(dgettext('module_name', 'The Photo Album %s
was successfully saved.'), '' . $this->getLabel()
. '') . "
\n";
If you don't fix these first, you will get syntax errors.
Working out the bugs
---------------------------------------
If you module is now showing up, the next step is cleaning up the
bugs. These should be pretty easy to spot as you click on forms and
links. Below are some changes to make your software function properly
again.
Checking for Javascript
--------------------------------------
The old way of checking to see if the user was using javascript was to
query the user session:
if($_SESSION['OBJ_user']->js_on) {
doSomething();
}
In 1.x use javascriptEnabled()
if(javascriptEnabled()) {
doSomething();
}
WYSIWYG
-----------------------------------
The old javascript wysiwyg (what you see is what you get) interface
was really simplistic.
You should allow users to access the new available interfaces. Look
for something like the following:
$tags['ALBUM_EXT'] = PHPWS_WizardBag::js_insert("wysiwyg",
"PHPWS_Album_edit", "Album_ext") . $tags['ALBUM_EXT'];
To fix this, first thing I do is go edit the template (editAlbum.tpl
for this module) and remove the ALBUM_EXT tag. We won't be needing it.
Then I find the code that makes the text box:
$form->add("Album_ext", "textarea", $this->_blurb1);
Now I just add the following:
$form->useEditor('Album_ext');
You may have to check the text going into the text area but otherwise
it should be fine.
Error Class
-----------------------------------
The error class has changed.
One of the major changes is that you can log your errors. This makes
the 'debug' option of the old error class obsolete. Errors dump a user
to an error page. You must plan for errors and guide the user out
gracefully.
As such, when an error occurs, make sure your code logs the error and
continues.
The new Error class is based on the PEAR error class. The old one was
written specifically for phpWebSite.
To change over, you need to look for instances where the old error
object was created. Here is an example from photoalbum.
if(!is_dir(PHOTOALBUM_DIR . $this->getId() . "/")) {
PHPWS_File::makeDir(PHOTOALBUM_DIR . $this->getId() . "/");
if(!is_dir(PHOTOALBUM_DIR . $this->getId() . "/")) {
$message = $_SESSION['translate']->it("The photo album image directory could not be created.");
$error = new PHPWS_Error("photoalbum", "PHPWS_Album::_save()", $message, "continue", PHOTOALBUM_DEBUG_MODE);
$error->message("CNT_photoalbum");
$_REQUEST['PHPWS_Album_op'] = "edit";
$this->action();
return;
}
}
$message = $_SESSION['translate']->it("The Photo Album [var1] was successfully saved.", "" . $this->getLabel() . "") . "
\n";
$_SESSION['PHPWS_AlbumManager']->message = new PHPWS_Message($message, "CNT_photoalbum");
$_REQUEST['PHPWS_Album_op'] = "view";
$this->action();
Now this code WILL work in 1.x. however I suggest you upgrade it to
the new format.
Here are my changes.
$directory = PHOTOALBUM_DIR . $this->getId() . "/";
if(!@mkdir($directory)) {
$message = dgettext('module_name', 'The photo album image directory could not be created.');
PHPWS_Error::log(PHOTOALBUM_NO_DIRECTORY, 'photoalbum', 'PHPWS_Album::_save', $directory);
$_REQUEST['PHPWS_Album_op'] = "edit";
} else {
$message = sprintf(dgettext('module_name', "The Photo Album %s was successfully saved."), "" . $this->getLabel() . "") . "
\n";
$_REQUEST['PHPWS_Album_op'] = "view";
}
$_SESSION['PHPWS_AlbumManager']->message = new PHPWS_Message($message, "CNT_photoalbum");
$this->action();
First, a few code changes. mkdir does the job of PHPWS_File::makeDir
and I don't need to check its existence after making it, I just catch
the result of mkdir. I also put the directory into a variable so I am
not concatenating the variables more than once.
Now if the directory creation fails I am logging the error in the
PHPWS_Error class. It then prints the failure message and goes
back to the edit screen. If it succeeds, it prints the success message
and goes to the view screen.
The message will be recorded in the log via the
PHOTOALBUM_NO_DIRECTORY assignment. If you are unfamiliar with how the
Error class works, please read the Error.txt documentation.
Adding Keys
-----------------------------------
There are some modules that will not work with your module unless you
give them 'keys'.
The phpWebSite key class cross indexes all content within your
site. This allows modules to "see" your content and help you organize
it. Fortunately, it isn't really hard to add this functionality to
your module.
First, remove calls to modules that now depend on keys. Categories is
one of those modules. In pre-1.x, to categorize an item, you had to
insert a FatCat select box into your form. Categories doesn't work
like that. So look for places that call Fatcat, and yank them out.
Next, you need to add a key_id column to the item you want to index. I
_could_ index the individual photos however I think I would prefer to
index the albums.
So I add a column to mod_photoalbum_albums:
ALTER TABLE mod_photoalbum_albums ADD key_id INT NOT NULL AFTER id;
Make sure to update your install.sql file (not with the above of
course, add the column to your create table).
I also create a new variable in the album class:
var $_key_id = 0;
Note that this module is using the Item class which expects an
underline before the variable name. You need to do this, or it won't
work correctly.
Next I need to create a key for each album. Since I want to create the
key after the item is saved, I look for the save function in the Album
class. Once there, I add my key creation function:
$this->saveKey();
My key creation function looks like so:
function saveKey()
{
$update_album = FALSE;
if (empty($this->_key_id)) {
$key = & new Key;
$update_album = TRUE;
} else {
$key = & new Key($this->_key_id);
if (PEAR::isError($key->_error)) {
$key = & new Key;
$update_album = TRUE;
}
}
$link = sprintf('index.php?module=photoalbum&PHPWS_Album_op=view&PHPWS_Album_id=%s', $this->_id);
$key->setModule('photoalbum');
$key->setItemName('album');
$key->setItemId($this->_id);
$key->setEditPermission('edit_album');
$key->setUrl($link);
$key->setTitle($this->_label);
$key->setSummary($this->_blurb0);
$result = $key->save();
$this->_key_id = $key->id;
if ($update_album) {
$this->commit();
}
return $key;
}
First I check for the existance of the key_id. If it is 0, we make a
new key. Next I set all the relevant key information (see the Key
documentation for more information).
Next I save the key, set the key_id in the object, and, if this is a
new key, update the album object.
Serving the Key
------------------------------------
We are halfway done with updating the key! Once your key is saved with
your item, you need to 'flag' it.
Go to that item's 'view' function. For PhotoAlbum the function is
'_view'.
Get the key id and instantiate a new key object:
$key = & new Key($this->_key_id);
Now flag it:
$key->flag();
Once the key raises its flag, all the modules that are looking for it
will now be accessible.
File types
------------------------------------
All file types (documents and images) are now kept in
config/core/file_types.php.
You can check if an image is allowed by using classes within the File
Cabinet module:
PHPWS_Core::initModClass('filecabinet', 'Image.php');
PHPWS_Image::allowType($filetype);
For other documents use:
PHPWS_Core::initModClass('filecabinet', 'Document.php');
PHPWS_Document::allowType($filetype);
Other things I changed
------------------------------------
Here are some odds and end of things I changed in PhotoAlbum.
PHPWS_Album checks for an error when it sets the id. Not needed as
long as it isn't zero.
I changed many the double quotes (") to single quotes (') where
possible. Double quotes require the text between to be parsed whereas
single quotes do not. This saves a wee bit of time.
Removed the tab characters and substituted 4 spaces per.