<?php 

//
// Copyright (C) Julian I. Kamil <julian.kamil@gmail.com>
//               Modified by mac (zgqinv402@sneakemail.com)
// No warranty is provided.  Use at your own risk.
//
// Name: todo.php
// Author: Julian I. Kamil <julian.kamil@gmail.com>
// Created: 2005-03-17
// Description:
//     This is a PmWiki plugin that provides the capability
//     to manage a list of to do items.
//
// History:
//     2005-06-22  mac  Added "Asigned to" field
//     2005-03-17  jik  Created.
//     2005-03-18  jik  Added the ability to filter todo lists
//                      by status and category.
//                      Added simple list view with checkboxes
//                      to complete items.
//                      Sorted items in the list by its titles.
//                      Added the ability to display a form
//                      with a fixed category.
//     2005-03-23  jik  Merged changes made by Didier Lebrun,
//                      which added the following capabilities:
//                      - fixed URLs in ToDoList()
//                      - sort on any field
//                      - allow any group
//                      - allow customized options lists
//                      - allow custom labels
//                      Fixed a bug in listing current pages
//                      when creating a new item.
//                      Fixed a bug that caused the category
//                      name to disappear when a fixed category
//                      is selected in the form.
//                      Added the ability to select completed
//                      items by month, sorted by date.
//     2005-03-24  jik  Fixed the problem with selection and 
//                      sorting in table and simple lists.
//                      Sorted simple lists by priority * urgency
//                      as a default.
//                      Highlighted overdue items, which works
//                      on PHP 5 but not on PHP 4.
//     2005-03-28  jik  Integrated changes proposed by Petko 
//                      Yotov which allow field titles that do
//                      not strt with letters in the A-Za-z range.
//                      This is useful for internationalized field
//                      titles.
//                      Made the link text decoration style 
//                      attribute to be specific to todo-list and
//                      todo-simple-list.
//
// Initials:
//     jik - Julian I. Kamil <julian.kamil@gmail.com>
//     dl  - Didier Lebrun <dl@vaour.net>
//     py  - Petko Yotov <sko@free.fr>
//    mac  - Magnus Carlsson <zgqinv402@sneakemail.com>

if (!defined('PmWiki')) {
    exit();
}

define(TODO_VERSION, '0.3.5');

// These values can be customized in 'local/config.php'.

//
// START customizable values definition
//

SDV($ToDoGroup, 'TODO');

SDV(
    $todo_priority_names,
    array(
        '5' => 'High',
        '4' => 'Medium high',
        '3' => 'Medium',
        '2' => 'Medium low',
        '1' => 'Low'
        )
    );

SDV(
    $todo_urgency_names,
    array(
        '5' => 'High',
        '4' => 'Medium high',
        '3' => 'Medium',
        '2' => 'Medium low',
        '1' => 'Low'
        )
    );

SDV(
    $todo_status_names,
    array(
        'Open',
        'In progress',
        'On hold',
        'Completed',
        'Overdue'
        )
    );

SDV(
    $todo_category_names,
    array(
        'Personal',
        'Business',
        'Other'
        )
    );

SDV(
    $todo_field_names, 
    array(
        'ID',
        'Category',
        'Status',
        'Priority',
        'Urgency',
        'Create date',
        'Due date',
        'Assigned To',
        'Description'
        )
    );

SDV(
    $todo_delay_names, 
    array(
        'tomorrow',
        'next week',
        )
    );

SDV(
    $todo_assign_names,
    array(
        '-',
        'Joe Smith',
        'John Doe',
        'Jane Doe'
        )
    );

SDV($todo_date_format, 'Y-m-d');

//
// END customizable values definition
//

markup(
    'todoform',
    'inline',
    '/\\(:todoform\\s*(.*?):\\)/e',
    "ToDoForm('', \$pagename, array('q' => PSS('$1')))"
    );

markup(
    'todolist',
    'directive',
    '/\\(:todolist\\s*(.*?):\\)/e',
    "ToDoList('', \$pagename, array('q' => PSS('$1')))"
    );

