*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
// Disallow direct access to this file for security reasons
if(!defined("IN_MYBB"))
{
die("Direct initialization of this file is not allowed.
Please make sure IN_MYBB is defined.");
}
/* --- Plugin API: --- */
function pluginlibrary_info()
{
return array(
"name" => "PluginLibrary",
"description" => "A collection of useful functions for other plugins.",
"website" => "http://mods.mybb.com/view/pluginlibrary",
"author" => "Andreas Klauer",
"authorsite" => "mailto:Andreas.Klauer@metamorpher.de",
"version" => "13",
"guid" => "839e9d72e2875a51fccbc0257dfeda03",
"compatibility" => "18*",
"codename" => "pluginlibrary",
);
}
function pluginlibrary_is_installed()
{
// Don't try this at home.
return false;
}
function pluginlibrary_install()
{
// Avoid unnecessary activation as a plugin with a friendly success message.
flash_message("You have successfully installed PluginLibrary 13.", 'success');
admin_redirect("index.php?module=config-plugins");
}
function pluginlibrary_uninstall()
{
}
function pluginlibrary_activate()
{
}
function pluginlibrary_deactivate()
{
}
/* --- PluginLibrary class: --- */
class PluginLibrary
{
/**
* Version number.
*/
public $version = 13;
/**
* Cache handler.
*/
public $cachehandler;
/* --- Setting groups and settings: --- */
/**
* Create and/or update setting group and settings.
*
* @param string Internal unique group name and setting prefix.
* @param string Group title that will be shown to the admin.
* @param string Group description that will show up in the group overview.
* @param array The list of settings to be added to that group.
* @param bool Generate language file. (Developer option, default false)
*/
function settings($name, $title, $description, $list, $makelang=false)
{
global $db;
/* Setting group: */
if($makelang)
{
header("Content-Type: text/plain; charset=UTF-8");
echo " $db->escape_string($name),
'title' => $db->escape_string($title),
'description' => $db->escape_string($description));
// Check if the group already exists.
$query = $db->simple_select("settinggroups", "gid", "name='${group['name']}'");
if($row = $db->fetch_array($query))
{
// We already have a group. Update title and description.
$gid = $row['gid'];
$db->update_query("settinggroups", $group, "gid='{$gid}'");
}
else
{
// We don't have a group. Create one with proper disporder.
$query = $db->simple_select("settinggroups", "MAX(disporder) AS disporder");
$row = $db->fetch_array($query);
$group['disporder'] = $row['disporder'] + 1;
$gid = $db->insert_query("settinggroups", $group);
}
/* Settings: */
// Deprecate all the old entries.
$db->update_query("settings",
array("description" => "PLUGINLIBRARYDELETEMARKER"),
"gid='$gid'");
// Create and/or update settings.
foreach($list as $key => $setting)
{
// Prefix all keys with group name.
$key = "{$name}_{$key}";
if($makelang)
{
echo "\$l['setting_{$key}'] = \"".addcslashes($setting['title'], '\\"$')."\";\n";
echo "\$l['setting_{$key}_desc'] = \"".addcslashes($setting['description'], '\\"$')."\";\n";
}
// Filter valid entries.
$setting = array_intersect_key($setting,
array(
'title' => 0,
'description' => 0,
'optionscode' => 0,
'value' => 0,
));
// Escape input values.
$setting = array_map(array($db, 'escape_string'), $setting);
// Add missing default values.
$disporder += 1;
$setting = array_merge(
array('description' => '',
'optionscode' => 'yesno',
'value' => '0',
'disporder' => $disporder),
$setting);
$setting['name'] = $db->escape_string($key);
$setting['gid'] = $gid;
// Check if the setting already exists.
$query = $db->simple_select('settings', 'sid',
"gid='$gid' AND name='{$setting['name']}'");
if($row = $db->fetch_array($query))
{
// It exists, update it, but keep value intact.
unset($setting['value']);
$db->update_query("settings", $setting, "sid='{$row['sid']}'");
}
else
{
// It doesn't exist, create it.
$db->insert_query("settings", $setting);
}
}
if($makelang)
{
echo "\n?>\n";
exit;
}
// Delete deprecated entries.
$db->delete_query("settings",
"gid='$gid' AND description='PLUGINLIBRARYDELETEMARKER'");
// Rebuild the settings file.
rebuild_settings();
}
/**
* Delete setting groups and settings.
*
* @param string Internal unique group name.
* @param bool Also delete groups starting with name_.
*/
function settings_delete($name, $greedy=false)
{
global $db;
$name = $db->escape_string($name);
$where = "name='{$name}'";
if($greedy)
{
$lname = strtr($name, array('=' => '==', '_' => '=_', '%' => '=%'));
$where .= " OR name LIKE '{$lname}=_%' ESCAPE '='";
}
// Query the setting groups.
$query = $db->simple_select('settinggroups', 'gid', $where);
// Delete the group and all its settings.
while($gid = $db->fetch_field($query, 'gid'))
{
$db->delete_query('settinggroups', "gid='{$gid}'");
$db->delete_query('settings', "gid='{$gid}'");
}
// Rebuild the settings file.
rebuild_settings();
}
/* --- Template groups and templates: --- */
/**
* Create and update template group and templates.
*
* @param string Prefix for the template group
* @param string Title for the template group
* @param array List of templates to be added to this group.
*/
function templates($prefix, $title, $list)
{
global $db;
// Template prefix must not be empty, and must not contain _
if(!strlen($prefix) || strpos($prefix, '_') !== false)
{
trigger_error("Invalid template prefix", E_USER_ERROR);
}
$group = array('prefix' => $db->escape_string($prefix),
'title' => $db->escape_string($title));
// Update or create template group:
$query = $db->simple_select('templategroups', 'prefix', "prefix='{$group['prefix']}'");
if($db->fetch_array($query))
{
$db->update_query('templategroups', $group, "prefix='{$group['prefix']}'");
}
else
{
$db->insert_query('templategroups', $group);
}
// Query already existing templates.
$query = $db->simple_select('templates', 'tid,title,template',
"sid=-2 AND (title='{$group['prefix']}' OR title LIKE '{$group['prefix']}=_%' ESCAPE '=')");
$templates = array();
$duplicates = array();
while($row = $db->fetch_array($query))
{
$title = $row['title'];
if(isset($templates[$title]))
{
// PluginLibrary had a bug that caused duplicated templates.
$duplicates[] = $row['tid'];
$templates[$title]['template'] = false; // force update later
}
else
{
$templates[$title] = $row;
}
}
// Delete duplicated master templates, if they exist.
if($duplicates)
{
$db->delete_query('templates', 'tid IN ('.implode(",", $duplicates).')');
}
// Update or create templates.
foreach($list as $name => $code)
{
if(strlen($name))
{
$name = "{$prefix}_{$name}";
}
else
{
$name = "{$prefix}";
}
$template = array('title' => $db->escape_string($name),
'template' => $db->escape_string($code),
'version' => 1,
'sid' => -2,
'dateline' => TIME_NOW);
// Update
if(isset($templates[$name]))
{
if($templates[$name]['template'] !== $code)
{
// Update version for custom templates if present
$db->update_query('templates', array('version' => 0), "title='{$template['title']}'");
// Update master template
$db->update_query('templates', $template, "tid={$templates[$name]['tid']}");
}
}
// Create
else
{
$db->insert_query('templates', $template);
}
// Remove this template from the earlier queried list.
unset($templates[$name]);
}
// Remove no longer used templates.
foreach($templates as $name => $row)
{
$name = $db->escape_string($name);
$db->delete_query('templates', "title='{$name}'");
}
}
/**
* Delete template group(s) and templates.
*
* @param string Prefix of the template group.
* @param bool Also delete other groups starting with the prefix.
*/
function templates_delete($prefix, $greedy=false)
{
global $db;
$prefix = $db->escape_string($prefix);
$where = "prefix='{$prefix}'";
if($greedy)
{
$where .= " OR prefix LIKE '{$prefix}%'";
}
// Query the template groups
$query = $db->simple_select('templategroups', 'prefix', $where);
// Build where string for templates
$twhere = array();
while($row = $db->fetch_array($query))
{
$tprefix = $db->escape_string($row['prefix']);
$twhere[] = "title='{$tprefix}' OR title LIKE '{$tprefix}=_%' ESCAPE '='";
}
if($twhere) // else there are no groups to delete
{
// Delete template groups.
$db->delete_query('templategroups', $where);
// Delete templates belonging to template groups.
$db->delete_query('templates', implode(' OR ', $twhere));
}
}
/* --- Stylesheets: --- */
/**
* build CSS string out of an [selector => [property => value]] array
*/
function _build_css($styles)
{
if(is_array($styles))
{
$css = "";
foreach($styles as $selector => $properties)
{
$rule = "{$selector} {\n";
if(is_array($properties))
{
foreach($properties as $property => $value)
{
$rule .= "\t{$property}: {$value};\n";
}
}
else
{
$rule .= "\t{$properties}\n";
}
$rule .= "}\n\n";
$css .= $rule;
}
$styles = $css;
}
return $styles;
}
/**
* build attachedto string out of an [file => [action, ]] array
*/
function _build_attachedto($attachedto)
{
if(is_array($attachedto))
{
$result = array();
foreach($attachedto as $file => $actions)
{
if(is_array($actions))
{
$actions = implode(",", $actions);
}
if($actions)
{
$file = "{$file}?{$actions}";
}
$result[] = $file;
}
$attachedto = implode("|", $result);
}
return $attachedto;
}
/**
* Update stylesheet metadata.
*
*/
function _update_themes_stylesheets($stylesheet=false)
{
global $mybb;
$tid = 1; // MyBB Master Style
require_once MYBB_ROOT.$mybb->config['admin_dir'].'/inc/functions_themes.php';
if($stylesheet)
{
cache_stylesheet($stylesheet['tid'], $stylesheet['cachefile'], $stylesheet['stylesheet']);
}
update_theme_stylesheet_list($tid, false, true); // includes all children
}
/**
* Add, update or activate a stylesheet
* @param string Name of the stylesheet - lowercase version used for cache file.
* @param string Stylesheet content.
* @param string The files/actions the stylesheet is attached to. For global attachment, don't include this parameter.
*/
function stylesheet($name, $styles, $attachedto="")
{
global $db;
// Build stylesheet data.
$tid = 1; // MyBB Master Style
if(substr($name, -4) != ".css")
{
$name .= '.css';
}
$styles = $this->_build_css($styles);
$attachedto = $this->_build_attachedto($attachedto);
$stylesheet = array(
'name' => $name,
'tid' => $tid,
'attachedto' => $attachedto,
'stylesheet' => $styles,
'cachefile' => $name,
'lastmodified' => TIME_NOW,
);
$dbstylesheet = array_map(array($db, 'escape_string'), $stylesheet);
// Activate children, if present.
$db->update_query('themestylesheets',
array('attachedto' => $dbstylesheet['attachedto']),
"name='{$dbstylesheet['name']}'");
// Update or insert parent stylesheet.
$query = $db->simple_select('themestylesheets',
'sid',
"tid='{$tid}' AND cachefile='{$name}'");
$sid = intval($db->fetch_field($query, 'sid'));
if($sid)
{
$db->update_query('themestylesheets', $dbstylesheet, "sid='$sid'");
}
else
{
$sid = $db->insert_query('themestylesheets', $dbstylesheet);
$stylesheet['sid'] = intval($sid);
}
$this->_update_themes_stylesheets($stylesheet);
}
/**
* Remove a stylesheet
* @param string Stylesheet name
*/
function stylesheet_delete($name, $greedy=false, $delete=true)
{
global $db;
// Check $name ends in .css and if not append it
$tid = 1; // MyBB Master Style
if(substr($name, -4) == ".css")
{
$name = substr($name, 0, -4);
}
// Query all stylesheets matching $name
$dbname = $db->escape_string($name);
$where = "name='{$dbname}.css'";
if($greedy)
{
$ldbname = strtr($dbname, array('=' => '==', '_' => '=_', '%' => '=%'));
$where .= " OR name LIKE '{$ldbname}=_%.css' ESCAPE '='";
}
// Delete stylesheets.
if($delete)
{
$query = $db->simple_select('themestylesheets', 'tid,name', $where);
while($stylesheet = $db->fetch_array($query))
{
@unlink(MYBB_ROOT."cache/themes/{$stylesheet['tid']}_{$stylesheet['name']}");
@unlink(MYBB_ROOT."cache/themes/theme{$stylesheet['tid']}/{$stylesheet['name']}");
}
$db->delete_query('themestylesheets', $where);
}
else
{
// Deactivate stylesheets.
$db->update_query('themestylesheets',
array('attachedto' => '-'),
$where);
}
$this->_update_themes_stylesheets();
}
/**
* Deactivate stylesheets without deleting them.
*
*/
function stylesheet_deactivate($name, $greedy=false)
{
$this->stylesheet_delete($name, $greedy, false);
}
/* --- Cache: --- */
/**
* Obtain a non-database cache handler.
*/
function _cache_handler()
{
global $cache;
if(is_object($cache->handler))
{
return $cache->handler;
}
if(is_object($this->cachehandler))
{
return $this->cachehandler;
}
// Fall back to disk handler.
require_once MYBB_ROOT.'/inc/cachehandlers/disk.php';
$this->cachehandler = new diskCacheHandler();
return $this->cachehandler;
}
/**
* Read on-demand cache.
*/
function cache_read($name)
{
global $cache;
if(isset($cache->cache[$name]))
{
return $cache->cache[$name];
}
$handler = $this->_cache_handler();
$contents = $handler->fetch($name);
$cache->cache[$name] = $contents;
return $contents;
}
/**
* Write on-demand cache.
*/
function cache_update($name, $contents)
{
global $cache;
$handler = $this->_cache_handler();
$cache->cache[$name] = $contents;
return $handler->put($name, $contents);
}
/**
* Delete cache.
*
* @param string Cache name or title.
* @param bool Also delete caches starting with name_.
*/
function cache_delete($name, $greedy=false)
{
global $db, $cache;
// Prepare for database query.
$dbname = $db->escape_string($name);
$where = "title='{$dbname}'";
// Delete on-demand or handler cache.
$handler = $this->_cache_handler();
$handler->delete($name);
// Greedy?
if($greedy)
{
// Collect possible additional names...
$names = array();
$name .= '_';
// ...from the currently loaded cache...
$keys = array_keys($cache->cache);
foreach($keys as $key)
{
if(strpos($key, $name) === 0)
{
$names[$key] = 0;
}
}
// ...from the database...
$ldbname = strtr($dbname, array('%' => '=%',
'=' => '==',
'_' => '=_'));
$where .= " OR title LIKE '{$ldbname}=_%' ESCAPE '='";
$query = $db->simple_select('datacache', 'title', $where);
while($row = $db->fetch_array($query))
{
$names[$row['title']] = 0;
}
// ...from the filesystem...
$start = strlen(MYBB_ROOT."cache/");
foreach((array)@glob(MYBB_ROOT."cache/{$name}*.php") as $filename)
{
if($filename)
{
$filename = substr($filename, $start, strlen($filename)-4-$start);
$names[$filename] = 0;
}
}
// ...and delete them all.
foreach($names as $key=>$val)
{
$handler->delete($key);
}
}
// Delete database caches too.
$db->delete_query('datacache', $where);
}
/* --- Corefile edits: --- */
/**
* insert comment at the beginning of each line
*/
function _comment($comment, $code)
{
if(is_array($code))
{
$code = implode("\n", $code);
}
if(!is_string($code) || !strlen($code))
{
return "";
}
if(substr($code, -1) == "\n")
{
$code = substr($code, 0, -1);
}
$code = str_replace("\n", "\n{$comment}", "\n{$code}");
return substr($code, 1)."\n";
}
/**
* remove comment at the beginning of each line
*/
function _uncomment($comment, $code)
{
if(!strlen($code))
{
return "";
}
$code = "\n{$code}";
$code = str_replace("\n{$comment}", "\n", $code);
return substr($code, 1);
}
/**
* remove lines with comment at the beginning entirely
*/
function _zapcomment($comment, $code)
{
return preg_replace("#^".preg_quote($comment, "#").".*\n?#m", "", $code);
}
/**
* align start and stop to newline characters in text
*/
function _align($text, &$start, &$stop)
{
// Align start to line boundary.
$nl = strrpos($text, "\n", -strlen($text)+$start);
$start = ($nl === false ? 0 : $nl + 1);
// Align stop to line boundary.
$nl = strpos($text, "\n", $stop);
$stop = ($nl === false ? strlen($text) : $nl + 1);
}
/**
* in text find the smallest first match for a series of search strings
*/
function _match($text, $search, &$start)
{
$stop = $start;
// forward search (determine smallest stop)
foreach($search as $needle)
{
$stop = strpos($text, $needle, $stop);
if($stop === false)
{
// we did not find out needle, so this does not match
return false;
}
$stop += strlen($needle);
}
// backward search (determine largest start)
$start = $stop;
foreach(array_reverse($search) as $needle)
{
$start = strrpos($text, $needle, -strlen($text)+$start-strlen($needle));
}
return $stop;
}
/**
* dissect text based on a series of edits
*/
function _dissect($text, &$edits)
{
$matches = array();
foreach($edits as &$edit)
{
$search = (array)$edit['search'];
$start = 0;
$edit['matches'] = array();
while(($stop = $this->_match($text, $search, $start)) !== false)
{
$pos = $stop;
$this->_align($text, $start, $stop);
// to count the matches, and help debugging
$edit['matches'][] = array($start, $stop,
substr($text, $start, $stop-$start));
if(isset($matches[$start]))
{
$matches[$start][1]['error'] = 'match collides with another edit';
$edit['error'] = 'match collides with another edit';
return false;
}
else if(count($edit['matches']) > 1 && !$edit['multi'])
{
$edit['error'] = 'multiple matches not allowed for this edit';
return false;
}
$matches[$start] = array($stop, &$edit);
$start = $pos;
}
if(!count($edit['matches']) && !$edit['none'])
{
$edit['error'] = 'zero matches not allowed for this edit';
return false;
}
}
ksort($matches);
return $matches;
}
/**
* edit text (perform the actual string modification)
*/
function _edit($text, &$edits, $ins='/**/', $del='/*/*')
{
$matches = $this->_dissect($text, $edits);
if($matches === false)
{
return false;
}
$result = array();
$pos = 0;
foreach($matches as $start => $val)
{
$stop = $val[0];
$edit = &$val[1];
if($start < $pos)
{
$edit['error'] = 'match overlaps with another edit';
$previous_edit['error'] = 'match overlaps with another edit';
return false;
}
// Keep previous edit for overlapping detection
$previous_edit = &$edit;
// unmodified text before match
$result[] = substr($text, $pos, $start-$pos);
// insert before
$result[] = $this->_comment($ins, $edit['before']);
// original matched text
$match = substr($text, $start, $stop-$start);
$pos = $stop;
$dirty = 0;
if($edit['replace']
|| is_string($edit['replace']) || is_array($edit['replace']))
{
// insert match (commented out)
$result[] = $this->_comment($del, $match);
$result[] = $this->_comment($ins, $edit['replace']);
if(!strlen($result[count($result)-1]))
{
$dirty = 1; // still a comment open
}
}
else
{
// insert match unmodified
$result[] = $match;
}
// insert after
$result[] = $this->_comment($ins, $edit['after']);
if($dirty && !strlen($result[count($result)-1]))
{
// close open comment
$result[] = "{$ins}\n";
}
}
// insert rest
$result[] = substr($text, $pos);
return implode("", $result);
}
/**
* edit core
*/
function edit_core($name, $file, $edits=array(), $apply=false, &$debug=null)
{
$ins = "/* + PL:{$name} + */ ";
$del = "/* - PL:{$name} - /* ";
$text = file_get_contents(MYBB_ROOT.$file);
$result = $text;
if($text === false)
{
return false;
}
// Convert single edit into array of edits.
if(array_key_exists('search', $edits))
{
$edits = array($edits);
}
// Step 1: remove old comments, if present.
$result = $this->_zapcomment($ins, $result);
$result = $this->_uncomment($del, $result);
// Step 2: prevent colliding edits by adding conditions.
$edits[] = array('search' => array('/* + PL:'),
'multi' => true,
'none' => true);
$edits[] = array('search' => array('/* - PL:'),
'multi' => true,
'none' => true);
// Step 3: perform edits.
$result = $this->_edit($result, $edits, $ins, $del);
// call_time_pass_reference :-(
$debug = $edits;
if($result === false)
{
// edits couldn't be performed
return false;
}
if($result == $text)
{
// edit made no changes
return true;
}
// try to write the file
if($apply && @file_put_contents(MYBB_ROOT.$file, $result) !== false)
{
// changes successfully applied
return true;
}
// return the string
return $result;
}
/* --- Group memberships: --- */
/**
* is_member
*/
function is_member($groups, $user=false)
{
global $mybb;
// Default to current user.
if($user === false)
{
$user = $mybb->user;
}
else if(is_array($user))
{
// do nothing
}
else
{
// assume it's a UID
$user = get_user($user);
}
// Collect the groups the user is in.
$memberships = explode(',', $user['additionalgroups']);
$memberships[] = $user['usergroup'];
// Convert search to an array of group ids
if(is_array($groups))
{
// already an array, do nothing
}
else if(is_string($groups))
{
$groups = explode(',', $groups);
}
else
{
// probably a single number
$groups = (array)$groups;
}
// Make sure we're comparing numbers.
$groups = array_map('intval', $groups);
$memberships = array_map('intval', $memberships);
// Remove 0 if present.
$groups = array_filter($groups);
// Return the group intersection.
return array_intersect($groups, $memberships);
}
/* --- String functions: --- */
/**
* url_append
*/
function url_append($url, $params, $sep="&", $encode=true)
{
if(strpos($url, '?') === false)
{
$separator = '?';
}
else
{
$separator = $sep;
}
$append = '';
foreach($params as $key => $value)
{
if($encode)
{
$value = urlencode($value);
}
$append .= "{$separator}{$key}={$value}";
$separator = $sep;
}
$pos = strpos($url, '#');
if($pos === false)
{
$pos = strlen($url);
}
return substr_replace($url, $append, $pos, 0);
}
/**
* _xml_element
*/
function _xml_tag($tag, $content, $indent=0)
{
$nl = "\n" . str_repeat(' ', $indent);
$result = '';
if(is_string($content))
{
// We can either htmlspecialchars,
$a = htmlspecialchars($content);
// or cdata (properly escaped),
$b = '', ']]]]>', $content)
.']]>';
// just pick whatever is shorter
$content = (strlen($a) < strlen($b) ? $a : $b);
$result .= "{$nl}<{$tag}>{$content}{$tag}>";
}
else if(is_bool($content))
{
$result .= "{$nl}<{$tag} type=\"BOOL\">{$content}{$tag}>";
}
else if(is_int($content))
{
$result .= "{$nl}<{$tag} type=\"INT\">{$content}{$tag}>";
}
else if(is_float($content))
{
$result .= "{$nl}<{$tag} type=\"FLOAT\">{$content}{$tag}>";
}
else if(is_array($content))
{
$result .= "{$nl}<{$tag}>".$this->_xml_array($content, $indent+2)."{$nl}{$tag}>";
}
return $result;
}
/**
* _xml_array
*/
function _xml_array($array, $indent=0)
{
$nl = "\n".str_repeat(' ', $indent);
$nl2 = $nl.' ';
$result = '';
foreach($array as $key => $value)
{
$key = $this->_xml_tag('key', $key, $indent+4);
if($key)
{
$value = $this->_xml_tag('value', $value, $indent+4);
if($value)
{
$result .= "{$nl2}{$key}{$value}{$nl2}";
}
}
}
return "{$nl}{$result}{$nl}";
}
/**
* xml_export
*/
function xml_export($data, $filename=false, $comment='MyBB PluginLibrary XML-Export :: {time}', $endcomment='End of file.')
{
$result = '';
if(is_array($data))
{
$xml = $this->_xml_array($data);
}
else
{
$xml = $this->_xml_tag('value', $data);
}
if($xml)
{
$result = "\n";
$time = date('c', TIME_NOW);
if($comment)
{
$comment = str_replace('{time}', $time, $comment);
$result .= "";
}
$result .= $xml."\n";
if($endcomment)
{
$endcomment = str_replace('{time}', $time, $endcomment);
$result .= "\n";
}
if($filename)
{
// Filename encoding sucks.
$filename = trim(basename('/'.$filename));
// Output the XML directly.
@header('Content-Type: application/xml; charset=UTF-8');
@header('Expires: Sun, 20 Feb 2011 13:47:47 GMT'); // past
@header('Last-Modified: '.gmdate('D, d M Y H:i:s T'));
@header('Pragma: no-cache');
@header('Content-Disposition: attachment; filename="'.$filename.'"');
@header('Content-Length: '.strlen($result));
echo $result;
exit;
}
}
return $result;
}
/**
* xml_import
*/
function xml_import($xml, &$error=null)
{
$parser = xml_parser_create();
$stack = array();
if(xml_parse_into_struct($parser, $xml, $values))
{
foreach($values as $value)
{
// Convert data
switch(strtoupper($value['attributes']['TYPE']))
{
case 'BOOL':
$value['value'] = $value['value'] && true;
break;
case 'INT':
$value['value'] = intval($value['value']);
break;
case 'FLOAT':
$value['value'] = floatval($value['value']);
break;
default:
// Assume string. Mainly for NULL => ''.
$value['value'] = strval($value['value']);
break;
}
$input = strtolower("{$value['tag']}-{$value['type']}");
// Parse XML element (sloppy)
switch($input)
{
case 'array-complete':
case 'array-open':
// Put array on stack
array_unshift($stack, array());
break;
case 'element-close':
// Put key, value in array
// Remove value, key from stack
$stack[2][$stack[1]] = $stack[0];
array_shift($stack);
array_shift($stack);
break;
case 'element-open':
// Put key, value on stack
array_unshift($stack, null, null);
break;
case 'key-complete':
// Set key
$stack[1] = $value['value'];
break;
case 'value-complete':
// Set value
$stack[0] = $value['value'];
break;
case 'value-open':
// Remove value from stack (new array should follow)
array_shift($stack);
break;
default:
// Ignore others.
break;
}
// If there is something wrong, quit early.
if(!sizeof($stack))
{
break;
}
}
// The stack should contain a single value now
if(sizeof($stack) == 1)
{
$result = $stack[0];
}
else
{
$error = array('line' => -1,
'code' => -1,
'error' => -1,
'message' => 'XML is valid, but there is no data to import.');
}
}
else
{
// collect error information for debugging purposes
$lines = explode("\n", $xml);
$error = array('line' => xml_get_current_line_number($parser),
'code' => $lines[xml_get_current_line_number($parser)-1],
'error' => xml_get_error_code($parser),
'message' => xml_error_string(xml_get_error_code($parser)));
}
xml_parser_free($parser);
return $result;
}
}
global $PL;
$PL = new PluginLibrary();
/* --- End of file. --- */