Go to main content

Textpattern CMS support forum

You are not logged in. Register | Login | Help

#685 2020-02-21 13:39:28

uli
Moderator
From: Cologne
Registered: 2006-08-15
Posts: 4,303

Re: gbp_permanent_links

Gil, this plugin has no help text at all, so maybe you missed it does have some tags.


In bad weather I never leave home without wet_plugout, smd_where_used and adi_form_links

Offline

#686 2020-02-21 18:00:49

THE BLUE DRAGON
Member
From: Israel
Registered: 2007-11-16
Posts: 619
Website

Re: gbp_permanent_links

uli wrote #321836:

Gil, this plugin has no help text at all, so maybe you missed it does have some tags.

Thanks, I didn’t know about them, anyway since I personally do not use them then for me now everything seems to work fine, just still not having the option to delete a rule without clearing all of them in the database using the SQL query Bloke provided.

Here’s my “working” code as it may help others too, it returns no errors (even on debugging mode) and does let you create and edit rules:

<?php
// Constants
@define('gbp_save', 'save');
@define('gbp_post', 'post');
@define('gbp_separator', '&~&~&');

// require_plugin() will reset the $txp_current_plugin global
global $txp_current_plugin;
$gbp_current_plugin = $txp_current_plugin;
require_plugin('gbp_admin_library');
$txp_current_plugin = $gbp_current_plugin;

class PermanentLinks extends GBPPlugin
{
	var $preferences = array(
		'show_prefix' => array('value' => 0, 'type' => 'yesnoradio'),
		'show_suffix' => array('value' => 0, 'type' => 'yesnoradio'),
		'omit_trailing_slash' => array('value' => 0 , 'type' => 'yesnoradio'),
		'redirect_mt_style_links' => array('value' => 1 , 'type' => 'yesnoradio'),
		'clean_page_archive_links' => array('value' => 1 , 'type' => 'yesnoradio'),
		'join_pretext_to_pagelinks' => array('value' => 1 , 'type' => 'yesnoradio'),
		'check_pretext_category_context' => array('value' => 0 , 'type' => 'yesnoradio'),
		'check_pretext_section_context' => array('value' => 0 , 'type' => 'yesnoradio'),
		'force_lowercase_urls' => array('value' => 1 , 'type' => 'yesnoradio'),
		'automatically_append_title' => array('value' => 1 , 'type' => 'yesnoradio'),
		'use_cleaver_partial_matches' => array('value' => 1 , 'type' => 'yesnoradio'),
		'permlink_redirect_http_status' => array('value' => '301' , 'type' => 'text_input'),
		'url_redirect_http_status' => array('value' => '302' , 'type' => 'text_input'),
		'text_and_regex_segment_scores' => array('value' => '0' , 'type' => 'text_input'),
		'debug' => array('value' => 0, 'type' => 'yesnoradio'),
	);
	var $matched_permlink = array();
	var $partial_matches = array();
	var $cleaver_partial_match;
	var $buffer_debug = array();

	function preload () {
		new PermanentLinksListTabView('list', 'list', $this);
		new PermanentLinksBuildTabView('build', 'build', $this);
		new GBPPreferenceTabView($this);
	}

	function main () {
		require_privs($this->event);
	}

	function get_all_permlinks ($sort = 0, $exclude = array()) {
		static $rs;
		if (!isset($rs))
			$rs = safe_column(
				"REPLACE(name, '{$this->plugin_name}_', '') AS id", 'txp_prefs',
				"`event` = '{$this->event}' AND `name` REGEXP '^{$this->plugin_name}_.{13}$'"
			);

		if (@txpinterface == 'public')
			$this->debug('Loading permlinks from db');
		$i = 0;

		$permlinks = array();
		foreach ($rs as $id) {
			$pl = $this->get_permlink($id);

			// Don't try and load permalink rules from the new version
			if (!isset($pl['components']))
				continue;

			if (count($exclude) > 0)
				foreach ($pl['components'] as $pl_c) {
					if (is_array($exclude) && in_array($pl_c['type'], $exclude))
						continue 2;
					if (is_string($exclude) && $pl_c['type'] === $exclude)
						continue 2;
				}

			$permlinks[$id] = $pl;

			if ($sort)
				$precedence[$id] = $permlinks[$id]['settings']['pl_precedence'];
		}

		// If more than one permanent link, sort by their precedence value.
		if ($sort && count($permlinks) > 1)
			array_multisort($precedence, SORT_DESC, $permlinks);

		if (@txpinterface == 'public') {
			foreach ($permlinks as $pl)
				$this->debug($pl['settings']['pl_precedence'].': '.$pl['settings']['pl_name'].' ('.$pl['settings']['pl_preview'].')');
		}

		return $permlinks;
	}

	function get_permlink ($id) {
		$permlink = $this->pref($id);
		return is_array($permlink) ? $permlink : array();
	}

	function remove_permlink ($id) {
		$permlink = $this->get_permlink($id);
		safe_delete('txp_prefs', "`event` = '{$this->event}' AND `name` LIKE '{$this->plugin_name}_{$id}%'");
		return $permlink['settings']['pl_name'];
	}

	function _feed_entry () {
		static $set;
		if (!isset($set)) {
			// We're in a feed force debugging off.
			$this->preferences['debug']['value'] = $GLOBALS['prefs'][$this->plugin_name.'_debug'] = 0;

			$this->set_permlink_mode(true);
			$set = true;
		}
	}