markup(
    'todosimplelist',
    'directive',
    '/\\(:todosimplelist\\s*(.*?):\\)/e',
    "ToDoSimpleList('', \$pagename, array('q' => PSS('$1')))"
    );

function CompareToDoListItem($item1, $item2)
{
    if ($item1 == $item2) {
        return 1;
    }

    return ($item1 < $item2) ? -1 : 1;
}

function GetDirectiveArgs($arguments, $defaults = array())
{
    $terms = 
        preg_split(
            '/((?<!\\S)[-+]?[\'"].*?[\'"](?!\\S)|\\S+)/',
            $arguments['q'],
            -1,
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
            );

    foreach($terms as $term) {
        if (trim($term)=='') {
            continue;
        }

        if (preg_match('/([^\'":=]*)[:=]([\'"]?)(.*?)\\2$/', $term, $match)) {
            $defaults[$match[1]] = str_replace("_"," ",$match[3]); 
        }
        else {
            if (substr($term, 0, 1)=='-') { 
                $defaults[substr($term, 1)] = 0; 
            }
            elseif (substr($term, 0, 1)=='+') { 
                $defaults[substr($term, 1)] = 1; 
            }
            else { 
                $defaults[$term] = 1;
            }
        }
    }
    
    return $defaults;
}

function ToDoForm($format, $pagename, $options) 
{
    global $todo_urgency_names;
    global $todo_priority_names;
    global $todo_status_names;
    global $todo_category_names;
    global $todo_field_names;
    global $todo_delay_names;
    global $todo_assign_names;
    global $todo_date_format;

    $arguments = GetDirectiveArgs($options, $settings);

    if (! empty($arguments['category'])) {
        if (! ($arguments['category'] === 'All')) {
            $selected_category = $arguments['category'];
        }
    }

    for ($index = 1; $index <= count($todo_priority_names); $index++) {
        $todo_priorities_code .=
            "<input type='radio' name='todo-priority' value='$index' />\n"
            ;
    }

    for ($index = 1; $index <= count($todo_urgency_names); $index++) {
        $todo_urgency_code .=
            "<input type='radio' name='todo-urgency' value='$index' />\n"
            ;
    }

    $todo_status_code = '<select name="todo-status">';

    for ($index = 0; $index < count($todo_status_names); $index++) {
        $todo_status_code .=
            "<option>$todo_status_names[$index]</option>\n"
            ;
    }

    $todo_status_code .= '</select>';

    $todo_assign_code = '<select name="todo-assign">';

    for ($index = 0; $index < count($todo_assign_names); $index++) {
        $todo_assign_code .=
            "<option>$todo_assign_names[$index]</option>\n"
            ;
    }

    $todo_assign_code .= '</select>';

    if (empty($selected_category)) {
        $todo_category_code = '<select name="todo-category">';
        
        for ($index = 0; $index < count($todo_category_names); $index++) {
            $todo_category_code .=
                "<option>$todo_category_names[$index]</option>\n"
                ;
        }
        
        $todo_category_code .= '</select>';
    }
    else {
        $todo_category_code = "$selected_category";
        $todo_category_hidden_code = "<input type='hidden' name='todo-category' value='$selected_category' />";
    }

    $todo_priority_low = $todo_priority_names[min(array_keys($todo_priority_names))];
    $todo_priority_high = $todo_priority_names[max(array_keys($todo_priority_names))];
    $todo_urgency_low = $todo_urgency_names[min(array_keys($todo_priority_names))];
    $todo_urgency_high = $todo_urgency_names[max(array_keys($todo_priority_names))];

    $create_date = date($todo_date_format);
    $tomorrow_date = date($todo_date_format, mktime(0, 0, 0, date('m'), date('d')+1, date('Y')));
    $next_week_date = date($todo_date_format, mktime(0, 0, 0, date('m'), date('d')+7, date('Y')));

    $output[] = <<< EOT
<form method='post'>
<input type='hidden' name='action' value='posttodo' />
<input type='hidden' name='todo-create-date' value='{$create_date}' />
{$todo_category_hidden_code}
<table cellspacing='0' cellpadding='0' class="todo-form">
    <tr>
        <td class='heading'>
            $todo_field_names[1]:
        </td>

        <td>
            {$todo_category_code}
        </td>
    </tr>

    <tr>
        <td class='heading'>
            $todo_field_names[2]:
        </td>

        <td>
            {$todo_status_code}
        </td>
    </tr>

    <tr>
        <td class='heading'>
            $todo_field_names[3]:
        </td>

        <td>
            {$todo_priority_low} {$todo_priorities_code}  {$todo_priority_high}
        </td>
    </tr>

    <tr>
        <td class='heading'>
            $todo_field_names[4]:
        </td>

        <td>
            {$todo_urgency_low} {$todo_urgency_code} {$todo_urgency_high}
        </td>
    </tr>

    <tr>
        <td class='heading'>
            Create date:
        </td>

        <td>
            $todo_field_names[5]:
        </td>
    </tr>

    <tr valign='top'>
        <td class='heading'>
            $todo_field_names[6]:
        </td>

        <td>
            <div>
            <input type='radio' name='todo-due-date' value='{$tomorrow_date}' checked> {$tomorrow_date} (tomorrow)
            </div>

            <div>
            <input type='radio' name='todo-due-date' value='{$next_week_date}'> {$next_week_date} (next week)
            </div>

            <div>
            <input type='radio' name='todo-due-date' value='0'>
            <input type='text' name='todo-due-specific-date' size='10'>
            </div>
        </td>
    </tr>

    <tr>
        <td class='heading'>
            $todo_field_names[7]:
        </td>

        <td>
            {$todo_assign_code}
        </td>
    </tr>

    <tr>
        <td class='heading'>
            $todo_field_names[8]:
        </td>

        <td>
            <input type='text' name='todo-description' size='54' />
        </td>
    </tr>

    <tr>
        <td class='heading'>
        </td>

        <td>
            <input type='submit' value='Submit' accesskey='s' />
        </td>
    </tr>
</table>
</form>
</post>
EOT;

    return FmtPageName(implode('', $output), $pagename);
}

