Multiple PHP Instances With One Apache

 

Long-winded Introduction

It took me a couple of days to figure this out due to lack of decent tutorials and not enough confidence in my Linux skills to build programs from source. I think I have the hang of it now, and write this up with the intent on providing another, or the only, tutorial on setting up CentOS 5 with multiple instances of PHP using one Apache install. That being said, there are a number of good tutorials out there, just none of them explicitly for CentOS and some leave out some details that n00bs like me get confused about.

PHP4 and PHP5 on SuSE 10.1 – This was by far the most helpful of the tutorials. Even though it was written for SuSE, it works almost straight across for CentOS.

There is also a great list of instructions in the comments on the php.net site under installing PHP for Apache 2.0 on Unix systems (see http://www.php.net/manual/en/install.unix.apache2.php#90478).

I found this one after I wrote up this tutorial at http://cuadradevelopment.com. It’s a bit different, but should work as well.

There are basically two different ways I could have done this. 1- run a single instance of Apache, and run one instance of PHP as a module, and other installs as CGI. 2- run several instances of Apache, each with it’s own instance of PHP as a module. I chose to do the first method for no particular reason. Dreamhost has a post about the good and bad with running PHP as CGI.

So basically, the steps are: 1. Set up Apache and have PHP install as a module. 2. Configure and make another instance of PHP to run as CGI. 3. Add a virtual host to Apache running under a different port to access the PHP as CGI.
Continue reading Multiple PHP Instances With One Apache

Convert WP to static HTML – part 2

This is a followup to this previous post.

So I’ve been converting some more blogs to static html files, and this time around things seemed to be so different, that I made up a new how to. Here are the steps that I’ve been using to convert blogs using the default Kubric theme.

  1. Update the permalink structure for the site so that it uses the year, month, day, postname structure.
    UPDATE `database`.`prefix_options` SET `option_value` = ‘/%year%/%monthnum%/%day%/%postname%/’ WHERE `prefix_options`.`option_name` = ‘permalink_structure’ LIMIT 1 ;
  2. Make sure the blog does not block search engines. If the blog is set to block them, wget can only download the index.html file. And this took me a while to figure out. So, for the sake of search engines, if wget only downloads the index.html file or wget recursive gets only index.html file, then remember to check your robots.txt or similar settings. Either edit in the admin section (under Settings->Privacy) or via SQL.
    UPDATE `database`.`prefix_options` SET `option_value` = '1' WHERE `prefix_options`.`option_name` = 'blog_public' LIMIT 1 ;
  3. Add the .htaccess file if not already there, where
    /path/to/wordpress/blog/

    starts at the URL root, not the absolute file path. So http://sitename.com/path/to/wordpress/blog/ would have the .htaccess file below in the ‘blog’ directory.

    RewriteEngine On
    RewriteBase /path/to/wordpress/blog/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /path/to/wordpress/blog/index.php [L]
  4. Get rid of the meta links through the sidebar widget in the admin, or delete the appropriate lines from the theme files (for default Kubric theme edit comments.php, sidebar.php, single.php, footer.php), or see the last step. Delete the code that puts in the search, comments, trackback, rss, and anything in the footer you want out.
  5. When all is good, run wget to grab the files.
    wget --mirror -P blog-static -nH -np -p -k -E --cut-dirs=5 http://sitename.com/blog/
  6. Rename the blog directory. mv blog blog-old
  7. Rename the static directory to be live. mv blog-static blog
  8. Copy the images directory from the old theme to the appropriate static directory.
    cp -r blog-old/wordpress/wp-content/themes/default/images/ blog/wordpress/wp-content/themes/default/
  9. Alternative to get rid of unwanted links, etc. Use the find command to find all html files, then use perl to delete the lines. Don’t forget to escape forward slashes in the search field. Unfortunately, this method requires you to do it for every line of code you want to delete. It’s much better to delete the lines out of the theme files. The code below has an unnecessary space in the opening H3 tag so it will render properly.
    find . -name \*.html | xargs perl -ni -e 'print unless /< h3>Leave a Reply< \/h3>/'

    Also, if you want to just search and replace instead of remove, this handy find and perl one-liner will find and replace text in all html files.

    find . -name *.html | xargs perl -p -i'' -e "s/search text here/replace text there/"

    The above would search for all the “search text here” phrases in all html files, and replace it with “replace text here”. You can obviously substitute whatever you want in those to places. If you have a ‘/’ (forward slash) character, it will need to be escaped with a ‘\’ (back slash) character. Perl uses the regular regular expression syntax, so look that up if you need help formulating a search and replace structure.

Major Update to Multiple SVN WordPress Installs

It’s been a while, but I thought it important to post an update to the wpupdate program I wrote to upgrade a whole mess of WordPress installs at one time. I took a cue from the program officially sponsored by WP, but think mine is much, much better. 🙂

Here are some of the features:

  1. Specify a file with a list of svn WP installs, or update the current directory, or specify the directory to update on the command line
  2. Use command flags or options. You can specify the program to update or switch, use tags or branches.
  3. Automatically saves a copy of the svn update to a file so your terminal is not overflowing with text, but does output any conflicts that arise.
  4. Automatically saves a backup copy of the wp-contents directory (just in case the update or switch screws something up).
  5. Automatically saves a copy of the database, backing up only the tables used by the WP install (based off of the wp-config supplied table prefix).
  6. Restores permissions to the original owner and group.

Anyhow, check out the new page devoted soley to this application: SVN WordPress Updater

License: Anyone is free to use this program however they want, as long as they give me due attribution. Also if you update or modify the program in any way, I need to know about it. That’s what free and open software is all about. Any updates should benefit us all.

MediaWiki’s version information

I spent way too long looking for MediaWiki’s version information. Most applications store the version info in a file or in the database. MediaWiki is a HUGE mess of code and database. I finally found it in a file. It’s in includes/DefaultSettings.php and is stored as a variable – wgVersion.  This is for post 1.3.7 versions of MediaWiki, perhaps even earlier versions.

I write this here because no amount of searching turned up this information. I found it through a series of lucky greps. So, to help out in Google searches:

where is MediaWiki’s version information stored?

locate MediaWiki’s version info

MediaWiki version file

MediaWiki version info

THAT podcast

Check out THAT podcast (THAT = The Humanities And Technology). It’s a new video pod cast put on by a couple of co-workers at CHNM. They interview someone in the technical field about software that helps those of us in the humanities.

The first episode includes an interview with Matt Mullenweg, creator of WordPress (the software running this site!) and shows you how to install and configure ScholarPress (a plug-in to WordPress written by Jeremy Boggs).

It’s great stuff, check it out!

Converting WordPress to static html

UPDATE: Check out the new post on a better way to do this here: Convert WP to Static HTML Part 2. Or see the page devoted to the script here: Make WordPress Static.

Usually people are wanting to convert their static html pages to some dynamic content management system. I’ve run into the issue of needing to go the other way.

A few professors at GMU love to use WordPress for their classes. It’s a really great way to get more student participation and involve some of those who aren’t so talkative in class.

But these blogs are usually only needed for one semester, and then just sit there. This can be a security risk if they are not kept up to date, and is cumbersome when trying to update many of them (one professor had over 30 blogs!).

Sometimes the content should still be viewable, but the need for a whole cms type back-end no longer exists. Sometimes the professor would just like a copy of the pages for their own future research or whatever.

So, I figured out a way to convert a dynamic WordPress site into static html pages.

Here are the basic steps I used:

  1. Change the permalink structure in the WordPress admin section. Alternatively, directly in the database change wp_options.permalink_structure.option_value to “/%postname%.html”.
    [code lang=”SQL”]
    UPDATE `database`.`prefix_options` SET `option_value` = ‘/%year%/%monthnum%/%day%/%postname%/’ WHERE `prefix_options`.`option_name` = ‘permalink_structure’ LIMIT 1 ;
    [/code] 

    UPDATE (2.12.08): Reading a post from Christopher Price (who linked to this post) about WP permalinks, I’m thinking using this structure (/archives/%post_id%.html) might afford the best results. I often found a page that displayed the raw HTML instead of being rendered. This just might fix that issue.

    UPDATE (3.11.08): I did some more dynamic to static conversions today, and found out the best permalink structure to use is just the post name. No extra categories and such. So the best structure to use would be this (/%postname%.html). The benefit is that the every page is unique with a descriptive name for the url (albeit sometime very long), and there are not as many subdirectory issues that arise.

    UPDATE (7.17.09): This time around, I have found that the following seems to work best for permalink: /%year%/%monthnum%/%day%/%postname%/ And cleaned up the SQL statement.

  2. Add the .htaccess to /path/to/wp/ if not already there (where /path/to/wp/ is from http://somedomain.com/path/to/wp/ ). If there already is a .htaccess file and it is set to have permalinks, then you can probably leave it as it is.
    RewriteEngine On
    RewriteBase /path/to/wp/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /path/to/wp/index.php [L]
  3. Use wget to copy all of the files as static html files.
    [code lang=”bash”]wget –mirror –wait=2 -P blogname-static -nH -np -p -k -E –cut-dirs=3 http://sitename.com/path/to/blog/[/code]
    *** Change –cut-dirs to the appropriate number associated with how many directories are after the domain name. The trailing slash plays a part too. ****
    UPDATE (03.11.08): I found that the –cut-dirs doesn’t really do anything this time around.
    UPDATE (7.17.09): This time around, I find the following to work best, even the –cut-dirs. 

    wget --mirror -P wpsite-static --cut-dirs=3 -nH -p -k -E https://site.com/path/to/wp/

    This has the bonus of making the directory for you, thus negating the make directory step. Make sure to use two dashes and not an em dash.

  4. Copy the contents of wp-content to save uploaded files, themes, etc. This way copies a lot of unnecessary php files, which could be potentially dangerous, but is really easy if you’re just converting to archive. To remove the security threat, just pick and choose the files you need.
    [code lang=”bash”]cp -r /path/to/wp/wp-content/* /path/to/static/wp-content/[/code]
  5. Sometimes the files are created with folders in the archives folder. To fix this run the following three commands in the archive folder to fix that up. To get rid of the feed file in all of the directories:
    [code]rm -f */feed [/code]
    To delete all of the now empty direcotries:
    [code]find . -type d -exec rmdir ‘{}’ \;[/code]
    To rename the files ###.1 to ###
    [code]rename .1 ” `find . -type f -name “*.1″`[/code] That’s two single quotes after the first ‘.1’
  6. UPDATE (03.11.08): I have found that the old ‘rename‘ command [rename .1 ” *.1]only works on the current directory. If you want to do a recursive renaming you have to use the ‘find‘ command. The above code has changed to reflect this.
    UPDATE (7.14.09): When the rename with find doesn’t work, it’s probably because the post has comments, so there is a folder with the same name as the post’s filename. In this case, just move the file (with the .1 extension) into the folder of the same name, but change the name of the file to index.html

  7. move to wp folder. make a backup of database: [code lang=”bash”]mysqldump -u [userfromwp-config.php] -p –opt databasename > databasename.sql[/code]
    UPDATE (03.11.08): I found I needed to backup just a few tables from a database that contained many copies of wordpress. To do this more easily, I used a little script I wrote earlier to dump tables with a common prefix. This could also work if you just put in the full name of only the tables you wanted to backup.
  8. move one directory above wp install. make tar backup of old wordpress folder: [code lang=”bash”]tar -cf wordpress.tar wordpress/[/code]
  9. rename the old wordpress folder [code]mv wordpress wordpress-old[/code]
  10. move the static copy into place [code]mv static/wordpress/ wordpress/[/code]
  11. test out the site. If it’s totally broke, just delete the wordpress directory and restore the original from the tar file.
  12. remove the tar file and wordpress-old directory as needed.

How does the yahoo/flash map work…

There’s a lot of tutorials out there, but here’s how I got it to work.

The Plan

Got what? Oh, well, basically it’s being able to use yahoo maps, flash, xml, php, mysql, and data to display a bunch of markers on a map. Each marker contains data pulled from the database, and has a link to a page that displays the full information. For now, I just made a display page. But sometime latter, it will be implemented into the fine Omeka install that the 1989 project will use.

The Steps

Here’s an outline of what I did.

  1. Create a flash file using the yahoo flash map api component. Available at Yahoo.
  2. Add some ActionScript to the first frame that will import data from an xml file.
  3. Create a php page that pulls in data from a MySQL database and displays as an xml file.

Create the swf

In order to make Yahoo and flash play nicely, you need to install the component linked above. Follow directions there. This will add a new component to your Flash program, available in your components window (Window->Components->Yahoo->com.yahoo.maps.api.flash.YahooMap).

Once that’s installed, create a new empty fla. Make a keframe in the first frame on layer one. Drag the yahooMap component onto your stage.

Next, add another layer for your actionscript.

the ActionScript

Here’s the tricky part. Here’s the code I used, heavily commented:

[code lang=”Actionscript”]
/*********************************
* File: yahoomap.as
* Author: Ammon Shepherd
* based off of many tutorials and code snippets, mostly from Yahoo! documentation.
********************************/
//Set the URL where the xml file is.
var xmlURL = “http://chnm.gmu.edu/1989/maps/xml.php”;

//Import Yahoo map tools
import com.yahoo.maps.LatLon;
import com.yahoo.maps.markers.CustomPOIMarker;
import com.yahoo.maps.tools.PanTool;
import com.yahoo.maps.widgets.*;

//Create a listener associated with the instance of the Yahoo map component.
// Set the map to pull down the yahoo map and get the setting/changes from
// the onMap function.
euroMap.addEventListener(com.yahoo.maps.api.flash.YahooMap.EVENT_INITIALIZE, onMap);

//This will add a zoom tool and set it as closed by default
var zoomer:NavigatorWidget = new NavigatorWidget(“closed”);

//The onMap function adds the tools, settings, and markers to the map.
function onMap(eventData) {
//create a new PanTool and name it pantool
var pantool:PanTool = new PanTool();

//tell the instance of the map to use the new PanTool called pantool
euroMap.addTool(pantool, true);

//Add the Navigator widget
euroMap.addWidget(zoomer);

//Create a variable called xmlData of the type XML
var locations_xml:XML = new XML();

//igore white space
locations_xml.ignoreWhite = true;

//when the xml file is loaded, do the fuction within
locations_xml.onLoad = function(success){
//if the xml file loaded successfully run the function addMarkers
if (success) addMarkers(this);
};

//then actually load the xml file.
locations_xml.load(xmlURL);

/*******************************************
* Used this for testing, you don’t really need it. I was thinking of using
* this in the future to display the lat and long to the user.
//Get the center of the map and store them in the variable called points.
var points = euroMap.getCenter();
//trace(“before convert” + points);
//swap the points, so long->long and lat->lat
convert(points);
//trace(points);
******************************************/

}

//This is the function that adds the markers to the map.
function addMarkers(xml:XML):Void {
//the addresses variable holds the path to the right xml element.
//firstChild is the and childNodes is an array of the tags
var addresses = xml.firstChild.childNodes;

for(var i=0; i then the second and so on for all of them.
address = addresses[i];

/***************************************************************
* MarkerData is the object that holds the info for each marker.
* index: is the text that shows up on the marker (usually just A or
* what have you. you could use the variable i to show an
* incremented number (each marker has it’s own number).
* title: sets the title of the marker and corresponds to the title
* parameter in the location tags in the xml file.
* description: is the larger text. It corresponds with the description
* parameter in the location tag in the xml field.
* markerColor: sets the color of the marker, duh 🙂 It’s the border
* color, and the color when not expanded.
* strokeColor: is the background.
***************************************************************/
var MarkerData:Object = {index: ‘\’89’, title:address.attributes.info, description:address.attributes.description, markerColor:0xcc0000, strokeColor:0xceccc7};

/****************************************************************
* This actually adds the address to the map using the custom point of
* interest style of marker (you can use your own style if you like.
* the address.attributes.loc grabs the latitude and longitude from
* the loc=”” part of the location tag in the xml file. MarkerData
* is the stuff we just set above.
****************************************************************/
euroMap.addMarkerByAddress(CustomPOIMarker, address.attributes.loc, MarkerData);
}
}
[/code]
Download yahoomap.as

(Special thanks to CodeSnippet for providing a way to show code. This was the 5th one I tried that finally worked.)

The PHP/XML

Next we need an xml file. I need to pull the data from a database, so I used a php script which pretends to be an xml file. The basics of that is to put this in the php file:
[code lang=”php”]
// Date in the past
header(“Expires: Mon, 26 Jul 1997 05:00:00 GMT”);
// always modified
header(“Last-Modified: ” . gmdate(“D, d M Y H:i:s”) . ” GMT”);
// HTTP/1.1
header(“Cache-Control: no-store, no-cache, must-revalidate”);
header(“Cache-Control: post-check=0, pre-check=0”, false);
// HTTP/1.0
header(“Pragma: no-cache”);
//XML Header
header(“content-type:text/xml”);
[/code]

Then, because our database doesn’t store the geocodes by default yet, nor do the items have any type of specific address or what not associated with them, I created an array with specific places and their lat, long. Each of the items in the database have tags associated with them. The array corresponds to tags that have countries names on them. This poses a problem if the tag name is changed, but for now it works OK.

[code lang=”php”]
$countries = array(
array(“Poland”, “52.1874047455997”, “19.072265625”),
array(“East Germany”, “52.517474393230245”, “13.41156005859375”),
array(“Czechoslovakia”, “49.210420445650286”, “17.490234375”),
array(“Hungary”, “47.45780853075031”, “19.05029296875”),
array(“romania”, “45.98169518512228”, “25.07080078125”),
array(“Yugoslavia”, “44.26093725039923”, “18.96240234375”),
array(“Bulgaria”, “42.601619944327965”, “25.24658203125”),
array(“Soviet Union”, “55.70235509327093”, “37.79296875”)
);
[/code]

The problem I ran into next was that there were over 15 markers that would share the same lat an long. This makes it hard to click on a marker when it’s buried under 20 other markers. So at first I tried just adding a couple of digits to the lat and long as it looped through the array. This led to a long line of markers, and didn’t look very good. Random numbers to the rescue! To create a clustering affect I generated two random numbers, one for the lat and one for the long. This number was added giving the markers a clustered appearance. Here’s that part of the code:

[code lang=”php”]
echo “< ?xml version='1.0' ?>

“;

for($g=0; $g‘;

}
}

echo ‘
‘;
[/code]

You may notice my call to the description function (which, in turn, calls a function called truncate, but you wouldn’t know that yet). This simply turns all HTML characters into their HTML entity characters. HTML in an XML file will break the XML file… more specifically the < and " ' characters. The truncate function I grabbed from php.net's comments for substr_replace. The truncate function shortens long text to a set number of words, and adds an ellipse at the end to signify more text. Here are those two functions:

[code lang=”php”]
function description($id,$text){
$description = wordwrap(htmlentities(truncate($text,200), ENT_QUOTES),30);

$fulltext = $description.”\n<a target=’_blank’ href=’http://chnm.gmu.edu/1989/maps/results.php?id=$id’>Show item</a>”;

return $fulltext;
}

function truncate($text,$numb) {
// source: www.kigoobe.com, please keep this if you are using the function
//$text = html_entity_decode($text, ENT_QUOTES);
if (strlen($text) > $numb) {
$text = substr($text, 0, $numb);
$text = substr($text,0,strrpos($text,” “));
$etc = ” …”;
$text = $text.$etc;
}
//$text = htmlentities($text, ENT_QUOTES);
return $text;
}
[/code]

Here’s the whole php file.
That basically does it.

xml, and lots of time.

Back again. Only two weeks left of school.

I spent the past two days trying to work XML into the picture for the time line aspect. The thought was, if all of the data for the ‘flash point’ or points of the events of 1989 were kept in an xml file, the the flash could dynamically add them in at the right place and time.  I can bring in the xml data, but I can’t get it to display the correct information for each event. It always shows the last events data. I have a for loop to grab all of the data from the xml file, then it attaches the ‘flash point’ movie clip in the correct place. Then I assign an onRollOver and onRollOut to that movie clip (dynamically named). But the onRollOver just holds the data from the last element in the xml instead of each attached movie clip holding the data for it’s respective event.

This would be really cool to do, because then to add or remove events from the timeline, just edit the xml file.  The xml filed could even theoretically pull the data in from the database, making so that each person can have their own specific timeline.  That would be way cool. But it unfortunately seems a bit beyond me. The other obstacle is about the time aspect. How can I get the flash point to show up at the right time. I’d have to come up with a bunch of mathematical voodo on where the timeline bar is (the _x position on the stage) and the percentage left and compare that with the date of the event somehow. Aargh! Way too confusing for my feeble brain.

I’ll just have to do the easy hard code the events into the timeline. Anyhow, here’s what I have so far on the intro/timeline (which, I’m realizing, I’ll need to have a front page where you select which one you want to view, or have them separate instead of one after the other). Version 0.0.2.3.9.5 (or something like that)

Getting it to work.

I’m finally getting somewhere with the Yahoo/Flash map. The latest is always viewable at http://chnm.gmu.edu/1989/maps/yahoo.html. Tonight I worked on getting the title and description to show up nice. The description is usually really long, so I found a php function that truncates it (without cutting of mid-word), and adds an elipse to show more text. I also figured out how to add html links to the xml data. There’s apparently a couple of different ways. I opted to have the first < in the a tag be written in as &lt ; (the HTML entity). That did the trick. Now the marker has a link to see the whole of the data. This should be a link back to the page that’s part of the real site (in Omeka).

Another idea: Add the ability in the admin side of things, to select an item, and then enter the geocodes for that item by clicking on the map in the appropriate spot. Flickr does something like that. It would make it really easy for admins to enter the geocodes.