<?php

# PLUGIN PREVIEW BY TEXTPATTERN.INFO


/*
    adi_cat_menu - Category & article menu

    Written by Adi Gilbert

    Released under the GNU General Public License

    Version history:
    0.6        - TXP 4.6 tag registration
    0.5        - new attribute: 'list_empty_cats' (for floodfish)
            - new attributes 'list_id' & 'list_id_prefix' (for floodfish)
            - new attributes: 'wraptag', 'wraptag_id', 'wraptag_class'
            - new attribute: 'active_parent' (for kbarlow)
    0.4        - new adi_cat_menu attributes: 'link' (for jpdupont), 'article_form' & 'rss_article_form' (for floodfish)
            - enhancement: extended 'categories' & 'exclude' functionality to section sensitive mode
            - fix: suppress empty <ul></ul> (e.g. when no categories found in section sensitive mode) (for jpdupont)
    0.3        - enhancement: restored 'section', 'this_section' attributes & extended functionality to cover article lists
            - enhancement: restored 'exclude' attribute functionality
            - enhancement: restored 'categories' attribute functionality
            - new adi_cat_menu attribute: 'section-sensitive'
    0.2        - enhancement: rss_unlimited_categories mode
            - help: updated instructions regarding article <li> active class
            - help: documented "sort' attribute
    0.1        - initial release

*/

// TXP 4.6 tag registration
if (class_exists('\Textpattern\Tag\Registry')) {
    
Txp::get('\Textpattern\Tag\Registry')
        ->
register('adi_cat_menu')
        ->
register('adi_cat_menu_if')
    ;
}

global 
$thing,$atts,$txp407;

$txp407 version_compare($prefs['version'], '4.0.7''>=');