$HTMLStylesFmt[] = <<< EOT

.todo-form {
    border: 1px none #aaa;
    text-decoration: none;
}

.todo-form tr td {
    font-weight: plain;
    text-align: left;
    padding: 4px;
}

.todo-form tr td.heading {
    text-align: right;
    width: 200px;
    padding-right: 6px;
}

.todo-simple-list {
    padding-left: 0px;
    list-style: none;
}

.todo-simple-list li {
    padding-bottom: 6px;
}

.todo-list {
    border: 1px solid #aaa;
    text-decoration: none;
}

.todo-list thead tr td {
    font-weight: plain;
    text-align: left;
    padding-top: 4px;
    padding-bottom: 4px;
    padding-left: 8px;
    padding-right: 8px;
    border-top: 1px solid #aaa;
    border-bottom: 1px solid #666;
    border-right: 1px solid #aaa;
    background-color: #ccc;
}

.todo-list thead tr td.first {
    border-left: 1px solid #aaa;
}

.todo-list tbody tr td {
    padding-top: 4px;
    padding-bottom: 4px;
    padding-left: 8px;
    padding-right: 8px;
    border-bottom: 1px solid #aaa;
    border-right: 1px solid #aaa;
}

.todo-list tbody tr td.first {
    border-left: 1px solid #aaa;
}

.todo-list tbody tr td.bottom {
    border-bottom: 1px solid #666;
}

.todo-list tbody tr td.description {
    background-color: #eee;
}

.todo-list tbody tr td div {
    padding: none;
    padding-top: 4px;
    padding-bottom: 4px;
}

.todo-list tbody tr td div.heading {
    border-bottom: 1px solid #aaa;
}

.todo-list a {
    text-decoration: none;
}

.todo-simple-list a {
    text-decoration: none;
}
EOT;

include_once("$FarmD/scripts/author.php");