	function _textpattern () {
		global $plugins_ver, $pretext, $prefs, $plugin_callback;

		$this->debug('Plugin: '.$this->plugin_name.' - '.$plugins_ver[$this->plugin_name]);
		$this->debug('Function: '.__FUNCTION__.'()');

		// URI
		$req = $pretext['req'];
		$req = preg_replace('%\?[^\/]+$%', '', $req);
		$this->debug('Request URI: '.$req);
		$uri = explode('/', trim($req, '/'));

		// The number of components comes in useful when determining the best partial match.
		$uri_component_count = count($uri);

		// Permanent links
		$permlinks = $this->get_all_permlinks(1);

		// Force Textpattern and tags to use messy URLs - these are easier to
		// find in regex
		$this->set_permlink_mode();

		if (count($permlinks)) {

			// We also want to match the front page of the site (for page numbers / feeds etc..).
			// Add a permlinks rule which will do that.
			$permlinks['default'] = array(
				'components' => array(),
				'settings' => array(
					'pl_name' => 'gbp_permanent_links_default', 'pl_precedence' => '', 'pl_preview' => '/',
					'con_section' => '', 'con_category' => '', 'des_section' => '', 'des_category' => '',
					'des_permlink' => '', 'des_feed' => '', 'des_location' => '', 'des_page' => ''
			));

			// Extend the pretext_replacement scope outside the foreach permlink loop
			$pretext_replacement = NULL;

			foreach($permlinks as $id => $pl) {
				// Extract the permlink settings
				$pl_settings = $pl['settings'];
				extract($pl_settings);

				$this->debug('Permlink name: '.$pl_name);
				$this->debug('Permlink id: '.$id);
				$this->debug('Preview: '.$pl_preview);

				$pl_components = $pl['components'];

				// URI components
				$uri_components = $uri;

				$this->debug('PL component count: '.count($pl_components));
				$this->debug('URL component count: '.count($uri_components));

				$date = false; $title_page_feed = false;
				foreach($pl_components as $pl_c)
					// Are we expecting a date component? If so the number of pl and uri components won't match
					if ($pl_c['type'] == 'date')
						$date = true;
					// Do we have either a title, page or a feed component?
					else if (in_array($pl_c['type'], array('title', 'page', 'feed')))
						$title_page_feed = true;

				if (!$title_page_feed)
					// If there isn't a title, page or feed component then append a special type for cleaver partial matching
					$pl_components[] = array('type' => 'title_page_feed', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => '');

				// Exit early if there are more URL components than PL components,
				// taking into account whether there is a data component
				if (!$uri_components[0] || count($uri_components) > count($pl_components) + ($date ? 2 : 0)) {
					$this->debug('More URL components than PL components');
					continue;
				}

				// Reset pretext_replacement as we are about to start another comparison
				$pretext_replacement = array('permlink_id' => $id);

				// Reset the article context string
				$context = array();
				unset($context_str);
				if (!empty($des_section))
					$context[] = "`Section` = '$des_section'";
				if (!empty($des_category))
					$context[] = "(`Category1` = '$des_category' OR `Category2` = '$des_category')";
				$context_str = (count($context) > 0 ? 'and '.join(' and ', $context) : '');

				// Assume there is no match
				$partial_match = false;
				$cleaver_partial_match = false;

				// Loop through the permlink components
				foreach ($pl_components as $pl_c_index => $pl_c) {
					// Assume there is no match
					$match = false;

					// Check to see if there are still URI components to be checked.
					if (count($uri_components))
						// Get the next component.
						$uri_c = array_shift($uri_components);

					else if (!$title_page_feed && count($pl_components) - 1 == $uri_component_count) {
						// If we appended a title_page_feed component earlier and permlink and URI components
						// counts are equal, we must of finished checking this permlink, and it matches so break.
						$match = true;
						break;

					} else {
						// If there are no more URI components then we have a partial match.
						// Store the partial match data unless there has been a preceding permlink with the
						// same number of components, as permlink have already been sorted by precedence.
						if (!array_key_exists($uri_component_count, $this->partial_matches))
							$this->partial_matches[$uri_component_count] = $pretext_replacement;

						// Unset pretext_replacement as changes could of been made in a preceding component
						unset($pretext_replacement);

						// Break early form the foreach permlink components loop.
						$partial_match = true;
						break;
					}

					// Extract the permlink components.
					extract($pl_c);

					// If it's a date, grab and combine the next two URI components.
					if ($type == 'date')
						$uri_c .= '/'.array_shift($uri_components).'/'.array_shift($uri_components);

					// Decode the URL
					$uri_c = urldecode($uri_c);

					// Always check the type unless the prefix or suffix aren't there
					$check_type = true;

					// Check prefix
					if ($prefix && $this->pref('show_prefix')) {
						$sanitized_prefix = urldecode($this->encode_url($prefix));
						if (($pos = strpos($uri_c, $sanitized_prefix)) === false || $pos != 0) {
							$check_type = false;
							$this->debug('Can\'t find prefix: '.$prefix);
						} else
							// Check passed, remove prefix ready for the next check
							$uri_c = substr_replace($uri_c, '', 0, strlen($sanitized_prefix));
					}

					// Check suffix
					if ($check_type && $suffix && $this->pref('show_suffix')) {
						$sanitized_suffix = urldecode($this->encode_url($suffix));
						if (($pos = strrpos($uri_c, $sanitized_suffix)) === false) {
							$check_type = false;
							$this->debug('Can\'t find suffix: '.$suffix);
						} else
							// Check passed, remove suffix ready for the next check
							$uri_c = substr_replace($uri_c, '', $pos, strlen($sanitized_suffix));
					}

					// Both the prefix and suffix settings have passed
					if ($check_type) {
						$this->debug('Checking if "'.$uri_c.'" is of type "'.$type.'"');
						$uri_c = doSlash($uri_c);

						//
						if (isset($prefs['permalink_title_format']) && $prefs['permalink_title_format']) {
							$mt_search = array('/_/', '/\.html$/');
						} else {
							$mt_search = array('/(?:^|_)(.)/', '/\.html$/');
						}
						$mt_uri_c = $this->pref('redirect_mt_style_links')
							? preg_replace_callback(
								$mt_search,
								function($m){
									if (isset($prefs['permalink_title_format']) && $prefs['permalink_title_format']) {
										$mt_replace = '-';
									} else {
										$mt_replace = 'strtoupper($m[1])';
									}
									return $mt_replace;
								},
								$uri_c)
							: '';

						// Compare based on type
						switch ($type) {
							case 'section':
								if ($rs = safe_row('name', 'txp_section', "(`name` like '$uri_c' or `name` like '$mt_uri_c') limit 1")) {
									$this->debug('Section name: '.$rs['name']);
									$pretext_replacement['s'] = $rs['name'];
									$context[] = "`Section` = '{$rs['name']}'";
									$match = true;
								}
							break;
							case 'category':
								if ($rs = safe_row('name', 'txp_category', "(`name` like '$uri_c' or `name` like '$mt_uri_c') and `type` = 'article' limit 1")) {
									$this->debug('Category name: '.$rs['name']);
									$pretext_replacement['c'] = $rs['name'];
									$context[] = "(`Category1` = '{$rs['name']}' OR `Category2` = '$uri_c')";
									$match = true;
								}
							break;
							case 'title':
								if ($rs = safe_row('url_title', 'textpattern', "(`url_title` like '$uri_c' or `url_title` like '$mt_uri_c') $context_str and `Status` >= 4 limit 1")) {
									$this->debug('URL Title: '.$rs['url_title']);
									$mt_redirect = ($uri_c != $mt_uri_c);
									$pretext_replacement['url_title'] = $rs['url_title'];
									$match = true;
								}
							break;
							case 'id':
								if ($rs = safe_row('ID, Posted', 'textpattern', "`ID` = '$uri_c' $context_str and `Status` >= 4 limit 1")) {
									$pretext_replacement['id'] = $rs['ID'];
									$pretext_replacement['Posted'] = $rs['Posted'];
									$pretext['numPages'] = 1;
									$pretext['is_article_list'] = false;
									$match = true;
								}
							break;
							case 'author':
								if ($author = safe_field('name', 'txp_users', "RealName like '$uri_c' limit 1")) {
									$pretext_replacement['author'] = $author;
									$context[] = "`AuthorID` = '$author'";
									$match = true;
								}
							break;
							case 'login':
								if ($author = safe_field('name', 'txp_users', "name like '$uri_c' limit 1")) {
									$pretext_replacement['author'] = $author;
									$context[] = "`AuthorID` = '$author'";
									$match = true;
								}
							break;
							case 'custom':
								$custom_options = array_values(array_map(array($this, "encode_url"), safe_column("custom_$custom", 'textpattern', "custom_$custom != ''")));
								if ($this->pref('force_lowercase_urls'))
									$custom_options = array_map("strtolower", $custom_options);
								if (in_array($uri_c, $custom_options)) {
									$match = true;
								}
							break;
							case 'date':
								if (preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $uri_c)) {
									$pretext_replacement['date'] = str_replace('/', '-', $uri_c);
									$match = true;
								}
							break;
							case 'year':
								if (preg_match('/^\d{4}$/', $uri_c)) {
									$pretext_replacement['year'] = $uri_c;
									$match = true;
								}
							break;
							case 'month':
							case 'day':
								if (preg_match('/^\d{2}$/', $uri_c)) {
									$pretext_replacement[$type] = $uri_c;
									$match = true;
								}
							break;
							case 'page':
								if (is_numeric($uri_c)) {
									$pretext_replacement['pg'] = $uri_c;
									$match = true;
								}
							break;
							case 'feed':
								if (in_array($uri_c, array('rss', 'atom'))) {
									$pretext_replacement[$uri_c] = 1;
									$match = true;
								}
							break;
							case 'search':
									$pretext_replacement['q'] = $uri_c;
									$match = true;
							break;
							case 'text':
								if ($this->encode_url($text) == $uri_c) {
									$match = true;
									$pretext_replacement["permlink_text_{$name}"] = $uri_c;
								}
							break;
							case 'regex':
								// Check to see if regex is valid without outputting error messages.
								ob_start();
								preg_match($regex, $uri_c, $regex_matches);
								$is_valid_regex = !(ob_get_clean());
								if ($is_valid_regex && @$regex_matches[0]) {
									$match = true;
									$pretext_replacement["permlink_regex_{$name}"] = $regex_matches[0];
								}
							break;
						} // switch type end

						// Update the article context string
						$context_str = (count($context) > 0 ? 'and '.join(' and ', $context) : '');

						$this->debug(($match == true) ? 'YES' : 'NO');

						if (!$match && !$cleaver_partial_match && $this->pref('use_cleaver_partial_matches')) {
							// There hasn't been a match or a complete cleaver partial match. Lets try to be cleaver and
							// check to see if this component is either a title, page or a feed. This makes it more probable
							// a successful match for a given permlink rule occurs.
							$this->debug('Checking if "'.$uri_c.'" is of type "title_page_feed"');

							if ($type != 'title' && $url_title = safe_field('url_title', 'textpattern', "`url_title` like '$uri_c' $context_str and `Status` >= 4 limit 1")) {
								$pretext_replacement['url_title'] = $url_title;
								$pretext['numPages'] = 1;
								$pretext['is_article_list'] = false;
								$cleaver_partial_match = true;
							} else if ($this->pref('clean_page_archive_links') && $type != 'page' && is_numeric($uri_c)) {
								$pretext_replacement['pg'] = $uri_c;
								$cleaver_partial_match = true;
							} else if ($type != 'feed' && in_array($uri_c, array('rss', 'atom'))) {
								$pretext_replacement[$uri_c] = 1;
								$cleaver_partial_match = true;
							}

							$this->debug(($cleaver_partial_match == true) ? 'YES' : 'NO');

							if ($cleaver_partial_match) {
								$this->cleaver_partial_match = $pretext_replacement;

								// Unset pretext_replacement as changes could of been made in a preceding component
								unset($pretext_replacement);

								$cleaver_partial_match = true;
								continue 2;
							}
						}
					} // check type end

					// Break early if the component doesn't match, as there is no point continuing
					if ($match == false) {
						// Unset pretext_replacement as changes could of been made in a preceding component
						unset($pretext_replacement);
						break;
					}
				} // foreach permlink component end

				if (!isset($pretext_replacement['id'])) {
					if (isset($pretext_replacement['url_title'])) {
						if (isset($pretext_replacement['date'])) {
							$date_val = $pretext_replacement['date'];
						} else if (isset($pretext_replacement['year'])) {
							$date_val = $pretext_replacement['year'];
							if (isset($pretext_replacement['month'])) {
								$date_val .= '-' . $pretext_replacement['month'];
								if (isset($pretext_replacement['day'])) {
									$date_val .= '-' . $pretext_replacement['day'];
								}
							}
						}
						if (isset($date_val))
							$context_str .= " and `Posted` like '$date_val%'";
						if ($rs = safe_row('ID, Posted', 'textpattern', "`url_title` like '{$pretext_replacement['url_title']}' $context_str and `Status` >= 4 order by `Posted` desc limit 1")) {
							if (isset($date_val))
								$this->debug('Found date and title-based match.');
							else
								$this->debug('Found title-based match.');
							$pretext_replacement['id'] = $rs['ID'];
							$pretext_replacement['Posted'] = $rs['Posted'];
							$pretext['numPages'] = 1;
							$pretext['is_article_list'] = false;
						} else {
							$match = false;
							unset($pretext_replacement);
						}
					}
				}

				if ($match || $partial_match || $cleaver_partial_match) {
					// Extract the settings for this permlink
					@extract($permlinks[$pretext_replacement['permlink_id']]['settings']);

					// Check the permlink section and category conditions
					if ((!empty($con_section) && $con_section != @$pretext_replacement['s']) ||
					(!empty($con_category) && $con_category != @$pretext_replacement['c'])) {
						$this->debug('Permlink conditions failed');
						if (@$con_section)  $this->debug('con_section = '. $con_section);
						if (@$con_category) $this->debug('con_category = '. $con_category);

						unset($pretext_replacement);
					}
					else if ($match && isset($pretext_replacement))
						$this->debug('We have a match!');

					else if ($partial_match && count($this->partial_matches))
						$this->debug('We have a \'partial match\'');

					else if ($cleaver_partial_match && isset($cleaver_partial_match))
						$this->debug('We have a \'cleaver partial match\'');

					else {
						$this->debug('Error: Can\'t determine the correct type match');
						// This permlink has failed, continue execution of the foreach permlinks loop
						unset($pretext_replacement);
					}
				}

				// We have a match
				if (@$pretext_replacement)
					break;

			} // foreach permlinks end

			// If there is no match restore the most likely partial match. Sorted by number of components and then precedence
			if (!@$pretext_replacement && count($this->partial_matches)) {
				$pt_slice = array_slice($this->partial_matches, -1);
				$pretext_replacement = array_shift($pt_slice);
			}
			unset($this->partial_matches);

			// Restore the cleaver_partial_match if there is no other matches
			if (!@$pretext_replacement && $this->cleaver_partial_match)
				$pretext_replacement = $this->cleaver_partial_match;
			unset($this->cleaver_partial_match);

			// Extract the settings for this permlink
			@extract($permlinks[$pretext_replacement['permlink_id']]['settings']);

			// If pretext_replacement is still set here then we have a match
			if (@$pretext_replacement) {
				$this->debug('Pretext Replacement '.print_r($pretext_replacement, 1));

				if (!empty($des_section))
					$pretext_replacement['s'] = $des_section;
				if (!empty($des_category))
					$pretext_replacement['c'] = $des_category;
				if (!empty($des_feed))
					$pretext_replacement[$des_feed] = 1;

				if (@$pretext_replacement['id'] && @$pretext_replacement['Posted']) {
					if ($np = getNextPrev($pretext_replacement['id'], $pretext_replacement['Posted'], @$pretext_replacement['s']))
						$pretext_replacement = array_merge($pretext_replacement, $np);
				}
				unset($pretext_replacement['Posted']);

				// If there is a match then we most set the http status correctly as txp's pretext might set it to 404
				$pretext_replacement['status'] = '200';

				// Store the orginial HTTP status code
				// We might need to log the page hit if it equals 404
				$orginial_status = $pretext['status'];

				// Txp only looks at the month, but due to how we phase the month we can manipulate the sql to our needs
				if (array_key_exists('date', $pretext_replacement)) {
					$pretext_replacement['month'] = $pretext_replacement['date'];
					unset($pretext_replacement['date']);
				} else if (array_key_exists('year', $pretext_replacement) ||
				array_key_exists('month', $pretext_replacement) ||
				array_key_exists('day', $pretext_replacement)) {
					$month = '';
					$month .= (array_key_exists('year', $pretext_replacement))
						? $pretext_replacement['year'].'-' : '____-';
					$month .= (array_key_exists('month', $pretext_replacement))
						? $pretext_replacement['month'].'-' : '__-';
					$month .= (array_key_exists('day', $pretext_replacement))
						? $pretext_replacement['day'].' ' : '__ ';

					$pretext_replacement['month'] = $month;
					unset($pretext_replacement['year']);
					unset($pretext_replacement['day']);
				}

				// Section needs to be defined so we can always get a page template.
				if (!array_key_exists('s', $pretext_replacement))
				{
					if (!@$pretext_replacement['id'])
						$pretext_replacement['s'] = 'default';
					else
						$pretext_replacement['s'] = safe_field('Section', 'textpattern', 'ID = '.$pretext_replacement['id']);
				}

				// Set the css and page template, otherwise we get an unknown section
				$section_settings = safe_row('css, page', 'txp_section', "name = '{$pretext_replacement['s']}' limit 1");
				$pretext_replacement['page'] = (@$des_page) ? $des_page : $section_settings['page'];
				$pretext_replacement['css']  = $section_settings['css'];

				$this->matched_permlink = $pretext_replacement;

				global $permlink_mode;

				if (in_array($prefs['permlink_mode'], array('id_title', 'section_id_title')) && @$pretext_replacement['pg'] && !@$pretext_replacement['id']) {
					$pretext_replacement['id'] = '';
					$pretext_replacement['is_article_list'] = true;
				}

				// Merge pretext_replacement with pretext
				$pretext = array_merge($pretext, $pretext_replacement);

				if (is_numeric(@$pretext['id'])) {
					$a = safe_row('*, unix_timestamp(Posted) as uPosted, unix_timestamp(Expires) as uExpires, unix_timestamp(LastMod) as uLastMod', 'textpattern', 'ID='.intval($pretext['id']).' and Status >= 4');
					populateArticleData($a);
				}

				// Export required values to the global namespace
				foreach (array('id', 's', 'c', 'pg', 'is_article_list', 'prev_id', 'prev_title', 'next_id', 'next_title', 'css') as $key) {
					if (array_key_exists($key, $pretext))
						$GLOBALS[$key] = $pretext[$key];
				}

				if (count($this->matched_permlink) || @$mt_redirect) {
					$pl_index = $pretext['permlink_id'];
					if (!@$mt_redirect || !$this->pref('redirect_mt_style_links')) {
						$pl = $this->get_permlink($pretext['permlink_id']);
						$pl_index = @$pl['settings']['des_permlink'];
					}

					if (@$pretext['id'] && $pl_index) {
						if (count($this->get_permlink($pl_index)) > 0) {
							ob_clean();
							global $siteurl;
							$rs = safe_row('*, ID as thisid, unix_timestamp(Posted) as posted', 'textpattern', "ID = '{$pretext['id']}'");
							$host = rtrim(str_replace(rtrim(doStrip($pretext['subpath']), '/'), '', hu), '/');
							$this->redirect($host.$this->_permlinkurl($rs, PERMLINKURL, $pl_index), $this->pref('permlink_redirect_http_status'));
						}
					} else if ($url = @$pl['settings']['des_location']) {
						ob_clean();
						$this->redirect($url, $this->pref('url_redirect_http_status'));
					}
				}

				if (@$pretext['rss']) {
					if (@$pretext['s'])
						$_POST['section'] = $pretext['s'];
					if (@$pretext['c'])
						$_POST['category'] = $pretext['c'];
					ob_clean();
					include txpath.'/publish/rss.php';
					exit(rss());
				}

				if (@$pretext['atom']) {
					if (@$pretext['s'])
						$_POST['section'] = $pretext['s'];
					if (@$pretext['c'])
						$_POST['category'] = $pretext['c'];
					ob_clean();
					include txpath.'/publish/atom.php';
					exit(atom());
				}

				$this->debug('Pretext '.print_r($pretext, 1));
			} else {
				$this->debug('NO CHANGES MADE');
			}

			// Log this page hit
			if (@$orginial_status == 404)
				log_hit($pretext['status']);

			// Start output buffering and pseudo callback to textpattern_end
			ob_start(array(&$this, '_textpattern_end_callback'));

			// TxP 4.0.5 (r2436) introduced the textpattern_end callback making the following redundant
			$version = array_sum(array_map(
				create_function('$line', 'if (preg_match(\'/^\$'.'LastChangedRevision: (\w+) \$/\', $line, $match)) return $match[1];'),
				@file(txpath . '/publish.php')
			));
			if ($version >= '2436') return;

			// Remove the plugin callbacks which have already been called
			function filter_callbacks($c) {
				if ($c['event']!='textpattern') return true;
				if (@$c['function'][0]->plugin_name == 'gbp_permanent_links' &&
						@$c['function'][1] == '_textpattern')
				{
					$GLOBALS['gbp_found_self'] = true;
					return false;
				}
				return @$GLOBALS['gbp_found_self'];
			}
			$plugin_callback = array_filter($plugin_callback, 'filter_callbacks');
			unset($GLOBALS['gbp_found_self']);

			// Re-call textpattern
			textpattern();

			// Call custom textpattern_end callback
			$this->_textpattern_end();

			// textpattern() has run, kill the connection
			die();
		}

	} // function _textpattern end

	function _textpattern_end () {
		// Redirect to a 404 if the page number is greater than the max number of pages
		// Has to be after textpattern() as $thispage is set during <txp:article />
		global $thispage, $pretext;
		if ((@$pretext['pg'] && isset($thispage)) &&
		($thispage['numPages'] < $pretext['pg'])) {
			ob_end_clean();
			txp_die(gTxt('404_not_found'), '404');
		}

		// Stop output buffering, this sends the buffer to _textpattern_end_callback()
		while (@ob_end_flush());

	} // function _textpattern_end end

	function _textpattern_end_callback ($html, $override = '') {
		global $pretext, $production_status;

		if ($override) $pretext['permlink_override'] = $override;
		$html = preg_replace_callback(
			'%href="('.hu.'|\?)([^"]*)"%',
			array(&$this, '_pagelinkurl'),
			$html
		);
		unset($pretext['permlink_override']);

		if ($this->pref('debug') && in_array($production_status, array('debug', 'testing'))) {
			$debug = join(n, $this->buffer_debug);
			$this->buffer_debug = array();
			if ($debug)
				$html = comment(n.$debug.n) . $html;
		}

		return $html;
	} // function _textpattern_end_callback end

	function check_permlink_conditions ($pl, $article_array) {
		if (empty($article_array['section'])) $article_array['section'] = @$article_array['Section'];
		if (empty($article_array['category1'])) $article_array['category1'] = @$article_array['Category1'];
		if (empty($article_array['category2'])) $article_array['category2'] = @$article_array['Category2'];

		if (@$pl['settings']['con_category'] && ($pl['settings']['con_category'] != $article_array['category1'] || $pl['settings']['con_category'] != $article_array['category2']))
			return false;
		if (@$pl['settings']['con_section'] && $pl['settings']['con_section'] != $article_array['section'])
			return false;

		return true;
	}

	function _permlinkurl ($article_array, $type = PERMLINKURL, $pl_index = NULL) {
		global $pretext, $prefs, $production_status;

		if ($type == PAGELINKURL)
			return $this->toggle_custom_url_func('pagelinkurl', $article_array);

		if (empty($article_array)) return;

		if ($pl_index)
			$pl = $this->get_permlink($pl_index);
		else {
			// Get the matched pretext replacement array.
			$matched = (count($this->matched_permlink))
			? $this->matched_permlink
			: @array_shift(array_slice($this->partial_matches, -1));

			if (!isset($pl) && $matched && array_key_exists('id', $matched)) {
				// The permlink id is stored in the pretext replacement array, so we can find the permlink.
				$pl = $this->get_permlink($matched['permlink_id']);
				foreach ($pl['components'] as $pl_c)
					if (in_array($pl_c['type'], array('feed', 'page')) || !$this->check_permlink_conditions($pl, $article_array)) {
						unset($pl);
						break;
					}
			}

			if (!isset($pl)) {
				// We have no permlink id so grab the permlink with the highest precedence.
				$permlinks = $this->get_all_permlinks(1, array('feed', 'page'));
				foreach ($permlinks as $key => $pl)
					if (!$this->check_permlink_conditions($pl, $article_array))
						unset($permlinks[$key]);
				$pl = array_shift($permlinks);
			}
		}

		$uri = '';

		if (is_array($pl) && array_key_exists('components', $pl)) {
			extract($article_array);

			if (!isset($title)) $title = $Title;
			if (empty($url_title)) $url_title = stripSpace($title);
			if (empty($section)) $section = $Section;
			if (empty($posted)) $posted = $Posted;
			if (empty($authorid)) $authorid = @$AuthorID;
			if (empty($category1)) $category1 = @$Category1;
			if (empty($category2)) $category2 = @$Category2;
			if (empty($thisid)) $thisid = $ID;

			$pl_components = $pl['components'];

			// Check to see if there is a title component.
			$title = false;
			foreach($pl_components as $pl_c)
				if ($pl_c['type'] == 'title' || $pl_c['type'] == 'id')
					$title = true;

			// If there isn't a title component then we need to append one to the end of the URI
			if (!$title && $this->pref('automatically_append_title'))
				$pl_components[] = array('type' => 'title', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => '');

			$uri = rtrim(doStrip(@$pretext['subpath']), '/');
			foreach ($pl_components as $pl_c) {
				$uri .= '/';

				$type = $pl_c['type'];
				switch ($type) {
					case 'category':
						if (!@$pl_c['category']) $pl_c['category'] = 1;
						$primary = 'category'. $pl_c['category'];
						$secondary = 'category'. (3-(int)$pl_c['category']);
						$check_context = ($this->pref('join_pretext_to_pagelinks') && $this->pref('check_pretext_category_context'));
						if (!$check_context || $$primary == $pretext['c'])
							$uri_c = $$primary;
						else if (!$check_context || $$secondary == $pretext['c'])
							$uri_c = $$secondary;
						else if ($this->pref('debug') && in_array($production_status, array('debug', 'testing')))
							$uri_c = '--INVALID_CATEGORY--';
						else {
							unset($uri);
							break 2;
						}
					break;
					case 'section':
						$check_context = ($this->pref('join_pretext_to_pagelinks') && $this->pref('check_pretext_section_context'));
						if (!$check_context || $section == $pretext['s'])
							$uri_c = $section;
						else {
							unset($uri);
							break 2;
						}
					break;
					case 'title': $uri_c = $url_title; break;
					case 'id': $uri_c = $thisid; break;
					case 'author': $uri_c = safe_field('RealName', 'txp_users', "name like '{$authorid}'"); break;
					case 'login': $uri_c = $authorid; break;
					case 'date': $uri_c = explode('/', date('Y/m/d', $posted)); break;
					case 'year': $uri_c = date('Y', $posted); break;
					case 'month': $uri_c = date('m', $posted); break;
					case 'day': $uri_c = date('d', $posted); break;
					case 'custom':
						if ($uri_c = @$article_array[$prefs["custom_{$pl_c['custom']}_set"]]);
						else if ($uri_c = @$article_array["custom_{$pl_c['custom']}"]);
						else if ($this->pref('debug') && in_array($production_status, array('debug', 'testing')))
							$uri_c = '--UNSET_CUSTOM_FIELD--';
						else {
							unset($uri);
							break 2;
						}
					break;
					case 'text': $uri_c = $pl_c['text']; break;
					case 'regex':
						// Check to see if regex is valid without outputting error messages.
						ob_start();
						preg_match($pl_c['regex'], $pl_c['regex'], $regex_matches);
						$is_valid_regex = !(ob_get_clean());
						if ($is_valid_regex) {
							$key = "permlink_regex_{$pl_c['name']}";
							$uri_c = (array_key_exists($key, $pretext)) ? $pretext[$key] : $regex_matches[0];
						} else if ($this->pref('debug'))
							$uri_c = '--INVALID_REGEX--';
					break;
				}

				if (empty($uri_c))
					if ($this->pref('debug') && in_array($production_status, array('debug', 'testing')))
						$uri_c = '--PERMLINK_FORMAT_ERROR--';
					else {
						unset($uri);
						break;
					}

				if (@$pl_c['prefix'])
					$uri .= $this->encode_url($pl_c['prefix']);

				if (is_array($uri_c)) {
					foreach ($uri_c as $uri_c2)
						$uri .= $this->encode_url($uri_c2) . '/';
					$uri = rtrim($uri, '/');
				} else
					$uri .= $this->encode_url($uri_c);

				if (@$pl_c['suffix'])
					$uri .= $this->encode_url($pl_c['suffix']);

				unset($uri_c);
			}

			if (isset($uri))
				$uri .= '/';
		}

		if ($uri_empty = empty($uri)) {
			// It is possible the uri is still empty if there is no match or if we're using
			// strict matching if so try the default permlink mode.
			$uri = $this->toggle_permlink_mode('permlinkurl', $article_array);
		}

		if ($this->pref('omit_trailing_slash'))
			$uri = rtrim($uri, '/');

		if (!$uri_empty && in_array(txpath.'/publish/rss.php', get_included_files()) || in_array(txpath.'/publish/atom.php', get_included_files()) || txpinterface == 'admin') {
			$host = rtrim(str_replace(rtrim(doStrip(@$pretext['subpath']), '/'), '', hu), '/');
			$uri = $host . $uri;
		}

		return ($this->pref('force_lowercase_urls')) ? strtolower($uri) : $uri;
	}

	function _pagelinkurl ($parts) {
		extract(array(
			'path'		=> 'index.php',
			'query'		=> '',
			'fragment'	=> '',
		), parse_url(html_entity_decode(str_replace('&#38;', '&', $parts[2]))));

		// Tidy up links back to the site homepage
		if ($path == 'index.php' && empty($query))
			return 'href="' .hu. '"';

		// Fix matches like href="?s=foo"
		else if ($path && empty($query) && $parts[1] == '?') {
			$query = $path;
			$path = 'index.php';
		}

		// Check to see if there is query to work with.
		else if (empty($query) || $path != 'index.php' || strpos($query, '/') === true)
			return $parts[0];

		// '&amp;' will break parse_str() if they are found in a query string
		$query = str_replace('&amp;', '&', $query);

		if ($fragment)
			$fragment = '#'.$fragment;

		global $pretext;
		parse_str($query, $query_part);
		if (!array_key_exists('pg', $query_part))
			$query_part['pg'] = 0;
		if (!array_key_exists('id', $query_part))
			$query_part['id'] = 0;
		if (!array_key_exists('rss', $query_part))
			$query_part['rss'] = 0;
		if (!array_key_exists('atom', $query_part))
			$query_part['atom'] = 0;
		if ($this->pref('join_pretext_to_pagelinks'))
			extract(array_merge($pretext, $query_part));
		else
			extract($query_part);

		// We have a id, pass to permlinkurl()
		if ($id) {
			if (@$s == 'file_download') {
				$title = (version_compare($dbversion, '4.2', '>=')) ? NULL : safe_field('filename', 'txp_file', "id = '{$id}'");
				$url = $this->toggle_permlink_mode('filedownloadurl', array($id, $title), true);
			} else {
				$rs = safe_row('*, ID as thisid, unix_timestamp(Posted) as posted', 'textpattern', "ID = '{$id}'");
				$url = $this->_permlinkurl($rs, PERMLINKURL) . $fragment;
			}
			return 'href="'. $url .'"';
		}

		if (@$s == 'default')
			unset($s);

		// Some TxP tags, e.g. <txp:feed_link /> use 'section' or 'category' inconsistent
		// with most other tags. Process these now so we only have to check $s and $c.
		if (@$section)  $s = $section;
		if (@$category) $c = $category;

		// Debugging for buffers
		$this->buffer_debug[] = 'url: '.str_replace('&amp;', '&', $parts[1].$parts[2]);
		$this->buffer_debug[] = 'path: '.$path;
		$this->buffer_debug[] = 'query: '.$query;
		if ($fragment) $this->buffer_debug[] = 'fragment: '.$fragment;

		if (@$id) $this->buffer_debug[] = 'id: '.$id;
		if (@$s) $this->buffer_debug[] = 's: '.$s;
		if (@$c) $this->buffer_debug[] = 'c: '.$c;
		if (@$rss) $this->buffer_debug[] = 'rss: '.$rss;
		if (@$atom) $this->buffer_debug[] = 'atom: '.$atom;
		if (@$pg) $this->buffer_debug[] = 'pg: '.$pg;
		if (@$q) $this->buffer_debug[] = 'q: '.$q;

		if (@$pretext['permlink_override']) {
			$override_ids = explode(',', $pretext['permlink_override']);
			foreach ($override_ids as $override_id) {
				$pl = $this->get_permlink($override_id);
				if (count($pl) > 0) $permlinks[] = $pl;
			}
		}

		if (empty($permlinks)) {
			$permlinks = $this->get_all_permlinks(1);

			$permlinks['gbp_permanent_links_default'] = array(
				'components' => array(
					array('type' => 'text', 'text' => strtolower(urlencode(gTxt('category')))),
					array('type' => 'category'),
				),
				'settings' => array(
					'pl_name' => 'gbp_permanent_links_default', 'pl_precedence' => '', 'pl_preview' => '',
					'con_section' => '', 'con_category' => '', 'des_section' => '', 'des_category' => '',
					'des_permlink' => '', 'des_feed' => '', 'des_location' => '',
			));
		}

		$current_segments = explode('/', ltrim($pretext['request_uri'], '/'));

		$highest_match_count = null;
		foreach ($permlinks as $key => $pl) {
			$this->buffer_debug[] = 'Testing permlink: '. $pl['settings']['pl_name'] .' - '. $key;
			$this->buffer_debug[] = 'Preview: '. $pl['settings']['pl_preview'];
			$out = array(); $keys = array(); $match_count = 0;
			foreach ($pl['components'] as $i => $pl_c) {
				switch ($pl_c['type']) {
					case 'text':
						$out[] = $pl_c['text'];
					break;
					case 'regex':
						$out[] = $pretext['permlink_regex_'.$pl_c['name']];
					break;
					case 'section':
						if (@$s) $out[] = $s;
						else break 2;
					break;
					case 'category':
						if (@$c) $out[] = $c;
						else break 2;
					break;
					case 'feed':
						if (@$rss) $keys[] = $out[] = 'rss';
						else if (@$atom) $keys[] = $out[] = 'atom';
						else break 2;
					break;
					case 'search':
						if (@$q) $out[] = $q;
						else break 2;
					break;
					case 'page':
						if (@$pg) {
							$out[] = $pg;
							$keys[] = 'pg';
						}
						else break 2;
					break;
					default: break 2;
				}
				if (in_array($pl_c['type'], array('text', 'regex'))) {
					if ($current_segments[$i] == end($out) && strlen(end($out)) > 0)
						$match_count += $this->pref('text_and_regex_segment_scores');
				}
				elseif (!in_array($pl_c['type'], array('title', 'id')))
					$match_count++;
				else break;
			}

			$this->buffer_debug[] = 'Match count: '. $match_count;

			// Todo: Store according to the precedence value
			if (count($out) > 0 && ($match_count > $highest_match_count || !isset($highest_match_count)) &&
			!($key == 'gbp_permanent_links_default' && !$match_count)) {
				extract($pl['settings']);
				if ((empty($s) && empty($c)) ||
				(empty($con_section) || @$s == $con_section) ||
				(empty($con_category) || @$c == $con_category)) {
					$this->buffer_debug[] = 'New highest match! '. implode('/', $out);
					$highest_match_count = $match_count;
					$match = $out;
					$match_keys = $keys;
				}
			}
		}

		if (empty($match) && (!(@$pg && $this->pref('clean_page_archive_links')) || (@$pg && @$q))) {
			global $prefs, $pretext, $permlink_mode;
			$this->buffer_debug[] = 'No match';
			$this->buffer_debug[] = '----';
			$pretext['permlink_mode'] = $permlink_mode = $prefs['permlink_mode'];
			$url = pagelinkurl($query_part);
			$pretext['permlink_mode'] = $permlink_mode = 'messy';
			return 'href="'. $url .'"';
		}

		$this->buffer_debug[] = 'match: '.      serialize($match);
		$this->buffer_debug[] = 'match_keys: '. serialize($match_keys);

		$url = '/'.join('/', $match);
		$url = rtrim(hu, '/').rtrim($url, '/').'/';

		if ($rss && !in_array('rss', $match_keys))
			$url .= 'rss';
		else if ($atom && !in_array('atom', $match_keys))
			$url .= 'atom';
		else if ($pg && !in_array('pg', $match_keys)) {
			if ($this->pref('clean_page_archive_links'))
				$url .= $pg;
			else {
				$url .= '?pg='. $pg;
				$omit_trailing_slash = true;
			}
		}

		$url = rtrim($url, '/') . '/';

		if (@$omit_trailing_slash || $this->pref('omit_trailing_slash'))
			$url = rtrim($url, '/');

		$this->buffer_debug[] = $url;
		$this->buffer_debug[] = '----';

		if ($path == 'index.php' && $url != hu)
			return 'href="'. $url . $fragment .'"';

		/*
		1 = index, textpattern/css, NULL (=index)
		2 = id, s, section, c, category, rss, atom, pg, q, (n, p, month, author)
		*/

		return ($this->pref('force_lowercase_urls')) ? strtolower($parts[0]) : $parts[0];
	}

	function set_permlink_mode ($reset_function = false) {
		global $prefs, $pretext, $permlink_mode;
		$prefs['custom_url_func'] = array(&$this, '_permlinkurl');

		if (!$reset_function)
			$pretext['permlink_mode'] = $permlink_mode = 'messy';
		else
			$pretext['permlink_mode'] = $permlink_mode = $prefs['permlink_mode'];
	}

	function reset_permlink_mode () {
		global $prefs, $pretext, $permlink_mode;
		unset($prefs['custom_url_func']);
		$pretext['permlink_mode'] = $permlink_mode = $prefs['permlink_mode'];
	}

	function toggle_custom_url_func ($func, $atts = NULL, $toogle_permlink_mode = false, $expand_arguments = false) {
		global $prefs, $pretext;

		if ($toogle_permlink_mode) {
			global $permlink_mode;
			$_permlink_mode = $permlink_mode;
		}

		$_call_user_func = @$prefs['custom_url_func'];

		unset($prefs['custom_url_func']);
		if ($toogle_permlink_mode)
			$pretext['permlink_mode'] = $permlink_mode = $prefs['permlink_mode'];

		if (is_callable($func)) {
			if (is_array($atts) and $expand_arguments)
				$rs = call_user_func_array($func, $atts);
			else
				$rs = call_user_func($func, $atts);
		}

		$prefs['custom_url_func'] = $_call_user_func;

		if ($toogle_permlink_mode)
			$pretext['permlink_mode'] = $permlink_mode = $_permlink_mode;

		return $rs;
	}

	function toggle_permlink_mode ($func, $atts = NULL, $expand_arguments = false) {
		return $this->toggle_custom_url_func($func, $atts, true, $expand_arguments);
	}

	function encode_url ($text) {
		return urlencode(trim(dumbDown(urldecode($text))));
	}

	function debug () {
		if ($this->pref('debug')) {
			global $production_status;
			$a = func_get_args();

			if (@txpinterface == 'admin')
				foreach ($a as $thing)
					dmp($thing);

			if (@txpinterface == 'public' && in_array($production_status, array('debug', 'testing')))
				foreach ($a as $thing)
					echo comment(is_scalar($thing) ? strval($thing) : var_export($thing, true)),n;
		}
	}
}