function 
adi_cat_menu_articles($category,$wraptag='',$article_attr='') {
// retrieve articles using <txp:article_custom /> or <txp:rss_unlimited_categories_article_list /> tags
    
global $rss_unlimited,$s,$section,$this_section,$txp407,$article_form,$rss_article_form;
    
$article_attr == '' // any article_custom attributes specified?
        
$attr '' :
        
$attr ' '.$article_attr;
    
// set default "all sections" attribute
    
if ($rss_unlimited)
        
$section_attribute ' section="*"';
    else
        
$section_attribute '';
    if (
$section// explicit section specified
        
$section_attribute ' section="'.$section.'"';
    if (
$this_section// current section required
        
$section_attribute ' section="'.$s.'"';
    
$rss_unlimited ?
        
$article_list trim(parse('<txp:rss_unlimited_categories_article_list category="'.$category.'"'.$attr.' '.$section_attribute.' form="'.$rss_article_form.'" />')) :
        
$article_list trim(parse('<txp:article_custom category="'.$category.'"'.$attr.' '.$section_attribute.' form="'.$article_form.'" />'));
    if (
$article_list == "")
        return 
'';
    else 
// articles found
        
if ($wraptag == "")
            return 
$article_list;
        else
            return 
tag($article_list,$wraptag,'');
}

function 
adi_cat_menu_if($atts$thing) {
// for use in the Form to identify the active article
// copy of TXP 4.0.7 if_article_id
    
global $thisarticle$pretext;
    
assert_article();
    
extract(lAtts(array(
        
'id' => $pretext['id'],
    ), 
$atts));
    if (
$id)
        return 
parse(EvalElse($thingin_list($thisarticle['thisid'], $id)));
}

function 
adi_cat_menu($atts) {
// <txp:adi_cat_menu /> tag
    
global $prefs,$s,$c,$thisarticle,$rss_unlimited,$section,$this_section,$txp407,$article_form,$rss_article_form;

    
extract(lAtts(array(
        
'class'                => 'cat_menu',        // class applied to top level <ul>
        
'menu_id'            => '',                // CSS ID for top level <ul>
        
'active_class'        => 'active_class',    // class applied to active <li>
        
'parent'            => '',                // category parent
        
'categories'        => '',                // list of categories to include
        
'exclude'            => '',                // list of categories to exclude
        
'restrict'            => '',                // category1 or category2
        
'sort'                => '',                // category sort options
        
'link'                => '1',                // output categories as links
        
'messy_url'            => '0',                // force links to be output in messy URL format
        
'list_id'            => '0',                // output <li> IDs or not
        
'list_id_prefix'    => 'cat_menu_',        // <li> ID prefix
        
'article_attr'        => '',                // attributes to pass to article_custom
        
'article_form'        => 'adi_cat_menu_articles'// form to use with article_custom
        
'active_only'        => '0',                // only show articles in currently active category
        
'active_parent'        => '0',                // apply active class to category <li> when in individual article mode (SHOULD BE DEFAULT BEHAVIOUR?)
        
'rss_unlimited'        => '0',                // rss_unlimited_categories mode
        
'rss_article_form'    => 'adi_cat_menu_rss_articles'// form to use with rss_unlimited_categories_article_list
        
'section'            => '',                // link categories to specified section
        
'this_section'        => '',                // link categories to current section (overrides 'section')
        
'section_sensitive'    => '0',                // display categories only that have articles belonging to specified 'section' or 'this_section'
        
'list_empty_cats'    => '1',                // display empty categories (i.e. categories that have no articles)
        
'type'                => 'article',        // leftover from TXP category_list
        
'wraptag'            => '',                // wrap a tag around the menu
        
'wraptag_class'        => 'menu_wrapper',    // class for wraptag
        
'wraptag_id'        => '',                // id for wraptag
        
'debug'                => '0',
    ), 
$atts));

    
// set up the basics
    //$break = 'li';
    //$wraptag = 'ul';
    
$sort doSlash($sort);
    
$include_list '';
    if (
$categories) {
        
$include_list do_list($categories);
        
$include_list join("','",doSlash($include_list));
    }
    
$exclude_list '';
    if (
$exclude) {
        
$exclude_list do_list($exclude);
        
$exclude_list join("','",doSlash($exclude_list));
    }

    
// debug action
    
if ($debug) {
        echo 
"VERSIONS";
        echo 
'<pre>';
        
$version safe_field("version""txp_plugin""name='adi_cat_menu'");
        
$status safe_field("status""txp_plugin""name='adi_cat_menu'");
        echo 
__FUNCTION__.': '.(empty($version)?'not installed':$version.($status?' (active)':' (not active)')).'<br/>';
        echo 
'TXP: '.$prefs['version'].($txp407 ' (4.0.7+)' ' (pre-4.0.7)').'<br/>';
        echo 
'PHP: '.phpversion().'<br/>';
        echo 
'MySQL: '.mysql_get_server_info();
        echo 
'</pre>';
        echo 
'SUPPLIED ATTRIBUTES:';
        
dmp($atts);
        echo 
"CURRENT CATEGORY = $c<br/>";
        echo 
"CURRENT SECTION = $s<br/>";
        echo 
"INITIAL INCLUDE LIST = $include_list<br/>";
        echo 
"INITIAL EXCLUDE LIST = $exclude_list<br/>";
    }

    
// here we go
    
if ($section_sensitive) {
        if (
$this_section// current section required, override $section
            
$section $s;
        
// set some cbs_category_list attribute values
        
$showcount FALSE;
        
$sticky FALSE;
        
$posted '';
        
// some clever stuff from cbs_category_list
        
if ($section == 'default') {
            
$table ', `'.PFX.'txp_section` AS s';
            
$sqlsection 's.name AND s.on_frontpage = 1';
        }
        else {
            
$table '';
            
$sqlsection "'$section'";
        }
        
// process cbs_category_list attributes
        
$parent = ($parent) ? " AND c.parent = '$parent'" '';
        
$showcount = ($showcount == 'true') ? ', COUNT(*) AS count' '';
        
$sticky = ($sticky == 'true') ? '5' '4';
        switch(
$posted) {
            case 
'all':
                
$posted '';
                break;
            case 
'future':
                
$posted ' AND t.Posted >= now()';
                break;
            default:
                
$posted ' AND t.Posted < now()';
        }
        
// I did these bits!
        
$sort ?
            
$sort ' ORDER BY c.'.$sort :
            
$sort ' ORDER BY c.name asc';
        if (
$include_list)
            
$include_list " AND c.name IN ('$include_list')";
        if (
$exclude_list)
            
$exclude_list " AND c.name NOT IN ('$exclude_list')";
        
// some magic SQL from cbs_category_list (with some include/exclude extras)
        
$rs startRows(
            
'SELECT c.name, c.title'.$showcount.' FROM `'.PFX.'txp_category` AS c, `'.PFX.'textpattern` AS t'.$table
            
.' WHERE (c.name = t.Category1 OR c.name = t.Category2)'
            
.$include_list
            
.$exclude_list
            
.' AND c.type = \'article\' AND t.Section = '.$sqlsection.$parent.$posted
            
.' AND t.Status = '.$sticky.' GROUP BY c.name'.$sort
            
,$debug);
    }
    else { 
// not section sensitive
        
if ($parent// exclude parent from list
            
if ($exclude_list// combine parent & exclude list
                
$exclude_list $exclude_list.','.$parent;
            else 
// just exclude parent
                
$exclude_list $parent;
        if (
$debug) echo "NEW EXCLUDE LIST = $exclude_list";
        
// based on code from TXP 4.0.6 category_list tag
        
if ($exclude_list) {
            
$exclude_list "AND name NOT IN ('$exclude_list')";
        }
        if (
$parent) {
            
$qs safe_row('lft, rgt''txp_category'"name = '".doSlash($parent)."'",$debug);
            if (
$qs) {
                
extract($qs);
                
$rs safe_rows_start('name, title''txp_category',
                    
"(lft between $lft and $rgt) and type = '".doSlash($type)."' and name != 'default' $exclude_list order by ".($sort $sort 'lft ASC'),$debug);
            }
        }
        else if (
$include_list) { // explicit list of categories supplied
            
$rs safe_rows_start('name, title''txp_category',
                
"type = '".doSlash($type)."' and name in ('$include_list') order by ".($sort $sort "field(name, '$include_list')"),$debug);
        }
        else { 
// empty parent attribute (= all categories)
            
$rs safe_rows_start('name, title''txp_category',
                
"type = '$type' and name not in('default','root') $exclude_list order by ".($sort $sort 'name ASC'),$debug);
        }
    }

    
// if parent category not found then we fail in same way as TXP category_list with "Undefined variable: rs" - COULD DO BETTER?
    
if ($rs) {
        
$out = array();
        empty(
$class) ? // set up <ul> class
            
$ul_class '' :
            
$ul_class ' class="'.$class.'"';
        empty(
$menu_id) ? // set up <ul> ID
            
$ul_id '' :
            
$ul_id ' id="'.$menu_id.'"';
        
$empties '';

        
// determine which categories current article belongs to (if individual article mode) or generate emmpty list if not
        
$article_cats = array();
        if (!empty(
$thisarticle)) { // individual article being displayed
            
if ($debug) {
                echo 
'CURRENT ARTICLE:';
                
dmp($thisarticle);
            }
            
// get current article's category(ies) (single article) (from TXP if_article_category tag)
            
if (!empty($thisarticle['category1'])) $article_cats[] = $thisarticle['category1'];
            if (!empty(
$thisarticle['category2'])) $article_cats[] = $thisarticle['category2'];
            
$article_cats array_unique($article_cats);
            if (
$debug) {
                echo 
'CURRENT ARTICLE\'S CATEGORIES ('.$thisarticle['title'].'):';
                
dmp($article_cats);
            }
        }

        
// work through retrieved list of categories
        
while ($a nextRow($rs)) {
            
extract($a); // extract category's $name & $title
            
if ($debug) {
                echo 
'CATEGORY '.$title.':';
                
dmp($a);
            }
            if (
$name) { // i.e. category name
                // set section for category link (if this_section set then current section, else use supplied section)
                
$section = ($this_section) ? ( $s == 'default' '' $s ) : $section// note: default = ''
                
$title htmlspecialchars($title);

                
// determine if current category is active (can't tell if individual article being displayed)
                // note use of strcasecmp - category names can be in different case if permanent link mode in TXP 4.0.5 is not messy!
                
$active_category = (strcasecmp($c$name) == 0);

                
// determine if category is parent to individual article
                
$parent_category FALSE;
                if (!empty(
$article_cats)) { // contains individual article's categories (if any) - empty if not individual article display mode
                    
$parent_category = (!(array_search($name,$article_cats) === FALSE));
                    if (
$debug && $parent_category) echo 'PARENT OF CURRENT ARTICLE<br/><br/>';
                }

                
// set <li> class: IF active_class attribute set AND (category currently active OR (active_parent_category attribute set AND category is parent of active individual article))
                
$li_class = ($active_class AND ($active_category OR ($active_parent AND $parent_category))) ?
                    
' class="'.$active_class.'"' :
                    
'';
                
$list_id ?
                    
$li_id ' id="'.$list_id_prefix.$title.'"' :
                    
$li_id '';
                
$li_tag '<li'.$li_class.$li_id.'>';
                
$messy_url ?
                    
$cat_url hu.'?c='.$name // messy URL
                    
$cat_url pagelinkurl(array('s' => $section'c' => $name)); // render link according to Permanent Link Mode
                
$link ?
                    
$a_tag '<a href="'.$cat_url.'">'.$title.'</a>' :
                    
$a_tag $title;

                
// determine whether to list articles or not
                
$list_articles TRUE// default setting
                
if ($active_only)
                    
$list_articles $active_category || $parent_category// list articles if category is active or is parent of current individual article
                
if ($list_articles) {
                    
$articles_found trim(adi_cat_menu_articles($name,'ul',$article_attr));
                    if (!
$articles_found && $debug$empties .= ' '.$title;
                }
                else
                    
$articles_found '';
                if (
$list_empty_cats || (!$list_empty_cats && $articles_found))  // generate category list item
                    
$out[] = $li_tag.$a_tag.$articles_found.'</li>';
            }
            if (
$debug) echo '<hr />';
        }

        if (
$debug$empties '<li>[Empties: '.$empties.']</li>';

        if (
$out) { // it's a wrap
            
$out '<ul'.$ul_class.$ul_id.'>'.$empties.join($out).'</ul>';
            if (
$wraptag) { // double wrapped for your protection
                
$wrap_attr '';
                if (
$wraptag_id$wrap_attr .= ' id="'.$wraptag_id.'"';
                if (
$wraptag_class$wrap_attr .= ' class="'.$wraptag_class.'"';
                
$out tag($out,$wraptag,$wrap_attr);
            }
            return 
$out;
        }
        else
            return 
'';

    }
    return 
'';
}