if ($action=='posttodo') {
    Lock(2);

    foreach(ListPages("/^$ToDoGroup\\.\\d/") as $i) {
        $todo = max(@$todo, substr($i, 5));
    }

    $pagename = sprintf("$ToDoGroup.%05d", @$todo+1);
    $action = 'edit';
    $_REQUEST['post'] = 1;

    $priority = $todo_priority_names[$_REQUEST['todo-priority']];
    $urgency = $todo_urgency_names[$_REQUEST['todo-urgency']];

    $_POST['text'] = <<< EOT
$todo_field_names[1]: {$_REQUEST['todo-category']}

$todo_field_names[2]: {$_REQUEST['todo-status']}

$todo_field_names[3]: {$_REQUEST['todo-priority']} -- {$priority}

$todo_field_names[4]: {$_REQUEST['todo-urgency']} -- {$urgency}

$todo_field_names[5]: {$_REQUEST['todo-create-date']}

$todo_field_names[6]: {$_REQUEST['todo-due-date']}

$todo_field_names[7]: {$_REQUEST['todo-assign']}

$todo_field_names[8]: {$_REQUEST['todo-description']}

EOT;
}
else if ($action == 'completetodo') {
    $pagename= 'foobar';
    $action = 'edit';
    $_REQUEST['post'] = 1;

    $completed_items = $_REQUEST['complete'];

    foreach ($completed_items as $completed_item) {
        $page = ReadPage($completed_item);

        $replaced = preg_replace("/(^|\n)Status:( )*(\w+)( )*/", "\nStatus: Completed", $page['text']);

        $match_count = preg_match("/(^|\n)Completed date:( )*(\w+)( )*/", $replaced);

        $completed_date = $_REQUEST['todo-complete-date'];

        if ($match_count == 0) {
            $_POST['text'] .= 
                "REPLACED\n" 
                . $completed_item . "\n\n" 
                . $replaced 
                . "\n\nCompleted date: " . $completed_date . "\n" 
                . "END REPLACED\n\n";
        }
        else {
            $replaced = preg_replace("/(^|\n)Completed date:( )*(.+)( )*/", "\nCompleted date: $completed_date", $replaced);

            $_POST['text'] .= 
                "REPLACED\n" 
                . $completed_item . "\n\n" 
                . $replaced 
                . "END REPLACED\n\n";
        }
    }
}