class PermanentLinksBuildTabView extends GBPAdminTabView
{
	function preload () {
		register_callback(array(&$this, 'post_save_permlink'), $this->parent->event, gbp_save, 1);
		register_callback(array(&$this, 'post_save_permlink'), $this->parent->event, gbp_post, 1);
	}

	function main () {
		global $prefs;
		extract(gpsa(array('step', gbp_id)));

		// With have an ID, either the permlink has just been saved or the user wants to edit it
		if ($id) {
			// Newly saved or beening edited, either way we're editing a permlink
			$step = gbp_save;
			// Use the ID to grab the permlink data (components & settings)
			$permlink = $this->parent->get_permlink($id);

			if(!isset($permlink['components'])){$permlink['components'] = array();}
			if(!isset($permlink['settings'])){$permlink['settings'] = array();}
			$components = $this->phpArrayToJsArray('components', $permlink['components']);
			$settings = $permlink['settings'];
		} else {
			// Creating a new ID and permlink.
			$step = gbp_post;
			$id = uniqid('');

			// Set the default set of components depending on whether there is parent permlink
			$components = $this->phpArrayToJsArray('components', array(
				array('type' => 'section', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => ''),
				array('type' => 'category', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => '', 'category' => '1'),
				array('type' => 'title', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => ''),
			));

			$settings = array(
				'pl_name' => 'Untitled', 'pl_precedence' => '0',
				'con_section' => '', 'con_category' => '',
				'des_section' => '', 'des_category' => '', 'des_page' => '',
				'des_permlink' => '', 'des_feed' => '', 'des_location' => '',
			);
		}

		// Extract settings - this will be useful when creating the user interface
		extract($settings);

		// PHP & Javascript constants;
		$separator = gbp_separator;
		$components_div = 'permlink_components_ui';
		$components_form = 'permlink_components';
		$settings_form = 'permlink_settings';
		$show_prefix = $this->pref('show_prefix');
		$show_suffix = $this->pref('show_suffix');

		// A little credit here and there doesn't hurt
		$out[] = "<!-- {$this->parent->plugin_name} by Graeme Porteous -->";

		// The Javascript
$out[] = <<<HTML
	<script type="text/javascript" language="javascript" charset="utf-8">
	// <![CDATA[

	// Global variables
var {$components}// components array for all the data

	var _current = 0; // Index of the components array, of the currently selected component
	var c_vals = new Array('type', 'custom', 'category', 'name', 'prefix', 'suffix', 'regex', 'text');

	window.onload = function() {
		component_refresh_all();
		component_switch(component(_current));
	}

	function component_add () {
		// Create data set
		var data = new Array();
		for (key in c_vals) {
			data[c_vals[key]] = '';
		}

		// Add data
		components.push(data);

		// Reset component type list
		form('{$components_form}').type.value = '';

		// Switch to the new component
		_current = components.length - 1;

		// Refresh UI
		component_refresh_all();
		component_update();
	}

	function component_refresh (element) {
		var c = components[element.id];

		// CSS
		if (_current == element.id)
			element.style['backgroundColor'] = 'rgb(249, 206, 73)';
		else
			element.style['backgroundColor'] = 'rgb(255, 254, 211)';
		element.style['color'] = 'rgb(0, 0, 0)';
		element.style['fontFamily'] = 'Arial';
		element.style['fontWeight'] = 'bold';
		element.style['verticalAlign'] = 'middle';
		element.style['textAlign'] = 'center';
		element.style['lineHeight'] = '1.5em';
		element.style['height'] = '1.5em';
		element.style['padding'] = '0 5px';
		element.style['marginRight'] = '5px';
		element.style['cssFloat'] = 'left';
		element.style['display'] = 'inline';

		// Remove all child nodes
		while (element.hasChildNodes()) { element.removeChild(element.firstChild); }

		// Create the visible string representing this component
		switch (c['type']) {
			case '' :
				string = '/';
				break;
			case 'regex' :
			case 'text' :
				string = c[c['type']] + ' /';
				break;
			case 'date' :
				string = c['type'] + ' /';
				break;
			case 'custom' :
				string = c['prefix'] + 'custom_' + c['custom'] + c['suffix'] + ' /';
				break;
			default :
				string = c['prefix'] + c['type'] + c['suffix'] + ' /';
			break;
		}

		// Set the visible string
		element.appendChild(document.createTextNode(string));

		return element;
	}

	function component_refresh_all () {
		// Remove all child nodes
		while (permlink_div().hasChildNodes()) { permlink_div().removeChild(permlink_div().firstChild); }

		for (var i = 0; i < components.length; i++) {
			var c = components[i];

			// Create the new component
			var new_component = document.createElement('div');

			// Set the id interger for this component
			new_component.id = i;

			// Javascript, onmouseup setting
			new_component.setAttribute('onmousedown', 'component_switch(this);');
			new_component.onmousedown = function() { component_switch(this); };

			// Refresh the look of the component
			new_component = component_refresh(new_component);

			// And add the new component to the ui
			permlink_div().appendChild(new_component);
		}
	}

	function component_remove () {
		if (components.length > 1) {
			components.splice(_current, 1);

			if (_current >= components.length)
				_current = components.length - 1;

			component_refresh_all();
		}
	}

	function component_switch (element) {
		// Update current index
		_current = element.id;

		// Refresh UI
		component_refresh_all();

		// Set form input values
		var c = components[_current];
		for (key in c_vals) {
			var k = c_vals[key];
			var e = form('{$components_form}').elements.namedItem(k);

			if (c[k]) e.value = c[k];
			else e.value = '';
		}

		// Hide unneeded form inputs
		component_update();
	}

	function component_update (element) {
		// Store the data in form inputs, and hide all form inputs
		var c = new Array()
		for (key in c_vals) {
			var k = c_vals[key];
			var e = form('{$components_form}').elements.namedItem(k);

			c[k] = e.value;

			e.parentNode.style['display'] = 'none';
		}

		// Reshow type option list
		form('{$components_form}').type.parentNode.style['display'] = '';

		// Set other form inputs to the correct visibility state, dependent on type
		switch (c['type']) {
			case '' :
			case 'date' : break;
			case 'regex' :
				form('{$components_form}').name.parentNode.style['display'] = '';
				form('{$components_form}').regex.parentNode.style['display'] = '';
			break;
			case 'text' :
				form('{$components_form}').name.parentNode.style['display'] = '';
				form('{$components_form}').text.parentNode.style['display'] = '';
			break;
			case 'custom' :
				form('{$components_form}').custom.parentNode.style['display'] = '';
				display_fixes();
			break;
			case 'category' :
				form('{$components_form}').category.parentNode.style['display'] = '';
				display_fixes();
			break;
			default :
				display_fixes();
			break;
		}

		// Save data
		components[_current] = c;

		// Refresh component to reflect new data
		component_refresh(component(_current));

		// Re-focus the active form input
		if (element)
			element.focus();
	}

	function display_fixes () {
		if ('{$show_prefix}')
			form('{$components_form}').prefix.parentNode.style['display'] = '';
		if ('{$show_suffix}')
			form('{$components_form}').suffix.parentNode.style['display'] = '';
	}

	function component_left () {
		if (components.length > 1 && _current > 0) {
			// Store current component
			var c = components[_current];

			// Remove current component
			components.splice(_current, 1);

			// Update current index
			_current--;

			// Re-add current component
			components.splice(_current, 0, c);

			// Refresh UI
			component_refresh_all();
		}
	}

	function component_right () {
		if (_current < components.length - 1) {
			// Store current component
			var c = components[_current];

			// Remove current component
			components.splice(_current, 1);

			// Update current index
			_current++;

			// Re-add current component
			components.splice(_current, 0, c);

			// Refresh UI
			component_refresh_all();
		}
	}

	function save (form) {
		var c = ''; var is_permlink = false; var has_page_or_search = false;
		for (var i = 0; i < components.length; i++) {
			if (components[i]['type'] == 'title' || components[i]['type'] == 'id')
				is_permlink = true;
			if (components[i]['type'] == 'page' || components[i]['type'] == 'feed' || components[i]['type'] == 'search')
				has_page_feed_search = true;
			c = c + jsArrayToPhpArray(components[i]) + '{$separator}';
		}

		if (is_permlink && has_page_or_search)
			alert("Your permanent link can't contain either a 'page', 'feed' or a 'search' component with 'title' or 'id' components.");

		else if (is_permlink && (form.pl_name.value == '' || form.pl_name.value == 'Untitled')) {
			document.getElementById('settings').style['display'] = '';
			form.pl_name.style['border'] = '3px solid rgb(221, 0, 0)';
			form.pl_precedence.style['border'] = '';
			alert('Please enter a name for this permanent link rule.');
		} else if (form.pl_precedence.value == '') {
			document.getElementById('settings').style['display'] = '';
			form.pl_precedence.style['border'] = '3px solid rgb(221, 0, 0)';
			form.pl_name.style['border'] = '';
			alert('Please enter a precedence value for this permanent link rule.');
		} else {
			form.components.value = c;
			if (permlink_div().textContent)
				form.pl_preview.value = permlink_div().textContent;
			else if (permlink_div().innerText)
				form.pl_preview.value = permlink_div().innerText;
			return true;
		}

		return false;
	}

	function jsArrayToPhpArray (array) {
		// http://farm.tucows.com/blog/_archives/2005/5/30/895901.html
		var array_php = "";
		var total = 0;
		for (var key in array) {
			++ total;
			array_php = array_php + "s:" +
				String(key).length + ":\"" + String(key) + "\";s:" +
				String(array[key]).length + ":\"" + String(array[key]) + "\";";
		}
		array_php = "a:" + total + ":{" + array_php + "}";
		return array_php;
	}

	function permlink_div () {
		// Return the permlink rule element
		return document.getElementById('{$components_div}');
	}

	function form (name) {
		if (!name)
			name = '{$components_form}'
		// Return the form element with name
		return document.forms.namedItem(name);
	}

	function component (index) {
		// Return component with index
		return permlink_div().childNodes[index];
	}

	// ]]>
	</script>
HTML;

		// --- Rule --- //

		$out[] = hed('Permanent link rule', 2);
		$out[] = '<div id="'.$components_div.'" style="background-color: rgb(230, 230, 230); width: auto; height: 1.5em; margin: 10px 0; padding: 5px;"></div>';
		$out[] = graf
			(
			$this->fInput('button', 'component_add', 'Add component', array('click' => 'component_add();')).n.
			$this->fInput('button', 'component_remove', 'Remove component', array('click' => 'component_remove();')).n.
			$this->fInput('button', 'component_left', 'Move left', array('click' => 'component_left();')).n.
			$this->fInput('button', 'component_right', 'Move right', array('click' => 'component_right();'))
			);

		// --- Component form --- //

		$out[] = '<form action="index.php" name="'.$components_form.'" onsubmit="return false;">';

		// --- Component type --- //

		$component_types = array (
			'section' => 'Section', 'category' => 'Category',
			'title' => 'Title', 'id' => 'ID',
			'date' => 'Date (yyyy/mm/dd)', 'year' => 'Year',
			'month' => 'Month', 'day' => 'Day',
			'author' => 'Author (Real name)', 'login' => 'Author (Login)',
			'custom' => 'Custom Field', 'page' => 'Page Number',
			'feed' => 'Feed', 'search' => 'Search request',
			'text' => 'Plain Text', 'regex' => 'Regular Expression'
		);
		$out[] = graf($this->fSelect('type', $component_types, '', 1, 'Component type', ' onchange="component_update();"'));

		// --- Component data --- //

		// Grab the custom field titles
		$custom_fields = array();
		for ($i = 1; $i <= 10; $i++) {
			if ($v = $prefs["custom_{$i}_set"])
				$custom_fields[$i] = $v;
		}

		$out[] = graf (
			$this->fSelect('custom', $custom_fields, '', 0, 'Custom', ' onchange="component_update(this);"').n.
			$this->fSelect('category', array('1' => 'Category 1', '2' => 'Category 2'), '', 0, 'Primary Category', ' onchange="component_update(this);"').n.
			$this->fInput('text', 'name', '', array('keyup' => 'component_update(this);'), 'Name').n.
			$this->fInput('text', 'prefix', '', array('keyup' => 'component_update(this);'), 'Prefix').n.
			$this->fInput('text', 'regex', '', array('keyup' => 'component_update(this);'), 'Regular Expression').n.
			$this->fInput('text', 'suffix', '', array('keyup' => 'component_update(this);'), 'Suffix').n.
			$this->fInput('text', 'text', '', array('keyup' => 'component_update(this);'), 'Text')
		);
		$hr = '<hr style="border: 0; height: 1px; background-color: rgb(200, 200, 200); color: rgb(200, 200, 200); margin-bottom: 10px;" />';
		$out[] = $hr;
		$out[] = '</form>';

		// --- Settings form --- //

		$out[] = '<form action="index.php" method="post" name="'.$settings_form.'" onsubmit="return save(this);">';

		// --- Settings --- //

		//if(!isset($pl_name)){$pl_name = null;}
		//if(!isset($pl_precedence)){$pl_precedence = null;}
		$out[] = hed('<a href="#" onclick="toggleDisplay(\'settings\'); return false;">Settings</a>', 2);
		$out[] = '<div id="settings">';
		$out[] = graf($this->fInput('text', 'pl_name', @$pl_name, NULL, 'Name'));
		$out[] = graf($this->fInput('text', 'pl_precedence', @$pl_precedence, NULL, 'Precedence'));
		$out[] = '</div>';
		$out[] = $hr;

		// --- Conditions --- //

		$out[] = hed('<a href="#" onclick="toggleDisplay(\'conditions\'); return false;">Conditions</a>', 2);
		$out[] = '<div id="conditions" style="display:none">';
		$out[] = graf(strong('Only use this permanent link if the following conditions apply...'));

		// Generate a sections array (name=>title)
		$sections = array();
		$rs = safe_rows('name, title', 'txp_section', "name != 'default' order by name");
		foreach ($rs as $sec) {
			$sections[$sec['name']] = $sec['title'];
		}

		// Generate a categories array (name=>title)
		$categories = array();
		$rs = safe_rows('name, title', 'txp_category', "type = 'article' and name != 'root' order by name");
		foreach ($rs as $sec) {
			$categories[$sec['name']] = $sec['title'];
		}

		//if(!isset($con_section)){$con_section = null;}
		//if(!isset($con_category)){$con_category = null;}
		$out[] = graf (
			$this->fSelect('con_section', $sections, @$con_section, 1, 'Within section').n.
			$this->fSelect('con_category', $categories, @$con_category, 1, 'Within category')
		);
		$out[] = '</div>';
		$out[] = $hr;

		// --- Destination --- //

		//if(!isset($des_section)){$des_section = null;}
		$out[] = hed('<a href="#" onclick="toggleDisplay(\'destination\'); return false;">Destination</a>', 2);
		$out[] = '<div id="destination" style="display:none">';
		$out[] = graf(strong('Forward this permanent link to...'));
		$out[] = graf (
			$this->fSelect('des_section', $sections, @$des_section, 1, 'Section').n.
			$this->fSelect('des_category', $categories, @$des_category, 1, 'Category')
		);
		$out[] = graf($this->fSelect('des_page', safe_column('name', 'txp_page', "1"), @$des_page, 1, 'Page'));
		$out[] = graf($this->fBoxes('des_feed', array('rss', 'atom', ''), @$des_feed, NULL, array('RSS feed', 'Atom feed', 'Neither')));
		$out[] = graf(strong('Redirect this permanent link to...'));
		// Generate a permlinks array
		$permlinks = $this->parent->get_all_permlinks(1);
		foreach ($permlinks as $key => $pl) {
			$permlinks[$key] = $pl['settings']['pl_name'];
		}
		unset($permlinks[$id]);
		$out[] = graf($this->fSelect('des_permlink', $permlinks, @$des_permlink, 1, 'Permanent link'));
		$out[] = graf($this->fInput('text', 'des_location', @$des_location, NULL, 'HTTP location'));
		$out[] = '</div>';
		$out[] = $hr;

		// Save button
		$out[] = fInput('submit', '', 'Save permanent link');

		// Extra form inputs which get filled on submit
		$out[] = hInput('components', '');
		$out[] = hInput('pl_preview', '');
		// Event and tab form inputs
		$out[] = $this->form_inputs();
		// Step and ID form inputs
		$out[] = sInput($step);
		$out[] = hInput(gbp_id, $id);

		$out[] = '</form>';

		// Lets echo everything out. Yah!
		echo join(n, $out);
	}

