<?php
/*
    picoBlog useless blogging tool
    Copyleft (C) 2007-2010 BohwaZ - http://dev.kd2.org/

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

if (phpversion() < 5)
{
    die("Argh you don't have PHP 5 ! Please install it right now !");
}

error_reporting(E_ALL);

// Against bad configurations
if (get_magic_quotes_gpc())
{
    foreach ($_POST as $k=>$v)
    {
        $_POST[$k] = stripslashes($v);
    }
}

// Default stylesheet
$default_css = '
* {
    margin: 0;
    padding: 0;
}

body {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 12px;
    background: #eee;
    color: #000;
}

#global {
    border: 2px solid #999;
    border-top: none;
    width: 50em;
    padding: 1.5em;
    padding-top: 1em;
    background: #fff;
    margin-left: auto;
    margin-right: auto;
}

h1 {
    color: #666;
    border-bottom: 1px dotted #999;
}
h2 {
    text-align: right;
    font-size: 1.3em;
    font-style: italic;
    margin-bottom: 1em;
    color: #666;
    letter-spacing: 0.2em;
}

#footer {
    border-top: 1px dashed #999;
    padding-top: 0.5em;
    padding-bottom: 0.5em;
    font-size: 0.9em;
    color: #666;
}

#footer a {
    color: #666;
}

#footer form {
    float: right;
}

#footer form p {
    display: inline;
    margin-left: 1em;
}

#footer label input {
    border: 1px solid #999;
    padding: 0.1em;
    width: 10em;
}

#content p, #content div, #content ul, #content h3 {
    margin-bottom: 1em;
}

#content fieldset {
    border: 2px solid #ccc;
    margin-bottom: 1em;
    width: 100%;
}

#content textarea {
    height: 20em;
    width: 95%;
    border: 1px solid #000;
    padding: 0.3em;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 1em;
}

#content fieldset dd {
    margin: 1em;
}

#content fieldset dt {
    font-weight: bold;
    margin: 0.5em;
}

#content input[type=text] {
    border: 1px solid #000;
    padding: 0.3em;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 1em;
}

.item {
    border: 1px dotted #999;
    padding: 0.5em;
    margin-bottom: 2em !important;
}

.item .link {
    margin-bottom: 0 !important;
    margin-top: -0.5em;
    margin-right: 2em;
    font-size: 0.9em;
    float: right;
    background: #fff;
    border: 1px dotted #999;
    padding: 0.3em;
}

.item .link a {
    color: darkblue;
}

.item .link a:hover {
    color: red;
}

.admin {
    color: red !important;
    background: yellow;
    padding: 0.2em;
}

#config fieldset {
    padding: 0.5em;
}

#config fieldset legend {
    font-weight: bold;
    padding-left: 1em;
    padding-right: 1em;
}

#config fieldset dd {
    margin: 0.5em;
}

#config input[type=text] {
    width: 90%;
    padding: 0.2em;
}

.pagination {
    list-style-type: none;
    text-align: center;
}

.pagination li {
    display: inline;
    margin: 0.5em;
}

.pagination .selected {
    font-weight: bold;
    font-size: 1.2em;
}

.pagination a {
    color: #999;
}

dl.tips dt { font-weight: bold; }
dl.tips dd { margin: 0.5em; margin-bottom: 1em; }
';

class picoBlog
{
    // The file containing the datas
    public $file = 'datas.php';

    // Number of entries to display per page
    public $bypage = 20;

    // Reversed order ?
    public $reverse_order = true;

    // Blog title
    public $title = 'My picoBlog';

    // Blog description
    public $desc = 'Another useless blog';

    // Blog locale
    public $locale = 'en_GB';
    public $date_format = '%A %d %B %Y at %H:%M';

    // Blog url (leave empty to autodetect)
    public $url = '';

    // User login
    public $user_login = 'admin';
    public $user_password = 'abcd';

    private $datas = array();
    public $version = '1.0.3';

    public function __construct()
    {
        $this->file = dirname(__FILE__) . '/' . $this->file;
        if (empty($this->url))
            $this->url = $this->getUrl();
    }

    public function login($login, $password)
    {
        if ($login != $this->user_login || $password != $this->user_password)
            return false;

        @session_start();
        $_SESSION['logged'] = true;
        return true;
    }

    public function logout()
    {
        @session_start();
        $_SESSION = array();
    }

    public function isLogged()
    {
        @session_start();
        if (!empty($_SESSION['logged']))
            return true;

        return false;
    }

    public function getUrl()
    {
        if (!empty($this->url))
            return $this->url;

        $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        $url = preg_replace('/([?&].*)$/', '', $url);
        return $url;
    }

    public function formatText($text)
    {
        $text = preg_replace('#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im', '\\1<a href="\\2">\\2</a>\\4', $text);
        $text = preg_replace('#(^|\s)wp:?([a-z]{2}|):([\w]+)#im', '\\1<a href="http://\\2.wikipedia.org/wiki/\\3">\\3</a>', $text);
        $text = str_replace('http://.wikipedia.org/wiki/', 'http://www.wikipedia.org/wiki/', $text);
        $text = str_replace('\wp:', 'wp:', $text);
        $text = str_replace('\http:', 'http:', $text);
        $text = nl2br($text);

        return $text;
    }

    public function loadDatas()
    {
        if (file_exists($this->file))
        {
            require_once $this->file;

            if (!isset($datas) || !is_array($datas))
                return false;

            $this->datas = $datas;
            unset($datas);

            $this->sortDatas();
            return true;
        }
        return false;
    }

    public function sortDatas()
    {
        if ($this->reverse_order)
            krsort($this->datas);
        else
            ksort($this->datas);
    }

    public function writeDatas()
    {
        $out = "<?php \$datas = array(\n";

        foreach ($this->datas as $id => $text)
        {
            $text = strtr($text, array('$' => '\\$', '"' => '\\"'));
            $out .= '"'.(int)$id.'" => "'.$text."\",\n";
        }

        $out.= '); ?>';

        if (!@file_put_contents($this->file, $out))
            return false;

        return true;
    }

    public function getEntry($id)
    {
        if (!isset($this->datas[$id]))
            return false;

        return $this->datas[$id];
    }

    public function getList($begin=0)
    {
        $list = array_slice($this->datas, $begin, $this->bypage, true);
        return $list;
    }

    public function editEntry($id, $text)
    {
        $this->datas[(int)$id] = $text;
    }

    public function deleteEntry($id)
    {
        unset($this->datas[(int)$id]);
    }

    public function getPagination()
    {
        if (count($this->datas) <= $this->bypage)
            return false;

        $pages = ceil(count($this->datas) / $this->bypage);
        return $pages;
    }
}

class picoConf
{
    public $file = 'userconfig.php';

    public function __construct()
    {
        $this->file = dirname(__FILE__) . '/' . $this->file;
    }

    public function write($datas)
    {
        $out = '<?php if (!class_exists("picoBlog")) die("just config");';
        $out.= "\n";

        foreach ($datas as $key=>$value)
        {
            $value = strtr($value, array('$' => '\\$', '"' => '\\"'));
            $out .= '$pb->'.$key.' = "'.$value."\";\n";
        }

        $out.= '?>';

        if (!@file_put_contents($this->file, $out))
            return false;

        return true;
    }
}

$pb = new picoBlog;

// Loading user config
if (file_exists(dirname(__FILE__) . '/userconfig.php'))
    require_once dirname(__FILE__) . '/userconfig.php';

// Loading datas or create dummy entry
if (!$pb->loadDatas())
{
    $pb->editEntry(time(),
        "Welcome to your <a href=\"http://dev.kd2.org/picoblog/\">picoBlog</a> (want to learn more about wp:Blogs ?).\n\n".
        "Edit this entry to see a bit how this thing works. Oh, I forgot to tell you, the default ".
        "login is <strong>admin</strong> and the password is <strong>abcd</strong>. I suggest you ".
        "to login and change them now in the configuration.");
    if (!$pb->writeDatas())
        die("Can't write to ".$pb->file);

    header('Location: '.$pb->getUrl().'?new');
    exit;
}

// We allow the user to have its own stylesheet
if (file_exists(dirname(__FILE__).'/userstyle.css'))
    $default_css = " @import url('".$pb->getUrl()."userstyle.css'); ";

// For translating things
setlocale(LC_TIME, $pb->locale);

///////////// PAGES ///////////////////////////////////////////////////////////////////////////////
// (only if i'm in picoBlog)

if (!defined('FROM_EXTERNAL') || !FROM_EXTERNAL)
{
    // Login process
    if (isset($_GET['login']))
    {
        if ($pb->login($_POST['login'], $_POST['password']))
        {
            header('Location: '.$pb->getUrl());
            exit;
        }
        die("Login failed !");
    }

    // Logout
    elseif (isset($_GET['logout']))
    {
        $pb->logout();
        header('Location: '.$pb->getUrl());
        exit;
    }

    // User configuration
    elseif (isset($_GET['config']) AND $pb->isLogged())
    {
        if (isset($_POST['save']))
        {
            $datas = array(
                'title' => $_POST['title'],
                'desc'  => $_POST['desc'],
                'locale' => preg_match('/^[a-z]{2}_[A-Z]{2}$/', $_POST['locale']) ? $_POST['locale'] : $pb->locale,
                'date_format' => $_POST['date_format'],
                'bypage' => (int) $_POST['bypage'],
                'reverse_order' => (bool) $_POST['reverse_order'],
                'user_login' => $_POST['user_login'],
                'user_password' => $_POST['user_password'],
            );

            $pc = new picoConf;
            if (!$pc->write($datas))
                die("Can't write to ".$pc->file);

            header('Location: '.$pb->getUrl());
            exit;
        }

        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>Configuration</title>
            <style type="text/css">
            '.$default_css.'
            </style>
        </head>

        <body id="config">
        <div id="global">
            <h1>Configuration - picoBlog '.$pb->version.'</h1>
            <h2>Why don\'t you <a href="http://dev.kd2.org/picoblog/">check for a new version</a> ?</h2>
            <div id="content">
            <dl class="tips">
                <dt>Do you know that you can change the style of your picoBlog ?</dt>
                <dd>Just create a new file called userstyle.css in your picoBlog directory and edit it :)<br />
                    Bored of your own style ? Just remove or rename the file.</dd>
            </dl>
            <form method="post" action="">
            <fieldset>
                <legend>Blog informations</legend>
                <dl>
                    <dt>Blog title</dt>
                    <dd><input type="text" name="title" value="'.htmlspecialchars($pb->title).'" /></dd>
                    <dt>Blog description (HTML allowed)</dt>
                    <dd><input type="text" name="desc" value="'.htmlspecialchars($pb->desc).'" /></dd>
                </dl>
            </fieldset>

            <fieldset>
                <legend>Language informations</legend>
                <dl>
                    <dt>Locale (eg. en_GB or fr_FR)</dt>
                    <dd><input type="text" maxlength="5" name="locale" value="'.htmlspecialchars($pb->locale).'" /></dd>
                    <dt>Date format (<a href="http://php.net/strftime">strftime</a> format)</dt>
                    <dd><input type="text" name="date_format" value="'.htmlspecialchars($pb->date_format).'" /></dd>
                </dl>
            </fieldset>

            <fieldset>
                <legend>Blog preferences</legend>
                <dl>
                    <dt>Number of entries by page</dt>
                    <dd><input type="text" maxlength="3" name="bypage" value="'.(int) $pb->bypage.'" /></dd>
                    <dt>Order of entries</dt>
                    <dd>
                        <label><input type="radio" name="reverse_order" value="0" '.(!$pb->reverse_order ? 'checked="checked"' : '').' /> From the latest to the newest</label><br />
                        <label><input type="radio" name="reverse_order" value="1" '.($pb->reverse_order ? 'checked="checked"' : '').' /> <strong>Reverse order:</strong> from the newest to the latest</label>
                    </dd>
                </dl>
            </fieldset>

            <fieldset>
                <legend>Login informations</legend>
                <dl>
                    <dt>Login</dt>
                    <dd><input type="text" name="user_login" value="'.htmlspecialchars($pb->user_login).'" /></dd>
                    <dt>Password</dt>
                    <dd><input type="text" name="user_password" value="'.htmlspecialchars($pb->user_password).'" /></dd>
                </dl>
            </fieldset>
            <p><input type="submit" name="save" value="Save" /></p>
            </form>
            </div>
        </div>
        </body>
        </html>';
        exit;
    }

    // Deleting an entry
    elseif (isset($_GET['delete']) AND $pb->isLogged())
    {
        $pb->deleteEntry($_GET['delete']);
        if (!$pb->writeDatas())
            die("Can't write to ".$pb->file);

        header('Location: '.$pb->getUrl());
        exit;
    }

    // Editing an entry
    elseif (isset($_GET['edit']) AND $pb->isLogged())
    {
        if (!empty($_POST['save']))
        {
            $id = (int) $_GET['edit'];

            if (empty($_GET['edit']))
                $id = time();

            if (!empty($_POST['date']))
            {
                $new_date = strtotime($_POST['date']);

                if ((int)$new_date != (int)$_GET['edit'] && !empty($new_date))
                {
                    $pb->deleteEntry($_GET['edit']);
                    $id = $new_date;
                }
            }

            $pb->editEntry($id, $_POST['text']);
            if (!$pb->writeDatas())
                die("Can't write to ".$pb->file);

            header('Location: '.$pb->getUrl().'?'.$id);
            exit;
        }

        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>'.$pb->title.'</title>
            <style type="text/css">
            '.$default_css.'
            </style>
        </head>

        <body>
        <div id="global">
            <h1>'.htmlspecialchars($pb->title).'</h1>
            <h2>'.$pb->desc.'</h2>
            <div id="content">';

        if (empty($_GET['edit']))
        {
            echo '<h3>New entry</h3>';
            $text = "Describe your entry here. HTML is <strong>allowed</strong>. URLs are automatically converted.\n\n"
                .   "You can use wp:Article to link to a wikipedia article. Or maybe, for an article in a specific language, "
                .   "try wp:lang:Article (eg. wp:nl:Homomonument).\n\nTry it !";

            $date = date('Y-m-d H:i:s');
        }
        else
        {
            echo '<h3>Edit '.(int)$_GET['edit'].'</h3>';
            $text = $pb->getEntry($_GET['edit']);
            $date = date('Y-m-d H:i:s', (int)$_GET['edit']);
        }

        echo '
            <form method="post" class="edit" action="?edit='.(int)$_GET['edit'].'">
            <fieldset>
                <dl>
                    <dd><textarea name="text" cols="70" rows="50">'.htmlspecialchars($text).'</textarea></dd>
                    <dt><label for="f_date">Entry date</label></dt>
                    <dd><input type="text" id="f_date" name="date" value="'.$date.'" /></dd>
                </dl>
            </fieldset>
            <p class="submit">
                <input type="submit" name="save" value="Save" />
            </p>
            </form>
            </div>
        </div>
        </body>
        </html>';
        exit;
    }

    // RSS feed
    elseif (isset($_GET['rss']))
    {
        $pb->reverse_order = true;
        $pb->sortDatas();

        $list = $pb->getList(0);
        $last_update = array_keys($list);
        $last_update = date(DATE_RSS, $last_update[0]);

        header('Content-Type: text/xml');

        echo  '<?xml version="1.0" encoding="UTF-8" ?>
        <rdf:RDF
          xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
          xmlns:dc="http://purl.org/dc/elements/1.1/"
          xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
          xmlns:content="http://purl.org/rss/1.0/modules/content/"
          xmlns="http://purl.org/rss/1.0/">

        <channel rdf:about="'.$pb->getUrl().'">
          <title>'.htmlspecialchars($pb->title).'</title>
          <description><![CDATA['.$pb->desc.']]></description>
          <link>'.$pb->getUrl().'</link>
          <dc:language>'.substr($pb->locale, 0, 2).'</dc:language>
          <dc:creator></dc:creator>
          <dc:rights></dc:rights>
          <dc:date>'.$last_update.'</dc:date>

          <sy:updatePeriod>daily</sy:updatePeriod>
          <sy:updateFrequency>1</sy:updateFrequency>
          <sy:updateBase>'.$last_update.'</sy:updateBase>

          <items>
          <rdf:Seq>
            ';

        foreach ($list as $id=>$content)
        {
            echo '<rdf:li rdf:resource="'.$pb->getUrl().'?'.$id.'" />
            ';
        }

        echo '
          </rdf:Seq>
          </items>
        </channel>';

        foreach ($list as $id=>$content)
        {
            echo '
                <item rdf:about="'.$pb->getUrl().'?'.$id.'">
                    <title>#'.$id.'</title>
                    <link>'.$pb->getUrl().'?'.$id.'</link>
                    <dc:date>'.date(DATE_RSS, $id).'</dc:date>
                    <dc:language>'.substr($pb->locale, 0, 2).'</dc:language>
                    <dc:creator></dc:creator>
                    <dc:subject>picoBlog</dc:subject>
                    <description>'.htmlspecialchars($content).'</description>
                    <content:encoded>
                        <![CDATA['.$content.']]>
                    </content:encoded>
                </item>';
        }

        echo '
        </rdf:RDF>';

        exit;
    }

    // Permalink to an entry
    elseif (!empty($_SERVER['argv'][0]) && is_numeric($_SERVER['argv'][0]))
    {
        $id = $_SERVER['argv'][0];

        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>'.$pb->title.'</title>
            <link rel="alternate" type="application/rss+xml" title="RSS" href="?rss" />
            <style type="text/css">
            '.$default_css.'
            </style>
        </head>

        <body>
        <div id="global">
            <h1>'.htmlspecialchars($pb->title).'</h1>
            <h2>'.$pb->desc.'</h2>
            <div id="content">
            ';

        $entry = $pb->getEntry($id);
        if (!$entry)
            echo "<p>Can't find this entry.</p>";
        else
        {
            echo '
            <div class="item">
                <h3>'.strftime($pb->date_format, $id).'</h3>
                <div class="content">'.$pb->formatText($entry).'</div>';
        }

        echo '
                <p class="link">
                    <a href="'.$pb->getUrl().'?'.$id.'">Permalink</a>';

                    if ($pb->isLogged())
                    {
                        echo ' | <a href="?edit='.$id.'" class="admin">Edit</a> |
                            <a href="?delete='.$id.'" class="admin" onclick="if (confirm(\'Sure?\') != true) return false;">Delete</a>';
                    }

        echo '
                </p>
            </div>
            <p><a href="'.$pb->getUrl().'">Back to homepage</a></p>
        </div>
        </div>
        </body>
        </html>';
        exit;
    }

    // Entries by page
    else
    {
        $page = 1;

        if (!empty($_SERVER['argv'][0]) && preg_match('/^p([0-9]+)$/', $_SERVER['argv'][0], $match))
        {
            $page = (int) $match[1];
        }
        $begin = ($page - 1) * $pb->bypage;

        $list = $pb->getList($begin);

        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>'.$pb->title.'</title>
            <link rel="alternate" type="application/rss+xml" title="RSS" href="?rss" />
            <style type="text/css">
            '.$default_css.'
            </style>
        </head>

        <body>
        <div id="global">
            <h1>'.htmlspecialchars($pb->title).'</h1>
            <h2>'.$pb->desc.'</h2>
            <div id="content">
            ';

        if ($pb->isLogged())
            echo '<p><a href="?edit" class="admin">New entry</a></p>';

        if (empty($list))
            echo '<p>No item.</p>';
        else
        {
            foreach ($list as $id=>$content)
            {
                echo '
                <div class="item">
                    <h3>'.strftime($pb->date_format, $id).'</h3>
                    <div class="content">
                        '.$pb->formatText($content).'
                    </div>
                    <p class="link">
                        <a href="'.$pb->getUrl().'?'.$id.'">Permalink</a>';

                    if ($pb->isLogged())
                    {
                        echo ' | <a href="?edit='.$id.'" class="admin">Edit</a> |
                            <a href="?delete='.$id.'" class="admin" onclick="if (confirm(\'Sure?\') != true) return false;">Delete</a>';
                    }

                    echo '</p>
                </div>';
            }
        }

        $pages = $pb->getPagination();
        if (!empty($pages))
        {
            echo '
            <ul class="pagination">
            ';

            for ($p = 1; $p <= $pages; $p++)
            {
                echo '<li'.($page == $p ? ' class="selected"' : '').'><a href="?p'.$p.'">'.$p.'</a></li>';
            }

            echo '
            </ul>';
        }

        echo '
            </div>
            <div id="footer">
                <a href="?rss">RSS</a>';

        if ($pb->isLogged())
            echo ' | <a href="?config" class="admin">Configuration</a> | <a href="?logout" class="admin">Logout</a>';
        else
        {
            echo '
            <form method="post" action="?login">
                <p><label>Login: <input type="text" name="login" /></label></p>
                <p><label>Password: <input type="password" name="password" /></label></p>
                <p><input type="submit" value="OK" class="submit" /></p>
            </form>';
        }

        echo '
            </div>
        </div>
        </body>
        </html>';
    }
}

?>