PageRenderTime 377ms CodeModel.GetById 141ms app.highlight 149ms RepoModel.GetById 32ms app.codeStats 2ms

/~enabled/smd_calendar.php

https://bitbucket.org/mrdale/txp-plugins
PHP | 3258 lines | 2916 code | 178 blank | 164 comment | 400 complexity | 9765c280f80ce3fb85716ea8c1d6be92 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3// This is a PLUGIN TEMPLATE for Textpattern CMS.
   4
   5// Copy this file to a new name like abc_myplugin.php.  Edit the code, then
   6// run this file at the command line to produce a plugin for distribution:
   7// $ php abc_myplugin.php > abc_myplugin-0.1.txt
   8
   9// Plugin name is optional.  If unset, it will be extracted from the current
  10// file name. Plugin names should start with a three letter prefix which is
  11// unique and reserved for each plugin author ("abc" is just an example).
  12// Uncomment and edit this line to override:
  13$plugin['name'] = 'smd_calendar';
  14
  15// Allow raw HTML help, as opposed to Textile.
  16// 0 = Plugin help is in Textile format, no raw HTML allowed (default).
  17// 1 = Plugin help is in raw HTML.  Not recommended.
  18# $plugin['allow_html_help'] = 1;
  19
  20$plugin['version'] = '0.54';
  21$plugin['author'] = 'Stef Dawson';
  22$plugin['author_uri'] = 'http://stefdawson.com/';
  23$plugin['description'] = 'Calendar / event / schedule system with events as Textpattern articles';
  24
  25// Plugin load order:
  26// The default value of 5 would fit most plugins, while for instance comment
  27// spam evaluators or URL redirectors would probably want to run earlier
  28// (1...4) to prepare the environment for everything else that follows.
  29// Values 6...9 should be considered for plugins which would work late.
  30// This order is user-overrideable.
  31$plugin['order'] = '5';
  32
  33// Plugin 'type' defines where the plugin is loaded
  34// 0 = public              : only on the public side of the website (default)
  35// 1 = public+admin        : on both the public and admin side
  36// 2 = library             : only when include_plugin() or require_plugin() is called
  37// 3 = admin               : only on the admin side (no AJAX)
  38// 4 = admin+ajax          : only on the admin side (AJAX supported)
  39// 5 = public+admin+ajax   : on both the public and admin side (AJAX supported)
  40$plugin['type'] = '0';
  41
  42// Plugin "flags" signal the presence of optional capabilities to the core plugin loader.
  43// Use an appropriately OR-ed combination of these flags.
  44// The four high-order bits 0xf000 are available for this plugin's private use
  45if (!defined('PLUGIN_HAS_PREFS')) define('PLUGIN_HAS_PREFS', 0x0001); // This plugin wants to receive "plugin_prefs.{$plugin['name']}" events
  46if (!defined('PLUGIN_LIFECYCLE_NOTIFY')) define('PLUGIN_LIFECYCLE_NOTIFY', 0x0002); // This plugin wants to receive "plugin_lifecycle.{$plugin['name']}" events
  47
  48$plugin['flags'] = '0';
  49
  50// Plugin 'textpack' is optional. It provides i18n strings to be used in conjunction with gTxt().
  51// Syntax:
  52// ## arbitrary comment
  53// #@event
  54// #@language ISO-LANGUAGE-CODE
  55// abc_string_name => Localized String
  56
  57/** Uncomment me, if you need a textpack
  58$plugin['textpack'] = <<< EOT
  59#@admin
  60#@language en-gb
  61abc_sample_string => Sample String
  62abc_one_more => One more
  63#@language de-de
  64abc_sample_string => Beispieltext
  65abc_one_more => Noch einer
  66EOT;
  67**/
  68// End of textpack
  69
  70if (!defined('txpinterface'))
  71        @include_once('zem_tpl.php');
  72
  73# --- BEGIN PLUGIN CODE ---
  74/**
  75 * smd_calendar
  76 *
  77 * A Textpattern CMS plugin for complete monthly event and calendar management
  78 *  -> Originally based on mdp_calendar - thanks Marshall!
  79 *  -> Full-size / mini calendar by month, with ISO week support
  80 *  -> Txp articles are events. Future, past, and today's events are supported. Multi-day (spanned) events are based on article expiry
  81 *  -> Next/prev month/year select list with customisable start/end years
  82 *  -> Filter events by cat / section / author / status / time / expiry
  83 *  -> Specify event frequency in custom field (1 week / 10 days / 3 months / etc)
  84 *  -> Optionally (re)schedule / cancel / omit event dates
  85 *  -> Customisable output for events and cells using multiple forms/container and classes
  86 *  -> Conditional tags for building custom logic
  87 *
  88 * @author Stef Dawson
  89 * @link   http://stefdawson.com/
  90 */
  91
  92// TODO:
  93//  * allow table header to be removed / restyled completely (the month/week dropdown & nav icons)
  94//     -- a form (navform?) for the header row with access to all vars such as which month is being displayed?
  95//     -- tools to allow the header to be generated from components and laid out in any manner?
  96//  * allow URL vars to be passed as POST (to bypass gbp_permlinks)
  97//  * add custom rows to the table (header, footer) -- header could be used to replace the current nav/dropdowns
  98//  * div-based calendar layout?
  99// TOFIX:
 100//  * Expiry dates on extra+ allspanned dates in smd_article_event (and calendar?). They currently 'creep' a day for every day of a spanned event
 101//  * Ranges in stepfields (http://forum.textpattern.com/viewtopic.php?pid=254395#p254395 and http://forum.textpattern.com/viewtopic.php?pid=255617#p255617)
 102
 103if( $date = gps('date') ) {
 104	$_GET['month'] = $date;
 105}
 106function smd_calendar($atts, $thing='') {
 107	global $pretext, $thisarticle, $variable, $prefs, $smd_cal_flag, $smd_date, $smd_calinfo, $smd_cal_ucls;
 108
 109	extract(lAtts(array(
 110		'time'          => 'any',
 111		'size'          => 'large',
 112		'expired'       => '',
 113		'category'      => null,
 114		'subcats'       => '',
 115		'section'       => '',
 116		'author'        => '',
 117		'realname'      => '',
 118		'status'        => 'live',
 119		'showall'       => '0',
 120		'static'        => '',
 121		'form'          => '',
 122		'spanform'      => 'SMD_SAME',
 123		'recurform'     => 'SMD_SAME',
 124		'cellform'      => '',
 125		'headerform'    => '',
 126		'stepfield'     => '',
 127		'skipfield'     => '',
 128		'omitfield'     => '',
 129		'extrafield'    => '',
 130		'extrastrict'   => '0',
 131		'datefields'    => '',
 132		'showskipped'   => '0',
 133		'showspanned'   => '1',
 134		'holidays'      => '',
 135		'holidayflags'  => 'standard',
 136		'classlevels'   => 'cell, event',
 137		'linkposted'    => 'recur, multi, multiprev, multilast',
 138		'classprefixes' => 'smd_cal_, smd_cal_ev_',
 139		'class'         => '',
 140		'rowclass'      => '',
 141		'cellclass'     => '',
 142		'emptyclass'    => 'empty',
 143		'isoweekclass'  => 'week',
 144		'navclass'      => 'navprev, navnext',
 145		'navarrow'      => '&#60;, &#62;',
 146		'navid'         => '',
 147		'eventclasses'  => 'category',
 148		'eventwraptag'  => 'span',
 149		'select'        => '',
 150		'selectbtn'     => '',
 151		'myclass'       => '',
 152		'mywraptag'     => '',
 153		'caption'       => '',
 154		'summary'       => '',
 155		'id'            => '',
 156		'week'          => '',
 157		'month'         => '',
 158		'year'          => '',
 159		'remap'         => '',
 160		'yearwidth'     => '0',
 161		'isoweeks'      => '',
 162		'dayformat'     => 'ABBR',
 163		'monthformat'   => 'FULL',
 164		'firstday'      => 0,
 165		'maintain'      => 'calid',
 166		'nameval'       => '',
 167		'event_delim'   => ',',
 168		'gmt'           => 0,
 169		'lang'          => '',
 170		'debug'         => 0,
 171	), $atts));
 172
 173	$size = (in_array($size, array('small', 'large'))) ? $size : 'large';
 174	$status = ($status) ? $status : 'live'; // in case status is empty
 175	$firstday = ($isoweeks == '') ? $firstday : 1;
 176	$spanform = ($spanform == 'SMD_SAME') ? $form : $spanform;
 177	$recurform = ($recurform == 'SMD_SAME') ? $form : $recurform;
 178	$cellform = (empty($cellform)) ? '' : fetch_form($cellform);
 179	$headerform = (empty($headerform)) ? '' : fetch_form($headerform);
 180	$frontpage = ($section=='' && $pretext['s']=='default') ? true : false;
 181
 182	// Set up the class prefixes
 183	$clevs = do_list($classlevels);
 184	$cls = do_list($classprefixes);
 185	$cls_pfx = $evc_pfx = $cls[0];
 186	if (count($cls) > 1){
 187		$evc_pfx = $cls[1];
 188	}
 189
 190	// Set up the nav class(es)
 191	$maintain = do_list($maintain);
 192	$navarrow = do_list($navarrow);
 193	$navparr = $navarrow[0];
 194	$navnarr = (count($navarrow) > 1) ? $navarrow[1] : $navarrow[0];
 195	$navclass = do_list($navclass);
 196	$navpclass = $navclass[0];
 197	$navnclass = (count($navclass) > 1) ? $navclass[1] : $navclass[0];
 198
 199	// Filters
 200	$fopts = array();
 201	$catSQL = $secSQL = $authSQL = $fpSQL = '';
 202	if($category !== null) {
 203		$uncats = false;
 204		$allcats = do_list($category);
 205		if (($pos = array_search('SMD_UNCAT', $allcats)) !== false) {
 206			$uncats = true;
 207			unset($allcats[$pos]);
 208			$category = join(',', $allcats);
 209		}
 210		$fopts['c'] = $category; // TODO: Can fopts take a list? Should it include subcats?
 211		$subcats = (empty($subcats)) ? 0 : ((strtolower($subcats)=="all") ? 99999 : intval($subcats));
 212		if ($subcats) {
 213			$outcats = array();
 214			foreach ($allcats as $cat) {
 215				$cats = getTree(doslash($cat), 'article');
 216				foreach ($cats as $jdx => $val) {
 217					if ($cats[$jdx]['level'] <= $subcats) {
 218						$outcats[] = $cats[$jdx]['name'];
 219					}
 220				}
 221			}
 222			$allcats = $outcats;
 223		}
 224		$catSQL = doQuote(join("','", doSlash($allcats)));
 225		$catSQL = ($uncats ? " AND (Category1 = '' AND Category2 = '')" : '') .
 226			($uncats && $allcats ? " OR " : ($allcats ? " AND " : '')) .
 227			($allcats ? "( Category1 IN (".$catSQL.") OR Category2 IN (".$catSQL.") ) " : '');
 228	}
 229	if($section) {
 230		$secs = do_list($section);
 231		$smd_calinfo['s'] = $secs[0];
 232		$secSQL = doQuote(join("','", doSlash($secs)));
 233		$secSQL = " AND Section IN (".$secSQL.") ";
 234	}
 235	if($realname) {
 236		$authors = safe_column('name', 'txp_users', 'RealName IN ('. doQuote(join("','", doArray(do_list($realname), 'urldecode'))) .')' );
 237		$author = join(',', $authors);
 238	}
 239	if($author) {
 240		$fopts['author'] = htmlentities(gps('author'));
 241		$authSQL = doQuote(join("','", doSlash(do_list($author))));
 242		$authSQL = " AND AuthorID IN (".$authSQL.") ";
 243	}
 244	if ($frontpage && !$showall) {
 245		$fpSQL = filterFrontPage();
 246	}
 247	$smd_calinfo['artid'] = $thisarticle['thisid'];
 248	$smd_calinfo['artitle'] = $thisarticle['url_title'];
 249	$nameval = do_list($nameval);
 250	foreach ($nameval as $nv) {
 251		$nv = explode("=", $nv);
 252		if ($nv[0]) {
 253			$fopts[$nv[0]] = ((isset($nv[1])) ? $nv[1] : '');
 254		}
 255	}
 256	$status = do_list($status);
 257	$stati = array();
 258	foreach ($status as $stat) {
 259		if (empty($stat)) {
 260			continue;
 261		} else if (is_numeric($stat)) {
 262			$stati[] = $stat;
 263		} else {
 264			$stati[] = getStatusNum($stat);
 265		}
 266	}
 267	$stati = " Status IN (".join(',', $stati).")";
 268
 269	$expired = ($expired) ? $expired : $prefs['publish_expired_articles'];
 270	$expired = (($expired) ? '' : ' AND (now() <= Expires OR Expires = '.NULLDATETIME.')');
 271	$eventclasses = do_list($eventclasses);
 272	$holidayflags = do_list($holidayflags);
 273	$linkposted = do_list($linkposted);
 274	$datefields = do_list($datefields);
 275
 276	// Work out the first and last posts to determine the year range - probably a better way of doing this than 3 queries
 277	$filt = $stati . (($category !== null) ? $catSQL : '') . (($section) ? $secSQL : '') . (($author) ? $authSQL : '') . $fpSQL;
 278	$earliest = safe_field('unix_timestamp(Posted) AS uPosted', 'textpattern', $filt .' ORDER BY Posted ASC LIMIT 0, 1', $debug);
 279	$lp = safe_field('unix_timestamp(Posted) AS uPosted', 'textpattern', $filt .' ORDER BY Posted DESC LIMIT 0, 1', $debug);
 280	$lm = safe_field('unix_timestamp(LastMod) AS uLastMod', 'textpattern', $filt .' ORDER BY LastMod DESC LIMIT 0, 1', $debug);
 281	$latest = ($time=="past") ? time() : (($lp > $lm) ? $lp : $lm);
 282
 283	$yearwidth = do_list($yearwidth);
 284	$yearwidth[0] = (empty($yearwidth[0])) ? 0 : $yearwidth[0];
 285	if (count($yearwidth) == 1) {
 286		$yearwidth[1] = $yearwidth[0];
 287	}
 288	$usenow = array(false,false);
 289	foreach ($yearwidth as $yridx => $yritem) {
 290		if (strpos($yritem,"c") !== false) {
 291			$yearwidth[$yridx] = intval($yritem);
 292			$usenow[$yridx] = true;
 293		}
 294	}
 295
 296	// Remap w/m/y to other vars if required
 297	$remap = do_list($remap);
 298	$dmap = array("y" => "y", "m" => "m", "w" => "w");
 299	foreach ($remap as $dpair) {
 300		$dpair = do_list($dpair, ':');
 301		$dmap[$dpair[0]] = (isset($dpair[1])) ? $dpair[1] : $dpair[0];
 302	}
 303	$earliest = date("Y", strtotime("-".$yearwidth[0]." year", ( (empty($earliest) || $usenow[0]==true) ? time() : $earliest) ) );
 304	$latest = date("Y", strtotime("+".$yearwidth[1]." year", ( (empty($latest) || $usenow[1]==true) ? time() : $latest) ) );
 305
 306	// Check the URL for current date and calendar target info
 307	$in_calid = gps('calid');
 308	$in_year = (gps($dmap["y"]) and is_numeric(gps($dmap["y"]))) ? (int)gps($dmap["y"]) : '';
 309	$in_month = (gps($dmap["m"]) and is_numeric(gps($dmap["m"]))) ? (int)gps($dmap["m"]) : '';
 310	$in_week = (gps($dmap["w"]) and is_numeric(gps($dmap["w"]))) ? (int)gps($dmap["w"]) : '';
 311
 312	if($static) { // if we're static w/o any supplied vars, use the current date
 313		if(!$year) { $year = safe_strftime('%Y'); }
 314		if(!$month) { $month = safe_strftime('%m'); }
 315	} else { // otherwise use current date only if there's nothing else
 316		if( $id == $in_calid ) { // use incoming
 317			$year = ($in_year) ? $in_year : (($year) ? $year : safe_strftime('%Y'));
 318			$month = ($in_month) ? $in_month : (($month) ? $month : safe_strftime('%m'));
 319			// If week is used, adjust month so it encompasses the given week
 320			$week = $in_week;
 321			if ($week) {
 322				$month = safe_strftime("%m", strtotime($year."W".str_pad($week, 2, '0', STR_PAD_LEFT))); // Get the month from the week
 323			}
 324		} else { // use current
 325			if(!$year) { $year = safe_strftime('%Y'); }
 326			if(!$month) { $month = safe_strftime('%m'); }
 327			if($week) { $month = safe_strftime("%m", strtotime($year."W".str_pad($week, 2, '0', STR_PAD_LEFT))); }
 328		}
 329	}
 330	$smd_calinfo['id'] = ($in_calid) ? $in_calid : $id;
 331	$smd_date['y'] = $year; $smd_date['m'] = $month; // $week/day/isoyear are set per event later
 332
 333	$ts_first = mktime(0, 0, 0, $month, 1, $year);
 334	$ts_last = mktime(23, 59, 59, $month, date('t',$ts_first), $year);
 335	$ts_lastoff = $ts_last - tz_offset($ts_last);
 336	if ($debug) {
 337		echo "++ THIS MONTH'S CALENDAR [ start stamp // end date // end stamp // end date // tz offset (end) ] ++";
 338		dmp($ts_first, date('Y-m-d H:i:s', $ts_first), $ts_last, date('Y-m-d H:i:s', $ts_last), $ts_lastoff);
 339	}
 340	$extrasql = $catSQL . $secSQL . $authSQL . $fpSQL;
 341
 342	switch($time) {
 343		case "any" : break;
 344		case "future" : $extrasql .= " AND Posted > now()"; break;
 345		default : $extrasql .= " AND Posted < now()"; break; // The past
 346	}
 347
 348	// Holidays are global 'exclusions', either defined directly or in a txp:variable
 349	$holidays = do_list($holidays);
 350	$txphols = do_list($holidays[0], ":");
 351	if ($txphols[0] == "txpvar") {
 352		$holidays = do_list($variable[$txphols[1]]);
 353	}
 354	// Force each holiday to a known format. Holidays without years use current year
 355	foreach ($holidays as $key => $val) {
 356		if (empty($val)) continue;
 357		$numparts = preg_match('/^([\d\w]+).?([\d\w]+).?([\d\w]+)?$/', $val, $parts);
 358
 359		if ($numparts) {
 360			if (count($parts) == 3) {
 361				$parts[3] = $year;
 362         }
 363			$val = str_pad($parts[1], 2, '0', STR_PAD_LEFT).'-'.str_pad($parts[2], 2, '0', STR_PAD_LEFT).'-'.$parts[3];
 364		}
 365		$holidays[$key] = date("d-M-Y", safe_strtotime($val));
 366	}
 367
 368	if ($debug > 0 && !empty($holidays) && $holidays[0] != '') {
 369		echo "++ HOLIDAYS ++ ";
 370		dmp($holidays);
 371	}
 372
 373	// Get all matching articles in (and before) this month
 374	$events = array();
 375	$uposted_field = (empty($datefields[0])) ? 'uPosted' : "UNIX_TIMESTAMP($datefields[0])";
 376	$sql2 = $stati . " HAVING $uposted_field <= ".$ts_lastoff. $expired . $extrasql ." ORDER BY Posted ASC";
 377	$grabCols = '*, unix_timestamp(Posted) as uPosted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires';
 378	$evlist = safe_rows($grabCols, 'textpattern', $sql2, $debug);
 379	article_push();
 380
 381	// If any events recur and fall within the current month, add those as well
 382	// If any dates are to be excluded, the entry is skipped UNLESS showskipped indicates otherwise
 383	foreach ($evlist as $row) {
 384		$idx = 0; // In case the 1st day of the month is a continuation of an event from the end of the previous month
 385		$start = (!empty($datefields[0]) && !empty($row[$datefields[0]]) && ($stdt = strtotime($row[$datefields[0]])) !== false) ? $stdt : $row['uPosted'] + tz_offset($row['uPosted']);
 386		$start_date = date("Y-m-d", $start); // For recurring/spanned events on a minical, this is the event the cell links to
 387		$real_end = (isset($datefields[1]) && !empty($row[$datefields[1]]) && ($endt = strtotime($row[$datefields[1]])) !== false) ? $endt : (($row['uExpires']==0) ? 0 : $row['uExpires'] + tz_offset($row['uExpires']));
 388
 389		// If end < start the user-specified dates cannot be trusted
 390		if ($real_end != 0 && $real_end <= $start) {
 391			$start = $row['uPosted'] + tz_offset($row['uPosted']);
 392			$real_end = $row['uExpires'] + tz_offset($row['uExpires']);
 393			trigger_error('Expiry cannot be before start date in "'.$row['Title'].'": ignored', E_USER_WARNING);
 394		}
 395
 396		$end = ($real_end != 0 && $real_end < $ts_last) ? $real_end : $ts_last;
 397		$real_diff = ($real_end==0) ? 0 : $real_end - $start;
 398		$real_end_month = ($real_end==0) ? 0 : date('m', $real_end);
 399		$real_end_year = ($real_end==0) ? 0 : date('Y', $real_end);
 400		$fake_diff = strtotime(date("Y-M-d", $real_end) . " 23:59:59");
 401		$diff = ($real_end==0) ? 0 : $fake_diff - $start;
 402		$smd_cal_flag = array();
 403		$smd_cal_ucls = array();
 404
 405		$ev_month = date('m', $start);
 406		$ev_year = date('Y', $start);
 407		$ev_hr = date('H', $start);
 408		$ev_mn = date('i', $start);
 409		$ev_sc = date('s', $start);
 410
 411		if ($debug > 1) {
 412			echo '++ EVENT START // END // (if non-zero) REAL END ++';
 413			dmp(date('d-M-Y H:i:s', $start) .' // '. date('d-M-Y H:i:s', $end) .' // '. ( ($real_end == 0) ? '' : date('d-M-Y H:i:s', $real_end) ));
 414			dmp($row['Title']);
 415			if ($debug > 2) {
 416				dmp($row);
 417			}
 418		}
 419
 420		$multi = (($end > $start) && ($real_end > $start) && ($real_end > $ts_first) && (date("d-m-Y", $real_end) != date("d-m-Y", $start))) ? true : false;
 421		$recur = (empty($row[$stepfield])) ? false : true;
 422		$hol_hit = in_array(date("d-M-Y", $start), $holidays);
 423		$evclasses = array();
 424		foreach ($eventclasses as $evcls) {
 425			switch ($evcls) {
 426				case "":
 427					break;
 428				case "gcat":
 429					if (isset($pretext['c']) && !empty($pretext['c'])) {
 430						$evclasses[] = $evc_pfx.$pretext['c'];
 431					}
 432					break;
 433				case "category":
 434					if (isset($row['Category1']) && !empty($row['Category1'])) {
 435						$evclasses[] = $evc_pfx.$row['Category1'];
 436					}
 437					if (isset($row['Category2']) && !empty($row['Category2'])) {
 438						$evclasses[] = $evc_pfx.$row['Category2'];
 439					}
 440					break;
 441				case "section":
 442					if (isset($pretext['s']) && !empty($pretext['s'])) {
 443						$evclasses[] = $evc_pfx.$pretext['s'];
 444					}
 445					break;
 446				case "author":
 447					if (isset($pretext['author']) && !empty($pretext['author'])) {
 448						$evclasses[] = $evc_pfx.$pretext['author'];
 449					}
 450					break;
 451				default:
 452					if (isset($row[$evcls]) && !empty($row[$evcls])) {
 453						$evclasses[] = $evc_pfx.$row[$evcls];
 454					}
 455					break;
 456			}
 457		}
 458		$ignore = array();
 459		$omit = array();
 460		$cflag = array();
 461
 462		if ($debug > 1 && $evclasses) {
 463			echo '++ EVENT CLASSES ++';
 464			dmp($evclasses);
 465		}
 466
 467		// Events that start or are added this month
 468		if (($start < $end) && ($start > $ts_first)) {
 469			populateArticleData($row);
 470
 471			// a standard event or start of a multi
 472			if ($showspanned && $multi && !$recur) {
 473				$smd_cal_flag[] = 'multifirst';
 474			}
 475			if ($recur) {
 476				$smd_cal_flag[] = 'recurfirst';
 477			}
 478			if (!$smd_cal_flag) {
 479				$smd_cal_flag[] = 'standard';
 480			}
 481			if ( ( $hol_hit && !in_array('multi',$holidayflags) && in_array('multifirst',$smd_cal_flag) ) || ( $hol_hit && !in_array('standard',$holidayflags) && in_array('standard',$smd_cal_flag) ) ) {
 482				$smd_cal_flag[] = 'cancel';
 483			}
 484			foreach ($smd_cal_flag as $item) {
 485				$cflag[] = $cls_pfx.$item;
 486			}
 487
 488			$idx = $smd_date['d'] = (int)strftime('%d', $start);
 489			$smd_date['w'] = strftime(smd_cal_reformat_win('%V', $start), $start);
 490			$smd_date['iy'] = strftime(smd_cal_reformat_win('%G', $start), $start);
 491			$use_posted = in_array('standard', $linkposted);
 492
 493			$op = ($thing) ? parse($thing) : (($form) ? parse_form($form) : (($size=="small") ? smd_cal_minilink($row, $idx, $month, $year, $use_posted) : href($row['Title'], permlinkurl($row), ' title="'.$row['Title'].'"')) );
 494			$events[$idx][] = array('ev' => $op, 'evid' => $row['ID'], 'flag' => $smd_cal_flag, 'classes' => array_merge($cflag, $smd_cal_ucls, $evclasses), 'posted' => $start_date);
 495			$smd_cal_flag = array();
 496			$cflag = array();
 497			$smd_cal_ucls = array();
 498			$use_posted = '';
 499		}
 500
 501		// Generate a skip array for this event
 502		if ($skipfield && $row[$skipfield] != '') {
 503			$ignores = do_list($row[$skipfield]);
 504			foreach ($ignores as $val) {
 505				$igrng = smd_expand_daterange($val, $start, $end);
 506				foreach ($igrng as $theval) {
 507					$ignore[] = date("d-M-Y", $theval); // Force each date to a known format
 508				}
 509			}
 510		}
 511		// Generate an omit array for this event
 512		if ($omitfield && $row[$omitfield] != '') {
 513			$omits = do_list($row[$omitfield]);
 514			foreach ($omits as $val) {
 515				$omrng = smd_expand_daterange($val, $start, $end);
 516				foreach ($omrng as $theval) {
 517					$omit[] = date("d-M-Y", $theval);
 518				}
 519			}
 520		}
 521		if ($debug > 1 && ($ignore || $omit)) {
 522			echo '++ OMITTED DATES ++';
 523			dmp($omit);
 524			echo '++ CANCELLED DATES ++';
 525			dmp($ignore);
 526		}
 527		// Calculate the date offsets and check recurring events that fall within the month of interest
 528		if ($stepfield && $row[$stepfield] != '') {
 529			$freq = do_list($row[$stepfield]);
 530			$stampoff = (int)(3600*$ev_hr) + (int)(60*$ev_mn) + (int)$ev_sc;
 531			foreach ($freq as $interval) {
 532				$max_loop = 99999; // Yuk, but practically limitless
 533				$origerval = $interval;
 534				$interval = str_replace("?month", date('F', mktime(0,0,0,$month,1)), $interval);
 535				$interval = str_replace("?year", $year, $interval);
 536				if (strpos($interval, "last") === 0) {
 537					$interval = date("l, F jS Y", strtotime( $interval, mktime(12, 0, 0, date("n", mktime(0,0,0,$month,1,$year))+1, 1, $year) ));
 538					$max_loop = 1;
 539				} else if (strpos($interval, "first") === 0) {
 540					$interval = date("l, F jS Y", strtotime( $interval, mktime(12, 0, 0, (($month>1) ? $month-1 : 12), date("t", mktime(0,0,0,$month-1,1,(($month==1) ? $year-1: $year))), (($month==1) ? $year-1: $year)) ));
 541					$max_loop = 1;
 542				} else if (strpos($interval, "this") === 0) {
 543					$max_loop = 1;
 544				}
 545				$ts_loop = 0;
 546				$ts_curr = $start;
 547				if (strpos($origerval, "?month") || strpos($origerval, "?year")) {
 548					$max_loop = 1;
 549            }
 550
 551//				$rng = smd_expand_daterange($interval);
 552//dmp($interval, $rng);
 553				while ($ts_curr < $end && $ts_loop < $max_loop) {
 554					if ($max_loop == 1) {
 555						$ts_curr = strtotime($interval);
 556						$ts_curr = ($ts_curr < $start || $ts_curr > $end) ? $start : $ts_curr;
 557					} else {
 558						$ts_curr = strtotime($interval, $ts_curr);
 559					}
 560					if ($ts_curr === false) {
 561						$ts_loop++;
 562						break;
 563					} else {
 564						if ($debug > 1) {
 565							dmp("INTERVAL: ". date('d-M-Y H:i:s', $ts_curr+$stampoff));
 566						}
 567						if ($ts_curr < $end && $ts_curr >= $ts_first && $ts_curr != $start) {
 568							// A recurring event. Check it isn't a holiday or to be ignored
 569							populateArticleData($row);
 570							$op = '';
 571							$idx = (int)strftime('%d', $ts_curr);
 572							$smd_cal_flag[] = 'recur';
 573							$thisdate = date("d-M-Y", $ts_curr);
 574							$omit_me = in_array($thisdate, $omit);
 575							$show_me = !in_array($thisdate, $ignore);
 576							$hol_hit = in_array($thisdate, $holidays);
 577							$show_hol = ($hol_hit && !in_array('recur',$holidayflags) ) ? false : true;
 578							$use_posted = smd_cal_in_array(array('recur', 'recurfirst'), $linkposted);
 579
 580							if ( $omit_me ) {
 581								$smd_cal_flag[] = 'omit';
 582							}
 583							if ( (!$show_me || !$show_hol) && !$omit_me ) {
 584								$smd_cal_flag[] = 'cancel';
 585							}
 586							foreach ($smd_cal_flag as $item) {
 587								$cflag[] = $cls_pfx.$item;
 588							}
 589
 590							// Create the events that appear in the cell but only if they've not appeared before, or are to be ignored/omitted
 591							if (!$omit_me) {
 592								if (($show_me && $show_hol) || $showskipped) {
 593									$smd_date['d'] = $idx;
 594									$smd_date['w'] = strftime(smd_cal_reformat_win('%V', $ts_curr), $ts_curr);
 595									$smd_date['iy'] = strftime(smd_cal_reformat_win('%G', $ts_curr), $ts_curr);
 596									$op = ($recurform) ? parse_form($recurform) : (($thing) ? parse($thing) : (($size=="small") ? smd_cal_minilink($row, $idx, $month, $year, $use_posted) : href($row['Title'], permlinkurl($row), ' title="'.$row['Title'].'"')) );
 597								}
 598							}
 599							$used = array();
 600							if (isset($events[$idx]) && $events[$idx] != NULL) {
 601								foreach ($events[$idx] as $ev) {
 602									$used[] = $ev['ev'];
 603								}
 604							}
 605							if (isset($events[$idx]) && $events[$idx] == NULL || !in_array($op, $used)) {
 606								$events[$idx][] = array('ev' => $op, 'evid' => $row['ID'], 'flag' => $smd_cal_flag, 'classes' => array_merge($cflag, $smd_cal_ucls, $evclasses), 'posted' => $start_date);
 607							}
 608							$smd_cal_flag = array();
 609							$cflag = array();
 610							$smd_cal_ucls = array();
 611							$use_posted = '';
 612						}
 613						$ts_loop++;
 614					}
 615				}
 616			}
 617		} else if ($showspanned && $multi) {
 618			// Non-recurring events may span more than one date but they must still respect ignored dates and holidays
 619			populateArticleData($row);
 620			$lastday = (int)strftime('%d', $end);
 621			$real_lastday = (int)strftime('%d', $real_end);
 622			while (++$idx <= $lastday) {
 623				$op = '';
 624				$multiflag = (($year==$real_end_year) && ($month==$real_end_month) && ($idx==$real_lastday)) ? 'multilast' : (($idx==1) ? 'multiprev' : 'multi');
 625				$smd_cal_flag[] = $multiflag;
 626				$thistime = mktime(0, 0, 0, $month, $idx, $year);
 627				$thisdate = date("d-M-Y", $thistime);
 628				$omit_me = in_array($thisdate, $omit);
 629				$show_me = !in_array($thisdate, $ignore);
 630				$hol_hit = in_array($thisdate, $holidays);
 631				$show_hol = ($hol_hit && !in_array('multi',$holidayflags) ) ? false : true;
 632				$use_posted = smd_cal_in_array(array('multi', 'multifirst', 'multilast', 'multiprev'), $linkposted);
 633				if ( $omit_me ) {
 634					$smd_cal_flag[] = 'omit';
 635				}
 636				if ( (!$show_me || !$show_hol) && !$omit_me ) {
 637					$smd_cal_flag[] = 'cancel';
 638				}
 639				foreach ($smd_cal_flag as $item) {
 640					$cflag[] = $cls_pfx.$item;
 641				}
 642				// Create the spanned event that appears in the cell
 643				if (!$omit_me) {
 644					if ( ($show_me && $show_hol) || $showskipped) {
 645						$smd_date['d'] = $idx;
 646						$smd_date['w'] = strftime(smd_cal_reformat_win('%V', $thistime), $thistime);
 647						$smd_date['iy'] = strftime(smd_cal_reformat_win('%G', $thistime), $thistime);
 648						$op = ($spanform) ? parse_form($spanform) : (($thing) ? parse($thing) : (($size=="small") ? smd_cal_minilink($row, $idx, $month, $year, $use_posted) : href('&rarr;', permlinkurl($row), ' title="'.$row['Title'].'"')) );
 649					}
 650				}
 651				$events[$idx][] = array('ev' => $op, 'evid' => $row['ID'], 'flag' => $smd_cal_flag, 'classes' => array_merge($cflag, $smd_cal_ucls, $evclasses), 'posted' => $start_date);
 652				$smd_cal_flag = array();
 653				$cflag = array();
 654				$smd_cal_ucls = array();
 655				$use_posted = '';
 656			}
 657		}
 658		// Add any extra dates for this event that are within the current month
 659		if ($extrafield && $row[$extrafield] != '') {
 660			$xtra = do_list($row[$extrafield]);
 661			$ev_hr = date('H', $start);
 662			$ev_mn = date('i', $start);
 663			$ev_sc = date('s', $start);
 664			$stampoff = (int)(3600*$ev_hr) + (int)(60*$ev_mn) + (int)$ev_sc;
 665			foreach ($xtra as $val) {
 666				if (strpos($val, "+") === false) {
 667					$exrng = smd_expand_daterange($val);
 668					$val = date("Y-m-d", $exrng[0]);
 669					$spidth = count($exrng);
 670					$spex = 0;
 671				} else {
 672					$chk = $showspanned && !$recur;
 673					$spidth = $chk ? ceil($diff / (60*60*24)) : 1; // days between dates
 674					$val = rtrim($val, '+');
 675					$spex = $chk ? 1 : 0;
 676				}
 677
 678				for ($jdx = 1; $jdx <= $spidth; $jdx++) {
 679					$tm = safe_strtotime($val . (($jdx==1) ? '' : '+'.($jdx-1).' days'));
 680					if ($diff > 0 && $jdx == 1) {
 681						$expstamp = $tm+$stampoff+$real_diff;
 682					}
 683					$idx = $smd_date['d'] = (int)strftime('%d', $tm);
 684					$dt = date("Y-m-d", $tm);
 685					$lst = ($extrastrict) ? $end : $ts_last;
 686					if ($tm < $lst && $tm >= $ts_first) {
 687						$fakerow = $row;
 688						$fakerow['Posted'] = date("Y-m-d H:i:s", $tm+$stampoff);
 689						$fakerow['uPosted'] = $tm+$stampoff;
 690						if ($diff>0) {
 691							$fakerow['Expires'] = date("Y-m-d H:i:s", $expstamp);
 692							$fakerow['uExpires'] = $expstamp;
 693						}
 694
 695						populateArticleData($fakerow);
 696						$smd_cal_flag[] = 'extra';
 697						$cflag[] = $cls_pfx.'extra';
 698						$omit_me = false;
 699						$show_me = $show_hol = true;
 700						if ($spex) {
 701							$multiflag = ($jdx==1) ? 'multifirst' : (($jdx==$spidth) ? 'multilast' : (($idx==1) ? 'multiprev' : 'multi'));
 702							$thisdate = date("d-M-Y", $tm);
 703							$omit_me = in_array($thisdate, $omit);
 704							$show_me = !in_array($thisdate, $ignore);
 705							$hol_hit = in_array($thisdate, $holidays);
 706							$show_hol = ($hol_hit && !in_array('multi',$holidayflags) ) ? false : true;
 707							$use_posted = in_array('extra', $linkposted);
 708							if ($omit_me) {
 709								$smd_cal_flag[] = 'omit';
 710							}
 711							if ( (!$show_me || !$show_hol) && !$omit_me ) {
 712								$smd_cal_flag[] = 'cancel';
 713							}
 714							$smd_cal_flag[] = $multiflag;
 715							$cflag[] = $cls_pfx.$multiflag;
 716						}
 717						if (!$omit_me) {
 718							if ( ($show_me && $show_hol) || $showskipped) {
 719								$smd_date['w'] = strftime(smd_cal_reformat_win('%V', $tm), $tm);
 720								$smd_date['iy'] = strftime(smd_cal_reformat_win('%G', $tm), $tm);
 721								$op = ($spex && $spanform) ? parse_form($spanform) : (($thing) ? parse($thing) : (($form) ? parse_form($form) : (($size=="small") ? smd_cal_minilink($row, $idx, $month, $year, $use_posted) : href((($spex && $jdx>1) ? '&rarr;' : $row['Title']), permlinkurl($row), ' title="'.$row['Title'].'"')) ));
 722								$events[$idx][] = array('ev' => $op, 'evid' => $row['ID'], 'flag' => $smd_cal_flag, 'classes' => array_merge($cflag, $smd_cal_ucls, $evclasses), 'posted' => $dt);
 723								$smd_cal_flag = array();
 724								$cflag = array();
 725								$smd_cal_ucls = array();
 726								$use_posted = '';
 727							}
 728						}
 729					}
 730				}
 731			}
 732		}
 733	}
 734	article_pop();
 735
 736	if ($debug > 1 && $events) {
 737		echo '++ ALL EVENTS ++';
 738		dmp($events);
 739	}
 740
 741	// Generate the calendar
 742	$calendar = new SMD_Calendar($size, $year, $month, $events, $section, $category, $debug);
 743	$calendar->setWeek($week);
 744	$calendar->setFirstDayOfWeek($firstday);
 745	$calendar->setGMT($gmt);
 746	$calendar->setLang($lang);
 747	$calendar->setClassLevels($clevs);
 748	$calendar->setClassPrefix($cls_pfx);
 749	$calendar->setEventWraptag($eventwraptag);
 750	$calendar->setCellForm($cellform);
 751	$calendar->setHdrForm($headerform);
 752	$calendar->setMYWraptag($mywraptag);
 753	$calendar->setSummary($summary);
 754	$calendar->setCaption($caption);
 755	$calendar->setTableID($id);
 756	$calendar->setTableClass($class);
 757	$calendar->setRowClass($rowclass);
 758	$calendar->setCellClass($cellclass);
 759	$calendar->setEmptyClass($emptyclass);
 760	$calendar->setISOWeekClass($isoweekclass);
 761	$calendar->setNavInfo($navpclass,$navnclass,$navparr,$navnarr,$navid);
 762	$calendar->setNavKeep($maintain);
 763	$calendar->setMYClass($myclass);
 764	$calendar->setNameFormat($dayformat, "d");
 765	$calendar->setNameFormat($monthformat, "m");
 766	$calendar->setRemap($dmap);
 767	$calendar->setShowISOWeek($isoweeks);
 768	$calendar->setEYear($earliest);
 769	$calendar->setLYear($latest);
 770	$calendar->setFilterOpts($fopts);
 771	$calendar->setDelim($event_delim);
 772	$calendar->setHolidays($holidays);
 773	$calendar->setSelectors(do_list($select), $selectbtn);
 774
 775	return $calendar->display($static);
 776}
 777
 778class SMD_Calendar extends SMD_Raw_Calendar {
 779	// Override Constructor
 780	// Permits multiple events to show per day
 781	var $section = '';
 782	var $category = '';
 783	var $size = '';
 784	var $debug = 0;
 785	var $events = array();
 786	function SMD_Calendar($size,$year,$month,$events,$section,$category, $debug=0) {
 787		$this->debug = $debug;
 788		$this->section = $section;
 789		$this->category = $category;
 790		$this->events = $events;
 791		$this->size = $size;
 792		$this->smd_Raw_Calendar($year,$month,$debug);
 793	}
 794
 795	// Override dspDayCell to display stuff right
 796	function dspDayCell($theday) {
 797		global $smd_cal_flag, $smd_calinfo, $smd_cal_ucls, $smd_date, $permlink_mode;
 798
 799		$smd_cal_flag = array();
 800		$smd_cal_ucls = array();
 801		$tdclass = array();
 802		$hasarticle = isset($this->events[$theday]);
 803		$now = time() + tz_offset();
 804
 805		$thedate = mktime(0, 0, 0, $this->month, $theday, $this->year);
 806		$hol_hit = in_array(date("d-M-Y", $thedate), $this->holidays);
 807		if ($hasarticle) {
 808			$smd_cal_flag[] = 'event';
 809		}
 810		if ($hol_hit) {
 811			$smd_cal_flag[] = 'hols';
 812		}
 813		$cflag = array();
 814		foreach ($smd_cal_flag as $item) {
 815			$cflag[] = $this->cls_pfx.$item;
 816		}
 817
 818		if ($this->cellclass) {
 819			$tdclass[] = $this->cellclass;
 820		}
 821		$tdclass = array_merge($tdclass, $cflag);
 822		$runningclass = (in_array("cell", $this->cls_lev) || in_array("cellplus", $this->cls_lev)) ? $tdclass : array();
 823
 824		if($this->year == date('Y',$now) and $this->month == date('n',$now) and $theday == date('j',$now) ) {
 825			$smd_cal_flag[] = 'today';
 826			$runningclass[] = $this->cls_pfx.'today';
 827		}
 828
 829		$out = array();
 830		$flags = array();
 831		$evid = array();
 832		$fout = array('standard'=>array(),'recur'=>array(),'recurfirst'=>array(),'multifirst'=>array(),'multi'=>array(),'multiprev'=>array(),'multilast'=>array(),'cancel'=>array(),'extra'=>array());
 833		if (empty($this->cellform) && $this->size == 'large') {
 834			$out[] = hed($theday,4);
 835		}
 836
 837		$evcnt = 0;
 838		if( isset($this->events[$theday]) ) {
 839			$days_events = $this->events[$theday];
 840			foreach($days_events as $ev) {
 841				$evclass = $ev['classes'];
 842				$evid[] = $ev['evid'];
 843				$flags = array_merge($flags, $ev['flag']);
 844				if (in_array("cellplus", $this->cls_lev)) {
 845					$runningclass = array_merge($runningclass, $evclass);
 846				}
 847				$cls = ($evclass && in_array("event", $this->cls_lev)) ? ' class="'.join(' ', $evclass).'"' : '';
 848				$op = ($this->evwraptag) ? tag($ev['ev'], $this->evwraptag, $cls) : $ev['ev'];
 849				foreach ($ev['flag'] as $flev) {
 850					$fout[$flev][] = $op;
 851				}
 852				$out[] = $op;
 853				$evcnt++;
 854				if ($this->size == 'small' && $evcnt == 1) {
 855					break;
 856				}
 857			}
 858		} elseif ($this->size == 'small') {
 859			$out[] = hed($theday,4);
 860		}
 861
 862		// Amalgamate the event-level classes and cell-level classes if required
 863		$runningclass = array_unique($runningclass);
 864		if (in_array("cellplus", $this->cls_lev)) {
 865			$smd_cal_flag = array_merge($smd_cal_flag, $flags);
 866		}
 867
 868		if ($this->cellform) {
 869			$thistime = mktime(0, 0, 0, $this->month, $theday, $this->year);
 870			$smd_calinfo['id'] = $this->tableID;
 871			$smd_date['y'] = $this->year;
 872			$smd_date['m'] = $this->month;
 873			$smd_date['w'] = strftime(smd_cal_reformat_win('%V', $thistime), $thistime);
 874			$smd_date['iy'] = strftime(smd_cal_reformat_win('%G', $thistime), $thistime);
 875			$smd_date['d'] = $theday;
 876			$reps = array(
 877				'{evid}' => join($this->event_delim, $evid),
 878				'{standard}' => join('',$fout['standard']),
 879				'{recur}' => join('',$fout['recur']),
 880				'{recurfirst}' => join('',$fout['recurfirst']),
 881				'{allrecur}' => join('',array_merge($fout['recur'], $fout['recurfirst'])),
 882				'{multifirst}' => join('',$fout['multifirst']),
 883				'{multiprev}' => join('',$fout['multiprev']),
 884				'{multi}' => join('',$fout['multilast']),
 885				'{multilast}' => join('',$fout['multilast']),
 886				'{allmulti}' => join('',array_merge($fout['multifirst'],$fout['multi'],$fout['multiprev'],$fout['multilast'])),
 887				'{cancel}' => join('',$fout['cancel']),
 888				'{extra}' => join('',$fout['extra']),
 889				'{events}' => join('',$out),
 890				'{numevents}' => $evcnt,
 891				'{day}' => $theday,
 892				'{dayzeros}' => str_pad($theday, 2, '0', STR_PAD_LEFT),
 893				'{weekday}' => ((is_array($this->dayNameFmt)) ? $this->dayNames[date('w',$thistime)] : strftime($this->dayNameFmt, $thistime)),
 894				'{weekdayabbr}' => strftime('%a', $thistime),
 895				'{weekdayfull}' => strftime('%A', $thistime),
 896				'{week}' => $smd_date['w'],
 897				'{month}' => $this->month,
 898				'{monthzeros}' => str_pad($this->month, 2, '0', STR_PAD_LEFT),
 899				'{monthname}' => ((is_array($this->mthNameFmt)) ? $this->mthNames[date('n',$thistime)] : strftime($this->mthNameFmt, $thistime)),
 900				'{monthnameabbr}' => strftime('%b', $thistime),
 901				'{monthnamefull}' => strftime('%B', $thistime),
 902				'{year}' => $this->year,
 903				'{shortyear}' => strftime('%y', $thistime),
 904				'{isoyear}' => $smd_date['iy'],
 905				'{shortisoyear}' => strftime(smd_cal_reformat_win('%g', $thistime), $thistime),
 906			);
 907			$cellout = parse(strtr($this->cellform, $reps));
 908			$carray = array_merge($runningclass, $smd_cal_ucls);
 909			$smd_cal_ucls = array();
 910
 911			return doTag($cellout,'td',join(' ',$carray));
 912		} else {
 913			return doTag(join('',$out),'td',join(' ',$runningclass));
 914		}
 915	}
 916
 917	function display($static=false) {
 918		$sum = ($this->tblSummary) ? ' summary="'.$this->tblSummary.'"' : '';
 919		$id = ($this->tableID) ? ' id="'.$this->tableID.'"' : '';
 920		$c[] = ($this->tblCaption) ? '<caption>'.$this->tblCaption.'</caption>' : '';
 921		$c[] = '<thead>';
 922		$c[] = $this->dspHeader($static);
 923		$c[] = $this->dspDayNames();
 924		$c[] = '</thead>';
 925		$c[] = $this->dspDayCells();
 926
 927		return doTag(join('',$c),'table',$this->tableclass,$sum.$id);
 928	}
 929
 930	function dspHeader($static) {
 931		global $pretext, $smd_calinfo, $permlink_mode;
 932
 933		$currmo = $this->month;
 934		$curryr = $this->year;
 935		$navpclass = $this->getNavInfo("pc");
 936		$navnclass = $this->getNavInfo("nc");
 937		$navparrow = $this->getNavInfo("pa");
 938		$navnarrow = $this->getNavInfo("na");
 939		$navid = $this->getNavInfo("id");
 940		$navpclass = ($navpclass) ? ' class="'.$navpclass.'"' : '';
 941		$navnclass = ($navnclass) ? ' class="'.$navnclass.'"' : '';
 942		$fopts = $this->fopts;
 943
 944		$sec = (isset($smd_calinfo['s']) && !empty($smd_calinfo['s'])) ? $smd_calinfo['s'] : '';
 945		foreach ($this->maintain as $col) {
 946			switch ($col) {
 947				case "section":
 948					if ($pretext['s'] && $permlink_mode != 'year_month_day_title') {
 949						$fopts = array('s' => $pretext['s']) + $fopts;
 950					}
 951					break;
 952				case "article":
 953					if ($pretext['id']) {
 954						$fopts = array('id' => $pretext['id']) + $fopts;
 955					}
 956					break;
 957				case "category":
 958					if ($pretext['c']) {
 959						$fopts = array('c' => $pretext['c']) + $fopts;
 960					}
 961					break;
 962				case "author":
 963					if (gps('author')) {
 964						$fopts = array('author' => gps('author')) + $fopts;
 965					}
 966					break;
 967				case "date":
 968					if (gps('date')) {
 969						$fopts = array('date' => gps('date')) + $fopts;
 970					}
 971					break;
 972				case "pg":
 973					if ($pretext['pg']) {
 974						$fopts = array('pg' => $pretext['pg']) + $fopts;
 975					}
 976					break;
 977				case "calid":
 978					if ($this->tableID) {
 979						$fopts = array('calid' => $this->tableID) + $fopts;
 980					}
 981					break;
 982				default:
 983					if (gps($col)) {
 984						$fopts = array($col => gps($col)) + $fopts;
 985					}
 986					break;
 987			}
 988		}
 989
 990		$fopts = array_unique($fopts);
 991		$filters = array();
 992		$filterHid = array();
 993		if (!$static) {
 994			foreach($fopts as $key => $val) {
 995				$filters[] = $key.'='.$val;
 996				$filterHid[] = hInput($key, $val);
 997			}
 998		}
 999
1000		// Week select list
1001		if ($this->useSelector('week') && !$static) {
1002			$currwk = ($this->week) ? $this->week : date('W', safe_strtotime($curryr."-".$currmo."-1 12:00"));
1003			for ( $idx = 1; $idx <= 53; $idx++ ) {
1004				$tagatts = ' value="'.$idx.'"';
1005				if ( $idx == $currwk ) $tagatts .= ' selected="selected"';
1006				$optiontags[] = doTag($this->selpfx['week'].str_pad($idx, 2, '0', STR_PAD_LEFT).$this->selsfx['week'], 'option', '', $tagatts);
1007			}
1008			$selector[] = doTag(join(n, $optiontags), 'select', (($this->mywraptag) ? '' : $this->myclass), ' name="'.$this->remap['w'].'"'.(($this->selbtn) ? '' : ' onchange="this.form.submit()"'), '')
1009   				. (($this->useSelector('year')) ? '' : hInput($this->remap['y'], $curryr));
1010			$optiontags = array(); // Blank out
1011		}
1012
1013		// Month select list - note mktime has the day forced to 1. If not you get
1014		// bizarre repeated month names on the 31st of some months :-\
1015		if (!$this->useSelector('week')) {
1016			if ($this->useSelector('month') && !$static) {
1017				for ( $idx = 1; $idx <= 12; $idx++ ) {
1018					$tagatts = ' value="'.$idx.'"';
1019					if ( $idx == $currmo ) $tagatts .= ' selected="selected"';
1020					$optiontags[] = doTag($this->selpfx['month'].((is_array($this->mthNameFmt)) ? $this->mthNames[date('n',mktime(12,0,0,$idx,1))] : safe_strftime($this->mthNameFmt, mktime(12,0,0,$idx,1) )).$this->selsfx['month'], 'option', '', $tagatts);
1021				}
1022				$selector[] = doTag(join(n, $optiontags), 'select', (($this->mywraptag) ? '' : $this->myclass), ' name="'.$this->remap['m'].'"'.(($this->selbtn) ? '' : ' onchange="this.form.submit()"'), '')
1023	   				. (($this->useSelector('year')) ? '' : hInput($this->remap['y'], $curryr));
1024				$optiontags = array(); // Blank out
1025			} else {
1026				$selector[] = doTag($this->getMonthName(), 'span', (($this->mywraptag) ? '' : $this->myclass));
1027			}
1028		}
1029
1030		// Year select list
1031		$y0 = $this->eyr;
1032		$y1 = $this->lyr;
1033		if ($this->useSelector('year') && ($y0 != $y1) && !$static) {
1034			for ( $idx = $y0; $idx <= $y1; $idx++ ) {
1035				$tagatts = ' value="'.$idx.'"';
1036				if ( $idx == $curryr ) $tagatts .= ' selected="selected"';
1037				$optiontags[] = doTag($this->selpfx['year'].$idx.$this->selsfx['year'], 'option', '', $tagatts);
1038			}
1039			$selector[] = doTag(join(n, $optiontags), 'select', (($this->mywraptag) ? '' : $this->myclass), ' name="'.$this->remap['y'].'"'.(($this->selbtn) ? '' : ' onchange="this.form.submit()"'), '')
1040					. (($this->useSelector('month') || $this->useSelector('week')) ? '' : hInput($this->remap['m'], $currmo));
1041		} else {
1042			$selector[] = doTag($curryr, 'span', (($this->mywraptag) ? '' : $this->myclass));
1043		}
1044
1045		$request = serverSet('REQUEST_URI');
1046		$redirect = serverSet('REDIRECT_URL');
1047		if (!empty($redirect) && ($request != $redirect) && is_callable('_l10n_set_browse_language')) {
1048			// MLP in da house: use the redirect URL instead
1049			$request = $redirect;
1050		}
1051		$urlp = parse_url($request);
1052		$action = $urlp['path'];
1053
1054		if ($permlink_mode == 'messy') {
1055			$out = makeOut('id','s','c','q','pg','p','month');
1056			foreach($out as $key => $val) {
1057				if ($val) {
1058					$filters[] = $key.'='.$val;
1059					$filterHid[] = hInput($key, $val);
1060				}
1061			}
1062		}
1063		$filterHid = array_unique($filterHid);
1064		$filters = array_unique($filters);
1065
1066		$extras = '';
1067		if (!$static && ( $this->useSelector('month') || $this->useSelector('year') )) {
1068			if ($this->selbtn) {
1069				$extras .= doTag('', 'input', 'smd_cal_input', ' type="submit" value="'.$this->selbtn.'"');
1070			}
1071			$extras .= join(n, $filterHid);
1072		}
1073
1074		$selector = '<form action="'.$action.'" method="get"'.(($navid) ? ' id="'.$navid.'"' : '').'>'.doTag(join(sp, $selector).$extras, $this->mywraptag, $this->myclass).'</form>';
1075
1076		$nav_back_link = $this->navigation($curryr, $currmo, '-', $filters, $urlp['path']);
1077		$nav_fwd_link  = $this->navigation($curryr, $currmo, '+', $filters, $urlp['path']);
1078
1079		$nav_back = (!$static && $nav_back_link) ? '<a href="'.$nav_back_link.'"'.$navpclass.'>'.$navparrow.'</a>' : '&nbsp;';
1080		$nav_fwd  = (!$static && $nav_fwd_link) ? '<a href="'.$nav_fwd_link.'"'.$navnclass.'>'.$navnarrow.'</a>' : '&nbsp;';
1081
1082		$c[] = doTag($nav_back,'th');
1083		$c[] = '<th colspan="'.(($this->showISOWeek) ? 6 : 5).'">'.$selector.'</th>';
1084		$c[] = doTag($nav_fwd,'th');
1085
1086		return doTag(join('',$c),'tr', 'smd_cal_navrow');
1087	}
1088
1089	function navigation($year,$month,$direction,$flt,$url='') {
1090		global $permlink_mode;
1091
1092		if($direction == '-') {
1093			if($month - 1 < 1) {
1094				$month = 12;
1095				$year -= 1;
1096			} else {
1097				$month -= 1;
1098			}
1099		} else {
1100			if($month + 1 > 12) {
1101				$month = 1;
1102				$year += 1;
1103			} else {
1104				$month += 1;
1105			}
1106		}
1107
1108		// Abort if we're about to go out of range
1109		if ($year < $this->eyr || $year > $this->lyr) {
1110			return '';
1111		}
1112
1113		$flt[] = $this->remap['m']."=$month";
1114		$flt[] = $this->remap['y']."=$year";
1115
1116		return $url . "?" . join(a, $flt);
1117	}
1118}
1119
1120/**
1121* Basic Calendar data and display
1122* http://www.oscarm.org/static/pg/calendarClass/
1123* @author Oscar Merida
1124* @created Jan 18 2004
1125*/
1126class SMD_Raw_Calendar {
1127var $gmt = 1, $lang, $debug = 0;
1128var $year, $eyr, $lyr, $month, $week;
1129var $dayNameFmt, $mthNameFmt, $dayNames, $mthNames, $startDay, $endDay, $firstDayOfWeek = 0, $startOffset = 0;
1130var $selectors, $selbtn, $selpfx, $selsfx;
1131var $showISOWeek, $ISOWeekHead, $ISOWeekCell;
1132var $cls_lev, $cls_pfx, $fopts;
1133var $evwraptag, $mywraptag;
1134var $rowclass, $cellclass, $emptyclass, $isoclass, $myclass, $tableID, $tblSummary, $tblCaption;
1135var $navpclass, $navnclass, $navparrow, $navnarrow, $navid;
1136var $holidays, $cellform, $hdrform, $maintain, $remap;
1137var $event_delim;
1138/**
1139* Constructor
1140*
1141* @param integer, year
1142* @param integer, month
1143* @return object
1144* @public
1145*/
1146function SMD_Raw_Calendar ($yr, $mo, $debug=0) {
1147	$this->setDebug($debug);
1148	$this->setYear($yr);
1149	$this->setMonth($mo);
1150	$this->setClassPrefix('smd_cal_');
1151
1152	$this->startTime = strtotime( "$yr-$mo-01 00:00" );
1153	$this->startDay = date( 'D', $this->startTime );
1154	$this->endDay = date( 't', $this->startTime );
1155	$this->endTime = strtotime( "$yr-$mo-".$this->endDay." 23:59:59" );
1156	if ($this->debug) {
1157		echo "++ THIS MONTH'S RENDERED CALENDAR [ start stamp // end date // start day // end stamp // end date // end day number ] ++";
1158		dmp($this->startTime, date('Y-m-d H:i:s', $this->startTime), $this->startDay, $this->endTime, date('Y-m-d H:i:s', $this->endTime), $this->endDay);
1159	}
1160	$this->setNameFormat('%a', 'd');
1161	$this->setNameFormat('%B', 'm');
1162	$this->setFirstDayOfWeek(0);
1163	$this->setShowISOWeek('');
1164	$this->setTableID('');
1165	$this->setTableClass('');
1166}
1167// === end Calendar ===
1168// Getters
1169function useSelector($val) { return in_array($val, $this->selectors); }
1170function getDayName($day) { return ($this->dayNames[$day%7]); }
1171function getMonthName() {
1172	if (is_array($this->mthNameFmt)) {
1173		return $this->mthNames[date('n',$this->startTime)];
1174	} else {
1175		return strftime($this->mthNameFmt, $this->startTime);
1176	}
1177}
1178function getNavInfo($type) {
1179	$r = '';
1180	switch ($type) {
1181		case "id": $r = $this->navid; break;
1182		case "pc": $r = $this->navpclass; break;
1183		case "nc": $r = $this->navnclass; break;
1184		case "pa": $r = $this->navparrow; break;
1185		case "na": $r = $this->navnarrow; break;
1186	}
1187	return $r;
1188}
1189// Setters
1190function setDebug($d){ $this->debug = $d; }
1191function setGMT($b){ $this->gmt = $b; }
1192function setLang($code){ $this->lang = $code; }
1193function setSummary($txt){ $this->tblSummary = $txt; }
1194function setCaption($txt){ $this->tblCaption = $txt; }
1195function setCellForm($frm){ $this->cellform = $frm; }
1196function setHdrForm($frm){ $this->hdrform = $frm; }
1197function setTableID($id){ $this->tableID = $id; }
1198function setYear($yr){ $this->year = $yr; }
1199function setEYear($yr){ $this->eyr = $yr; }
1200function setLYear($yr){ $this->lyr = $yr; }
1201function setMonth($mth){ $this->month = (int)$mth; }
1202function setWeek($wk){
1203	if ($wk) {
1204		$wk = str_pad($wk, 2, '0', STR_PAD_LEFT);
1205		$this->week = $wk;
1206		$this->month = safe_strftime("%m", strtotime($this->year."W".$wk));
1207	}
1208}
1209function setNavKeep($ar){ $this->maintain = $ar; }
1210function setShowISOWeek($val) {
1211	$this->showISOWeek = ($val) ? true : false;
1212	if ($val) {
1213		$val = do_list($val);
1214		$this->ISOWeekHead = $val[0];
1215		$this->ISOWeekCell = (isset($val[1])) ? $val[1] : '{week}';
1216	}
1217}
1218function setRemap($map){ $this->remap = $map; }
1219function setClassLevels($cls){ $this->cls_lev = $cls; }
1220function setClassPrefix($cls){ $this->cls_pfx = $cls; }
1221function setEventWraptag($wrap){ $this->evwraptag = $wrap; }
1222function setMYWraptag($wrap){ $this->mywraptag = $wrap; }
1223function setTableClass($cls) { $this->tableclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1224function setRowClass($cls){ $this->rowclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1225function setCellClass($cls){ $this->cellclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1226function setEmptyClass($cls){ $this->emptyclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1227function setISOWeekClass($cls){ $this->isoclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1228function setDelim($dlm){ $this->event_delim = $dlm; }
1229function setNavInfo($clsp, $clsn, $arrp, $arrn, $nid){
1230	$this->navpclass = ($clsp) ? $this->cls_pfx.$clsp : '';
1231	$this->navnclass = ($clsn) ? $this->cls_pfx.$clsn : '';
1232	$this->navparrow = ($arrp) ? $arrp : '';
1233	$this->navnarrow = ($arrn) ? $arrn : '';
1234	$this->navid = ($nid) ? $this->cls_pfx.$nid : '';
1235}
1236function setMYClass($cls){ $this->myclass = ($cls) ? $this->cls_pfx.$cls : ''; }
1237function setFilterOpts($f) { $this->fopts = $f; }
1238function setHolidays($hols) { $this->holidays = $hols; }
1239function setSelectors($sel, $btn) {
1240	foreach ($sel as $idx => $item) {
1241		$selparts = explode(":", $item);
1242		$sel[$idx] = $selparts[0];
1243		$this->selpfx[$selparts[0]] = (isset($selparts[1])) ? $selparts[1] : '';
1244		$this->selsfx[$selparts[0]] = (isset($selparts[2])) ? $selparts[2] : '';
1245	}
1246	$this->selectors = $sel;
1247	$this->selbtn = $btn;
1248}
1249function setFirstDayOfWeek($d) {
1250	$this->firstDayOfWeek = ((int)$d <= 6 and (int)$d >= 0) ? (int)$d : 0;
1251	$this->startOffset = date('w', $this->startTime) - $this->firstDayOfWeek;
1252	if ( $this->startOffset < 0 ) {
1253		$this->startOffset = 7 - abs($this->startOffset);
1254	}
1255}
1256/**
1257* frm: any valid PHP strftime() string or ABBR/FULL
1258* typ: d to set day, m to set month format
1259*/
1260function setNameFormat($frm, $typ="d") {
1261	switch ($frm) {
1262		case "full":
1263		case "FULL":
1264			$fmt = ($typ == 'd') ? "%A" : "%B";
1265			break;
1266		case "abbr":
1267		case "ABBR":
1268			$fmt = ($typ == 'd') ? "%a" : "%b";
1269			break;
1270		default:
1271			if (strpos($frm, '%') === 0) {
1272				$fmt = $frm;
1273			} else {
1274				$frm = trim($frm, '{}');
1275				$frm = do_list($frm);
1276				$fmt = $frm;
1277			}
1278			break;
1279	}
1280
1281	if ($typ == "d") {
1282		$this->dayNameFmt = $fmt;
1283		$this->dayNames = array();
1284
1285		// This is done to make sure Sunday is always the first day of our array
1286		$start = 0;
1287		$end = $start + 7;
1288		$sunday = strtotime('1970-Jan-04 12:00:00');
1289
1290		for($i=$start; $i<$end; $i++) {
1291			if (is_array($fmt)) {
1292				$this->dayNames[] = $fmt[$i-$start];
1293			} else {
1294				$this->dayNames[] = ucfirst(strftime($fmt, ($sunday + (86400*$i))));
1295			}
1296		}
1297	} else {
1298		$this->mthNameFmt = $fmt;
1299		$this->mthNames = array();
1300		for ($i=0; $i<12; $i++) {
1301			if (is_array($fmt)) {
1302				$this->mthNames[$i+1] = $fmt[$i];
1303			}
1304		}
1305	}
1306}
1307/**
1308* Return markup for displaying the calendar.
1309* @return
1310* @public
1311*/
1312function display ( ) {
1313	$id = ($this->tableID) ? ' id="'.$this->tableID.'"' : '';
1314	$c[] = '<table'.$id.'>';
1315	$c[] = '<thead>' . $this->dspDayNames() . '</thead>';
1316	$c[] = $this->dspDayCells();
1317	$c[] = '</table>';
1318
1319	return join('',$c);
1320}
1321/

Large files files are truncated, but you can click here to view the full file