	function fLabel ($label, $contents = '', $label_right = false) {
		// <label> the contents with the name $lable
		$contents = ($label_right)
		? $contents.$label
		: $label.($contents ? ': '.$contents : '');
		return tag($contents, 'label');
	}

	function fBoxes ($name = '', $value = '', $checked_value = '', $on = array(), $label = '') {
		$out = array();
		if (is_array($value)) {
			$i = 0;
			foreach ($value as $val) {
				$o = '<input type="radio" name="'.$name.'" value="'.$val.'"';
				$o .= ($checked_value == $val) ? ' checked="checked"' : '';
				if (is_array($on)) foreach($on as $k => $v)
					$o .= ($on) ? ' on'.$k.'="'.$v.'"' : '';
				$o .= ' />';
				$out[] = $this->fLabel($label[$i++], $o, true);
			}
		} else {
			$o = '<input type="checkbox" name="'.$name.'" value="'.$value.'"';
			$o .= ($checked_value == $value) ? ' checked="checked"' : '';
			if (is_array($on)) foreach($on as $k => $v)
				$o .= ($on) ? ' on'.$k.'="'.$v.'"' : '';
			$o .= ' />';
			$out[] = $this->fLabel($label, $o, true);
		}

		return join('', $out);
	}

	function fInput ($type, $name = '', $value = '', $on = array(), $label = '') {
		if ($type == 'radio' || $type == 'checkbox')
			return $this->fBoxes($name, $value, $on, $label);

		$o = '<input type="'.$type.'" name="'.$name.'" value="'.$value.'"';
		if (is_array($on)) foreach($on as $k => $v)
			$o .= ($on) ? ' on'.$k.'="'.$v.'"' : '';
		$o .= ' />';
		return ($label) ? $this->fLabel($label, $o) : $o;
	}