function ToDoList($format, $pagename, $options)
{
    global $ToDoGroup, $todo_field_names;

    $settings['status'] = 'All';
    $settings['category'] = 'All';

    $arguments = GetDirectiveArgs($options, $settings);

    if (isset($_GET['order'])) {
        $order = $_GET['order'];
    }
    elseif (isset($_POST['order'])) {
        $order = $_POST['order'];
    }
    else {
        $order = '';
    }

    $output[] = <<< EOT
<table cellspacing='0' cellpadding='0' class="todo-list" align='center'>
EOT;

    $output[] = <<< EOT
    <thead>
    <tr>
        <td class='first'>[[{$pagename}?order=id | $todo_field_names[0]]]</td>
        <td>[[{$pagename}?order=status | $todo_field_names[2]]]</td>
        <td>[[{$pagename}?order=priority | $todo_field_names[3]]]</td>
        <td>[[{$pagename}?order=urgency | $todo_field_names[4]]]</td>
        <td>[[{$pagename}?order=create_date | $todo_field_names[5]]]</td>
        <td>[[{$pagename}?order=due_date | $todo_field_names[6]]]</td>
        <td>[[{$pagename}?order=assign | $todo_field_names[7]]]</td>
    </tr>
    </thead>
EOT;

    $todo_list = ListPages("/^$ToDoGroup\\.\\d+$/");

    foreach ($todo_list as $todo_item) {
        $page = ReadPage($todo_item);

        preg_match_all(
            "/(^|\n)([^:]*?):([^\n]*)/", // "/(^|\n)([A-Za-z][^:]*):([^\n]*)/",
            $page['text'],
            $match
            );

        $title_parts = split('\.', $todo_item);
        $category = split(':', $match[0][0]);
        $status = split(':', $match[0][1]);
        $priority = split(':', $match[0][2]);
        $numeric_priority = split('--', $priority[1]);
        $urgency = split(':', $match[0][3]);
        $numeric_urgency = split('--', $urgency[1]);
        $create_date = split(':', $match[0][4]);
        $due_date = split(':', $match[0][5]);
        $description = split(':', $match[0][7]);
        $assign = split(':', $match[0][6]);
        $display = TRUE;

        if (
            ! empty($arguments['assign'])
            && ! ($arguments['assign'] === 'All')
            && ! (trim($assign[1]) === $arguments['assign'])
        )
        {
            $display = FALSE;
        }
        if (
            ! empty($arguments['status'])
            && ! ($arguments['status'] === 'All')
            && ! (trim($status[1]) === $arguments['status'])
        )
        {
            $display = FALSE;
        }

        if (
            $display 
            && ! empty($arguments['category'])
            && ! ($arguments['category'] === 'All')
            && ! (trim($category[1]) === $arguments['category'])
            ) 
        {
            $display = FALSE;
        }

        if ($display) {
            $todo_items[] = 
                array(
                    id               => $title_parts[1],
                    category         => $category[1],
                    status           => $status[1],
                    priority         => $priority[1],
                    numeric_priority => $numeric_priority[0],
                    urgency          => $urgency[1],
                    numeric_urgency  => $numeric_urgency[0],
                    create_date      => $create_date[1],
                    due_date         => $due_date[1],
                    description      => $description[1], 
                    assign           => $assign[1]
                    );

            if ($order) {
                $sort_field[] = ${$order}[1];
            }
        }
    }

    if ($order) {
        array_multisort($sort_field, SORT_DESC, $todo_items);
    }

    if (count($todo_items) > 0) {
        foreach ($todo_items as $item) {

            $output[] = <<< EOT
    <tbody>
    <tr>
        <td valign='top' colspan='1' rowspan='2' class='first bottom'>
            <div>[[{$ToDoGroup}.{$item['id']} | {$item['id']}]]</div>
            <div>[[{$ToDoGroup}.{$item['id']}?action=edit | (edit)]]</div>
        </td>

        <td colspan='1'>
            {$item['status']}
        </td>
        <td colspan='1'>
            {$item['numeric_priority']}
        </td>
        <td colspan='1'>
            {$item['numeric_urgency']}
        </td>
        <td colspan='1'>
            {$item['create_date']}
        </td>
        <td colspan='1'>
            {$item['due_date']}
        </td>
        <td colspan='1'>
            {$item['assign']}
        </td>
    </tr>

    <tr>
        <td colspan='6' class='bottom description'>
            {$item['category']} &mdash; {$item['description']}
        </td>
    </tr>
    </tbody>
EOT;
        }
    }

    $output[] = <<< EOT
</table>
EOT;

    return FmtPageName(implode('', $output), $pagename);
}

