Source for file blog.class.php
Documentation is available at blog.class.php
* Contains the class Blog, which represents the main interface, connected to smaller components such as settings and blog.
* Depends on datacontainer.class.php, dataitem.class.php and post.class.php
* @author Daniel Nordstrom <dnordstrom@mankindorganization.com>
* @link www.mrnordstrom.com
* Holds and handles settings and posts. Also takes care of saving data to file.
private $settings_blog; // DataContainer with blog settings.
private $posts; // Array of DataContainers, each containing a post.
private $current_post; // Used in post iteration. ID of post we're currently displaying.
private $current_page; // Current page being displayed.
private $logged_in; // Are we logged in as owner?
private $single; // Are we viewing a single post?
private $pages; // Our admin pages and their files.
private $authpages; // Pages that needs authentification before access.
private $current_page_include; // Filename of page we're viewing.
private $current_page_name; // Name of the page we're viewing.
* Loads dynamic settings, only meant to be used internally and by subclasses.
* @param string $file File to load settings from.
* @param DataContainer &$container Container to dump data in.
private function LoadSettings($file, &$container)
while(!feof($settings_file))
// Read key and value from file, and trim away EOL garbage.
$container->AddItem($key, $value); // Adds item to specified data container
* Loads posts into data container.
* @param integer $number Optional, how many posts to load.
* @param integer $id Optional, if we choose to load only a single file, we here specify if we want to load a certain ID.
private function LoadPostsFromFlatfile($number = 0, $id = - 1)
unset ($this->posts); // Remove any loaded posts and...
$this->posts = array(); // ...initialize a new array.
throw new Exception("Invalid file. Cannot load posts.", 3);
// Load all, or a number of, the posts stored in FILE_POSTS.
while(!feof($posts_file))
$done = 0; // Marks when we're done and ready to add post. Without this, we're gonna have trouble with empty lines at the end of the file.
while(!feof($posts_file))
// Read key and value from file, and trim away EOL garbage.
if(strcmp($key, "") == 0) // If line was empty, we don't want todo anything.
elseif(strcmp($key, "STARTPOST") == 0) // If line is a start post marker, we skip to the next iteration to add the fields.
$done = 1; // Done, now add post.
elseif(strcmp($key, "ENDPOST") == 0) // If line is an end of post marker, we stop the iteration and jump to the next post.
$post->AddItem($key, $value); // Adds item to post data container
if($id >= 0 && $done && strcmp($post->GetValue("ID"), $id) == 0) // If we want to load just a single specific post, we check if this is the one.
break; // We break, since we don't want to add any more posts.
elseif($id == - 1 && $done)
if($number && $post_counter >= $number) // Break if user don't want to load any more posts.
* Loads posts from XML file into data container.
* @param integer $number Optional, how many posts to load.
* @param integer $id Optional, if we choose to load only a single file, we here specify if we want to load a certain ID.
private function LoadPostsFromXML($number = 0, $id = - 1)
unset ($this->posts); // Reset the posts variable, to kick any old posts out of there.
throw new Exception("Invalid file. Cannot load posts.", 3);
// Load all, or a number of, the posts stored in FILE_POSTS.
foreach($posts_file->children() as $child) // For every post...
foreach($child->children() as $subchild) // ...add all it's items,
if($key == "CONTENT") // If this is the post content, we want to decode HTML entities and convert back any HTML content.
$post->AddItem($key, $value);
if($id >= 0 && strcmp($post->GetValue("ID"), $id) == 0) // If we want to load just a single specific post, we check if this is the one.
break; // We break, since we don't want to add any more posts.
elseif($id == - 1) // Otherwise we just...
array_push($this->posts, $post); // ...push em' onto the array.
$post_counter++ ; // And boom, we've added a post.
if($number && ($post_counter >= $number)) // Break if user don't want to load any more posts.
* Loads a single post from source defined in config.php
* @param integer $id ID of post to load.
$this->LoadPostsFromXML(1, $id);
$this->LoadPostsFromFlatfile(1, $id);
* Loads post depending on what source we specify in config.php.
$this->LoadPostsFromXML($number);
$this->LoadPostsFromFlatfile($number);
* Publishes a new post to the storage file specified in config.php. Differs from the AddPost function in the way that it actually loads and saves posts to and from file, thus also rebuilding the ID structure.
* @param Post &$post Reference to object containing the post we're adding.
* Inserts a new post at the beginning of the array.
* @param Post &$post Reference to object containing the posts we're adding.
private function AddPost(&$post)
// We need to reverse the array in order to add the new post to the right side.
if(!array_push($this->posts, $post)) // Throw exception if we fail to push and...
throw new Exception("Could not add new post. array_push failed.", 6);
return 1; // ...return 1 for success otherwise.
* Sets a new value of specified field of specified post. If no such field yet exists, it creates a new field. Does not, however, save the posts to file, so we need to do that ourselves.
* @param integer $id ID of post to edit.
* @param string $field Key of field/DataItem to edit.
* @param string $value New value of field/DataItem.
private function SetPostField($id, $field, $value)
$key = $this->GetKeyByID($id);
if(!($this->posts[$key]->EditItem($field, $value))) // If we can't edit it, it doesn't exist, so we...
$this->posts[$key]->AddItem($field, $value); // ...add it as a new item.
* Writes currently loaded posts to file using the storage method specified in config.php
throw new Exception("Cannot open XML file for writing.", 3);
$id = count($this->posts) - 1; // We set the first ID to the number of posts there are (minus one since the first post has ID 0), since we want the newest post to have the highest ID.
fwrite($posts_file, '<?xml version="1.0"?>' . PHP_EOL . '<POSTS>' . PHP_EOL);
foreach($this->posts as $post)
$post->DeleteItem('ID'); // Remove old ID to allow for a new, clean and linear structure.
$post->AddItem('ID', $id); // Give the post a new ID.
fwrite($posts_file, $post->AsXML()); // Write each post to file using an XML format.
fwrite($posts_file, '</POSTS>');
throw new Exception("Cannot open flatfile for writing.", 3);
$id = count($this->posts) - 1;
foreach($this->posts as $post)
$post->DeleteItem('ID'); // Remove old ID to allow for a new, clean and linear structure.
$post->AddItem('ID', $id); // Give the post an ID.
fwrite($posts_file, $post->AsText()); // Write each post to file with our own flat format.
* Returns key to the post with the specified ID.
private function GetKeyByID($id)
foreach($this->posts as $current_key => $post)
if(strcmp($post->GetValue("ID"), $id) == 0)
if(!$this->PostsLoaded()) // Check if posts are already loaded.
$this->LoadPosts(); // If they aren't, we load them now.
// Compose all sections of the RSS data.
$header = '<?xml version="1.0"?>' . PHP_EOL . '<rss version="2.0">' . PHP_EOL . '<channel>' . PHP_EOL;
$header .= '<title>' . $this->GetSetting("TITLE") . '</title>' . PHP_EOL;
$header .= '<description>' . $this->GetSetting("DESCRIPTION") . '</description>' . PHP_EOL;
$header .= '<link>' . $this->GetSetting("URL") . '</link>' . PHP_EOL;
foreach($this->posts as $post)
$items .= '<item>' . PHP_EOL;
$items .= '<title>' . $post->GetTitle() . '</title>' . PHP_EOL;
$items .= '<description>' . $post->GetContent() . '</description>' . PHP_EOL;
$items .= '<pubDate>' . $post->GetDate() . '</pubDate>' . PHP_EOL;
$items .= '<link>' . $this->GetSetting("URL") . 'index.php?post=' . $post->GetID() . '</link>' . PHP_EOL;
$items .= '</item>' . PHP_EOL;
$footer = '</channel>' . PHP_EOL . '</rss>';
echo $header . $items . $footer; // Print it all out.
* Returns reference to the post with the specified ID.
foreach($this->posts as $post)
if(strcmp($post->GetValue("ID"), $id) == 0)
* Deletes the post with the specified ID.
* This assumes there are only one post with that ID, which we make sure of by giving the posts new unique IDs every time we save them to file.
for($x = 0; $x < $this->PostsLoaded(); $x++ ) // Find out which one we should delete, try to match against specified ID.
if (strcmp ( $id, $this->items[$x]->GetKey() ) == 0)
unset ($this->posts[$x]); // Here it is, let's delete it and...
$this->SavePosts(); // ...make sure we save this to file as well, and finally...
return 1; // ...return 1 on success.
// If post was not found, -1 is returned.
* Returns number of posts currently loaded.
* This is a private function, only meant to be used within this class.
* @return integer Number of posts currently loaded. Thus returning 0 if no posts have been loaded.
private function PostsLoaded()
return count($this->posts);
* Prints author of post currently being displayed.
echo $this->posts[$this->current_post]->GetAuthor();
* Prints title of post currently being displayed.
echo $this->posts[$this->current_post]->GetTitle();
* Prints content of post currently being displayed.
echo $this->posts[$this->current_post]->GetContent();
* Prints date of post currently being displayed.
echo $this->posts[$this->current_post]->GetDate();
* Prints tags of post currently being displayed.
echo $this->posts[$this->current_post]->GetTags();
* Set post being displayed to next one in order.
if(is_object($this->GetPostByID($this->current_post + 1))) // Check if there are no more posts at all.
if(isset ($this->current_page) && ($this->current_post + 1) >= $this->GetSetting('POSTS PER PAGE') * $this->current_page)
$this->current_post = - 1; // If this is the end of the page, set to negative.
$this->current_post++ ; // Set current post to next post.
$this->current_post = - 1; // Set to negative, to show HavePosts() that there are no more posts.
* Checks if there are more posts to display after the current one.
* @return bool True if there are more posts, false otherwise.
if($this->current_post == - 1) // If current post is minus one, then there are no more posts and we return false.
* Checks if there are more posts to display after the current one.
* @return bool True if there are more pages, false otherwise.
if(!is_object($this->GetPostByID($this->current_post + 1))) // Test for different scenarios where there are no more posts after this page.
* Checks if there are newer pages to display before the current one.
* @return bool True if there are more newer pages, false otherwise.
* Authenticates user if password is supplied. If not, checks if user is logged in or redirects him to login page. Sets login variable to true on success.
* @param string $password Password for login attempt.
* @return bool True if user is authenticated or authentication was successful, false otherwise.
if($password == "") // Password not set, we want to check if user is authenticated.
if(!$this->LoggedIn() && in_array($this->current_page_name, $this->authpages)) // If trying to view protected page without being logged in.
header("location: " . $this->pages['admin-login'] . "&error=password"); //&nb |