	function fSelect ($name = '', $array = '', $value = '', $blank_first = '', $label = '', $on_submit = '') {
		$o = selectInput($name, $array, $value, $blank_first, $on_submit);
		return ($label ? $this->fLabel($label, $o) : $o);
	}

	function post_save_permlink () {
		// The function posts or saves a permanent link to txp_prefs

		extract(gpsa(array('step', gbp_id)));

		// Grab the user defined settings from the form POST data
		$settings = gpsa(array(
			'pl_name', 'pl_precedence', 'pl_preview',
			'con_section', 'con_category',
			'des_section', 'des_category', 'des_page',
			'des_permlink', 'des_feed', 'des_location',
		));

		// Remove spaces from the permanent link preview
		$settings['pl_preview'] = preg_replace('%\s+/\s*%', '/', $settings['pl_preview']);

		// Explode the separated string of serialize components - this was made by JavaScript.
		$serialize_components = explode(gbp_separator, rtrim(gps('components'), gbp_separator));

		// Unserialize the components
		$components = array();
		foreach ($serialize_components as $c)
			$components[] = unserialize(stripslashes($c));

		// Complete the permanent link array - this is exactly what needs to be stored in the db
		$permlink = array('settings' => $settings, 'components' => $components);

		// Save it
		$this->set_preference($id, $permlink, 'gbp_serialized');

		$this->parent->message = messenger('', $settings['pl_name'], 'saved');
	}