function ToDoSimpleList($format, $pagename, $options) 
{
    global $ToDoGroup, $todo_field_names;

    $settings['status'] = 'All';
    $settings['category'] = 'All';
    $settings['assign'] = 'All';

    $arguments = GetDirectiveArgs($options, $settings);
    $todo_list = ListPages("/^$ToDoGroup\\.\\d+$/");
    usort($todo_list, "CompareToDoListItem");

    $sort_by_completed_date = FALSE;
    $sort_order = SORT_DESC;

    $unix_now_time = strtotime("now");

    foreach ($todo_list as $todo_item) {
        $page = ReadPage($todo_item);

        preg_match_all(
            "/(^|\n)([^:]*?):([^\n]*)/", // "/(^|\n)([A-Za-z][^:]*):([^\n]*)/",
            $page['text'],
            $match
            );

        $title_parts = split('\.', $todo_item);
        $category = split(':', $match[0][0]);
        $status = split(':', $match[0][1]);
        $priority = split(':', $match[0][2]);
        $numeric_priority = split('--', $priority[1]);
        $urgency = split(':', $match[0][3]);
        $numeric_urgency = split('--', $urgency[1]);
        $create_date = split(':', $match[0][4]);
        $due_date = split(':', $match[0][5]);
        $description = split(':', $match[0][7]);
        $completed_date = split(':', $match[0][8]);
        $assign = split(':', $match[0][6]);

        $description_text = trim($description[1]);
        $completed_date_text = str_replace("-", "", trim($completed_date[1]));

        $todo_rank = trim($numeric_priority[0]) * trim($numeric_urgency[0]);

        $display = TRUE;

        if (
            ! empty($arguments['assign'])
            && ! ($arguments['assign'] === 'All')
            && ! (trim($assign[1]) === $arguments['assign'])
        )
        {
            $display = FALSE;
        }

        if (
            ! empty($arguments['status'])
            && ! ($arguments['status'] === 'All')
            && ! (trim($status[1]) === $arguments['status'])
        )
        {
            $display = FALSE;
        }

        if (
            $display 
            && ! empty($arguments['category'])
            && ! ($arguments['category'] === 'All')
            && ! (trim($category[1]) === $arguments['category'])
            ) 
        {
            $display = FALSE;
        }

        if (
            $display 
            && ! empty($arguments['month'])
            && ! ($arguments['month'] === 'All')
            ) 
        {
            if (! empty($completed_date_text)) {
                $month_argument = explode("-", $arguments['month']);

                $unix_time = strtotime($completed_date_text);

                $display = 
                    ($month_argument[1] == date("m", $unix_time))
                    && ($month_argument[0] == date("Y", $unix_time))
                    ? TRUE : FALSE;

                $this_completed_date = substr(trim($completed_date[1]), 8, 2);
                $sort_by_completed_date = TRUE;
                $sort_order = SORT_ASC;
                $sort_field[] = $this_completed_date;
                $show_date_code = "[" . $this_completed_date . "] ";
            }
            else {
                $display = FALSE;
            }
        }

        if ($display) {
            $checked_flag =
                (trim($status[1]) === 'Completed')
                ? 'checked' : '';

            $original_status = trim($status[1]);
            $original_status_code = "<input type='hidden' name='todo-original-status' value='$original_status'/>";

            $unix_due_time = strtotime(trim($due_date[1]));

            $overdue = 
                (($unix_due_time < $unix_now_time) && ! $checked_flag)
                ? TRUE : FALSE;

            if (! $sort_by_completed_date) {
                $sort_field[] = $todo_rank;

                if (! $overdue) {
                    $show_date_code = 
                        "["
                        . trim($numeric_priority[0])
                        . ","
                        . trim($numeric_urgency[0])
                        //. ","
                        //. $unix_due_time
                        //. ","
                        //. $unix_now_time
                        //. ","
                        //. trim($due_date[1])
                        . "] "
                        ;
                }
                else {
                    $show_date_code = 
                        "%red%["
                        . trim($numeric_priority[0])
                        . ","
                        . trim($numeric_urgency[0])
                        //. ","
                        //. $unix_due_time
                        //. ","
                        //. $unix_now_time
                        //. ","
                        //. trim($due_date[1])
                        . "]%% "
                        ;
                }
            }

            $todo_items[] =
                array(
                    todo_rank            => $todo_rank,
                    id                   => $todo_item,
                    status               => trim($status[1]),
                    original_status_code => $original_status_code,
                    checked_flag         => $checked_flag,
                    completed_date_text  => $completed_date_text,
                    this_completed_date  => $this_completed_date,
                    show_date_code       => $show_date_code,
                    due_date             => trim($due_date[1]),
                    description_text     => $description_text,
                    assign               => trim($assign[1])
                    );
        }
    }

    $rc = array_multisort($sort_field, $sort_order, $todo_items);

    $output[] = <<< EOT
<form method='post'>
<input type='hidden' name='action' value='completetodo' />
<input type='hidden' name='todo-complete-date' value='{$complete_date}' />
<ul class='todo-simple-list'>
EOT;

    foreach ($todo_items as $item) {
        $output[] = <<< EOT
    <li><input type='checkbox' name='complete[]' value='{$item["id"]}' {$item['checked_flag']} />{$item['show_date_code']}{$item['description_text']} <a href='{$PageUrl}?n={$item["id"]}&action=edit'>(edit)</a>{$item['original_status_code']}</li>
EOT;
    }

    if (count($todo_items) > 0) {
        $submit_code = ""; // "<input type='submit' value='Update' accesskey='s' />";
    }
    else {
        $submit_code = "No matching to do items found.";
    }

    $output[] = <<< EOT
</ul>
{$submit_code}
</form>
EOT;

    return FmtPageName(implode('', $output), $pagename);
}

?>