	function phpArrayToJsArray ($name, $array) {
		// From PHP.net
		if (is_array($array)) {
			$result = $name.' = new Array();'.n;
			foreach ($array as $key => $value)
				$result .= $this->phpArrayToJsArray($name.'[\''.$key.'\']',$value,'').n;
		} else {
			$result = $name.' = \''.$array.'\';';
		}
		return $result;
	}
}

class PermanentLinksListTabView extends GBPAdminTabView
{
	function preload () {
		register_callback(array(&$this, $this->parent->event.'_multi_edit'), $this->parent->event, $this->parent->event.'_multi_edit', 1);
		register_callback(array(&$this, $this->parent->event.'_change_pageby'), $this->parent->event, $this->parent->event.'_change_pageby', 1);
	}

	function main () {
		extract(gpsa(array('page', 'sort', 'dir', 'crit', 'search_method')));

		$event = $this->parent->event;

		$permlinks = $this->parent->get_all_permlinks();
		$total = count($permlinks);

		if ($total < 1) {
			echo graf('You haven\'t created any custom permanent links rules yet.', ' style="text-align: center;"').
				 graf('<a href="'.$this->url(array(gbp_tab => 'build'), true).'">Click here</a> to add one.', ' style="text-align: center;"');
			return;
		}

		$limit = max($this->pref('list_pageby'), 15);

		list($page, $offset, $numPages) = $this->pager($total, $limit, $page);

		if (empty($sort))
			$sort = 'pl_precedence';

		if (empty($dir))
			$dir = 'desc';

		$dir = ($dir == 'desc') ? 'desc' : 'asc';

		// Sort the permlinks via the selected column and then their names.
		foreach ($permlinks as $id => $permlink) {
			$sort_keys[$id] = $permlink['settings'][$sort];
			$name[$id] = $permlink['settings']['pl_name'];
		}
		array_multisort($sort_keys, (($dir == 'desc') ? SORT_DESC : SORT_ASC), $name, SORT_ASC, $permlinks);

		$switch_dir = ($dir == 'desc') ? 'asc' : 'desc';

		$permlinks = array_slice($permlinks, $offset, $limit);

		if (count($permlinks)) {
			echo n.n.'<form name="longform" method="post" action="index.php" onsubmit="return verify(\''.gTxt('are_you_sure').'\')">'.

				n.startTable('list').
				n.tr(
					n.column_head('name', 'pl_name', $event, true, $switch_dir, $crit, $search_method).
					hCell().
					column_head('preview', 'pl_preview', $event, true, $switch_dir, $crit, $search_method).
					column_head('precedence', 'pl_precedence', $event, true, $switch_dir, $crit, $search_method).
					hCell()
				);

			include_once txpath.'/publish/taghandlers.php';

			foreach ($permlinks as $id => $permlink) {
				extract($permlink['settings']);

				$manage = n.'<ul'.(version_compare($GLOBALS['thisversion'], '4.0.3', '<=') ? ' style="margin:0;padding:0;list-style-type:none;">' : '>').
						n.t.'<li>'.href(gTxt('edit'), $this->url(array(gbp_tab => 'build', gbp_id => $id), true)).'</li>'.
						n.'</ul>';

				echo n.n.tr(

					td(
						href($pl_name, $this->url(array(gbp_tab => 'build', gbp_id => $id), true))
					, 75).

					td($manage, 35).

					td($pl_preview, 175).
					td($pl_precedence.'&nbsp;', 50).

					td(
						fInput('checkbox', 'selected[]', $id)
					)
				);
			}
/*
			echo n.n.tr(
				tda(
					select_buttons().
					$this->permlinks_multiedit_form($page, $sort, $dir, $crit, $search_method)
				,' colspan="4" style="text-align: right; border: none;"')
			).

			n.endTable().
			n.'</form>'.
*/
			n.$this->nav_form($event, $page, $numPages, $sort, $dir, $crit, $search_method).

			n.pageby_form($event, $this->pref('list_pageby'));
		}
	}

	function pager ($total, $limit, $page) {
		if (function_exists('pager'))
			return pager($total, $limit, $page);

		// This is taken from txplib_misc.php r1588 it is required for 4.0.3 compatibitly
		$num_pages = ceil($total / $limit);
		$page = $page ? (int) $page : 1;
		$page = min(max($page, 1), $num_pages);
		$offset = max(($page - 1) * $limit, 0);
		return array($page, $offset, $num_pages);
	}

	function nav_form ($event, $page, $numPages, $sort, $dir, $crit, $method) {
		if (function_exists('nav_form'))
			return nav_form($event, $page, $numPages, $sort, $dir, $crit, $method);

		// This is basically stolen from the 4.0.3 version of includes/txp_list.php
		// - list_nav_form() for 4.0.3 compatibitly
		$nav[] = ($page > 1)
			? PrevNextLink($event, $page-1, gTxt('prev'), 'prev', $sort, $dir) : '';
		$nav[] = sp.small($page. '/'.$numPages).sp;
		$nav[] = ($page != $numPages)
			? PrevNextLink($event, $page+1, gTxt('next'), 'next', $sort, $dir) : '';
		return ($nav)
			? graf(join('', $nav), ' align="center"') : '';
	}

	function permlinks_multiedit_form ($page, $sort, $dir, $crit, $search_method) {
		$methods = array(
			'delete' => gTxt('delete'),
		);

		return event_multiedit_form($this->parent->event, $methods, $page, $sort, $dir, $crit, $search_method);
	}

	function permlinks_change_pageby () {
		$this->set_preference('list_pageby', gps('qty'));
	}

	function permlinks_multi_edit () {
		$method = gps('edit_method')
			? gps('edit_method') // From Txp 4.0.4 and greater
			: gps('method'); // Up to Txp 4.0.3

		switch ($method) {
			case 'delete':
				foreach (gps('selected') as $id) {
							$deleted[] = $this->parent->remove_permlink($id);
				}
			break;
		}

		$this->parent->message = (isset($deleted) && is_array($deleted) && count($deleted))
			? messenger('', join(', ', $deleted) ,'deleted')
			: messenger('an error occurred', '', '');
	}
}

global $gbp_pl;
$gbp_pl = new PermanentLinks('Permanent Links', 'permlinks', 'admin');
if (@txpinterface == 'public') {
	register_callback(array(&$gbp_pl, '_feed_entry'), 'rss_entry');
	register_callback(array(&$gbp_pl, '_feed_entry'), 'atom_entry');
	register_callback(array(&$gbp_pl, '_textpattern'), 'textpattern');
	register_callback(array(&$gbp_pl, '_textpattern_end'), 'textpattern_end');

	function gbp_if_regex ($atts, $thing) {
		global $pretext;
		extract(lAtts(array(
			'name' => '',
			'val'  => '',
		),$atts));
		$match = (@$pretext["permlink_regex_{$name}"] == $val);
		return parse(EvalElse($thing, $match));
	}

	function gbp_if_text ($atts, $thing) {
		global $pretext;
		extract(lAtts(array(
			'name' => '',
			'val'  => '',
		),$atts));

		$match = false;
		if (!empty($name)) {
			if (empty($val))
				$match = (isset($pretext["permlink_text_{$name}"]));
			else
				$match = (@$pretext["permlink_text_{$name}"] == $val);
		}
		return parse(EvalElse($thing, $match));
	}

	function gbp_use_pagelink ($atts, $thing = '') {
		global $gbp_pl;
		extract(lAtts(array(
			'rule' => '',
		),$atts));
		return $gbp_pl->_textpattern_end_callback(parse($thing), $rule);
	}

	function gbp_disable_permlinks ($atts, $thing = '') {
		global $gbp_pl;
		return $gbp_pl->toggle_permlink_mode('parse', $thing);
	}
}

Offline

#687 2023-01-13 23:45:18

THE BLUE DRAGON
Member
From: Israel
Registered: 2007-11-16
Posts: 619
Website

Re: gbp_permanent_links

TXP v4.8.8
Errors in gbp_admin_library

Methods with the same name as their class will not be constructors in a future version of PHP; GBPPlugin has a deprecated constructor.

Same for these:
GBPAdminTabView
GBPPreferenceTabView
GBPWizardTabView

Fix:
Change all these function names to __construct
Here’s the fixed code:

Edit!
There was also a need to change the way calling the parent contructor,
so also changed GBPAdminTabView::GBPAdminTabView to parent::__construct

// Constants
define('gbp_tab', 'tab');
define('gbp_id', 'id');

class GBPPlugin {
	// Internal variables
	var $plugin_name;
	var $title;
	var $event;
	var $message = '';
	var $tabs = array();
	var $active_tab = 0;
	var $use_tabs = false;
	var $gp = array();
	var $preferences = array();
	var $permissions = '1,2,3,4,5,6';
	var $wizard_key;
	var $wizard_installed = false;

	// Constructor
	function __construct($title = '', $event = '', $parent_tab = '') {

		global $txp_current_plugin;

		// Store a reference to this class so we can get PHP 4 to work
		if (version_compare(phpversion(), '5.0.0', '<'))
			global $gbp_admin_lib_refs; $gbp_admin_lib_refs[$txp_current_plugin] = &$this;

		// Get the plugin_name from the global txp_current_plugin variable
		$this->plugin_name = $txp_current_plugin;

		// When making a GBPAdminView there must be event attributes
		$this->event = $event;

		// Add privs for this event
		global $txp_permissions;
		$perms = @$txp_permissions[$this->permissions];
		add_privs($this->event, ($perms ? $perms : $this->permissions));

		if (@txpinterface == 'admin') {
			// We are admin-side.

			// There must be title and event attributes
			$this->title = $title;

			// The parent_tab can only be one of four things, make sure it is
			if ($event AND $title AND $parent_tab AND array_search($parent_tab, array('content', 'presentation', 'admin', 'extensions')) === false)
				$parent_tab = 'extensions';

			// Set up the get-post array
			$this->gp = array_merge(array('event', gbp_tab), $this->gp);

			// Check if our event is active, if so call preload()
			if (gps('event') == $event) {

				$this->load_preferences();

				$this->preload();

				// Tabs should be loaded by now
				if ($parent_tab && $this->use_tabs) {

					foreach (array_keys($this->tabs) as $key) {
						$tab = &$this->tabs[$key];
						$tab->php_4_fix();
						if (is_a($tab, 'GBPWizardTabView')) {
							$this->wizard_key = $key;
							$this->wizard_installed = $tab->installed();
						}
					}

					if (!$this->wizard_installed && $this->wizard_key)
						$this->active_tab = $this->wizard_key;

					// Let the active_tab know it's active and call it's preload()
					$tab = &$this->tabs[$this->active_tab];
					$tab->is_active = 1;
					$tab->preload();
				}
			}

			// Call txp functions to register this plugin
			if ($parent_tab) {
				register_tab($parent_tab, $event, $title);
				register_callback(array(&$this, 'render'), $event, null, 0);
			}
		}
		if (@txpinterface == 'public')
			$this->load_preferences();
	}

	function load_preferences () {
		/*
		Grab and store all preferences with event matching this plugin.
		*/
		global $prefs;

		// Override the default values if the prefs have been stored in the preferences table.
		$preferences = safe_rows("name, html as type",
		'txp_prefs', "event = '{$this->event}'");

		// Add the default preferences which aren't saved in the db but defined in the plugin's source.
		foreach ($this->preferences as $key => $pref) {
			$db_pref = array('name' => $this->plugin_name.'_'.$key, 'type' => $pref['type']);
			if (array_search($db_pref, $preferences) === false)
				$preferences[] = $db_pref + array('default_value' => $pref['value']);
		}

		foreach ($preferences as $name => $pref) {
			// Extract the name and type.
			extract($pref);

			$base_name = $name;
			$value = get_pref($name);

			// If there is no value then revert to the default value if it exists.
			if ((!$value || (@!$value[0] && count($value) <= 1)) && isset($default_value))
				$value = $default_value;

			// Else if this a custom type (E.g. gbp_serialized OR gbp_array_text)
			// call it's db_get method to decode it's value.
			else if (is_callable(array(&$this, $type)))
				$value = call_user_func(array(&$this, $type), 'db_get', $value);

			// Re-set the combined and decoded value to the global prefs array.
			$prefs[$base_name] = $value;

			// If the preference exists in our preference array set the new value and correct type.
			$base_name = substr($base_name, strlen($this->plugin_name.'_'));
			if (array_key_exists($base_name, $this->preferences))
				$this->preferences[$base_name] = array('value' => $value, 'type' => $type);
		}
	}

	function set_preference ($key, $value, $type = '') {
		global $prefs, $txp_current_plugin;

		// If the plugin_name or event isn't set is it safe to assume
		// $txp_current_plugin and gps('event') are correct?
		$plugin = ($this->plugin_name) ? $this->plugin_name : $txp_current_plugin;
		$event = ($this->event) ? $this->event : gps('event');

		// Set some standard db fields
		$base_name = $plugin.'_'.$key;
		$name = $base_name;

		// If a type hasn't been specified then look the key up in our preferences.
		// Else assume it's type is 'text_input'.
		if (empty($type) && array_key_exists($key, $this->preferences))
			$type = $this->preferences[$key]['type'];
		else if (empty($type))
			$type = 'text_input';

		// Set the new value to the global prefs array and if the preference exists
		// to our own preference array.
		$prefs[$name] = $value;
		if (array_key_exists($key, $this->preferences))
			$this->preferences[$key] = array('value' => $value, 'type' => $type);

		// If this preference has a custom type (E.g. gbp_serialized OR gbp_array_text)
		// call it's db_set method to encode the value.
		if (is_callable(array(&$this, $type)))
			$value = call_user_func(array(&$this, $type), 'db_set', $value);

		$this->remove_preference($name);

		// Make sure preferences which equal NULL are saved
		if (empty($value))
			set_pref($name, '', $event, 2, $type);

		$value = doSlash($value);
		set_pref($name, $value, $event, 2, $type);
	}

	function remove_preference ($key) {
		$event = $this->event;
		safe_delete('txp_prefs', "event = '$event' AND ((name LIKE '$key') OR (name LIKE '{$key}_%'))");
	}

	function gbp_serialized ($step, $value, $item = '') {
		switch (strtolower($step)) {
			default:
			case 'ui_in':
				if (!is_array($value)) $value = array($value);
				return text_input($item, implode(',', $value), 50);
			break;
			case 'ui_out':
				return explode(',', $value);
			break;
			case 'db_set':
				return base64_encode(serialize($value));
			break;
			case 'db_get':
				return unserialize(base64_decode($value));
			break;
		}
		return '';
	}

	function gbp_array_text ($step, $value, $item = '') {
		switch (strtolower($step)) {
			default:
			case 'ui_in':
				if (!is_array($value)) $value = array($value);
				return text_input($item, implode(',', $value), 50);
			break;
			case 'ui_out':
				return explode(',', $value);
			break;
			case 'db_set':
				return implode(',', $value);
			break;
			case 'db_get':
				return explode(',', $value);
			break;
		}
		return '';
	}

	function &add_tab ($tab, $is_default = NULL) {

		// Check to see if the tab is active
		if (($is_default && !gps(gbp_tab)) || (gps(gbp_tab) == $tab->event))
			$this->active_tab = count($this->tabs);

		if (is_a($tab, 'GBPWizardTabView')) {
			$tab->parent = &$this;

			// Wizard routines
			$step = gps('step');
			if (in_array($step, array('setup', 'cleanup'))) {
				$installation_steps = ($step == 'setup')
					? array_keys($tab->installation_steps)
					: array_reverse(array_keys($tab->installation_steps));
				foreach ($installation_steps as $key) {
					$function = array(&$tab, $step.'_'.$key);
					if (is_callable($function)) {
						$optional = @$tab->installation_steps[$key]['optional'];
						if (($optional && gps('optional_'.$key)) || !$optional)
							call_user_func($function);
						else
							$tab->add_report_item($tab->installation_steps[$key][$step], 'skipped');
					}
				}
			}
		}

		// Store the tab
		$this->tabs[] = $tab;

		// We've got a tab, lets assume we want to use it
		$this->use_tabs = true;

		return $this;
	}

	function preload () {
		// Override this function if you require sub tabs.
	}

	function render () {

		// render() gets called because it is specified in txp's register_callback()

		// After a callback we lose track of the current plugin in PHP 4
		global $txp_current_plugin;
		$txp_current_plugin = $this->plugin_name;

		$this->render_header();
		$this->main();

		if ($this->use_tabs) {

			$this->render_tabs();
			$this->render_tab_main();
		}

		$this->render_footer();
		$this->end();
	}

	function render_header () {

		// Render the pagetop, a txp function
		pagetop($this->title, $this->message);

		// Once a message has been used we discard it
		$this->message = '';
	}

	function render_tabs () {
		// This table, which contains the tags, will have to be changed if any improvements
		// happen to the admin interface
		$out[] = '<table cellpadding="0" cellspacing="0" width="100%" style="margin-top:-2em;margin-bottom:2em;">';
		$out[] = '<tr><td align="center" class="tabs">';
		$out[] = '<table cellpadding="0" cellspacing="0" align="center"><tr>';

		$style = 'style="padding: 0 30px;"';
		// Force the wizard to be the only tab if the plugin isn't installed
		if ($this->wizard_installed || !$this->wizard_key)
			foreach (array_keys($this->tabs) as $key) {
				// Render each tab but keep a reference to the tab so any changes made are stored
				$tab = &$this->tabs[$key];
				$out[] = $tab->render_tab();
				$fn = array(&$tab , 'get_canvas_style');
				if (is_callable($fn)) {
					$res = call_user_func($fn);
					$style = (false!==$res) ? $res : $style ;
				}
			}
		else {
			$tab = &$this->tabs[$this->wizard_key];
			$out[] = $tab->render_tab();
			$fn = array(&$tab , 'get_canvas_style');
			if (is_callable($fn)) {
				$res = call_user_func($fn);
				$style = (false!==$res) ? $res : $style ;
			}
		}

		$out[] = '</tr></table>';
		$out[] = '</td></tr>';
		$out[] = '</table><div '.$style.'>';

		echo join('', $out);
	}

	function main () {
		// Override this function
	}

	function render_tab_main () {

		// Call main() for the active_tab
		$tab = &$this->tabs[$this->active_tab];
		if (($this->wizard_installed || !$this->wizard_key || $tab->event == 'wizard') && has_privs($this->event.'.'.$tab->event))
			$tab->main();
		else
			echo '<p style="margin-top:3em;text-align:center">'.gTxt('restricted_area').'</p>';
	}

	function render_footer () {

		// A simple footer
		global $plugins_ver;
		$out[] = '</div>';
		$out[] = '<div style="padding-top: 3em; text-align: center; clear: both;">';
		$out[] = $this->plugin_name;
		if (@$plugins_ver[$this->plugin_name])
		 	$out[] = ' &#183; ' . $plugins_ver[$this->plugin_name];
		$out[] = '</div>';

		echo join('', $out);
	}

	function end () {
		// Override this function
	}

	function form_inputs () {

		$out[] = eInput($this->event);

		if ($this->use_tabs) {

			$tab = $this->tabs[$this->active_tab];
			$out[] = hInput(gbp_tab, $tab->event);
		}

		return join('', $out);
	}

	function url ($vars = array(), $gp = false) {
		/*
		Expands $vars into a get style url and redirects to that location. These can be
		overriden with the current get, post, session variables defined in $this->gp
		by setting $gp = true
		NOTE: If $vars is not an array or is empty then we assume $gp = true.
		*/

		if (!is_array($vars))
			$vars = gpsa($this->gp);
		else if ($gp || !count($vars))
			$vars = array_merge(gpsa($this->gp), $vars);

		foreach ($vars as $key => $value) {
			if (!empty($value))
				$out[] = $key.'='.$value;
		}

		$script = hu.basename(txpath).'/index.php';
		return $script . (isset($out)
			? '?'.join('&', $out)
			: '');
	}

	function redirect ($url = '', $status = 303) {
		/*
		If $vars is an array, use url() to expand as an GET style url and redirect to
		that location using the HTTP status code definition defined by $status.
		*/

		static $status_definitions = array (
			301 => "Moved Permanently",
			302 => "Found",
			303 => "See Other",
			307 => "Temporary Redirect"
		);

		if (!in_array($status, array_keys($status_definitions)))
			$status = 303;

		if (is_array($url))
			$url = $this->url($url);
		else {
			$url_details = parse_url($url);
			if (!@$url_details['scheme'])
				$url = 'http://'.$url;
		}

		if (empty($_SERVER['FCGI_ROLE']) and empty($_ENV['FCGI_ROLE'])) {
			header('HTTP/1.1 '.$status.' '.$status_definitions[$status]);
			header('Status: '.$status);
			header('Location: '.$url);
			header('Connection: close');
			header('Content-Length: 0');
			exit(0);
			} else {
			global $sitename;
			$url = htmlspecialchars($url);
			echo <<<END
			<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
			<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
			<head>
				<title>$sitename</title>
				<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
				<meta http-equiv="refresh" content="0;url=$url" />
			</head>
			<body>
			<a href="$url">{$status_definitions[$status]}</a>
			</body>
			</html>
END;
		}
	}

	function pref ($key) {
		global $prefs, $txp_current_plugin;

		$plugin = ($this->plugin_name) ? $this->plugin_name : $txp_current_plugin;
		$key = $plugin.'_'.$key;

		if (@$this->preferences[$key])
			return $this->preferences[$key]['value'];
		if (@$prefs[$key])
			return $prefs[$key];
		return NULL;
	}
}

class GBPAdminTabView {
	//	Internal variables
	var $title;
	var $event;
	var $is_active;
	var $parent;
	var $permissions = '1,2,3,4,5,6';

	//	Constructor
	function __construct($title, $event, &$parent, $is_default = NULL) {

		$this->title = (function_exists('mb_convert_case'))
			? mb_convert_case($title, MB_CASE_TITLE, "UTF-8")
			: ucwords($title);

		$this->event = $event;

		// Note: $this->parent only gets set correctly for PHP 5
		$this->parent =& $parent->add_tab($this, $is_default);

		// Add privs for this tab
		global $txp_permissions;
		$perms = @$txp_permissions[$this->permissions];
		add_privs($this->parent->event.'.'.$this->event, ($perms ? $perms : $this->permissions));
	}

	function php_4_fix () {

		// Fix references in PHP 4 so sub tabs can access their parent tab
		if (version_compare(phpversion(), '5.0.0', '<')) {
			global $txp_current_plugin, $gbp_admin_lib_refs;
			$this->parent =& $gbp_admin_lib_refs[$txp_current_plugin];
		}
	}

	function preload () {
		// Override this function
	}

	function render_tab () {

		// Grab the url to this tab
		$url = $this->parent->url(array(gbp_tab => $this->event), true);

		// Will need updating if any improvements happen to the admin interface
		$out[] = '<td class="' . ($this->is_active ? 'tabup' : 'tabdown2');
		$out[] = '" onclick="window.location.href=\'' .$url. '\'">';
		$out[] = '<a href="' .$url. '" class="plain">' .$this->title. '</a></td>';

		return join('', $out);
	}

	function main () {
		// Override this function
	}

	function pref ($key) {
		return @$this->parent->pref($key);
	}

	function redirect ($vars = '') {
		$this->parent->redirect($vars);
	}

	function set_preference ($key, $value, $type = '') {
		return $this->parent->set_preference($key, $value, $type);
	}

	function remove_preference ($key) {
		return $this->parent->remove_preference($key);
	}

	function url ($vars, $gp = false) {
		return $this->parent->url($vars, $gp);
	}

	function form_inputs () {
		return $this->parent->form_inputs();
	}
}

class GBPPreferenceTabView extends GBPAdminTabView {

	var $permissions = 'prefs';

	function __construct(&$parent, $is_default = NULL) {
		// Call the parent constructor
		parent::__construct(gTxt('tab_preferences'), 'preference', $parent, $is_default);
	}

	function preload () {
		if (ps('step') == 'prefs_save') {
			foreach ($this->parent->preferences as $key => $pref) {
				extract($pref);
				$value = ps($key);
				if (is_callable(array(&$this->parent, $type)))
					$value = call_user_func(array(&$this->parent, $type), 'ui_out', $value);
				$this->parent->set_preference($key, $value);
			}
		}
	}

	function main () {
		// Make txp_prefs.php happy :)
		global $event;
		$event = $this->parent->event;

		include_once txpath.'/include/txp_prefs.php';

		echo
			'<form action="index.php" method="post">',
			startTable('list');

		foreach ($this->parent->preferences as $key => $pref) {
			extract($pref);

			$out = tda(gTxt($key), ' style="text-align:right;vertical-align:middle"');

			switch ($type) {
				case 'text_input':
					$out .= td(pref_func('text_input', $key, $value, 20));
				break;
				default:
					if (is_callable(array(&$this->parent, $type)))
						$out .= td(call_user_func(array(&$this->parent, $type), 'ui_in', $value, $key));
					else
						$out .= td(pref_func($type, $key, $value, 50));
				break;
			}

			$out .= tda($this->popHelp($key), ' style="vertical-align:middle"');
			echo tr($out);
		}

		echo
			tr(tda(fInput('submit', 'Submit', gTxt('save_button'), 'publish'), ' colspan="3" class="noline"')),
			endTable(),
			$this->parent->form_inputs(),
			sInput('prefs_save'),
			'</form>';
	}

	function popHelp ($helpvar) {
		$script = hu.basename(txpath).'/index.php';
		return '<a href="'.$script.'?event=plugin&step=plugin_help&name='.$this->parent->plugin_name.'#'.$helpvar.'" class="pophelp">?</a>';
	}
}

class GBPWizardTabView extends GBPAdminTabView {

	var $installation_steps = array();
	var $wiz_report = array();
	var $permissions = 'admin.edit';

	function __construct(&$parent, $is_default = NULL, $title = 'Wizards') {
		global $textarray;

		#
		#	Get the strings and merge into the textarray before we get the steps...
		#
		$strings = $this->get_strings();
		$textarray = array_merge($strings , $textarray);

		#
		#	Now get the steps...
		#
		$this->installation_steps = $this->get_steps();

		// Call the parent constructor
		parent::__construct($title, 'wizard', $parent, $is_default);
	}

	function get_steps () {
		#
		#	Override this method in derived classes to return the appropriate setup/cleanup steps.
		#
		$steps = array(
			'basic' 		=> array('setup' => 'Basic setup step', 'cleanup' => 'Basic cleanup step'),
			'optional'		=> array('setup' => 'Optional setup step', 'cleanup' => 'Optional cleanup step', 'optional' => true , 'checked' => 0),
			'has_options'	=> array('setup' => 'Setup step with a option', 'cleanup' => 'Cleanup step with a option', 'has_options' => true),
		);
		return $steps;
	}

	function get_strings ($language = '') {
		#
		#	Override this function in derived classes to define/change the set of strings to
		# inject into $textarray to localise the wizard.
		#
		$strings = array(
			'gbp_adlib_wiz-version_errors'		=> 'Version Errors',
			'gbp_adlib_wiz-version_reason'		=> 'This plugin cannot operate in this installation because&#8230;',
			'gbp_adlib_wiz-version_item'		=> 'It requires <strong class="failure">{name} {min}</strong> or above, current install is {current}.',
			'gbp_adlib_wiz-setup' 				=> 'Setup',
			'gbp_adlib_wiz-setup_steps' 		=> 'The following setup steps will be taken&#8230;',
			'gbp_adlib_wiz-setup_report'		=> 'Setup Report&#8230;',
			'gbp_adlib_wiz-cleanup' 			=> 'Cleanup',
			'gbp_adlib_wiz-cleanup_steps' 		=> 'The following cleanup steps will be taken&#8230;',
			'gbp_adlib_wiz-cleanup_report'		=> 'Cleanup Report&#8230;',
			'gbp_adlib_wiz-cleanup_next' 		=> 'The plugin can now be disabled and/or uninstalled.',
			'gbp_adlib_wiz-done' 				=> 'Done',
			'gbp_adlib_wiz-skipped'				=> 'Skipped',
			'gbp_adlib_wiz-failed'				=> 'Failure',
			'gbp_adlib_wiz-step_basic'			=> 'Basic Step',
			'gbp_adlib_wiz-step_optional'		=> 'Optional step',
			'gbp_adlib_wiz-step_complex'		=> 'Step with option(s)',
			'gbp_adlib_wiz-step_complex_txt'	=> 'This {step} step has an option/options.',
		);
		return $strings;
	}

	function versions_ok () {
		$msg = '';

		#
		#	Check the plugin can run in this environment.
		#
		#	Return: TRUE -> Yes.
		#	HTML formatted string -> No, and explain why.
		#
		$tests = $this->get_required_versions();
		if (count($tests))
			foreach ($tests as $name => $versions) {
				if( array_key_exists( 'custom_handler' , $versions ) && is_callable( $versions['custom_handler'] ) ) {
					#
					# Allow derived classes to define their own checking routines...
					#
					$fn = $versions['custom_handler'];
					$res = call_user_func( $fn , $name , $versions );
					if( $res )
						$msg[] = tag($res , 'li', ' style="text-align: left; padding-top: 0.75em;"');
				}
				else {
					if (version_compare($versions['current'], $versions['min'] , '<')) {
						$res = gTxt('gbp_adlib_wiz-version_item' , array('{name}' => $name , '{min}' => $versions['min'] , '{current}' => $versions['current']));
						$msg[] = tag($res , 'li', ' style="text-align: left; padding-top: 0.75em;"');
					}
				}
			}

		if (!empty($msg))
			return tag(join('' , $msg) , 'ol');

		return true;
	}

	function get_required_versions () {
		global $prefs;

		#
		#	Override this function to return an array of tests to be carried out.
		#
		$tests = array('TxP' => array(
			'current'	=> $prefs['version'],
			'min'		=> '4.0.3',
		));
		return $tests;
	}

	function main () {
		$out[] = '<style type="text/css"> .success { color: #009900; } .failure { color: #FF0000; } .skipped { color: #0000FF; } </style>';
		$out[] = '<div style="border: 1px solid gray; width: 50em; text-align: center; margin: 1em auto; padding: 1em; clear: both;">';

		$feaildset_style = ' style="text-align: left; padding: 1em 0"';

		$step = gps('step');
		if (empty($step)) {
			$result = $this->versions_ok();
			if (is_string($result))
				$step = 'version_error';
			else
				$step = ($this->installed()) ? 'cleanup-verify' : 'setup-verify';

			$_POST['step'] = $step;
		}

		switch ($step) {
			case 'version_error':
				$out[] = hed(gTxt('gbp_adlib_wiz-version_errors') , 1);
				$out[] = graf(gTxt('gbp_adlib_wiz-version_reason'));
				$out[] = $result;
			break;

			case 'setup-verify':
			// Render the setup wizard initial step...
				$out[] = hed(gTxt('gbp_adlib_wiz-setup') , 1);
				$out[] = graf(gTxt('gbp_adlib_wiz-setup_steps'));
				$out[] = tag(tag($this->wizard_steps('setup') , 'ol') , 'fieldset', $feaildset_style);
				$out[] = fInput('submit', '', gTxt('gbp_adlib_wiz-setup'), '');
				$out[] = $this->form_inputs();
				$out[] = sInput('setup');
			break;

			case 'setup':
			// Render the post-setup screen...
				$out[] = hed(gTxt('gbp_adlib_wiz-setup_report') , 1);
				$out[] = tag($this->wizard_report() , 'fieldset', $feaildset_style);
				$out[] = fInput('submit', '' , gTxt('next') , '');
				$out[] = eInput($this->parent->event);
				$out[] = hInput(gbp_tab, 'preference');
			break;

			case 'cleanup-verify':
			// Render the cleanup wizard initial step...
				$out[] = hed(gTxt('gbp_adlib_wiz-cleanup') , 1);
				$out[] = graf(gTxt('gbp_adlib_wiz-cleanup_steps'));
				$out[] = tag(tag($this->wizard_steps('cleanup') , 'ol') , 'fieldset', $feaildset_style);
				$out[] = fInput('submit', '', gTxt('gbp_adlib_wiz-cleanup'), '');
				$out[] = $this->form_inputs();
				$out[] = sInput('cleanup');
			break;

			case 'cleanup':
			// Render the post-cleanup screen...
				$out[] = hed(gTxt('gbp_adlib_wiz-cleanup_report') , 1);
				$out[] = tag($this->wizard_report() , 'fieldset', $feaildset_style);
				$out[] = graf(gTxt('gbp_adlib_wiz-cleanup_next'));
				$out[] = fInput('submit', '' , gTxt('next') , '');
				$out[] = eInput('plugin');
			break;
		}

		$out[] = '</div>';

		$verify = (in_array($step, array('setup-verify', 'cleanup-verify')))
			? "verify('".doSlash(gTxt('are_you_sure'))."')"
			: '';

		echo form(join(n, $out), '', $verify);
	}

	function installed () {
		return false;
	}

	function wizard_steps ($step) {
		$step_details = '';

		foreach ($this->installation_steps as $key => $detail) {
			if (@$detail[$step]) {
				$options = '';
				if (@$detail['has_options']) {
					$function = array(&$this, 'option_'.$key);
					if (is_callable($function))
						$options = n.tag(call_user_func($function, $step), 'span', ' id="wizard_'.$key.'" style="display: block; margin-right: 1em; padding: 0.5em; background-color: #eee;"');
				}

				$checkbox = '';
				if (@$detail['optional']) {
					$checked = (isset($detail['checked'])) ? $detail['checked'] : 0 ;
					$checkbox = checkbox2('optional_'.$key, $checked, ($options ? '" onclick="toggleDisplay(\'wizard_'.$key.'\');' : ''));
				}

				$step_details .= n.tag(graf(
					tag($detail[$step].$checkbox, 'label').$options
				), 'li');
			}
		}

		return $step_details.n;
	}

	function wizard_report () {
		// Render the wizard report as an ordered list. There maybe
		// 'sub' reports which we need to also render as ordered lists
		$out = array();
		foreach ($this->wiz_report as $report) {
			$out_sub = array();

			// Skip the first element as it is in fact the parent report
			next($report);

			// Lets generate a sub report - if there are more elements
			while (list($key, $report_sub) = each($report))
				$out_sub[] = tag($report_sub , 'li');

			// Check to see if we actually have a sub report - tag it as necessary
			$out_sub = (count($out_sub) > 0)
				? tag(join(n , $out_sub), 'ol')
				: '';

			$out[] = tag($report[0] . $out_sub , 'li');
		}
		return tag(join(n , $out) , 'ol');
	}

	function add_report_item ($string , $ok = NULL, $sub = false) {
		if (isset($ok)) {
			switch ($ok) {
				case '1' :
					$class = 'success';
					$okfail = gTxt('gbp_adlib_wiz-done');
				break;

				default :
				case '0' :
					$class = 'failure';
					$okfail = gTxt('gbp_adlib_wiz-failed');
				break;

				case 'skipped' :
					$class = 'skipped';
					$okfail = gTxt('gbp_adlib_wiz-skipped');
				break;
			}
			$okfail = ' : <span class="'.$class.'">'.tag($okfail, 'strong').'</span>';
		}

		$line = graf($string . (isset($okfail) ? $okfail : ''));

		if ($sub && count($this->wiz_report) > 0)
			$this->wiz_report[count($this->wiz_report) - 1][] = $line;
		else
			$this->wiz_report[] = array($line);
	}

	function setup_basic () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_basic'), true);
	}

	function cleanup_basic () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_basic'), false);
	}

	function setup_optional () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_optional'), true);
	}

	function cleanup_optional () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_optional'), false);
	}

	function setup_has_options () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_complex'), true);
	}

	function cleanup_has_options () {
		$this->add_report_item(gTxt('gbp_adlib_wiz-step_complex'), false);
	}

	function option_has_options ($step) {
		return graf(gTxt('gbp_adlib_wiz-step_complex_txt' , array('{step}' => $step))).yesnoRadio('wizard_has_options_test', 1);
	}
}

Last edited by THE BLUE DRAGON (2023-01-14 13:39:04)

Offline

Board footer

Powered by FluxBB