Initial commit

Signed-off-by: Luke Tainton <luke@tainton.uk>
This commit is contained in:
Luke Tainton
2020-02-26 12:23:50 +00:00
commit 39782c53ef
500 changed files with 141257 additions and 0 deletions

View File

@@ -0,0 +1,913 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// Possible fields to be displayed in ticket list
$hesk_settings['possible_ticket_list'] = array(
'id' => $hesklang['id'],
'trackid' => $hesklang['trackID'],
'dt' => $hesklang['submitted'],
'lastchange' => $hesklang['last_update'],
'category' => $hesklang['category'],
'name' => $hesklang['name'],
'email' => $hesklang['email'],
'subject' => $hesklang['subject'],
'status' => $hesklang['status'],
'owner' => $hesklang['owner'],
'replies' => $hesklang['replies'],
'staffreplies' => $hesklang['replies'] . ' (' . $hesklang['staff'] .')',
'lastreplier' => $hesklang['last_replier'],
'time_worked' => $hesklang['ts'],
);
define('HESK_NO_ROBOTS', true);
/*** FUNCTIONS ***/
function hesk_show_column($column)
{
global $hesk_settings;
return in_array($column, $hesk_settings['ticket_list']) ? true : false;
} // END hesk_show_column()
function hesk_getHHMMSS($in)
{
$in = hesk_getTime($in);
return explode(':', $in);
} // END hesk_getHHMMSS();
function hesk_getTime($in)
{
$in = trim($in);
/* If everything is OK this simple check should return true */
if ( preg_match('/^([0-9]{2,3}):([0-5][0-9]):([0-5][0-9])$/', $in) )
{
return $in;
}
/* No joy, let's try to figure out the correct values to use... */
$h = 0;
$m = 0;
$s = 0;
/* How many parts do we have? */
$parts = substr_count($in, ':');
switch ($parts)
{
/* Only two parts, let's assume minutes and seconds */
case 1:
list($m, $s) = explode(':', $in);
break;
/* Three parts, so explode to hours, minutes and seconds */
case 2:
list($h, $m, $s) = explode(':', $in);
break;
/* Something other was entered, let's assume just minutes */
default:
$m = $in;
}
/* Make sure all inputs are integers */
$h = intval($h);
$m = intval($m);
$s = intval($s);
/* Convert seconds to minutes if 60 or more seconds */
if ($s > 59)
{
$m = floor($s / 60) + $m;
$s = intval($s % 60);
}
/* Convert minutes to hours if 60 or more minutes */
if ($m > 59)
{
$h = floor($m / 60) + $h;
$m = intval($m % 60);
}
/* MySQL accepts max time value of 838:59:59 */
if ($h > 838)
{
return '838:59:59';
}
/* That's it, let's send out formatted time string */
return str_pad($h, 2, "0", STR_PAD_LEFT) . ':' . str_pad($m, 2, "0", STR_PAD_LEFT) . ':' . str_pad($s, 2, "0", STR_PAD_LEFT);
} // END hesk_getTime();
function hesk_mergeTickets($merge_these, $merge_into)
{
global $hesk_settings, $hesklang, $hesk_db_link;
/* Target ticket must not be in the "merge these" list */
if ( in_array($merge_into, $merge_these) )
{
$merge_these = array_diff($merge_these, array( $merge_into ) );
}
/* At least 1 ticket needs to be merged with target ticket */
if ( count($merge_these) < 1 )
{
$_SESSION['error'] = $hesklang['merr1'];
return false;
}
/* Make sure target ticket exists */
$res = hesk_dbQuery("SELECT `id`,`trackid`,`category` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($merge_into)."' LIMIT 1");
if (hesk_dbNumRows($res) != 1)
{
$_SESSION['error'] = $hesklang['merr2'];
return false;
}
$ticket = hesk_dbFetchAssoc($res);
/* Make sure user has access to ticket category */
if ( ! hesk_okCategory($ticket['category'], 0) )
{
$_SESSION['error'] = $hesklang['merr3'];
return false;
}
/* Set some variables for later */
$merge['attachments'] = '';
$merge['replies'] = array();
$merge['notes'] = array();
$sec_worked = 0;
$history = '';
$merged = '';
/* Get messages, replies, notes and attachments of tickets that will be merged */
foreach ($merge_these as $this_id)
{
/* Validate ID */
if ( is_array($this_id) )
{
continue;
}
$this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']);
/* Get required ticket information */
$res = hesk_dbQuery("SELECT `id`,`trackid`,`category`,`name`,`message`,`dt`,`time_worked`,`attachments` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($this_id)."' LIMIT 1");
if (hesk_dbNumRows($res) != 1)
{
continue;
}
$row = hesk_dbFetchAssoc($res);
/* Has this user access to the ticket category? */
if ( ! hesk_okCategory($row['category'], 0) )
{
continue;
}
/* Insert ticket message as a new reply to target ticket */
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`name`,`message`,`dt`,`attachments`) VALUES ('".intval($ticket['id'])."','".hesk_dbEscape($row['name'])."','".hesk_dbEscape($row['message'])."','".hesk_dbEscape($row['dt'])."','".hesk_dbEscape($row['attachments'])."')");
/* Update attachments */
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` SET `ticket_id`='".hesk_dbEscape($ticket['trackid'])."' WHERE `ticket_id`='".hesk_dbEscape($row['trackid'])."'");
/* Get old ticket replies and insert them as new replies */
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."' ORDER BY `id` ASC");
while ( $reply = hesk_dbFetchAssoc($res) )
{
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`rating`,`read`) VALUES ('".intval($ticket['id'])."','".hesk_dbEscape($reply['name'])."','".hesk_dbEscape($reply['message'])."','".hesk_dbEscape($reply['dt'])."','".hesk_dbEscape($reply['attachments'])."','".intval($reply['staffid'])."','".intval($reply['rating'])."','".intval($reply['read'])."')");
}
/* Delete replies to the old ticket */
hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."'");
/* Get old ticket notes and insert them as new notes */
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."' ORDER BY `id` ASC");
while ( $note = hesk_dbFetchAssoc($res) )
{
hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` (`ticket`,`who`,`dt`,`message`,`attachments`) VALUES ('".intval($ticket['id'])."','".intval($note['who'])."','".hesk_dbEscape($note['dt'])."','".hesk_dbEscape($note['message'])."','".hesk_dbEscape($note['attachments'])."')");
}
/* Delete replies to the old ticket */
hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."'");
/* Delete old ticket */
hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($row['id'])."'");
/* Log that ticket has been merged */
$history .= sprintf($hesklang['thist13'],hesk_date(),$row['trackid'],$_SESSION['name'].' ('.$_SESSION['user'].')');
/* Add old ticket ID to target ticket "merged" field */
$merged .= '#' . $row['trackid'];
/* Convert old ticket "time worked" to seconds and add to $sec_worked variable */
list ($hr, $min, $sec) = explode(':', $row['time_worked']);
$sec_worked += (((int)$hr) * 3600) + (((int)$min) * 60) + ((int)$sec);
}
/* Convert seconds to HHH:MM:SS */
$sec_worked = hesk_getTime('0:'.$sec_worked);
// Get number of replies
$total = 0;
$staffreplies = 0;
$res = hesk_dbQuery("SELECT COUNT(*) as `cnt`, (CASE WHEN `staffid` = 0 THEN 0 ELSE 1 END) AS `staffcnt` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`=".intval($ticket['id'])." GROUP BY `staffcnt`");
while ( $row = hesk_dbFetchAssoc($res) )
{
$total += $row['cnt'];
$staffreplies += ($row['staffcnt'] ? $row['cnt'] : 0);
}
$replies_sql = " `replies`={$total}, `staffreplies`={$staffreplies} , ";
// Get first staff reply
if ($staffreplies)
{
$res = hesk_dbQuery("SELECT `dt`, `staffid` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`=".intval($ticket['id'])." AND `staffid`>0 ORDER BY `dt` ASC LIMIT 1");
$reply = hesk_dbFetchAssoc($res);
$replies_sql .= " `firstreply`='".hesk_dbEscape($reply['dt'])."', `firstreplyby`=".intval($reply['staffid'])." , ";
}
/* Update history (log) and merged IDs of target ticket */
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET $replies_sql `time_worked`=ADDTIME(`time_worked`, '".hesk_dbEscape($sec_worked)."'), `merged`=CONCAT(`merged`,'".hesk_dbEscape($merged . '#')."'), `history`=CONCAT(`history`,'".hesk_dbEscape($history)."') WHERE `id`='".intval($merge_into)."'");
return true;
} // END hesk_mergeTickets()
function hesk_updateStaffDefaults()
{
global $hesk_settings, $hesklang;
// Demo mode
if ( defined('HESK_DEMO') )
{
return true;
}
// Remove the part that forces saving as default - we don't need it every time
$default_list = str_replace('&def=1','',$_SERVER['QUERY_STRING']);
// Update database
$res = hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `default_list`='".hesk_dbEscape($default_list)."' WHERE `id`='".intval($_SESSION['id'])."'");
// Update session values so the changes take effect immediately
$_SESSION['default_list'] = $default_list;
return true;
} // END hesk_updateStaffDefaults()
function hesk_makeJsString($in)
{
return addslashes(preg_replace("/\s+/",' ',$in));
} // END hesk_makeJsString()
function hesk_checkNewMail()
{
global $hesk_settings, $hesklang;
$res = hesk_dbQuery("SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` WHERE `to`='".intval($_SESSION['id'])."' AND `read`='0' AND `deletedby`!='".intval($_SESSION['id'])."' ");
$num = hesk_dbResult($res,0,0);
return $num;
} // END hesk_checkNewMail()
function hesk_dateToString($dt, $returnName=1, $returnTime=0, $returnMonth=0, $from_database=false)
{
global $hesk_settings, $hesklang;
$dt = strtotime($dt);
// Adjust MySQL time if different from PHP time
if ($from_database)
{
if ( ! defined('MYSQL_TIME_DIFF') )
{
define('MYSQL_TIME_DIFF', time()-hesk_dbTime() );
}
if (MYSQL_TIME_DIFF != 0)
{
$dt += MYSQL_TIME_DIFF;
}
}
list($y,$m,$n,$d,$G,$i,$s) = explode('-', date('Y-n-j-w-G-i-s', $dt) );
$m = $hesklang['m'.$m];
$d = $hesklang['d'.$d];
if ($returnName)
{
return "$d, $m $n, $y";
}
if ($returnTime)
{
return "$d, $m $n, $y $G:$i:$s";
}
if ($returnMonth)
{
return "$m $y";
}
return "$m $n, $y";
} // End hesk_dateToString()
function hesk_getCategoriesArray($kb = 0) {
global $hesk_settings, $hesklang, $hesk_db_link;
$categories = array();
if ($kb)
{
$result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_categories` ORDER BY `cat_order` ASC');
}
else
{
$result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` ORDER BY `cat_order` ASC');
}
while ($row=hesk_dbFetchAssoc($result))
{
$categories[$row['id']] = $row['name'];
}
return $categories;
} // END hesk_getCategoriesArray()
function hesk_getHTML($in)
{
global $hesk_settings, $hesklang;
$replace_from = array("\t","<?","?>","$","<%","%>");
$replace_to = array("","&lt;?","?&gt;","\$","&lt;%","%&gt;");
$in = trim($in);
$in = str_replace($replace_from,$replace_to,$in);
$in = preg_replace('/\<script(.*)\>(.*)\<\/script\>/Uis',"<script$1></script>",$in);
$in = preg_replace('/\<\!\-\-(.*)\-\-\>/Uis',"<!-- comments have been removed -->",$in);
if (HESK_SLASH === true)
{
$in = addslashes($in);
}
$in = str_replace('\"','"',$in);
return $in;
} // END hesk_getHTML()
function hesk_activeSessionValidate($username, $password_hash, $tag)
{
// Salt and hash need to be separated by a |
if ( ! strpos($tag, '|') )
{
return false;
}
// Get two parts of the tag
list($salt, $hash) = explode('|', $tag, 2);
// Make sure the hash matches existing username and password
if ($hash == sha1($salt . hesk_mb_strtolower($username) . $password_hash) )
{
return true;
}
return false;
} // hesk_activeSessionValidate
function hesk_activeSessionCreateTag($username, $password_hash)
{
$salt = uniqid(mt_rand(), true);
return $salt . '|' . sha1($salt . hesk_mb_strtolower($username) . $password_hash);
} // END hesk_activeSessionCreateTag()
function hesk_autoLogin($noredirect=0)
{
global $hesk_settings, $hesklang, $hesk_db_link;
if (!$hesk_settings['autologin'])
{
return false;
}
$user = hesk_htmlspecialchars( hesk_COOKIE('hesk_username') );
$hash = hesk_htmlspecialchars( hesk_COOKIE('hesk_p') );
define('HESK_USER', $user);
if (empty($user) || empty($hash))
{
return false;
}
/* Login cookies exist, now lets limit brute force attempts */
hesk_limitBfAttempts();
// Admin login URL
$url = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/index.php?a=login&notice=1';
/* Check username */
$result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."users` WHERE `user` = '".hesk_dbEscape($user)."' LIMIT 1");
if (hesk_dbNumRows($result) != 1)
{
hesk_setcookie('hesk_username', '');
hesk_setcookie('hesk_p', '');
header('Location: '.$url);
exit();
}
$res=hesk_dbFetchAssoc($result);
/* Check password */
if ($hash != hesk_Pass2Hash($res['pass'] . hesk_mb_strtolower($user) . $res['pass']) )
{
hesk_setcookie('hesk_username', '');
hesk_setcookie('hesk_p', '');
header('Location: '.$url);
exit();
}
// Set user details
foreach ($res as $k=>$v)
{
$_SESSION[$k]=$v;
}
/* Check if default password */
if ($_SESSION['pass'] == '499d74967b28a841c98bb4baaabaad699ff3c079')
{
hesk_process_messages($hesklang['chdp'],'NOREDIRECT','NOTICE');
}
// Set a tag that will be used to expire sessions after username or password change
$_SESSION['session_verify'] = hesk_activeSessionCreateTag($user, $_SESSION['pass']);
// We don't need the password hash anymore
unset($_SESSION['pass']);
/* Login successful, clean brute force attempts */
hesk_cleanBfAttempts();
/* Regenerate session ID (security) */
hesk_session_regenerate_id();
/* Get allowed categories */
if (empty($_SESSION['isadmin']))
{
$_SESSION['categories']=explode(',',$_SESSION['categories']);
}
/* Renew cookies */
hesk_setcookie('hesk_username', "$user", strtotime('+1 year'));
hesk_setcookie('hesk_p', "$hash", strtotime('+1 year'));
/* Close any old tickets here so Cron jobs aren't necessary */
if ($hesk_settings['autoclose'])
{
$revision = sprintf($hesklang['thist3'],hesk_date(),$hesklang['auto']);
$dt = date('Y-m-d H:i:s',time() - $hesk_settings['autoclose']*86400);
// Notify customer of closed ticket?
if ($hesk_settings['notify_closed'])
{
// Get list of tickets
$result = hesk_dbQuery("SELECT * FROM `".$hesk_settings['db_pfix']."tickets` WHERE `status` = '2' AND `lastchange` <= '".hesk_dbEscape($dt)."' ");
if (hesk_dbNumRows($result) > 0)
{
global $ticket;
// Load required functions?
if ( ! function_exists('hesk_notifyCustomer') )
{
require(HESK_PATH . 'inc/email_functions.inc.php');
}
while ($ticket = hesk_dbFetchAssoc($result))
{
$ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$ticket = hesk_ticketToPlain($ticket, 1, 0);
hesk_notifyCustomer('ticket_closed');
}
}
}
// Update ticket statuses and history in database
hesk_dbQuery("UPDATE `".$hesk_settings['db_pfix']."tickets` SET `status`='3', `closedat`=NOW(), `closedby`='-1', `history`=CONCAT(`history`,'".hesk_dbEscape($revision)."') WHERE `status` = '2' AND `lastchange` <= '".hesk_dbEscape($dt)."' ");
}
/* If session expired while a HESK page is open just continue using it, don't redirect */
if ($noredirect)
{
return true;
}
/* Redirect to the destination page */
header('Location: ' . hesk_verifyGoto() );
exit();
} // END hesk_autoLogin()
function hesk_isLoggedIn()
{
global $hesk_settings;
$referer = hesk_input($_SERVER['REQUEST_URI']);
$referer = str_replace('&amp;','&',$referer);
// Admin login URL
$url = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/index.php?a=login&notice=1&goto='.urlencode($referer);
if ( empty($_SESSION['id']) || empty($_SESSION['session_verify']) )
{
if ($hesk_settings['autologin'] && hesk_autoLogin(1) )
{
// Users online
if ($hesk_settings['online'])
{
require(HESK_PATH . 'inc/users_online.inc.php');
hesk_initOnline($_SESSION['id']);
}
return true;
}
hesk_session_stop();
header('Location: '.$url);
exit();
}
else
{
hesk_session_regenerate_id();
// Let's make sure access data is up-to-date
$res = hesk_dbQuery( "SELECT `user`, `pass`, `isadmin`, `categories`, `heskprivileges` FROM `".$hesk_settings['db_pfix']."users` WHERE `id` = '".intval($_SESSION['id'])."' LIMIT 1" );
// Exit if user not found
if (hesk_dbNumRows($res) != 1)
{
hesk_session_stop();
header('Location: '.$url);
exit();
}
// Fetch results from database
$me = hesk_dbFetchAssoc($res);
// Verify this session is still valid
if ( ! hesk_activeSessionValidate($me['user'], $me['pass'], $_SESSION['session_verify']) )
{
hesk_session_stop();
header('Location: '.$url);
exit();
}
// Update session variables as needed
if ($me['isadmin'] == 1)
{
$_SESSION['isadmin'] = 1;
}
else
{
$_SESSION['isadmin'] = 0;
$_SESSION['categories'] = explode(',', $me['categories']);
$_SESSION['heskprivileges'] = $me['heskprivileges'];
}
// Users online
if ($hesk_settings['online'])
{
require(HESK_PATH . 'inc/users_online.inc.php');
hesk_initOnline($_SESSION['id']);
}
return true;
}
} // END hesk_isLoggedIn()
function hesk_verifyGoto()
{
// Default redirect URL
$url_default = 'admin_main.php';
// If no "goto" parameter is set, redirect to the default page
if ( ! hesk_isREQUEST('goto') )
{
return $url_default;
}
// Get the "goto" parameter
$url = hesk_REQUEST('goto');
// Fix encoded "&"
$url = str_replace('&amp;', '&', $url);
// Parse the URL for verification
$url_parts = parse_url($url);
// The "path" part is required
if ( ! isset($url_parts['path']) )
{
return $url_default;
}
// Extract the file name from path
$url = basename($url_parts['path']);
// Allowed files for redirect
$OK_urls = array(
'admin_main.php' => '',
'admin_settings_general.php' => '',
'admin_settings_help_desk.php' => '',
'admin_settings_knowledgebase.php' => '',
'admin_settings_email.php' => '',
'admin_settings_ticket_list.php' => '',
'admin_settings_misc.php' => '',
'admin_settings_save.php' => 'admin_settings_general.php',
'admin_ticket.php' => '',
'archive.php' => '',
'assign_owner.php' => '',
'banned_emails.php' => '',
'banned_ips.php' => '',
'change_status.php' => '',
'custom_fields.php' => '',
'custom_statuses.php' => '',
'edit_post.php' => '',
'email_templates.php' => '',
'export.php' => '',
'find_tickets.php' => '',
'generate_spam_question.php' => '',
'knowledgebase_private.php' => '',
'lock.php' => '',
'mail.php' => '',
'mail.php?a=read&id=1' => '',
'manage_canned.php' => '',
'manage_categories.php' => '',
'manage_knowledgebase.php' => '',
'manage_ticket_templates.php' => '',
'manage_users.php' => '',
'new_ticket.php' => '',
'profile.php' => '',
'reports.php' => '',
'service_messages.php' => '',
'show_tickets.php' => '',
);
// URL must match one of the allowed ones
if ( ! isset($OK_urls[$url]) )
{
return $url_default;
}
// Modify redirect?
if ( strlen($OK_urls[$url]) )
{
$url = $OK_urls[$url];
}
// All OK, return the URL with query if set
return isset($url_parts['query']) ? $url.'?'.$url_parts['query'] : $url;
} // END hesk_verifyGoto()
function hesk_Pass2Hash($plaintext) {
$majorsalt = '';
$len = strlen($plaintext);
for ($i=0;$i<$len;$i++)
{
$majorsalt .= sha1(substr($plaintext,$i,1));
}
$corehash = sha1($majorsalt);
return $corehash;
} // END hesk_Pass2Hash()
function hesk_formatDate($dt, $from_database=true)
{
$dt=hesk_date($dt, $from_database);
$dt=str_replace(' ','<br />',$dt);
return $dt;
} // End hesk_formatDate()
function hesk_jsString($str)
{
$str = addslashes($str);
$str = str_replace('<br />' , '' , $str);
$from = array("/\r\n|\n|\r/", '/\<a href="mailto\:([^"]*)"\>([^\<]*)\<\/a\>/i', '/\<a href="([^"]*)" target="_blank"\>([^\<]*)\<\/a\>/i');
$to = array("\\r\\n' + \r\n'", "$1", "$1");
return preg_replace($from,$to,$str);
} // END hesk_jsString()
function hesk_myOwnership()
{
if ( ! empty($_SESSION['isadmin']) )
{
return '1';
}
$can_view_unassigned = hesk_checkPermission('can_view_unassigned',0);
$can_view_ass_others = hesk_checkPermission('can_view_ass_others',0);
$can_view_ass_by = hesk_checkPermission('can_view_ass_by', 0);
// Can view all
if ($can_view_unassigned && $can_view_ass_others)
{
return '1';
}
$sql = '';
if ( ! $can_view_unassigned && ! $can_view_ass_others)
{
$sql .= "`owner`=" . intval($_SESSION['id']);
}
elseif ( ! $can_view_unassigned)
{
$sql .= "`owner` != 0 ";
}
elseif ( ! $can_view_ass_others)
{
$sql .= "`owner` IN (0, " . intval($_SESSION['id']) . ") ";
}
// Include tickets he/she assigned to others?
if ($can_view_ass_by)
{
return "(" . $sql . " OR `assignedby`=" . intval($_SESSION['id']) . ")";
}
return $sql;
} // END hesk_myOwnership()
function hesk_myCategories($what='category')
{
if ( ! empty($_SESSION['isadmin']) )
{
return '1';
}
else
{
return " `".hesk_dbEscape($what)."` IN ('" . implode("','", array_map('intval', $_SESSION['categories']) ) . "')";
}
} // END hesk_myCategories()
function hesk_okCategory($cat,$error=1,$user_isadmin=false,$user_cat=false)
{
global $hesklang;
/* Checking for current user or someone else? */
if ($user_isadmin === false)
{
$user_isadmin = $_SESSION['isadmin'];
}
if ($user_cat === false)
{
$user_cat = $_SESSION['categories'];
}
/* Is admin? */
if ($user_isadmin)
{
return true;
}
/* Staff with access? */
elseif (in_array($cat,$user_cat))
{
return true;
}
/* No access */
else
{
if ($error)
{
hesk_error($hesklang['not_authorized_tickets']);
}
else
{
return false;
}
}
} // END hesk_okCategory()
function hesk_checkPermission($feature,$showerror=1) {
global $hesklang;
/* Admins have full access to all features */
if ( isset($_SESSION['isadmin']) && $_SESSION['isadmin'])
{
return true;
}
/* Check other staff for permissions */
if ( isset($_SESSION['heskprivileges']) && strpos($_SESSION['heskprivileges'], $feature) === false)
{
if ($showerror)
{
hesk_error($hesklang['no_permission'].'<p>&nbsp;</p><p align="center"><a href="index.php">'.$hesklang['click_login'].'</a>');
}
else
{
return false;
}
}
else
{
return true;
}
} // END hesk_checkPermission()
function hesk_purge_cache($type = '', $expire_after_seconds = 0)
{
global $hesk_settings;
$cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';
if ( ! is_dir($cache_dir))
{
return false;
}
switch ($type)
{
case 'export':
$files = glob($cache_dir.'hesk_export_*', GLOB_NOSORT);
break;
case 'status':
$files = glob($cache_dir.'status_*', GLOB_NOSORT);
break;
case 'cf':
$files = glob($cache_dir.'cf_*', GLOB_NOSORT);
break;
case 'kb':
$files = array($cache_dir.'kb.cache.php');
break;
default:
hesk_rrmdir(trim($cache_dir, '/'), true);
return true;
}
if (is_array($files))
{
array_walk($files, 'hesk_unlink_callable', $expire_after_seconds);
}
return true;
} // END hesk_purge_cache()
function hesk_rrmdir($dir, $keep_top_level=false)
{
$files = $keep_top_level ? array_diff(scandir($dir), array('.','..','index.htm')) : array_diff(scandir($dir), array('.','..'));
foreach ($files as $file)
{
(is_dir("$dir/$file")) ? hesk_rrmdir("$dir/$file") : @unlink("$dir/$file");
}
return $keep_top_level ? true : @rmdir($dir);
} // END hesk_rrmdir()

View File

@@ -0,0 +1,109 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
/* Assignment */
// -> SELF
$s_my[$fid] = empty($_GET['s_my']) ? 0 : 1;
// -> OTHERS
$s_ot[$fid] = empty($_GET['s_ot']) ? 0 : 1;
// -> UNASSIGNED
$s_un[$fid] = empty($_GET['s_un']) ? 0 : 1;
// -> Setup SQL based on selected ticket assignments
/* Make sure at least one is chosen */
if ( ! $s_my[$fid] && ! $s_ot[$fid] && ! $s_un[$fid])
{
$s_my[$fid] = 1;
$s_ot[$fid] = 1;
$s_un[$fid] = 1;
if (!defined('MAIN_PAGE'))
{
hesk_show_notice($hesklang['e_nose']);
}
}
// Can view tickets assigned by him/her?
$s_by[$fid] = hesk_checkPermission('can_view_ass_by',0) ? $s_ot[$fid] : 0;
/* If the user doesn't have permission to view assigned to others block those */
if ( ! hesk_checkPermission('can_view_ass_others',0))
{
$s_ot[$fid] = 0;
}
/* If the user doesn't have permission to view unassigned tickets block those */
if ( ! hesk_checkPermission('can_view_unassigned',0))
{
$s_un[$fid] = 0;
}
/* Process assignments */
if ( ! $s_my[$fid] || ! $s_ot[$fid] || ! $s_un[$fid])
{
if ($s_my[$fid] && $s_ot[$fid])
{
// All but unassigned
$sql .= " AND `owner` > 0 ";
}
elseif ( ! $s_ot[$fid] && $s_by[$fid])
{
// Can't view tickets assigned to others, but can see tickets he/she assigned to someone
if ($s_my[$fid] && $s_un[$fid])
{
$sql .= " AND (`owner` IN ('0', '" . intval($_SESSION['id']) . "') OR `assignedby` = " . intval($_SESSION['id']) . ") ";
}
elseif($s_my[$fid])
{
$sql .= " AND (`owner` = '" . intval($_SESSION['id']) . "' OR `assignedby` = " . intval($_SESSION['id']) . ") ";
}
elseif($s_un[$fid])
{
$sql .= " AND (`owner` = 0 OR `assignedby` = " . intval($_SESSION['id']) . ") ";
}
else
{
$sql .= " AND `assignedby` = " . intval($_SESSION['id']) . " ";
}
$s_ot[$fid] = 1;
}
elseif ($s_my[$fid] && $s_un[$fid])
{
// My tickets + unassigned
$sql .= " AND `owner` IN ('0', '" . intval($_SESSION['id']) . "') ";
}
elseif ($s_ot[$fid] && $s_un[$fid])
{
// Assigned to others + unassigned
$sql .= " AND `owner` != '" . intval($_SESSION['id']) . "' ";
}
elseif ($s_my[$fid])
{
// Assigned to me only
$sql .= " AND `owner` = '" . intval($_SESSION['id']) . "' ";
}
elseif ($s_ot[$fid])
{
// Assigned to others
$sql .= " AND `owner` NOT IN ('0', '" . intval($_SESSION['id']) . "') ";
}
elseif ($s_un[$fid])
{
// Only unassigned
$sql .= " AND `owner` = 0 ";
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
/***************************
Function hesk_uploadFiles()
***************************/
function hesk_uploadFile($i)
{
global $hesk_settings, $hesklang, $trackingID, $hesk_error_buffer;
/* Return if name is empty */
if (empty($_FILES['attachment']['name'][$i])) {return '';}
/* Parse the name */
$file_realname = hesk_cleanFileName($_FILES['attachment']['name'][$i]);
/* Check file extension */
$ext = strtolower(strrchr($file_realname, "."));
if ( ! in_array($ext,$hesk_settings['attachments']['allowed_types']))
{
return hesk_fileError(sprintf($hesklang['type_not_allowed'], $ext, $file_realname));
}
/* Check file size */
if ($_FILES['attachment']['size'][$i] > $hesk_settings['attachments']['max_size'])
{
return hesk_fileError(sprintf($hesklang['file_too_large'], $file_realname));
}
else
{
$file_size = $_FILES['attachment']['size'][$i];
}
/* Generate a random file name */
$useChars='AEUYBDGHJLMNPQRSTVWXZ123456789';
$tmp = uniqid();
for($j=1;$j<10;$j++)
{
$tmp .= $useChars[mt_rand(0,29)];
}
if (defined('KB'))
{
$file_name = substr(md5($tmp . $file_realname), 0, 200) . $ext;
}
else
{
$file_name = substr($trackingID . '_' . md5($tmp . $file_realname), 0, 200) . $ext;
}
// Does the temporary file exist? If not, probably server-side configuration limits have been reached
// Uncomment this for debugging purposes
/*
if ( ! file_exists($_FILES['attachment']['tmp_name'][$i]) )
{
return hesk_fileError($hesklang['fnuscphp']);
}
*/
/* If upload was successful let's create the headers */
if ( ! move_uploaded_file($_FILES['attachment']['tmp_name'][$i], dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/'.$file_name))
{
return hesk_fileError($hesklang['cannot_move_tmp']);
}
$info = array(
'saved_name'=> $file_name,
'real_name' => $file_realname,
'size' => $file_size
);
return $info;
} // End hesk_uploadFile()
function hesk_fileError($error)
{
global $hesk_settings, $hesklang, $trackingID;
global $hesk_error_buffer;
$hesk_error_buffer['attachments'] = $error;
return false;
} // End hesk_fileError()
function hesk_removeAttachments($attachments)
{
global $hesk_settings, $hesklang;
$hesk_settings['server_path'] = dirname(dirname(__FILE__)).'/'.$hesk_settings['attach_dir'].'/';
foreach ($attachments as $myatt)
{
hesk_unlink($hesk_settings['server_path'].$myatt['saved_name']);
}
return true;
} // End hesk_removeAttachments()

2394
hesk/inc/common.inc.php Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,247 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// Get and append custom fields setup to the settings
hesk_load_custom_fields();
// Save number of custom fields
$hesk_settings['num_custom_fields'] = count($hesk_settings['custom_fields']);
// Load custom fields for admin functions
if (function_exists('hesk_checkPermission'))
{
foreach ($hesk_settings['custom_fields'] as $k => $v)
{
$hesk_settings['possible_ticket_list'][$k] = $hesk_settings['custom_fields'][$k]['title'];
}
}
/*** FUNCTIONS ***/
function hesk_load_custom_fields($category=0, $use_cache=1)
{
global $hesk_settings, $hesklang;
// Do we have a cached version available
$cache_dir = dirname(dirname(__FILE__)).'/'.$hesk_settings['cache_dir'].'/';
$cache_file = $cache_dir . 'cf_' . sha1($hesk_settings['language']).'.cache.php';
if ($use_cache && file_exists($cache_file))
{
require($cache_file);
return true;
}
// Get custom fields from the database
$hesk_settings['custom_fields'] = array();
// Make sure we have database connection
hesk_load_database_functions();
hesk_dbConnect();
$res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."custom_fields` WHERE `use` IN ('1', '2') ORDER BY `place` ASC, `order` ASC");
while ($row = hesk_dbFetchAssoc($res))
{
$id = 'custom' . $row['id'];
unset($row['id']);
// Let's set field name for current language (or the first one we find)
$names = json_decode($row['name'], true);
$row['name'] = (isset($names[$hesk_settings['language']])) ? $names[$hesk_settings['language']] : reset($names);
// Name for display in ticket list; punctuation removed and shortened
$row['title'] = hesk_remove_punctuation($row['name']);
$row['title'] = hesk_mb_strlen($row['title']) > 30 ? hesk_mb_substr($row['title'], 0, 30) . '...' : $row['title'];
// A version with forced punctuation
$row['name:'] = in_array(substr($row['name'], -1), array(':', '?', '!', '.') ) ? $row['name'] : $row['name'] . ':';
// Decode categories
$row['category'] = strlen($row['category']) ? json_decode($row['category'], true) : array();
// Decode options
$row['value'] = json_decode($row['value'], true);
// Add to custom_fields array
$hesk_settings['custom_fields'][$id] = $row;
}
// Try to cache results
if ($use_cache && (is_dir($cache_dir) || ( @mkdir($cache_dir, 0777) && is_writable($cache_dir) ) ) )
{
// Is there an index.htm file?
if ( ! file_exists($cache_dir.'index.htm'))
{
@file_put_contents($cache_dir.'index.htm', '');
}
// Write data
@file_put_contents($cache_file, '<?php if (!defined(\'IN_SCRIPT\')) {die();} $hesk_settings[\'custom_fields\']=' . var_export($hesk_settings['custom_fields'], true) . ';' );
}
return true;
} // END hesk_load_custom_fields()
function hesk_is_custom_field_in_category($custom_id, $category_id)
{
global $hesk_settings;
return (
empty($hesk_settings['custom_fields'][$custom_id]['category']) ||
in_array($category_id, $hesk_settings['custom_fields'][$custom_id]['category'])
) ? true : false;
} // END hesk_is_custom_field_in_category()
function hesk_custom_field_type($type)
{
global $hesklang;
switch ($type)
{
case 'text':
return $hesklang['stf'];
case 'textarea':
return $hesklang['stb'];
case 'radio':
return $hesklang['srb'];
case 'select':
return $hesklang['ssb'];
case 'checkbox':
return $hesklang['scb'];
case 'email':
return $hesklang['email'];
case 'date':
return $hesklang['date'];
case 'hidden':
return $hesklang['sch'];
default:
return false;
}
} // END hesk_custom_field_type()
function hesk_custom_date_display_format($timestamp, $format = 'F j, Y')
{
global $hesklang;
if ($timestamp == '')
{
return '';
}
if ( ! is_int($timestamp))
{
$timestamp = $timestamp * 1;
}
if ($hesklang['LANGUAGE']=='English')
{
return gmdate($format, $timestamp);
}
// Attempt to translate date for non-English users
$translate_months = array(
'January' => $hesklang['m1'],
'February' => $hesklang['m2'],
'March' => $hesklang['m3'],
'April' => $hesklang['m4'],
'May' => $hesklang['m5'],
'June' => $hesklang['m6'],
'July' => $hesklang['m7'],
'August' => $hesklang['m8'],
'September' => $hesklang['m9'],
'October' => $hesklang['m10'],
'November' => $hesklang['m11'],
'December' => $hesklang['m12']
);
$translate_months_short = array(
'Jan' => $hesklang['ms01'],
'Feb' => $hesklang['ms02'],
'Mar' => $hesklang['ms03'],
'Apr' => $hesklang['ms04'],
'May' => $hesklang['ms05'],
'Jun' => $hesklang['ms06'],
'Jul' => $hesklang['ms07'],
'Aug' => $hesklang['ms08'],
'Sep' => $hesklang['ms09'],
'Oct' => $hesklang['ms10'],
'Nov' => $hesklang['ms11'],
'Dec' => $hesklang['ms12']
);
$translate_days = array(
'Monday' => $hesklang['d1'],
'Tuesday' => $hesklang['d2'],
'Wednesday' => $hesklang['d3'],
'Thursday' => $hesklang['d4'],
'Friday' => $hesklang['d5'],
'Saturday' => $hesklang['d6'],
'Sunday' => $hesklang['d0']
);
$translate_days_short = array(
'Mon' => $hesklang['mo'],
'Tuw' => $hesklang['tu'],
'Wes' => $hesklang['we'],
'Thu' => $hesklang['th'],
'Fri' => $hesklang['fr'],
'Sat' => $hesklang['sa'],
'Sun' => $hesklang['su']
);
$date_translate = array();
if (strpos($format, 'F') !== false)
{
$date_translate = array_merge($date_translate, $translate_months);
}
if (strpos($format, 'M') !== false)
{
$date_translate = array_merge($date_translate, $translate_months_short);
}
if (strpos($format, 'l') !== false)
{
$date_translate = array_merge($date_translate, $translate_days);
}
if (strpos($format, 'D') !== false)
{
$date_translate = array_merge($date_translate, $translate_days_short);
}
if (count($date_translate))
{
return str_replace( array_keys($date_translate), array_values($date_translate), gmdate($format, $timestamp));
}
return gmdate($format, $timestamp);
} // END hesk_custom_date_display_format()
function hesk_remove_punctuation($in)
{
return rtrim($in, ':?!.');
} // END hesk_remove_punctuation()

259
hesk/inc/database.inc.php Normal file
View File

@@ -0,0 +1,259 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
function hesk_dbCollate()
{
global $hesklang;
// MySQL vesions prior to 5.6 don't support some collations
if ( in_array($hesklang['_COLLATE'], array('utf8_croatian_ci', 'utf8_german2_ci', 'utf8_vietnamese_ci')) )
{
if ( version_compare( hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ), '5.6', '<') )
{
$hesklang['_COLLATE'] = 'utf8_general_ci';
}
}
return hesk_dbEscape($hesklang['_COLLATE']);
} // END hesk_dbCollate()
function hesk_dbSetNames()
{
global $hesk_settings, $hesk_db_link;
if ($hesk_settings['db_vrsn'])
{
mysql_set_charset('utf8', $hesk_db_link);
}
else
{
hesk_dbQuery("SET NAMES 'utf8'");
}
} // END hesk_dbSetNames()
function hesk_dbFormatEmail($email, $field = 'email')
{
global $hesk_settings;
$email = hesk_dbLike($email);
if ($hesk_settings['multi_eml'])
{
return " (`".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' OR `".hesk_dbEscape($field)."` LIKE '%,".hesk_dbEscape($email)."' OR `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email).",%' OR `".hesk_dbEscape($field)."` LIKE '%,".hesk_dbEscape($email).",%') ";
}
else
{
return " `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' ";
}
} // END hesk_dbFormatEmail()
function hesk_dbTime()
{
$res = hesk_dbQuery("SELECT NOW()");
return strtotime(hesk_dbResult($res,0,0));
} // END hesk_dbTime()
function hesk_dbSetTimezone()
{
global $hesk_settings;
hesk_dbQuery('SET time_zone = "'.hesk_timeToHHMM(date('Z')).'"');
return true;
} // END hesk_dbSetTimezone()
function hesk_dbEscape($in)
{
global $hesk_db_link;
$in = mysql_real_escape_string(stripslashes($in), $hesk_db_link);
$in = str_replace('`','&#96;',$in);
return $in;
} // END hesk_dbEscape()
function hesk_dbLike($in)
{
return str_replace( array('\\', '_', '%'), array('\\\\', '\\\\_', '\\\\%'), $in); // '
} // END hesk_dbLike()
function hesk_dbConnect()
{
global $hesk_settings;
global $hesk_db_link;
global $hesklang;
// Do we have an existing active link?
if ($hesk_db_link)
{
return $hesk_db_link;
}
// Is mysql supported?
if ( ! function_exists('mysql_connect') )
{
die($hesklang['emp']);
}
// Connect to the database
$hesk_db_link = @mysql_connect($hesk_settings['db_host'], $hesk_settings['db_user'], $hesk_settings['db_pass']);
// Errors?
if ( ! $hesk_db_link)
{
if ($hesk_settings['debug_mode'])
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
}
else
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
}
}
if ( ! @mysql_select_db($hesk_settings['db_name'], $hesk_db_link))
{
if ($hesk_settings['debug_mode'])
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
}
else
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
}
}
// Check MySQL/PHP version and set encoding to utf8
hesk_dbSetNames();
// Set the correct timezone
hesk_dbSetTimezone();
return $hesk_db_link;
} // END hesk_dbConnect()
function hesk_dbClose()
{
global $hesk_db_link;
return @mysql_close($hesk_db_link);
} // END hesk_dbClose()
function hesk_dbQuery($query)
{
global $hesk_last_query;
global $hesk_db_link;
global $hesklang, $hesk_settings;
if ( ! $hesk_db_link && ! hesk_dbConnect())
{
return false;
}
$hesk_last_query = $query;
#echo "<p>EXPLAIN $query</p>\n";
if ($res = @mysql_query($query, $hesk_db_link))
{
return $res;
}
elseif ($hesk_settings['debug_mode'])
{
hesk_error("$hesklang[cant_sql]: $query</p><p>$hesklang[mysql_said]:<br />".mysql_error()."</p>");
}
else
{
hesk_error("$hesklang[cant_sql]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
}
} // END hesk_dbQuery()
function hesk_dbFetchAssoc($res)
{
return @mysql_fetch_assoc($res);
} // END hesk_FetchAssoc()
function hesk_dbFetchRow($res)
{
return @mysql_fetch_row($res);
} // END hesk_FetchRow()
function hesk_dbResult($res, $row = 0, $column = 0)
{
return @mysql_result($res, $row, $column);
} // END hesk_dbResult()
function hesk_dbInsertID()
{
global $hesk_db_link;
if ($lastid = @mysql_insert_id($hesk_db_link))
{
return $lastid;
}
} // END hesk_dbInsertID()
function hesk_dbFreeResult($res)
{
return mysql_free_result($res);
} // END hesk_dbFreeResult()
function hesk_dbNumRows($res)
{
return @mysql_num_rows($res);
} // END hesk_dbNumRows()
function hesk_dbAffectedRows()
{
global $hesk_db_link;
return @mysql_affected_rows($hesk_db_link);
} // END hesk_dbAffectedRows()

View File

@@ -0,0 +1,266 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
function hesk_dbCollate()
{
global $hesklang;
// MySQL vesions prior to 5.6 don't support some collations
if ( in_array($hesklang['_COLLATE'], array('utf8_croatian_ci', 'utf8_german2_ci', 'utf8_vietnamese_ci')) )
{
if ( version_compare( hesk_dbResult( hesk_dbQuery('SELECT VERSION() AS version') ), '5.6', '<') )
{
$hesklang['_COLLATE'] = 'utf8_general_ci';
}
}
return hesk_dbEscape($hesklang['_COLLATE']);
} // END hesk_dbCollate()
function hesk_dbSetNames()
{
global $hesk_settings, $hesk_db_link;
if ($hesk_settings['db_vrsn'])
{
mysqli_set_charset($hesk_db_link, 'utf8');
}
else
{
hesk_dbQuery("SET NAMES 'utf8'");
}
} // END hesk_dbSetNames()
function hesk_dbFormatEmail($email, $field = 'email')
{
global $hesk_settings;
$email = hesk_dbLike($email);
if ($hesk_settings['multi_eml'])
{
return " (`".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' OR `".hesk_dbEscape($field)."` LIKE '%,".hesk_dbEscape($email)."' OR `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email).",%' OR `".hesk_dbEscape($field)."` LIKE '%,".hesk_dbEscape($email).",%') ";
}
else
{
return " `".hesk_dbEscape($field)."` LIKE '".hesk_dbEscape($email)."' ";
}
} // END hesk_dbFormatEmail()
function hesk_dbTime()
{
$res = hesk_dbQuery("SELECT NOW()");
return strtotime(hesk_dbResult($res,0,0));
} // END hesk_dbTime()
function hesk_dbSetTimezone()
{
global $hesk_settings;
hesk_dbQuery('SET time_zone = "'.hesk_timeToHHMM(date('Z')).'"');
return true;
} // END hesk_dbSetTimezone()
function hesk_dbEscape($in)
{
global $hesk_db_link;
$in = mysqli_real_escape_string($hesk_db_link, stripslashes($in));
$in = str_replace('`','&#96;',$in);
return $in;
} // END hesk_dbEscape()
function hesk_dbLike($in)
{
return str_replace( array('\\', '_', '%'), array('\\\\', '\\\\_', '\\\\%'), $in); // '
} // END hesk_dbLike()
function hesk_dbConnect()
{
global $hesk_settings;
global $hesk_db_link;
global $hesklang;
// Do we have an existing active link?
if ($hesk_db_link)
{
return $hesk_db_link;
}
// Is mysqli supported?
if ( ! function_exists('mysqli_connect') )
{
die($hesklang['emp']);
}
// Do we need a special port? Check and connect to the database
if ( strpos($hesk_settings['db_host'], ':') )
{
list($hesk_settings['db_host_no_port'], $hesk_settings['db_port']) = explode(':', $hesk_settings['db_host']);
$hesk_db_link = @mysqli_connect($hesk_settings['db_host_no_port'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name'], intval($hesk_settings['db_port']) );
}
else
{
$hesk_db_link = @mysqli_connect($hesk_settings['db_host'], $hesk_settings['db_user'], $hesk_settings['db_pass'], $hesk_settings['db_name']);
}
// Errors?
if ( ! $hesk_db_link)
{
if ($hesk_settings['debug_mode'])
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[mysql_said]:<br />(".mysqli_connect_errno().") ".mysqli_connect_error()."</p>");
}
else
{
hesk_error("$hesklang[cant_connect_db]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
}
}
// Check MySQL/PHP version and set encoding to utf8
hesk_dbSetNames();
// Set the correct timezone
hesk_dbSetTimezone();
return $hesk_db_link;
} // END hesk_dbConnect()
function hesk_dbClose()
{
global $hesk_db_link;
return @mysqli_close($hesk_db_link);
} // END hesk_dbClose()
function hesk_dbQuery($query)
{
global $hesk_last_query;
global $hesk_db_link;
global $hesklang, $hesk_settings;
if ( ! $hesk_db_link && ! hesk_dbConnect())
{
return false;
}
$hesk_last_query = $query;
# echo "<p>EXPLAIN $query</p>\n";
if ($res = @mysqli_query($hesk_db_link, $query))
{
return $res;
}
elseif ($hesk_settings['debug_mode'])
{
hesk_error("$hesklang[cant_sql]: $query</p><p>$hesklang[mysql_said]:<br />".mysqli_error($hesk_db_link)."</p>");
}
else
{
hesk_error("$hesklang[cant_sql]</p><p>$hesklang[contact_webmsater] <a href=\"mailto:$hesk_settings[webmaster_mail]\">$hesk_settings[webmaster_mail]</a></p>");
}
} // END hesk_dbQuery()
function hesk_dbFetchAssoc($res)
{
return @mysqli_fetch_assoc($res);
} // END hesk_FetchAssoc()
function hesk_dbFetchRow($res)
{
return @mysqli_fetch_row($res);
} // END hesk_FetchRow()
function hesk_dbResult($res, $row = 0, $column = 0)
{
$i=0;
$res->data_seek(0);
while ($tmp = @mysqli_fetch_array($res, MYSQLI_NUM))
{
if ($i==$row)
{
return $tmp[$column];
}
$i++;
}
return '';
} // END hesk_dbResult()
function hesk_dbInsertID()
{
global $hesk_db_link;
if ($lastid = @mysqli_insert_id($hesk_db_link))
{
return $lastid;
}
} // END hesk_dbInsertID()
function hesk_dbFreeResult($res)
{
return @mysqli_free_result($res);
} // END hesk_dbFreeResult()
function hesk_dbNumRows($res)
{
return @mysqli_num_rows($res);
} // END hesk_dbNumRows()
function hesk_dbAffectedRows()
{
global $hesk_db_link;
return @mysqli_affected_rows($hesk_db_link);
} // END hesk_dbAffectedRows()

View File

@@ -0,0 +1,669 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// Make sure custom fields are loaded
require_once(HESK_PATH . 'inc/custom_fields.inc.php');
// Make sure statuses are loaded
require_once(HESK_PATH . 'inc/statuses.inc.php');
/* Get includes for SMTP */
if ($hesk_settings['smtp'])
{
require(HESK_PATH . 'inc/mail/smtp.php');
if (strlen($hesk_settings['smtp_user']) || strlen($hesk_settings['smtp_password']))
{
require_once(HESK_PATH . 'inc/mail/sasl/sasl.php');
}
}
function hesk_notifyCustomer($email_template = 'new_ticket')
{
global $hesk_settings, $hesklang, $ticket;
// Demo mode
if ( defined('HESK_DEMO') )
{
return true;
}
// No customer email
if ($ticket['email'] == '')
{
return true;
}
// Make sure customer gets response in correct language
if ( isset($ticket['language']) )
{
hesk_setLanguage($ticket['language']);
}
// Format email subject and message
$subject = hesk_getEmailSubject($email_template,$ticket);
$message = hesk_getEmailMessage($email_template,$ticket);
// Send e-mail
hesk_mail($ticket['email'], $subject, $message);
// Reset language if needed
hesk_resetLanguage();
return true;
} // END hesk_notifyCustomer()
function hesk_notifyAssignedStaff($autoassign_owner, $email_template, $type = 'notify_assigned')
{
global $hesk_settings, $hesklang, $ticket;
// Demo mode
if ( defined('HESK_DEMO') )
{
return true;
}
$ticket['owner'] = intval($ticket['owner']);
/* Need to lookup owner info from the database? */
if ($autoassign_owner === false)
{
$res = hesk_dbQuery("SELECT `name`, `email`,`language`,`notify_assigned`,`notify_reply_my` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`='" . $ticket['owner'] . "' LIMIT 1");
$autoassign_owner = hesk_dbFetchAssoc($res);
$hesk_settings['user_data'][$ticket['owner']] = $autoassign_owner;
/* If owner selected not to be notified or invalid stop here */
if ( empty($autoassign_owner[$type]) )
{
return false;
}
}
/* Set new language if required */
hesk_setLanguage($autoassign_owner['language']);
/* Format email subject and message for staff */
$subject = hesk_getEmailSubject($email_template,$ticket);
$message = hesk_getEmailMessage($email_template,$ticket,1);
/* Send email to staff */
hesk_mail($autoassign_owner['email'], $subject, $message);
/* Reset language to original one */
hesk_resetLanguage();
return true;
} // END hesk_notifyAssignedStaff()
function hesk_notifyStaff($email_template,$sql_where,$is_ticket=1)
{
global $hesk_settings, $hesklang, $ticket;
// Demo mode
if ( defined('HESK_DEMO') )
{
return true;
}
$admins = array();
$res = hesk_dbQuery("SELECT `email`,`language`,`isadmin`,`categories` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE $sql_where ORDER BY `language`");
while ($myuser = hesk_dbFetchAssoc($res))
{
/* Is this an administrator? */
if ($myuser['isadmin'])
{
$admins[] = array('email' => $myuser['email'], 'language' => $myuser['language']);
continue;
}
/* Not admin, is he/she allowed this category? */
$myuser['categories']=explode(',',$myuser['categories']);
if (in_array($ticket['category'],$myuser['categories']))
{
$admins[] = array('email' => $myuser['email'], 'language' => $myuser['language']);
continue;
}
}
if (count($admins) > 0)
{
/* Make sure each user gets email in his/her preferred language */
$current_language = 'NONE';
$recipients = array();
/* Loop through staff */
foreach ($admins as $admin)
{
/* If admin language is NULL force default HESK language */
if ( ! $admin['language'] || ! isset($hesk_settings['languages'][$admin['language']]) )
{
$admin['language'] = HESK_DEFAULT_LANGUAGE;
}
/* Generate message or add email to the list of recepients */
if ($admin['language'] == $current_language)
{
/* We already have the message, just add email to the recipients list */
$recipients[] = $admin['email'];
}
else
{
/* Send email messages in previous languages (if required) */
if ($current_language != 'NONE')
{
/* Send e-mail to staff */
hesk_mail(implode(',',$recipients), $subject, $message );
/* Reset list of email addresses */
$recipients = array();
}
/* Set new language */
hesk_setLanguage($admin['language']);
/* Format staff email subject and message for this language */
$subject = hesk_getEmailSubject($email_template,$ticket);
$message = hesk_getEmailMessage($email_template,$ticket,$is_ticket);
/* Add email to the recipients list */
$recipients[] = $admin['email'];
/* Remember the last processed language */
$current_language = $admin['language'];
}
}
/* Send email messages to the remaining staff */
hesk_mail(implode(',',$recipients), $subject, $message );
/* Reset language to original one */
hesk_resetLanguage();
}
return true;
} // END hesk_notifyStaff()
function hesk_validEmails()
{
global $hesklang;
return array(
/*** Emails sent to CLIENT ***/
// --> Send reminder about existing tickets
'forgot_ticket_id' => $hesklang['forgot_ticket_id'],
// --> Staff replied to a ticket
'new_reply_by_staff' => $hesklang['new_reply_by_staff'],
// --> New ticket submitted
'new_ticket' => $hesklang['ticket_received'],
// --> Ticket closed
'ticket_closed' => $hesklang['ticket_closed'],
/*** Emails sent to STAFF ***/
// --> Ticket moved to a new category
'category_moved' => $hesklang['category_moved'],
// --> Client replied to a ticket
'new_reply_by_customer' => $hesklang['new_reply_by_customer'],
// --> New ticket submitted
'new_ticket_staff' => $hesklang['new_ticket_staff'],
// --> New ticket assigned to staff
'ticket_assigned_to_you'=> $hesklang['ticket_assigned_to_you'],
// --> New private message
'new_pm' => $hesklang['new_pm'],
// --> New note by someone to a ticket assigned to you
'new_note' => $hesklang['new_note'],
// --> Staff password reset email
'reset_password' => $hesklang['reset_password'],
);
} // END hesk_validEmails()
function hesk_mail($to,$subject,$message)
{
global $hesk_settings, $hesklang;
// Demo mode
if ( defined('HESK_DEMO') )
{
return true;
}
// Empty recipient?
if ($to == '')
{
return true;
}
// Stop if we find anything suspicious in the headers
if ( preg_match("/\n|\r|\t|%0A|%0D|%08|%09/", $to . $subject) )
{
return false;
}
// Encode subject to UTF-8
$subject = hesk_encodeIfNotAscii( hesk_html_entity_decode($subject) );
// Setup "name <email>" for headers
if ($hesk_settings['noreply_name'])
{
$hesk_settings['from_header'] = hesk_encodeIfNotAscii( hesk_html_entity_decode($hesk_settings['noreply_name']) ) . " <" . $hesk_settings['noreply_mail'] . ">";
}
else
{
$hesk_settings['from_header'] = $hesk_settings['noreply_mail'];
}
// Uncomment for debugging
# echo "<p>TO: $to<br >SUBJECT: $subject<br >MSG: $message</p>";
# return true;
// Remove duplicate recipients
$to_arr = array_unique(explode(',', $to));
$to_arr = array_values($to_arr);
$to = implode(',', $to_arr);
// Use PHP's mail function
if ( ! $hesk_settings['smtp'])
{
// Set additional headers
$headers = "From: $hesk_settings[from_header]\n";
$headers.= "Reply-To: $hesk_settings[from_header]\n";
$headers.= "Return-Path: $hesk_settings[webmaster_mail]\n";
$headers.= "Date: " . date(DATE_RFC2822) . "\n";
$headers.= "Message-ID: " . hesk_generateMessageID() . "\n";
$headers.= "MIME-Version: 1.0\n";
$headers.= "Content-Type: text/plain; charset=" . $hesklang['ENCODING'];
// Send using PHP mail() function
ob_start();
mail($to,$subject,$message,$headers);
$tmp = trim(ob_get_contents());
ob_end_clean();
return (strlen($tmp)) ? $tmp : true;
}
// Use a SMTP server directly instead
$smtp = new smtp_class;
$smtp->host_name = $hesk_settings['smtp_host_name'];
$smtp->host_port = $hesk_settings['smtp_host_port'];
$smtp->timeout = $hesk_settings['smtp_timeout'];
$smtp->ssl = $hesk_settings['smtp_ssl'];
$smtp->start_tls = $hesk_settings['smtp_tls'];
$smtp->user = $hesk_settings['smtp_user'];
$smtp->password = hesk_htmlspecialchars_decode($hesk_settings['smtp_password']);
$smtp->debug = 1;
// Start output buffering so that any errors don't break headers
ob_start();
// Send the e-mail using SMTP
if ( ! $smtp->SendMessage($hesk_settings['noreply_mail'], $to_arr, array(
"From: $hesk_settings[from_header]",
"To: $to",
"Reply-To: $hesk_settings[from_header]",
"Return-Path: $hesk_settings[webmaster_mail]",
"Subject: " . $subject,
"Date: " . date(DATE_RFC2822),
"Message-ID: " . hesk_generateMessageID(),
"MIME-Version: 1.0",
"Content-Type: text/plain; charset=" . $hesklang['ENCODING']
), $message))
{
// Suppress errors unless we are in debug mode
if ($hesk_settings['debug_mode'])
{
$error = $hesklang['cnsm'] . ' ' . $to . '<br /><br />' .
$hesklang['error'] . ': ' . htmlspecialchars($smtp->error). '<br /><br />' .
'<textarea name="smtp_log" rows="10" cols="60">' . ob_get_contents() . '</textarea>';
ob_end_clean();
hesk_error($error);
}
else
{
$_SESSION['HESK_2ND_NOTICE'] = true;
$_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $hesklang['contact_webmsater'] . ' <a href="mailto:' . $hesk_settings['webmaster_mail'] . '">' . $hesk_settings['webmaster_mail'] . '</a>';
}
}
ob_end_clean();
return true;
} // END hesk_mail()
function hesk_getEmailSubject($eml_file, $ticket='', $is_ticket=1, $strip=0)
{
global $hesk_settings, $hesklang;
// Demo mode
if ( defined('HESK_DEMO') )
{
return '';
}
/* Get list of valid emails */
$valid_emails = hesk_validEmails();
/* Verify this is a valid email include */
if ( ! isset($valid_emails[$eml_file]))
{
hesk_error($hesklang['inve']);
}
else
{
$msg = $valid_emails[$eml_file];
}
/* If not a ticket-related email return subject as is */
if ( ! $ticket )
{
return $msg;
}
/* Strip slashes from the subject only if it's a new ticket */
if ($strip)
{
$ticket['subject'] = stripslashes($ticket['subject']);
}
/* Not a ticket, but has some info in the $ticket array */
if ( ! $is_ticket)
{
return str_replace('%%SUBJECT%%', $ticket['subject'], $msg);
}
/* Set category title */
$ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1);
/* Get priority */
switch ($ticket['priority'])
{
case 0:
$ticket['priority'] = $hesklang['critical'];
break;
case 1:
$ticket['priority'] = $hesklang['high'];
break;
case 2:
$ticket['priority'] = $hesklang['medium'];
break;
default:
$ticket['priority'] = $hesklang['low'];
}
/* Set status */
$ticket['status'] = hesk_get_status_name($ticket['status']);
/* Replace all special tags */
$msg = str_replace('%%SUBJECT%%', $ticket['subject'], $msg);
$msg = str_replace('%%TRACK_ID%%', $ticket['trackid'], $msg);
$msg = str_replace('%%CATEGORY%%', $ticket['category'], $msg);
$msg = str_replace('%%PRIORITY%%', $ticket['priority'], $msg);
$msg = str_replace('%%STATUS%%', $ticket['status'], $msg);
return $msg;
} // hesk_getEmailSubject()
function hesk_getEmailMessage($eml_file, $ticket, $is_admin=0, $is_ticket=1, $just_message=0)
{
global $hesk_settings, $hesklang;
// Demo mode
if ( defined('HESK_DEMO') )
{
return '';
}
/* Get list of valid emails */
$valid_emails = hesk_validEmails();
/* Verify this is a valid email include */
if ( ! isset($valid_emails[$eml_file]))
{
hesk_error($hesklang['inve']);
}
/* Get email template */
$eml_file = 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/emails/' . $eml_file . '.txt';
if (file_exists(HESK_PATH . $eml_file))
{
$msg = file_get_contents(HESK_PATH . $eml_file);
}
else
{
hesk_error($hesklang['emfm'].': '.$eml_file);
}
/* Return just the message without any processing? */
if ($just_message)
{
return $msg;
}
// Convert any entities in site title to plain text
$hesk_settings['site_title'] = hesk_msgToPlain($hesk_settings['site_title'], 1);
/* If it's not a ticket-related mail (like "a new PM") just process quickly */
if ( ! $is_ticket)
{
$trackingURL = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/mail.php?a=read&id=' . intval($ticket['id']);
$msg = str_replace('%%NAME%%', $ticket['name'] ,$msg);
$msg = str_replace('%%SUBJECT%%', $ticket['subject'] ,$msg);
$msg = str_replace('%%TRACK_URL%%', $trackingURL ,$msg);
$msg = str_replace('%%SITE_TITLE%%',$hesk_settings['site_title'] ,$msg);
$msg = str_replace('%%SITE_URL%%', $hesk_settings['site_url'] ,$msg);
$msg = str_replace('%%FIRST_NAME%%',hesk_full_name_to_first_name($ticket['name']),$msg);
if ( isset($ticket['message']) )
{
return str_replace('%%MESSAGE%%', $ticket['message'], $msg);
}
else
{
return $msg;
}
}
// Is email required to view ticket (for customers only)?
$hesk_settings['e_param'] = $hesk_settings['email_view_ticket'] ? '&e=' . rawurlencode($ticket['email']) : '';
/* Generate the ticket URLs */
$trackingURL = $hesk_settings['hesk_url'];
$trackingURL.= $is_admin ? '/' . $hesk_settings['admin_dir'] . '/admin_ticket.php' : '/ticket.php';
$trackingURL.= '?track='.$ticket['trackid'].($is_admin ? '' : $hesk_settings['e_param']).'&Refresh='.rand(10000,99999);
/* Set category title */
$ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1, 0);
/* Set priority title */
switch ($ticket['priority'])
{
case 0:
$ticket['priority'] = $hesklang['critical'];
break;
case 1:
$ticket['priority'] = $hesklang['high'];
break;
case 2:
$ticket['priority'] = $hesklang['medium'];
break;
default:
$ticket['priority'] = $hesklang['low'];
}
/* Get owner name */
$ticket['owner'] = hesk_msgToPlain( hesk_getOwnerName($ticket['owner']), 1);
/* Set status */
$ticket['status'] = hesk_get_status_name($ticket['status']);
// Get name of the person who posted the last message
if ( ! isset($ticket['last_reply_by']))
{
$ticket['last_reply_by'] = hesk_getReplierName($ticket);
}
/* Replace all special tags */
$msg = str_replace('%%NAME%%', $ticket['name'] ,$msg);
$msg = str_replace('%%SUBJECT%%', $ticket['subject'] ,$msg);
$msg = str_replace('%%TRACK_ID%%', $ticket['trackid'] ,$msg);
$msg = str_replace('%%TRACK_URL%%', $trackingURL ,$msg);
$msg = str_replace('%%SITE_TITLE%%',$hesk_settings['site_title'],$msg);
$msg = str_replace('%%SITE_URL%%', $hesk_settings['site_url'] ,$msg);
$msg = str_replace('%%CATEGORY%%', $ticket['category'] ,$msg);
$msg = str_replace('%%PRIORITY%%', $ticket['priority'] ,$msg);
$msg = str_replace('%%OWNER%%', $ticket['owner'] ,$msg);
$msg = str_replace('%%STATUS%%', $ticket['status'] ,$msg);
$msg = str_replace('%%EMAIL%%', $ticket['email'] ,$msg);
$msg = str_replace('%%CREATED%%', $ticket['dt'] ,$msg);
$msg = str_replace('%%UPDATED%%', $ticket['lastchange'] ,$msg);
$msg = str_replace('%%ID%%', $ticket['id'] ,$msg);
$msg = str_replace('%%TIME_WORKED%%', $ticket['time_worked'] ,$msg);
$msg = str_replace('%%LAST_REPLY_BY%%',$ticket['last_reply_by'] ,$msg);
$msg = str_replace('%%FIRST_NAME%%',hesk_full_name_to_first_name($ticket['name']),$msg);
/* All custom fields */
for ($i=1; $i<=50; $i++)
{
$k = 'custom'.$i;
if (isset($hesk_settings['custom_fields'][$k]))
{
$v = $hesk_settings['custom_fields'][$k];
switch ($v['type'])
{
case 'checkbox':
$ticket[$k] = str_replace("<br />","\n",$ticket[$k]);
break;
case 'date':
$ticket[$k] = hesk_custom_date_display_format($ticket[$k], $v['value']['date_format']);
break;
}
$msg = str_replace('%%'.strtoupper($k).'%%',stripslashes($ticket[$k]),$msg);
}
else
{
$msg = str_replace('%%'.strtoupper($k).'%%','',$msg);
}
}
// Is message tag in email template?
if (strpos($msg, '%%MESSAGE%%') !== false)
{
// Replace message
$msg = str_replace('%%MESSAGE%%',$ticket['message'],$msg);
// Add direct links to any attachments at the bottom of the email message
if ($hesk_settings['attachments']['use'] && isset($ticket['attachments']) && strlen($ticket['attachments']) )
{
$msg .= "\n\n\n" . $hesklang['fatt'];
$att = explode(',', substr($ticket['attachments'], 0, -1));
foreach ($att as $myatt)
{
list($att_id, $att_name) = explode('#', $myatt);
$msg .= "\n\n" . $att_name . "\n" . $hesk_settings['hesk_url'] . '/download_attachment.php?att_id='.$att_id.'&track='.$ticket['trackid'].$hesk_settings['e_param'];
}
}
// For customer notifications: if we allow email piping/pop 3 fetching and
// stripping quoted replies add an "reply above this line" tag
if ( ! $is_admin && ($hesk_settings['email_piping'] || $hesk_settings['pop3']) && $hesk_settings['strip_quoted'])
{
$msg = $hesklang['EMAIL_HR'] . "\n\n" . $msg;
}
}
return $msg;
} // END hesk_getEmailMessage
function hesk_encodeIfNotAscii($str)
{
// Match anything outside of ASCII range
if (preg_match('/[^\x00-\x7F]/', $str))
{
return "=?UTF-8?B?" . base64_encode($str) . "?=";
}
return $str;
} // END hesk_encodeIfNotAscii()
function hesk_generateMessageID()
{
if (function_exists('openssl_random_pseudo_bytes'))
{
$id = base_convert(bin2hex(openssl_random_pseudo_bytes(8)), 16, 36);
}
else
{
$id = uniqid('', true);
}
// If run from CLI, set the Hesk URL as host name
if (isset($_SERVER['SERVER_NAME']))
{
$host = $_SERVER['SERVER_NAME'];
}
else
{
global $hesk_settings;
$parts = parse_url($hesk_settings['hesk_url']);
if (empty($parts['host']))
{
$host = gethostname();
$host = str_replace('>', '', $host);
}
else
{
$host = $parts['host'];
}
}
return '<' . $id . '.' . gmdate('YmdHis') . '@' . $host . '>';
} // END hesk_generateMessageID()

View File

@@ -0,0 +1,420 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
/*** FUNCTIONS ***/
function hesk_export_to_XML($sql, $export_selected = false)
{
global $hesk_settings, $hesklang, $ticket, $my_cat;
// We'll need HH:MM:SS format for hesk_date() here
$hesk_settings['timeformat'] = 'H:i:s';
// Get staff names
$admins = array();
$result = hesk_dbQuery("SELECT `id`,`name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ORDER BY `name` ASC");
while ($row=hesk_dbFetchAssoc($result))
{
$admins[$row['id']]=$row['name'];
}
// Get category names
if ( ! isset($my_cat))
{
$my_cat = array();
$res2 = hesk_dbQuery("SELECT `id`, `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."categories` WHERE " . hesk_myCategories('id') . " ORDER BY `cat_order` ASC");
while ($row=hesk_dbFetchAssoc($res2))
{
$my_cat[$row['id']] = hesk_msgToPlain($row['name'], 1);
}
}
// This will be the export directory
$export_dir = HESK_PATH.$hesk_settings['cache_dir'].'/';
// This will be the name of the export and the XML file
$export_name = 'hesk_export_'.date('Y-m-d_H-i-s').'_'.mt_rand(100000,999999);
$save_to = $export_dir . $export_name . '.xml';
// Do we have the export directory?
if ( is_dir($export_dir) || ( @mkdir($export_dir, 0777) && is_writable($export_dir) ) )
{
// Is there an index.htm file?
if ( ! file_exists($export_dir.'index.htm'))
{
@file_put_contents($export_dir.'index.htm', '');
}
// Cleanup old files
hesk_purge_cache('export', 86400);
}
else
{
hesk_error($hesklang['ede']);
}
// Make sure the file can be saved and written to
@file_put_contents($save_to, '');
if ( ! file_exists($save_to) )
{
hesk_error($hesklang['eef']);
}
// Start generating the report message and generating the export
$success_msg = '';
$flush_me = '<br /><br />';
$flush_me .= hesk_date() . " | {$hesklang['inite']} ";
// Is this export of a date or date range?
if ($export_selected === false)
{
global $date_from, $date_to;
if ($date_from == $date_to)
{
$flush_me .= "(" . hesk_dateToString($date_from,0) . ")";
}
else
{
$flush_me .= "(" . hesk_dateToString($date_from,0) . " - " . hesk_dateToString($date_to,0) . ")";
}
}
$flush_me .= "<br />\n";
// Start generating file contents
$tmp = '<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>8250</WindowHeight>
<WindowWidth>16275</WindowWidth>
<WindowTopX>360</WindowTopX>
<WindowTopY>90</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:CharSet="238" x:Family="Swiss" ss:Size="11"
ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62">
<NumberFormat ss:Format="General Date"/>
</Style>
<Style ss:ID="s63">
<NumberFormat ss:Format="Short Date"/>
</Style>
<Style ss:ID="s65">
<NumberFormat ss:Format="[h]:mm:ss"/>
</Style>
</Styles>
<Worksheet ss:Name="Sheet1">
<Table>
';
// Define column width
$tmp .= '
<Column ss:AutoFitWidth="0" ss:Width="50"/>
<Column ss:AutoFitWidth="0" ss:Width="84" ss:Span="1"/>
<Column ss:AutoFitWidth="0" ss:Width="110"/>
<Column ss:AutoFitWidth="0" ss:Width="110"/>
<Column ss:AutoFitWidth="0" ss:Width="90"/>
<Column ss:AutoFitWidth="0" ss:Width="90"/>
<Column ss:AutoFitWidth="0" ss:Width="87"/>
<Column ss:AutoFitWidth="0" ss:Width="57.75"/>
<Column ss:AutoFitWidth="0" ss:Width="57.75"/>
<Column ss:AutoFitWidth="0" ss:Width="100"/>
<Column ss:AutoFitWidth="0" ss:Width="100"/>
<Column ss:AutoFitWidth="0" ss:Width="80"/>
<Column ss:AutoFitWidth="0" ss:Width="80"/>
';
foreach ($hesk_settings['custom_fields'] as $k=>$v)
{
if ($v['use'])
{
$tmp .= '<Column ss:AutoFitWidth="0" ss:Width="80"/>' . "\n";
}
}
// Define first row (header)
$tmp .= '
<Row>
<Cell><Data ss:Type="String">#</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['trackID'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['date'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['last_update'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['name'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['email'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['category'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['priority'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['status'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['subject'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['message'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['owner'].'</Data></Cell>
<Cell><Data ss:Type="String">'.$hesklang['ts'].'</Data></Cell>
';
foreach ($hesk_settings['custom_fields'] as $k=>$v)
{
if ($v['use'])
{
$tmp .= '<Cell><Data ss:Type="String">'.$v['name'].'</Data></Cell>' . "\n";
}
}
$tmp .= "</Row>\n";
// Write what we have by now into the XML file
file_put_contents($save_to, $tmp, FILE_APPEND);
$flush_me .= hesk_date() . " | {$hesklang['gXML']}<br />\n";
// OK, now start dumping data and writing it into the file
$tickets_exported = 0;
$save_after = 100;
$this_round = 0;
$tmp = '';
$result = hesk_dbQuery($sql);
while ($ticket=hesk_dbFetchAssoc($result))
{
$ticket['status'] = hesk_get_status_name($ticket['status']);
switch ($ticket['priority'])
{
case 0:
$ticket['priority']=$hesklang['critical'];
break;
case 1:
$ticket['priority']=$hesklang['high'];
break;
case 2:
$ticket['priority']=$hesklang['medium'];
break;
default:
$ticket['priority']=$hesklang['low'];
}
$ticket['archive'] = !($ticket['archive']) ? $hesklang['no'] : $hesklang['yes'];
$ticket['message'] = hesk_msgToPlain($ticket['message'], 1, 0);
$ticket['subject'] = hesk_msgToPlain($ticket['subject'], 1, 0);
$ticket['owner'] = isset($admins[$ticket['owner']]) ? $admins[$ticket['owner']] : '';
$ticket['category'] = isset($my_cat[$ticket['category']]) ? $my_cat[$ticket['category']] : '';
// Format for export dates
$hesk_settings['timeformat'] = "Y-m-d\TH:i:s\.000";
// Create row for the XML file
$tmp .= '
<Row>
<Cell><Data ss:Type="Number">'.$ticket['id'].'</Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['trackid'].']]></Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['dt'], true).'</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="DateTime">'.hesk_date($ticket['lastchange'], true).'</Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.hesk_msgToPlain($ticket['name'], 1).']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['email'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['category'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['priority'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['status'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['subject'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['message'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['owner'].']]></Data></Cell>
<Cell><Data ss:Type="String"><![CDATA['.$ticket['time_worked'].']]></Data></Cell>
';
// Add custom fields
foreach ($hesk_settings['custom_fields'] as $k=>$v)
{
if ($v['use'])
{
switch ($v['type'])
{
case 'date':
$tmp_dt = hesk_custom_date_display_format($ticket[$k], 'Y-m-d\T00:00:00.000');
$tmp .= strlen($tmp_dt) ? '<Cell ss:StyleID="s63"><Data ss:Type="DateTime">'.$tmp_dt : '<Cell><Data ss:Type="String">';
$tmp .= "</Data></Cell> \n";
break;
default:
$tmp .= '<Cell><Data ss:Type="String"><![CDATA['.hesk_msgToPlain($ticket[$k], 1, 0).']]></Data></Cell> ' . "\n";
}
}
}
$tmp .= "</Row>\n";
// Write every 100 rows into the file
if ($this_round >= $save_after)
{
file_put_contents($save_to, $tmp, FILE_APPEND);
$this_round = 0;
$tmp = '';
usleep(1);
}
$tickets_exported++;
$this_round++;
} // End of while loop
// Go back to the HH:MM:SS format for hesk_date()
$hesk_settings['timeformat'] = 'H:i:s';
// Append any remaining rows into the file
if ($this_round > 0)
{
file_put_contents($save_to, $tmp, FILE_APPEND);
}
// If any tickets were exported, continue, otherwise cleanup
if ($tickets_exported > 0)
{
// Finish the XML file
$tmp = '
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<Selected/>
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>4</ActiveRow>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet2">
<Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet3">
<Table ss:ExpandedColumnCount="1" ss:ExpandedRowCount="1" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<PageSetup>
<Header x:Margin="0.3"/>
<Footer x:Margin="0.3"/>
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
</PageSetup>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
';
file_put_contents($save_to, $tmp, FILE_APPEND);
// Log how many rows we exported
$flush_me .= hesk_date() . " | " . sprintf($hesklang['nrow'], $tickets_exported) . "<br />\n";
// We will convert XML to Zip to save a lot of space
$save_to_zip = $export_dir.$export_name.'.zip';
// Log start of Zip creation
$flush_me .= hesk_date() . " | {$hesklang['cZIP']}<br />\n";
// Preferrably use the zip extension
if (extension_loaded('zip'))
{
$save_to_zip = $export_dir.$export_name.'.zip';
$zip = new ZipArchive;
$res = $zip->open($save_to_zip, ZipArchive::CREATE);
if ($res === TRUE)
{
$zip->addFile($save_to, "{$export_name}.xml");
$zip->close();
}
else
{
die("{$hesklang['eZIP']} <$save_to_zip>\n");
}
}
// Some servers have ZipArchive class enabled anyway - can we use it?
elseif ( class_exists('ZipArchive') )
{
require(HESK_PATH . 'inc/zip/Zip.php');
$zip = new Zip();
$zip->addLargeFile($save_to, "{$export_name}.xml");
$zip->finalize();
$zip->setZipFile($save_to_zip);
}
// If not available, use a 3rd party Zip class included with HESK
else
{
require(HESK_PATH . 'inc/zip/pclzip.lib.php');
$zip = new PclZip($save_to_zip);
$zip->add($save_to, PCLZIP_OPT_REMOVE_ALL_PATH);
}
// Delete XML, just leave the Zip archive
hesk_unlink($save_to);
// Echo memory peak usage
$flush_me .= hesk_date() . " | " . sprintf($hesklang['pmem'], (@memory_get_peak_usage(true) / 1048576)) . "<br />\r\n";
// We're done!
$flush_me .= hesk_date() . " | {$hesklang['fZIP']}<br /><br />";
// Success message
$referer = isset($_SERVER['HTTP_REFERER']) ? hesk_input($_SERVER['HTTP_REFERER']) : 'export.php';
$referer = str_replace('&amp;','&',$referer);
if (strpos($referer, 'export.php'))
{
$referer = 'export.php';
}
$success_msg .= $hesk_settings['debug_mode'] ? $flush_me : '<br /><br />';
$success_msg .= $hesklang['step1'] . ': <a href="' . $save_to_zip . '">' . $hesklang['ch2d'] . '</a><br /><br />' . $hesklang['step2'] . ': <a href="export.php?delete='.urlencode($export_name).'&amp;goto='.urlencode($referer).'">' . $hesklang['dffs'] . '</a>';
}
// No tickets exported, cleanup
else
{
hesk_unlink($save_to);
}
return array($success_msg, $tickets_exported);
} // END hesk_export_to_XML()

94
hesk/inc/footer.inc.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
// Check if this is a valid include
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// Users online
if (defined('SHOW_ONLINE'))
{
hesk_printOnline();
}
/*******************************************************************************
The code below handles HESK licensing and must be included in the template.
Removing this code is a direct violation of the HESK End User License Agreement,
will void all support and may result in unexpected behavior.
To purchase a HESK license and support future HESK development please visit:
https://www.hesk.com/buy.php
*******************************************************************************/
$hesk_settings['hesk_license']('Qo8Zm9vdGVyIGNsYXNzPSJmb290ZXIiPg0KICAgIDxwIGNsY
XNzPSJ0ZXh0LWNlbnRlciI+UG93ZXJlZCBieSA8YSBocmVmPSJodHRwczovL3d3dy5oZXNrLmNvbSIgY
2xhc3M9ImxpbmsiPkhlbHAgRGVzayBTb2Z0d2FyZTwvYT4gPHNwYW4gY2xhc3M9ImZvbnQtd2VpZ2h0L
WJvbGQiPkhFU0s8L3NwYW4+LCBpbiBwYXJ0bmVyc2hpcCB3aXRoIDxhIGhyZWY9Imh0dHBzOi8vd3d3L
nN5c2FpZC5jb20vP3V0bV9zb3VyY2U9SGVzayZhbXA7dXRtX21lZGl1bT1jcGMmYW1wO3V0bV9jYW1wY
Wlnbj1IZXNrUHJvZHVjdF9Ub19IUCIgY2xhc3M9ImxpbmsiPlN5c0FpZCBUZWNobm9sb2dpZXM8L2E+P
C9wPg0KPC9mb290ZXI+DQo=',"\104", "347db01e129edd4b3877f70ea6fed019462ae827");
/*******************************************************************************
END LICENSE CODE
*******************************************************************************/
?>
</main> <!-- End main -->
<?php
if (isset($login_wrapper)) {
echo '</div> <!-- End wrapper login -->';
}
?>
</div> <!-- End wrapper -->
<input type="hidden" name="HESK_PATH" value="<?php echo HESK_PATH; ?>">
<script src="<?php echo HESK_PATH; ?>js/svg4everybody.min.js"></script>
<script src="<?php echo HESK_PATH; ?>js/jquery.scrollbar.min.js"></script>
<script src="<?php echo HESK_PATH; ?>js/selectize.min.js"></script>
<script src="<?php echo HESK_PATH; ?>js/datepicker.min.js"></script>
<script src="<?php echo HESK_PATH; ?>js/datepicker.en.js"></script>
<script src="<?php echo HESK_PATH; ?>js/jquery.autocomplete.js"></script>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/app<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.js"></script>
<?php
// Auto-select first empty or error field on non-staff pages?
if (defined('AUTOFOCUS'))
{
?>
<script language="javascript">
(function(){
var forms = document.forms || [];
for(var i = 0; i < forms.length; i++)
{
for(var j = 0; j < forms[i].length; j++)
{
if(
!forms[i][j].readonly != undefined &&
forms[i][j].type != "hidden" &&
forms[i][j].disabled != true &&
forms[i][j].style.display != 'none' &&
(forms[i][j].className == 'isError' || forms[i][j].className == 'isNotice' || forms[i][j].value == '')
)
{
forms[i][j].focus();
return;
}
}
}
})();
</script>
<?php
}
echo '
</body>
</html>
';
$hesk_settings['security_cleanup']('exit');

143
hesk/inc/header.inc.php Normal file
View File

@@ -0,0 +1,143 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// We'll use this later
$onload='';
?>
<!DOCTYPE html>
<html lang="<?php echo $hesk_settings['languages'][$hesk_settings['language']]['folder'] ?>">
<head>
<title><?php echo (isset($hesk_settings['tmp_title']) ? $hesk_settings['tmp_title'] : $hesk_settings['hesk_title']); ?></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" media="all" href="<?php echo HESK_PATH; ?>css/app<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.css?<?php echo $hesk_settings['hesk_version']; ?>">
<script src="<?php echo HESK_PATH; ?>js/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/hesk_javascript<?php echo $hesk_settings['debug_mode'] ? '' : '.min'; ?>.js?<?php echo $hesk_settings['hesk_version']; ?>"></script>
<?php
/* Tickets shouldn't be indexed by search engines */
if (defined('HESK_NO_ROBOTS'))
{
?>
<meta name="robots" content="noindex, nofollow" />
<?php
}
/* If page requires WYSIWYG editor include TinyMCE Javascript */
if (defined('WYSIWYG') && $hesk_settings['kb_wysiwyg'])
{
?>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>inc/tiny_mce/5.2.0/tinymce.min.js"></script>
<?php
}
/* If page requires timer load Javascript */
if (defined('TIMER'))
{
?>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>inc/timer/hesk_timer.js"></script>
<?php
/* Need to load default time or a custom one? */
if ( isset($_SESSION['time_worked']) )
{
$t = hesk_getHHMMSS($_SESSION['time_worked']);
$onload .= "load_timer('time_worked', " . $t[0] . ", " . $t[1] . ", " . $t[2] . ");";
unset($t);
}
else
{
$onload .= "load_timer('time_worked', 0, 0, 0);";
}
/* Autostart timer? */
if ( ! empty($_SESSION['autostart']) )
{
$onload .= "ss();";
}
}
// Use ReCaptcha
if (defined('RECAPTCHA'))
{
echo '<script src="https://www.google.com/recaptcha/api.js?hl='.$hesklang['RECAPTCHA'].'" async defer></script>';
echo '<script language="Javascript" type="text/javascript">
function recaptcha_submitForm() {
document.getElementById("form1").submit();
}
</script>';
}
// Auto reload
if (defined('AUTO_RELOAD') && hesk_checkPermission('can_view_tickets',0) && ! isset($_SESSION['hide']['ticket_list']) )
{
?>
<script type="text/javascript">
var count = <?php echo empty($_SESSION['autoreload']) ? 30 : intval($_SESSION['autoreload']); ?>;
var reloadcounter;
var countstart = count;
function heskReloadTimer()
{
count=count-1;
if (count <= 0)
{
clearInterval(reloadcounter);
window.location.reload();
return;
}
document.getElementById("timer").innerHTML = "(" + count + ")";
}
function heskCheckReloading()
{
if (<?php if ($_SESSION['autoreload']) echo "getCookie('autorefresh') == null || "; ?>getCookie('autorefresh') == '1')
{
document.getElementById("reloadCB").checked=true;
document.getElementById("timer").innerHTML = "(" + count + ")";
reloadcounter = setInterval(heskReloadTimer, 1000);
}
}
function toggleAutoRefresh(cb)
{
if (cb.checked)
{
setCookie('autorefresh', '1');
document.getElementById("timer").innerHTML = "(" + count + ")";
reloadcounter = setInterval(heskReloadTimer, 1000);
}
else
{
setCookie('autorefresh', '0');
count = countstart;
clearInterval(reloadcounter);
document.getElementById("timer").innerHTML = "";
}
}
</script>
<?php
}
?>
</head>
<body onload="<?php echo $onload; unset($onload); ?>">
<div class="wrapper">

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
require(HESK_PATH . 'inc/htmlpurifier/HTMLPurifier.standalone.php');
class HeskHTMLPurifier extends HTMLPurifier
{
private $allowIframes;
private $cacheDir;
public function __construct($cacheDir = 'cache', $allowIframes = 1)
{
$this->allowIframes = $allowIframes;
$this->cacheDir = $this->setupCacheDir($cacheDir);
}
public function heskPurify($content)
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Attr.AllowedRel', array('follow', 'referrer', 'nofollow', 'noreferrer') );
$config->set('Attr.AllowedFrameTargets', array('_blank', '_self', '_parent', '_top') );
$config->set('Cache.SerializerPath', $this->cacheDir);
$config->set('URI.AllowedSchemes', array(
'http' => true,
'https' => true,
'mailto' => true,
'ftp' => true,
'nntp' => true,
'news' => true,
'tel' => true,
'data' => true,
)
);
if ($this->allowIframes)
{
require(HESK_PATH . 'inc/htmlpurifier/custom/heskIframe.php');
$config->set('Filter.Custom', array(new HTMLPurifier_Filter_HeskIframe()));
}
$purifier = new HTMLPurifier($config);
return $purifier->purify($content);
}
private function setupCacheDir($cache_dir)
{
$cache_dir = dirname(dirname(dirname(__FILE__))).'/'.$cache_dir.'/hp';
if (is_dir($cache_dir) || ( @mkdir($cache_dir, 0777) && is_writable($cache_dir) ) )
{
return $cache_dir;
}
return null;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/**
* Based on: http://sachachua.com/blog/2011/08/drupal-html-purifier-embedding-iframes-youtube/
* Iframe filter that does some primitive whitelisting in a somewhat recognizable and tweakable way
*/
class HTMLPurifier_Filter_HeskIframe extends HTMLPurifier_Filter
{
public $name = 'HeskIframe';
/**
*
* @param string $html
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return string
*/
public function preFilter($html, $config, $context)
{
$html = preg_replace('#<iframe#i', '<img class="HeskIframe"', $html);
$html = preg_replace('#</iframe>#i', '</img>', $html);
return $html;
}
/**
*
* @param string $html
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return string
*/
public function postFilter($html, $config, $context)
{
$post_regex = '#<img class="HeskIframe"([^>]+?)/?>#';
return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html);
}
/**
*
* @param array $matches
* @return string
*/
protected function postFilterCallback($matches)
{
// Domain Whitelist
$youTubeMatch = preg_match('#src="https?://www.youtube(-nocookie)?.com/#i', $matches[1]);
$vimeoMatch = preg_match('#src="https?://player.vimeo.com/#i', $matches[1]);
if ($youTubeMatch || $vimeoMatch) {
$extra = ' frameborder="0"';
if ($youTubeMatch) {
$extra .= ' allowfullscreen';
} elseif ($vimeoMatch) {
$extra .= ' webkitAllowFullScreen mozallowfullscreen allowFullScreen';
}
return '<iframe ' . $matches[1] . $extra . '></iframe>';
} else {
return '';
}
}
}

View File

@@ -0,0 +1,8 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
You don't have permission to access this folder.<P>
<hr />
</BODY></HTML>

View File

@@ -0,0 +1,8 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
You don't have permission to access this folder.<P>
<hr />
</BODY></HTML>

View File

@@ -0,0 +1,48 @@
<?php
/**
* Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
* representation used to perform checks on user configuration.
*/
class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
{
/**
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
* @return HTMLPurifier_ConfigSchema
*/
public function build($interchange)
{
$schema = new HTMLPurifier_ConfigSchema();
foreach ($interchange->directives as $d) {
$schema->add(
$d->id->key,
$d->default,
$d->type,
$d->typeAllowsNull
);
if ($d->allowed !== null) {
$schema->addAllowedValues(
$d->id->key,
$d->allowed
);
}
foreach ($d->aliases as $alias) {
$schema->addAlias(
$alias->key,
$d->id->key
);
}
if ($d->valueAliases !== null) {
$schema->addValueAliases(
$d->id->key,
$d->valueAliases
);
}
}
$schema->postProcess();
return $schema;
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,144 @@
<?php
/**
* Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
* which can be further processed to generate documentation.
*/
class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
{
/**
* @type HTMLPurifier_ConfigSchema_Interchange
*/
protected $interchange;
/**
* @type string
*/
private $namespace;
/**
* @param string $html
*/
protected function writeHTMLDiv($html)
{
$this->startElement('div');
$purifier = HTMLPurifier::getInstance();
$html = $purifier->purify($html);
$this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
$this->writeRaw($html);
$this->endElement(); // div
}
/**
* @param mixed $var
* @return string
*/
protected function export($var)
{
if ($var === array()) {
return 'array()';
}
return var_export($var, true);
}
/**
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
*/
public function build($interchange)
{
// global access, only use as last resort
$this->interchange = $interchange;
$this->setIndent(true);
$this->startDocument('1.0', 'UTF-8');
$this->startElement('configdoc');
$this->writeElement('title', $interchange->name);
foreach ($interchange->directives as $directive) {
$this->buildDirective($directive);
}
if ($this->namespace) {
$this->endElement();
} // namespace
$this->endElement(); // configdoc
$this->flush();
}
/**
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
*/
public function buildDirective($directive)
{
// Kludge, although I suppose having a notion of a "root namespace"
// certainly makes things look nicer when documentation is built.
// Depends on things being sorted.
if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
if ($this->namespace) {
$this->endElement();
} // namespace
$this->namespace = $directive->id->getRootNamespace();
$this->startElement('namespace');
$this->writeAttribute('id', $this->namespace);
$this->writeElement('name', $this->namespace);
}
$this->startElement('directive');
$this->writeAttribute('id', $directive->id->toString());
$this->writeElement('name', $directive->id->getDirective());
$this->startElement('aliases');
foreach ($directive->aliases as $alias) {
$this->writeElement('alias', $alias->toString());
}
$this->endElement(); // aliases
$this->startElement('constraints');
if ($directive->version) {
$this->writeElement('version', $directive->version);
}
$this->startElement('type');
if ($directive->typeAllowsNull) {
$this->writeAttribute('allow-null', 'yes');
}
$this->text($directive->type);
$this->endElement(); // type
if ($directive->allowed) {
$this->startElement('allowed');
foreach ($directive->allowed as $value => $x) {
$this->writeElement('value', $value);
}
$this->endElement(); // allowed
}
$this->writeElement('default', $this->export($directive->default));
$this->writeAttribute('xml:space', 'preserve');
if ($directive->external) {
$this->startElement('external');
foreach ($directive->external as $project) {
$this->writeElement('project', $project);
}
$this->endElement();
}
$this->endElement(); // constraints
if ($directive->deprecatedVersion) {
$this->startElement('deprecated');
$this->writeElement('version', $directive->deprecatedVersion);
$this->writeElement('use', $directive->deprecatedUse->toString());
$this->endElement(); // deprecated
}
$this->startElement('description');
$this->writeHTMLDiv($directive->description);
$this->endElement(); // description
$this->endElement(); // directive
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
<?php
/**
* Exceptions related to configuration schema
*/
class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception
{
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,47 @@
<?php
/**
* Generic schema interchange format that can be converted to a runtime
* representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members
* are completely validated.
*/
class HTMLPurifier_ConfigSchema_Interchange
{
/**
* Name of the application this schema is describing.
* @type string
*/
public $name;
/**
* Array of Directive ID => array(directive info)
* @type HTMLPurifier_ConfigSchema_Interchange_Directive[]
*/
public $directives = array();
/**
* Adds a directive array to $directives
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
* @throws HTMLPurifier_ConfigSchema_Exception
*/
public function addDirective($directive)
{
if (isset($this->directives[$i = $directive->id->toString()])) {
throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
}
$this->directives[$i] = $directive;
}
/**
* Convenience function to perform standard validation. Throws exception
* on failed validation.
*/
public function validate()
{
$validator = new HTMLPurifier_ConfigSchema_Validator();
return $validator->validate($this);
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,89 @@
<?php
/**
* Interchange component class describing configuration directives.
*/
class HTMLPurifier_ConfigSchema_Interchange_Directive
{
/**
* ID of directive.
* @type HTMLPurifier_ConfigSchema_Interchange_Id
*/
public $id;
/**
* Type, e.g. 'integer' or 'istring'.
* @type string
*/
public $type;
/**
* Default value, e.g. 3 or 'DefaultVal'.
* @type mixed
*/
public $default;
/**
* HTML description.
* @type string
*/
public $description;
/**
* Whether or not null is allowed as a value.
* @type bool
*/
public $typeAllowsNull = false;
/**
* Lookup table of allowed scalar values.
* e.g. array('allowed' => true).
* Null if all values are allowed.
* @type array
*/
public $allowed;
/**
* List of aliases for the directive.
* e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))).
* @type HTMLPurifier_ConfigSchema_Interchange_Id[]
*/
public $aliases = array();
/**
* Hash of value aliases, e.g. array('alt' => 'real'). Null if value
* aliasing is disabled (necessary for non-scalar types).
* @type array
*/
public $valueAliases;
/**
* Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
* Null if the directive has always existed.
* @type string
*/
public $version;
/**
* ID of directive that supercedes this old directive.
* Null if not deprecated.
* @type HTMLPurifier_ConfigSchema_Interchange_Id
*/
public $deprecatedUse;
/**
* Version of HTML Purifier this directive was deprecated. Null if not
* deprecated.
* @type string
*/
public $deprecatedVersion;
/**
* List of external projects this directive depends on, e.g. array('CSSTidy').
* @type array
*/
public $external = array();
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,58 @@
<?php
/**
* Represents a directive ID in the interchange format.
*/
class HTMLPurifier_ConfigSchema_Interchange_Id
{
/**
* @type string
*/
public $key;
/**
* @param string $key
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* @return string
* @warning This is NOT magic, to ensure that people don't abuse SPL and
* cause problems for PHP 5.0 support.
*/
public function toString()
{
return $this->key;
}
/**
* @return string
*/
public function getRootNamespace()
{
return substr($this->key, 0, strpos($this->key, "."));
}
/**
* @return string
*/
public function getDirective()
{
return substr($this->key, strpos($this->key, ".") + 1);
}
/**
* @param string $id
* @return HTMLPurifier_ConfigSchema_Interchange_Id
*/
public static function make($id)
{
return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,226 @@
<?php
class HTMLPurifier_ConfigSchema_InterchangeBuilder
{
/**
* Used for processing DEFAULT, nothing else.
* @type HTMLPurifier_VarParser
*/
protected $varParser;
/**
* @param HTMLPurifier_VarParser $varParser
*/
public function __construct($varParser = null)
{
$this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
}
/**
* @param string $dir
* @return HTMLPurifier_ConfigSchema_Interchange
*/
public static function buildFromDirectory($dir = null)
{
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
return $builder->buildDir($interchange, $dir);
}
/**
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
* @param string $dir
* @return HTMLPurifier_ConfigSchema_Interchange
*/
public function buildDir($interchange, $dir = null)
{
if (!$dir) {
$dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
}
if (file_exists($dir . '/info.ini')) {
$info = parse_ini_file($dir . '/info.ini');
$interchange->name = $info['name'];
}
$files = array();
$dh = opendir($dir);
while (false !== ($file = readdir($dh))) {
if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
continue;
}
$files[] = $file;
}
closedir($dh);
sort($files);
foreach ($files as $file) {
$this->buildFile($interchange, $dir . '/' . $file);
}
return $interchange;
}
/**
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
* @param string $file
*/
public function buildFile($interchange, $file)
{
$parser = new HTMLPurifier_StringHashParser();
$this->build(
$interchange,
new HTMLPurifier_StringHash($parser->parseFile($file))
);
}
/**
* Builds an interchange object based on a hash.
* @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build
* @param HTMLPurifier_StringHash $hash source data
* @throws HTMLPurifier_ConfigSchema_Exception
*/
public function build($interchange, $hash)
{
if (!$hash instanceof HTMLPurifier_StringHash) {
$hash = new HTMLPurifier_StringHash($hash);
}
if (!isset($hash['ID'])) {
throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
}
if (strpos($hash['ID'], '.') === false) {
if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
$hash->offsetGet('DESCRIPTION'); // prevent complaining
} else {
throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
}
} else {
$this->buildDirective($interchange, $hash);
}
$this->_findUnused($hash);
}
/**
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
* @param HTMLPurifier_StringHash $hash
* @throws HTMLPurifier_ConfigSchema_Exception
*/
public function buildDirective($interchange, $hash)
{
$directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
// These are required elements:
$directive->id = $this->id($hash->offsetGet('ID'));
$id = $directive->id->toString(); // convenience
if (isset($hash['TYPE'])) {
$type = explode('/', $hash->offsetGet('TYPE'));
if (isset($type[1])) {
$directive->typeAllowsNull = true;
}
$directive->type = $type[0];
} else {
throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
}
if (isset($hash['DEFAULT'])) {
try {
$directive->default = $this->varParser->parse(
$hash->offsetGet('DEFAULT'),
$directive->type,
$directive->typeAllowsNull
);
} catch (HTMLPurifier_VarParserException $e) {
throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
}
}
if (isset($hash['DESCRIPTION'])) {
$directive->description = $hash->offsetGet('DESCRIPTION');
}
if (isset($hash['ALLOWED'])) {
$directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
}
if (isset($hash['VALUE-ALIASES'])) {
$directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
}
if (isset($hash['ALIASES'])) {
$raw_aliases = trim($hash->offsetGet('ALIASES'));
$aliases = preg_split('/\s*,\s*/', $raw_aliases);
foreach ($aliases as $alias) {
$directive->aliases[] = $this->id($alias);
}
}
if (isset($hash['VERSION'])) {
$directive->version = $hash->offsetGet('VERSION');
}
if (isset($hash['DEPRECATED-USE'])) {
$directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
}
if (isset($hash['DEPRECATED-VERSION'])) {
$directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
}
if (isset($hash['EXTERNAL'])) {
$directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
}
$interchange->addDirective($directive);
}
/**
* Evaluates an array PHP code string without array() wrapper
* @param string $contents
*/
protected function evalArray($contents)
{
return eval('return array(' . $contents . ');');
}
/**
* Converts an array list into a lookup array.
* @param array $array
* @return array
*/
protected function lookup($array)
{
$ret = array();
foreach ($array as $val) {
$ret[$val] = true;
}
return $ret;
}
/**
* Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
* object based on a string Id.
* @param string $id
* @return HTMLPurifier_ConfigSchema_Interchange_Id
*/
protected function id($id)
{
return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
}
/**
* Triggers errors for any unused keys passed in the hash; such keys
* may indicate typos, missing values, etc.
* @param HTMLPurifier_StringHash $hash Hash to check.
*/
protected function _findUnused($hash)
{
$accessed = $hash->getAccessed();
foreach ($hash as $k => $v) {
if (!isset($accessed[$k])) {
trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
}
}
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,248 @@
<?php
/**
* Performs validations on HTMLPurifier_ConfigSchema_Interchange
*
* @note If you see '// handled by InterchangeBuilder', that means a
* design decision in that class would prevent this validation from
* ever being necessary. We have them anyway, however, for
* redundancy.
*/
class HTMLPurifier_ConfigSchema_Validator
{
/**
* @type HTMLPurifier_ConfigSchema_Interchange
*/
protected $interchange;
/**
* @type array
*/
protected $aliases;
/**
* Context-stack to provide easy to read error messages.
* @type array
*/
protected $context = array();
/**
* to test default's type.
* @type HTMLPurifier_VarParser
*/
protected $parser;
public function __construct()
{
$this->parser = new HTMLPurifier_VarParser();
}
/**
* Validates a fully-formed interchange object.
* @param HTMLPurifier_ConfigSchema_Interchange $interchange
* @return bool
*/
public function validate($interchange)
{
$this->interchange = $interchange;
$this->aliases = array();
// PHP is a bit lax with integer <=> string conversions in
// arrays, so we don't use the identical !== comparison
foreach ($interchange->directives as $i => $directive) {
$id = $directive->id->toString();
if ($i != $id) {
$this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
}
$this->validateDirective($directive);
}
return true;
}
/**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
* @param HTMLPurifier_ConfigSchema_Interchange_Id $id
*/
public function validateId($id)
{
$id_string = $id->toString();
$this->context[] = "id '$id_string'";
if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
// handled by InterchangeBuilder
$this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
}
// keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
// we probably should check that it has at least one namespace
$this->with($id, 'key')
->assertNotEmpty()
->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
array_pop($this->context);
}
/**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
*/
public function validateDirective($d)
{
$id = $d->id->toString();
$this->context[] = "directive '$id'";
$this->validateId($d->id);
$this->with($d, 'description')
->assertNotEmpty();
// BEGIN - handled by InterchangeBuilder
$this->with($d, 'type')
->assertNotEmpty();
$this->with($d, 'typeAllowsNull')
->assertIsBool();
try {
// This also tests validity of $d->type
$this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
} catch (HTMLPurifier_VarParserException $e) {
$this->error('default', 'had error: ' . $e->getMessage());
}
// END - handled by InterchangeBuilder
if (!is_null($d->allowed) || !empty($d->valueAliases)) {
// allowed and valueAliases require that we be dealing with
// strings, so check for that early.
$d_int = HTMLPurifier_VarParser::$types[$d->type];
if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
$this->error('type', 'must be a string type when used with allowed or value aliases');
}
}
$this->validateDirectiveAllowed($d);
$this->validateDirectiveValueAliases($d);
$this->validateDirectiveAliases($d);
array_pop($this->context);
}
/**
* Extra validation if $allowed member variable of
* HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
*/
public function validateDirectiveAllowed($d)
{
if (is_null($d->allowed)) {
return;
}
$this->with($d, 'allowed')
->assertNotEmpty()
->assertIsLookup(); // handled by InterchangeBuilder
if (is_string($d->default) && !isset($d->allowed[$d->default])) {
$this->error('default', 'must be an allowed value');
}
$this->context[] = 'allowed';
foreach ($d->allowed as $val => $x) {
if (!is_string($val)) {
$this->error("value $val", 'must be a string');
}
}
array_pop($this->context);
}
/**
* Extra validation if $valueAliases member variable of
* HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
*/
public function validateDirectiveValueAliases($d)
{
if (is_null($d->valueAliases)) {
return;
}
$this->with($d, 'valueAliases')
->assertIsArray(); // handled by InterchangeBuilder
$this->context[] = 'valueAliases';
foreach ($d->valueAliases as $alias => $real) {
if (!is_string($alias)) {
$this->error("alias $alias", 'must be a string');
}
if (!is_string($real)) {
$this->error("alias target $real from alias '$alias'", 'must be a string');
}
if ($alias === $real) {
$this->error("alias '$alias'", "must not be an alias to itself");
}
}
if (!is_null($d->allowed)) {
foreach ($d->valueAliases as $alias => $real) {
if (isset($d->allowed[$alias])) {
$this->error("alias '$alias'", 'must not be an allowed value');
} elseif (!isset($d->allowed[$real])) {
$this->error("alias '$alias'", 'must be an alias to an allowed value');
}
}
}
array_pop($this->context);
}
/**
* Extra validation if $aliases member variable of
* HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
* @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
*/
public function validateDirectiveAliases($d)
{
$this->with($d, 'aliases')
->assertIsArray(); // handled by InterchangeBuilder
$this->context[] = 'aliases';
foreach ($d->aliases as $alias) {
$this->validateId($alias);
$s = $alias->toString();
if (isset($this->interchange->directives[$s])) {
$this->error("alias '$s'", 'collides with another directive');
}
if (isset($this->aliases[$s])) {
$other_directive = $this->aliases[$s];
$this->error("alias '$s'", "collides with alias for directive '$other_directive'");
}
$this->aliases[$s] = $d->id->toString();
}
array_pop($this->context);
}
// protected helper functions
/**
* Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
* for validating simple member variables of objects.
* @param $obj
* @param $member
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
protected function with($obj, $member)
{
return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
}
/**
* Emits an error, providing helpful context.
* @throws HTMLPurifier_ConfigSchema_Exception
*/
protected function error($target, $msg)
{
if ($target !== false) {
$prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
} else {
$prefix = ucfirst($this->getFormattedContext());
}
throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
}
/**
* Returns a formatted context string.
* @return string
*/
protected function getFormattedContext()
{
return implode(' in ', array_reverse($this->context));
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,130 @@
<?php
/**
* Fluent interface for validating the contents of member variables.
* This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
* use-cases. We name this an 'atom' because it's ONLY for validations that
* are independent and usually scalar.
*/
class HTMLPurifier_ConfigSchema_ValidatorAtom
{
/**
* @type string
*/
protected $context;
/**
* @type object
*/
protected $obj;
/**
* @type string
*/
protected $member;
/**
* @type mixed
*/
protected $contents;
public function __construct($context, $obj, $member)
{
$this->context = $context;
$this->obj = $obj;
$this->member = $member;
$this->contents =& $obj->$member;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertIsString()
{
if (!is_string($this->contents)) {
$this->error('must be a string');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertIsBool()
{
if (!is_bool($this->contents)) {
$this->error('must be a boolean');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertIsArray()
{
if (!is_array($this->contents)) {
$this->error('must be an array');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertNotNull()
{
if ($this->contents === null) {
$this->error('must not be null');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertAlnum()
{
$this->assertIsString();
if (!ctype_alnum($this->contents)) {
$this->error('must be alphanumeric');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertNotEmpty()
{
if (empty($this->contents)) {
$this->error('must not be empty');
}
return $this;
}
/**
* @return HTMLPurifier_ConfigSchema_ValidatorAtom
*/
public function assertIsLookup()
{
$this->assertIsArray();
foreach ($this->contents as $v) {
if ($v !== true) {
$this->error('must be a lookup array');
}
}
return $this;
}
/**
* @param string $msg
* @throws HTMLPurifier_ConfigSchema_Exception
*/
protected function error($msg)
{
throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
}
}
// vim: et sw=4 sts=4

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
Attr.AllowedClasses
TYPE: lookup/null
VERSION: 4.0.0
DEFAULT: null
--DESCRIPTION--
List of allowed class values in the class attribute. By default, this is null,
which means all classes are allowed.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Attr.AllowedFrameTargets
TYPE: lookup
DEFAULT: array()
--DESCRIPTION--
Lookup table of all allowed link frame targets. Some commonly used link
targets include _blank, _self, _parent and _top. Values should be
lowercase, as validation will be done in a case-sensitive manner despite
W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute
so this directive will have no effect in that doctype. XHTML 1.1 does not
enable the Target module by default, you will have to manually enable it
(see the module documentation for more details.)
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
Attr.AllowedRel
TYPE: lookup
VERSION: 1.6.0
DEFAULT: array()
--DESCRIPTION--
List of allowed forward document relationships in the rel attribute. Common
values may be nofollow or print. By default, this is empty, meaning that no
document relationships are allowed.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
Attr.AllowedRev
TYPE: lookup
VERSION: 1.6.0
DEFAULT: array()
--DESCRIPTION--
List of allowed reverse document relationships in the rev attribute. This
attribute is a bit of an edge-case; if you don't know what it is for, stay
away.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,19 @@
Attr.ClassUseCDATA
TYPE: bool/null
DEFAULT: null
VERSION: 4.0.0
--DESCRIPTION--
If null, class will auto-detect the doctype and, if matching XHTML 1.1 or
XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise,
it will use a relaxed CDATA definition. If true, the relaxed CDATA definition
is forced; if false, the NMTOKENS definition is forced. To get behavior
of HTML Purifier prior to 4.0.0, set this directive to false.
Some rational behind the auto-detection:
in previous versions of HTML Purifier, it was assumed that the form of
class was NMTOKENS, as specified by the XHTML Modularization (representing
XHTML 1.1 and XHTML 2.0). The DTDs for HTML 4.01 and XHTML 1.0, however
specify class as CDATA. HTML 5 effectively defines it as CDATA, but
with the additional constraint that each name should be unique (this is not
explicitly outlined in previous specifications).
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Attr.DefaultImageAlt
TYPE: string/null
DEFAULT: null
VERSION: 3.2.0
--DESCRIPTION--
This is the content of the alt tag of an image if the user had not
previously specified an alt attribute. This applies to all images without
a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which
only applies to invalid images, and overrides in the case of an invalid image.
Default behavior with null is to use the basename of the src tag for the alt.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
Attr.DefaultInvalidImage
TYPE: string
DEFAULT: ''
--DESCRIPTION--
This is the default image an img tag will be pointed to if it does not have
a valid src attribute. In future versions, we may allow the image tag to
be removed completely, but due to design issues, this is not possible right
now.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,8 @@
Attr.DefaultInvalidImageAlt
TYPE: string
DEFAULT: 'Invalid image'
--DESCRIPTION--
This is the content of the alt tag of an invalid image if the user had not
previously specified an alt attribute. It has no effect when the image is
valid but there was no alt attribute present.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,10 @@
Attr.DefaultTextDir
TYPE: string
DEFAULT: 'ltr'
--DESCRIPTION--
Defines the default text direction (ltr or rtl) of the document being
parsed. This generally is the same as the value of the dir attribute in
HTML, or ltr if that is not specified.
--ALLOWED--
'ltr', 'rtl'
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Attr.EnableID
TYPE: bool
DEFAULT: false
VERSION: 1.2.0
--DESCRIPTION--
Allows the ID attribute in HTML. This is disabled by default due to the
fact that without proper configuration user input can easily break the
validation of a webpage by specifying an ID that is already on the
surrounding HTML. If you don't mind throwing caution to the wind, enable
this directive, but I strongly recommend you also consider blacklisting IDs
you use (%Attr.IDBlacklist) or prefixing all user supplied IDs
(%Attr.IDPrefix). When set to true HTML Purifier reverts to the behavior of
pre-1.2.0 versions.
--ALIASES--
HTML.EnableAttrID
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,8 @@
Attr.ForbiddenClasses
TYPE: lookup
VERSION: 4.0.0
DEFAULT: array()
--DESCRIPTION--
List of forbidden class values in the class attribute. By default, this is
empty, which means that no classes are forbidden. See also %Attr.AllowedClasses.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,10 @@
Attr.ID.HTML5
TYPE: bool/null
DEFAULT: null
VERSION: 4.8.0
--DESCRIPTION--
In HTML5, restrictions on the format of the id attribute have been significantly
relaxed, such that any string is valid so long as it contains no spaces and
is at least one character. In lieu of a general HTML5 compatibility flag,
set this configuration directive to true to use the relaxed rules.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,5 @@
Attr.IDBlacklist
TYPE: list
DEFAULT: array()
DESCRIPTION: Array of IDs not allowed in the document.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
Attr.IDBlacklistRegexp
TYPE: string/null
VERSION: 1.6.0
DEFAULT: NULL
--DESCRIPTION--
PCRE regular expression to be matched against all IDs. If the expression is
matches, the ID is rejected. Use this with care: may cause significant
degradation. ID matching is done after all other validation.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Attr.IDPrefix
TYPE: string
VERSION: 1.2.0
DEFAULT: ''
--DESCRIPTION--
String to prefix to IDs. If you have no idea what IDs your pages may use,
you may opt to simply add a prefix to all user-submitted ID attributes so
that they are still usable, but will not conflict with core page IDs.
Example: setting the directive to 'user_' will result in a user submitted
'foo' to become 'user_foo' Be sure to set %HTML.EnableAttrID to true
before using this.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
Attr.IDPrefixLocal
TYPE: string
VERSION: 1.2.0
DEFAULT: ''
--DESCRIPTION--
Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If you
need to allow multiple sets of user content on web page, you may need to
have a seperate prefix that changes with each iteration. This way,
seperately submitted user content displayed on the same page doesn't
clobber each other. Ideal values are unique identifiers for the content it
represents (i.e. the id of the row in the database). Be sure to add a
seperator (like an underscore) at the end. Warning: this directive will
not work unless %Attr.IDPrefix is set to a non-empty value!
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,31 @@
AutoFormat.AutoParagraph
TYPE: bool
VERSION: 2.0.1
DEFAULT: false
--DESCRIPTION--
<p>
This directive turns on auto-paragraphing, where double newlines are
converted in to paragraphs whenever possible. Auto-paragraphing:
</p>
<ul>
<li>Always applies to inline elements or text in the root node,</li>
<li>Applies to inline elements or text with double newlines in nodes
that allow paragraph tags,</li>
<li>Applies to double newlines in paragraph tags</li>
</ul>
<p>
<code>p</code> tags must be allowed for this directive to take effect.
We do not use <code>br</code> tags for paragraphing, as that is
semantically incorrect.
</p>
<p>
To prevent auto-paragraphing as a content-producer, refrain from using
double-newlines except to specify a new paragraph or in contexts where
it has special meaning (whitespace usually has no meaning except in
tags like <code>pre</code>, so this should not be difficult.) To prevent
the paragraphing of inline text adjacent to block elements, wrap them
in <code>div</code> tags (the behavior is slightly different outside of
the root node.)
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
AutoFormat.Custom
TYPE: list
VERSION: 2.0.1
DEFAULT: array()
--DESCRIPTION--
<p>
This directive can be used to add custom auto-format injectors.
Specify an array of injector names (class name minus the prefix)
or concrete implementations. Injector class must exist.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
AutoFormat.DisplayLinkURI
TYPE: bool
VERSION: 3.2.0
DEFAULT: false
--DESCRIPTION--
<p>
This directive turns on the in-text display of URIs in &lt;a&gt; tags, and disables
those links. For example, <a href="http://example.com">example</a> becomes
example (<a>http://example.com</a>).
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
AutoFormat.Linkify
TYPE: bool
VERSION: 2.0.1
DEFAULT: false
--DESCRIPTION--
<p>
This directive turns on linkification, auto-linking http, ftp and
https URLs. <code>a</code> tags with the <code>href</code> attribute
must be allowed.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
AutoFormat.PurifierLinkify.DocURL
TYPE: string
VERSION: 2.0.1
DEFAULT: '#%s'
ALIASES: AutoFormatParam.PurifierLinkifyDocURL
--DESCRIPTION--
<p>
Location of configuration documentation to link to, let %s substitute
into the configuration's namespace and directive names sans the percent
sign.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
AutoFormat.PurifierLinkify
TYPE: bool
VERSION: 2.0.1
DEFAULT: false
--DESCRIPTION--
<p>
Internal auto-formatter that converts configuration directives in
syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags
with the <code>href</code> attribute must be allowed.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
AutoFormat.RemoveEmpty.Predicate
TYPE: hash
VERSION: 4.7.0
DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src'))
--DESCRIPTION--
<p>
Given that an element has no contents, it will be removed by default, unless
this predicate dictates otherwise. The predicate can either be an associative
map from tag name to list of attributes that must be present for the element
to be considered preserved: thus, the default always preserves <code>colgroup</code>,
<code>th</code> and <code>td</code>, and also <code>iframe</code> if it
has a <code>src</code>.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions
TYPE: lookup
VERSION: 4.0.0
DEFAULT: array('td' => true, 'th' => true)
--DESCRIPTION--
<p>
When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp
are enabled, this directive defines what HTML elements should not be
removede if they have only a non-breaking space in them.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,15 @@
AutoFormat.RemoveEmpty.RemoveNbsp
TYPE: bool
VERSION: 4.0.0
DEFAULT: false
--DESCRIPTION--
<p>
When enabled, HTML Purifier will treat any elements that contain only
non-breaking spaces as well as regular whitespace as empty, and remove
them when %AutoFormat.RemoveEmpty is enabled.
</p>
<p>
See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements
that don't have this behavior applied to them.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,46 @@
AutoFormat.RemoveEmpty
TYPE: bool
VERSION: 3.2.0
DEFAULT: false
--DESCRIPTION--
<p>
When enabled, HTML Purifier will attempt to remove empty elements that
contribute no semantic information to the document. The following types
of nodes will be removed:
</p>
<ul><li>
Tags with no attributes and no content, and that are not empty
elements (remove <code>&lt;a&gt;&lt;/a&gt;</code> but not
<code>&lt;br /&gt;</code>), and
</li>
<li>
Tags with no content, except for:<ul>
<li>The <code>colgroup</code> element, or</li>
<li>
Elements with the <code>id</code> or <code>name</code> attribute,
when those attributes are permitted on those elements.
</li>
</ul></li>
</ul>
<p>
Please be very careful when using this functionality; while it may not
seem that empty elements contain useful information, they can alter the
layout of a document given appropriate styling. This directive is most
useful when you are processing machine-generated HTML, please avoid using
it on regular user HTML.
</p>
<p>
Elements that contain only whitespace will be treated as empty. Non-breaking
spaces, however, do not count as whitespace. See
%AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior.
</p>
<p>
This algorithm is not perfect; you may still notice some empty tags,
particularly if a node had elements, but those elements were later removed
because they were not permitted in that context, or tags that, after
being auto-closed by another tag, where empty. This is for safety reasons
to prevent clever code from breaking validation. The general rule of thumb:
if a tag looked empty on the way in, it will get removed; if HTML Purifier
made it empty, it will stay.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
AutoFormat.RemoveSpansWithoutAttributes
TYPE: bool
VERSION: 4.0.1
DEFAULT: false
--DESCRIPTION--
<p>
This directive causes <code>span</code> tags without any attributes
to be removed. It will also remove spans that had all attributes
removed during processing.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
CSS.AllowDuplicates
TYPE: bool
DEFAULT: false
VERSION: 4.8.0
--DESCRIPTION--
<p>
By default, HTML Purifier removes duplicate CSS properties,
like <code>color:red; color:blue</code>. If this is set to
true, duplicate properties are allowed.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,8 @@
CSS.AllowImportant
TYPE: bool
DEFAULT: false
VERSION: 3.1.0
--DESCRIPTION--
This parameter determines whether or not !important cascade modifiers should
be allowed in user CSS. If false, !important will stripped.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
CSS.AllowTricky
TYPE: bool
DEFAULT: false
VERSION: 3.1.0
--DESCRIPTION--
This parameter determines whether or not to allow "tricky" CSS properties and
values. Tricky CSS properties/values can drastically modify page layout or
be used for deceptive practices but do not directly constitute a security risk.
For example, <code>display:none;</code> is considered a tricky property that
will only be allowed if this directive is set to true.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
CSS.AllowedFonts
TYPE: lookup/null
VERSION: 4.3.0
DEFAULT: NULL
--DESCRIPTION--
<p>
Allows you to manually specify a set of allowed fonts. If
<code>NULL</code>, all fonts are allowed. This directive
affects generic names (serif, sans-serif, monospace, cursive,
fantasy) as well as specific font families.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,18 @@
CSS.AllowedProperties
TYPE: lookup/null
VERSION: 3.1.0
DEFAULT: NULL
--DESCRIPTION--
<p>
If HTML Purifier's style attributes set is unsatisfactory for your needs,
you can overload it with your own list of tags to allow. Note that this
method is subtractive: it does its job by taking away from HTML Purifier
usual feature set, so you cannot add an attribute that HTML Purifier never
supported in the first place.
</p>
<p>
<strong>Warning:</strong> If another directive conflicts with the
elements here, <em>that</em> directive will win and override.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
CSS.DefinitionRev
TYPE: int
VERSION: 2.0.0
DEFAULT: 1
--DESCRIPTION--
<p>
Revision identifier for your custom definition. See
%HTML.DefinitionRev for details.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,13 @@
CSS.ForbiddenProperties
TYPE: lookup
VERSION: 4.2.0
DEFAULT: array()
--DESCRIPTION--
<p>
This is the logical inverse of %CSS.AllowedProperties, and it will
override that directive or any other directive. If possible,
%CSS.AllowedProperties is recommended over this directive,
because it can sometimes be difficult to tell whether or not you've
forbidden all of the CSS properties you truly would like to disallow.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
CSS.MaxImgLength
TYPE: string/null
DEFAULT: '1200px'
VERSION: 3.1.1
--DESCRIPTION--
<p>
This parameter sets the maximum allowed length on <code>img</code> tags,
effectively the <code>width</code> and <code>height</code> properties.
Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is
in place to prevent imagecrash attacks, disable with null at your own risk.
This directive is similar to %HTML.MaxImgLength, and both should be
concurrently edited, although there are
subtle differences in the input format (the CSS max is a number with
a unit).
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,10 @@
CSS.Proprietary
TYPE: bool
VERSION: 3.0.0
DEFAULT: false
--DESCRIPTION--
<p>
Whether or not to allow safe, proprietary CSS values.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
CSS.Trusted
TYPE: bool
VERSION: 4.2.1
DEFAULT: false
--DESCRIPTION--
Indicates whether or not the user's CSS input is trusted or not. If the
input is trusted, a more expansive set of allowed properties. See
also %HTML.Trusted.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
Cache.DefinitionImpl
TYPE: string/null
VERSION: 2.0.0
DEFAULT: 'Serializer'
--DESCRIPTION--
This directive defines which method to use when caching definitions,
the complex data-type that makes HTML Purifier tick. Set to null
to disable caching (not recommended, as you will see a definite
performance degradation).
--ALIASES--
Core.DefinitionCache
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,13 @@
Cache.SerializerPath
TYPE: string/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--
<p>
Absolute path with no trailing slash to store serialized definitions in.
Default is within the
HTML Purifier library inside DefinitionCache/Serializer. This
path must be writable by the webserver.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Cache.SerializerPermissions
TYPE: int/null
VERSION: 4.3.0
DEFAULT: 0755
--DESCRIPTION--
<p>
Directory permissions of the files and directories created inside
the DefinitionCache/Serializer or other custom serializer path.
</p>
<p>
In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
which means that no chmod'ing or directory creation shall
occur.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,18 @@
Core.AggressivelyFixLt
TYPE: bool
VERSION: 2.1.0
DEFAULT: true
--DESCRIPTION--
<p>
This directive enables aggressive pre-filter fixes HTML Purifier can
perform in order to ensure that open angled-brackets do not get killed
during parsing stage. Enabling this will result in two preg_replace_callback
calls and at least two preg_replace calls for every HTML document parsed;
if your users make very well-formed HTML, you can set this directive false.
This has no effect when DirectLex is used.
</p>
<p>
<strong>Notice:</strong> This directive's default turned from false to true
in HTML Purifier 3.2.0.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Core.AggressivelyRemoveScript
TYPE: bool
VERSION: 4.9.0
DEFAULT: true
--DESCRIPTION--
<p>
This directive enables aggressive pre-filter removal of
script tags. This is not necessary for security,
but it can help work around a bug in libxml where embedded
HTML elements inside script sections cause the parser to
choke. To revert to pre-4.9.0 behavior, set this to false.
This directive has no effect if %Core.Trusted is true,
%Core.RemoveScriptContents is false, or %Core.HiddenElements
does not contain script.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Core.AllowHostnameUnderscore
TYPE: bool
VERSION: 4.6.0
DEFAULT: false
--DESCRIPTION--
<p>
By RFC 1123, underscores are not permitted in host names.
(This is in contrast to the specification for DNS, RFC
2181, which allows underscores.)
However, most browsers do the right thing when faced with
an underscore in the host name, and so some poorly written
websites are written with the expectation this should work.
Setting this parameter to true relaxes our allowed character
check so that underscores are permitted.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Core.AllowParseManyTags
TYPE: bool
DEFAULT: false
VERSION: 4.10.1
--DESCRIPTION--
<p>
This directive allows parsing of many nested tags.
If you set true, relaxes any hardcoded limit from the parser.
However, in that case it may cause a Dos attack.
Be careful when enabling it.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Core.CollectErrors
TYPE: bool
VERSION: 2.0.0
DEFAULT: false
--DESCRIPTION--
Whether or not to collect errors found while filtering the document. This
is a useful way to give feedback to your users. <strong>Warning:</strong>
Currently this feature is very patchy and experimental, with lots of
possible error messages not yet implemented. It will not cause any
problems, but it may not help your users either.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,160 @@
Core.ColorKeywords
TYPE: hash
VERSION: 2.0.0
--DEFAULT--
array (
'aliceblue' => '#F0F8FF',
'antiquewhite' => '#FAEBD7',
'aqua' => '#00FFFF',
'aquamarine' => '#7FFFD4',
'azure' => '#F0FFFF',
'beige' => '#F5F5DC',
'bisque' => '#FFE4C4',
'black' => '#000000',
'blanchedalmond' => '#FFEBCD',
'blue' => '#0000FF',
'blueviolet' => '#8A2BE2',
'brown' => '#A52A2A',
'burlywood' => '#DEB887',
'cadetblue' => '#5F9EA0',
'chartreuse' => '#7FFF00',
'chocolate' => '#D2691E',
'coral' => '#FF7F50',
'cornflowerblue' => '#6495ED',
'cornsilk' => '#FFF8DC',
'crimson' => '#DC143C',
'cyan' => '#00FFFF',
'darkblue' => '#00008B',
'darkcyan' => '#008B8B',
'darkgoldenrod' => '#B8860B',
'darkgray' => '#A9A9A9',
'darkgrey' => '#A9A9A9',
'darkgreen' => '#006400',
'darkkhaki' => '#BDB76B',
'darkmagenta' => '#8B008B',
'darkolivegreen' => '#556B2F',
'darkorange' => '#FF8C00',
'darkorchid' => '#9932CC',
'darkred' => '#8B0000',
'darksalmon' => '#E9967A',
'darkseagreen' => '#8FBC8F',
'darkslateblue' => '#483D8B',
'darkslategray' => '#2F4F4F',
'darkslategrey' => '#2F4F4F',
'darkturquoise' => '#00CED1',
'darkviolet' => '#9400D3',
'deeppink' => '#FF1493',
'deepskyblue' => '#00BFFF',
'dimgray' => '#696969',
'dimgrey' => '#696969',
'dodgerblue' => '#1E90FF',
'firebrick' => '#B22222',
'floralwhite' => '#FFFAF0',
'forestgreen' => '#228B22',
'fuchsia' => '#FF00FF',
'gainsboro' => '#DCDCDC',
'ghostwhite' => '#F8F8FF',
'gold' => '#FFD700',
'goldenrod' => '#DAA520',
'gray' => '#808080',
'grey' => '#808080',
'green' => '#008000',
'greenyellow' => '#ADFF2F',
'honeydew' => '#F0FFF0',
'hotpink' => '#FF69B4',
'indianred' => '#CD5C5C',
'indigo' => '#4B0082',
'ivory' => '#FFFFF0',
'khaki' => '#F0E68C',
'lavender' => '#E6E6FA',
'lavenderblush' => '#FFF0F5',
'lawngreen' => '#7CFC00',
'lemonchiffon' => '#FFFACD',
'lightblue' => '#ADD8E6',
'lightcoral' => '#F08080',
'lightcyan' => '#E0FFFF',
'lightgoldenrodyellow' => '#FAFAD2',
'lightgray' => '#D3D3D3',
'lightgrey' => '#D3D3D3',
'lightgreen' => '#90EE90',
'lightpink' => '#FFB6C1',
'lightsalmon' => '#FFA07A',
'lightseagreen' => '#20B2AA',
'lightskyblue' => '#87CEFA',
'lightslategray' => '#778899',
'lightslategrey' => '#778899',
'lightsteelblue' => '#B0C4DE',
'lightyellow' => '#FFFFE0',
'lime' => '#00FF00',
'limegreen' => '#32CD32',
'linen' => '#FAF0E6',
'magenta' => '#FF00FF',
'maroon' => '#800000',
'mediumaquamarine' => '#66CDAA',
'mediumblue' => '#0000CD',
'mediumorchid' => '#BA55D3',
'mediumpurple' => '#9370DB',
'mediumseagreen' => '#3CB371',
'mediumslateblue' => '#7B68EE',
'mediumspringgreen' => '#00FA9A',
'mediumturquoise' => '#48D1CC',
'mediumvioletred' => '#C71585',
'midnightblue' => '#191970',
'mintcream' => '#F5FFFA',
'mistyrose' => '#FFE4E1',
'moccasin' => '#FFE4B5',
'navajowhite' => '#FFDEAD',
'navy' => '#000080',
'oldlace' => '#FDF5E6',
'olive' => '#808000',
'olivedrab' => '#6B8E23',
'orange' => '#FFA500',
'orangered' => '#FF4500',
'orchid' => '#DA70D6',
'palegoldenrod' => '#EEE8AA',
'palegreen' => '#98FB98',
'paleturquoise' => '#AFEEEE',
'palevioletred' => '#DB7093',
'papayawhip' => '#FFEFD5',
'peachpuff' => '#FFDAB9',
'peru' => '#CD853F',
'pink' => '#FFC0CB',
'plum' => '#DDA0DD',
'powderblue' => '#B0E0E6',
'purple' => '#800080',
'rebeccapurple' => '#663399',
'red' => '#FF0000',
'rosybrown' => '#BC8F8F',
'royalblue' => '#4169E1',
'saddlebrown' => '#8B4513',
'salmon' => '#FA8072',
'sandybrown' => '#F4A460',
'seagreen' => '#2E8B57',
'seashell' => '#FFF5EE',
'sienna' => '#A0522D',
'silver' => '#C0C0C0',
'skyblue' => '#87CEEB',
'slateblue' => '#6A5ACD',
'slategray' => '#708090',
'slategrey' => '#708090',
'snow' => '#FFFAFA',
'springgreen' => '#00FF7F',
'steelblue' => '#4682B4',
'tan' => '#D2B48C',
'teal' => '#008080',
'thistle' => '#D8BFD8',
'tomato' => '#FF6347',
'turquoise' => '#40E0D0',
'violet' => '#EE82EE',
'wheat' => '#F5DEB3',
'white' => '#FFFFFF',
'whitesmoke' => '#F5F5F5',
'yellow' => '#FFFF00',
'yellowgreen' => '#9ACD32'
)
--DESCRIPTION--
Lookup array of color names to six digit hexadecimal number corresponding
to color, with preceding hash mark. Used when parsing colors. The lookup
is done in a case-insensitive manner.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
Core.ConvertDocumentToFragment
TYPE: bool
DEFAULT: true
--DESCRIPTION--
This parameter determines whether or not the filter should convert
input that is a full document with html and body tags to a fragment
of just the contents of a body tag. This parameter is simply something
HTML Purifier can do during an edge-case: for most inputs, this
processing is not necessary.
--ALIASES--
Core.AcceptFullDocuments
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,17 @@
Core.DirectLexLineNumberSyncInterval
TYPE: int
VERSION: 2.0.0
DEFAULT: 0
--DESCRIPTION--
<p>
Specifies the number of tokens the DirectLex line number tracking
implementations should process before attempting to resyncronize the
current line count by manually counting all previous new-lines. When
at 0, this functionality is disabled. Lower values will decrease
performance, and this is only strictly necessary if the counting
algorithm is buggy (in which case you should report it as a bug).
This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is
not being used.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
Core.DisableExcludes
TYPE: bool
DEFAULT: false
VERSION: 4.5.0
--DESCRIPTION--
<p>
This directive disables SGML-style exclusions, e.g. the exclusion of
<code>&lt;object&gt;</code> in any descendant of a
<code>&lt;pre&gt;</code> tag. Disabling excludes will allow some
invalid documents to pass through HTML Purifier, but HTML Purifier
will also be less likely to accidentally remove large documents during
processing.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,9 @@
Core.EnableIDNA
TYPE: bool
DEFAULT: false
VERSION: 4.4.0
--DESCRIPTION--
Allows international domain names in URLs. This configuration option
requires the PEAR Net_IDNA2 module to be installed. It operates by
punycoding any internationalized host names for maximum portability.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,15 @@
Core.Encoding
TYPE: istring
DEFAULT: 'utf-8'
--DESCRIPTION--
If for some reason you are unable to convert all webpages to UTF-8, you can
use this directive as a stop-gap compatibility change to let HTML Purifier
deal with non UTF-8 input. This technique has notable deficiencies:
absolutely no characters outside of the selected character encoding will be
preserved, not even the ones that have been ampersand escaped (this is due
to a UTF-8 specific <em>feature</em> that automatically resolves all
entities), making it pretty useless for anything except the most I18N-blind
applications, although %Core.EscapeNonASCIICharacters offers fixes this
trouble with another tradeoff. This directive only accepts ISO-8859-1 if
iconv is not enabled.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Core.EscapeInvalidChildren
TYPE: bool
DEFAULT: false
--DESCRIPTION--
<p><strong>Warning:</strong> this configuration option is no longer does anything as of 4.6.0.</p>
<p>When true, a child is found that is not allowed in the context of the
parent element will be transformed into text as if it were ASCII. When
false, that element and all internal tags will be dropped, though text will
be preserved. There is no option for dropping the element but preserving
child nodes.</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,7 @@
Core.EscapeInvalidTags
TYPE: bool
DEFAULT: false
--DESCRIPTION--
When true, invalid tags will be written back to the document as plain text.
Otherwise, they are silently dropped.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,13 @@
Core.EscapeNonASCIICharacters
TYPE: bool
VERSION: 1.4.0
DEFAULT: false
--DESCRIPTION--
This directive overcomes a deficiency in %Core.Encoding by blindly
converting all non-ASCII characters into decimal numeric entities before
converting it to its native encoding. This means that even characters that
can be expressed in the non-UTF-8 encoding will be entity-ized, which can
be a real downer for encodings like Big5. It also assumes that the ASCII
repetoire is available, although this is the case for almost all encodings.
Anyway, use UTF-8!
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,19 @@
Core.HiddenElements
TYPE: lookup
--DEFAULT--
array (
'script' => true,
'style' => true,
)
--DESCRIPTION--
<p>
This directive is a lookup array of elements which should have their
contents removed when they are not allowed by the HTML definition.
For example, the contents of a <code>script</code> tag are not
normally shown in a document, so if script tags are to be removed,
their contents should be removed to. This is opposed to a <code>b</code>
tag, which defines some presentational changes but does not hide its
contents.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,10 @@
Core.Language
TYPE: string
VERSION: 2.0.0
DEFAULT: 'en'
--DESCRIPTION--
ISO 639 language code for localizable things in HTML Purifier to use,
which is mainly error reporting. There is currently only an English (en)
translation, so this directive is currently useless.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,36 @@
Core.LegacyEntityDecoder
TYPE: bool
VERSION: 4.9.0
DEFAULT: false
--DESCRIPTION--
<p>
Prior to HTML Purifier 4.9.0, entities were decoded by performing
a global search replace for all entities whose decoded versions
did not have special meanings under HTML, and replaced them with
their decoded versions. We would match all entities, even if they did
not have a trailing semicolon, but only if there weren't any trailing
alphanumeric characters.
</p>
<table>
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yena</td><td>&amp;yena</td><td>&amp;yena</td></tr>
<tr><td>&amp;yen=</td><td>&yen;=</td><td>&yen;=</td></tr>
</table>
<p>
In HTML Purifier 4.9.0, we changed the behavior of entity parsing
to match entities that had missing trailing semicolons in less
cases, to more closely match HTML5 parsing behavior:
</p>
<table>
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yena</td><td>&yen;a</td><td>&amp;yena</td></tr>
<tr><td>&amp;yen=</td><td>&yen;=</td><td>&amp;yen=</td></tr>
</table>
<p>
This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,34 @@
Core.LexerImpl
TYPE: mixed/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--
<p>
This parameter determines what lexer implementation can be used. The
valid values are:
</p>
<dl>
<dt><em>null</em></dt>
<dd>
Recommended, the lexer implementation will be auto-detected based on
your PHP-version and configuration.
</dd>
<dt><em>string</em> lexer identifier</dt>
<dd>
This is a slim way of manually overridding the implementation.
Currently recognized values are: DOMLex (the default PHP5
implementation)
and DirectLex (the default PHP4 implementation). Only use this if
you know what you are doing: usually, the auto-detection will
manage things for cases you aren't even aware of.
</dd>
<dt><em>object</em> lexer instance</dt>
<dd>
Super-advanced: you can specify your own, custom, implementation that
implements the interface defined by <code>HTMLPurifier_Lexer</code>.
I may remove this option simply because I don't expect anyone
to use it.
</dd>
</dl>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Core.MaintainLineNumbers
TYPE: bool/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--
<p>
If true, HTML Purifier will add line number information to all tokens.
This is useful when error reporting is turned on, but can result in
significant performance degradation and should not be used when
unnecessary. This directive must be used with the DirectLex lexer,
as the DOMLex lexer does not (yet) support this functionality.
If the value is null, an appropriate value will be selected based
on other configuration.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Core.NormalizeNewlines
TYPE: bool
VERSION: 4.2.0
DEFAULT: true
--DESCRIPTION--
<p>
Whether or not to normalize newlines to the operating
system default. When <code>false</code>, HTML Purifier
will attempt to preserve mixed newline files.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Core.RemoveInvalidImg
TYPE: bool
DEFAULT: true
VERSION: 1.3.0
--DESCRIPTION--
<p>
This directive enables pre-emptive URI checking in <code>img</code>
tags, as the attribute validation strategy is not authorized to
remove elements from the document. Revert to pre-1.3.0 behavior by setting to false.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Core.RemoveProcessingInstructions
TYPE: bool
VERSION: 4.2.0
DEFAULT: false
--DESCRIPTION--
Instead of escaping processing instructions in the form <code>&lt;? ...
?&gt;</code>, remove it out-right. This may be useful if the HTML
you are validating contains XML processing instruction gunk, however,
it can also be user-unfriendly for people attempting to post PHP
snippets.
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,12 @@
Core.RemoveScriptContents
TYPE: bool/null
DEFAULT: NULL
VERSION: 2.0.0
DEPRECATED-VERSION: 2.1.0
DEPRECATED-USE: Core.HiddenElements
--DESCRIPTION--
<p>
This directive enables HTML Purifier to remove not only script tags
but all of their contents.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Filter.Custom
TYPE: list
VERSION: 3.1.0
DEFAULT: array()
--DESCRIPTION--
<p>
This directive can be used to add custom filters; it is nearly the
equivalent of the now deprecated <code>HTMLPurifier-&gt;addFilter()</code>
method. Specify an array of concrete implementations.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,14 @@
Filter.ExtractStyleBlocks.Escaping
TYPE: bool
VERSION: 3.0.0
DEFAULT: true
ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping
--DESCRIPTION--
<p>
Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
as \3C, \3E and \26, respectively. This is can be safely set to false
if the contents of StyleBlocks will be placed in an external stylesheet,
where there is no risk of it being interpreted as HTML.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,29 @@
Filter.ExtractStyleBlocks.Scope
TYPE: string/null
VERSION: 3.0.0
DEFAULT: NULL
ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
--DESCRIPTION--
<p>
If you would like users to be able to define external stylesheets, but
only allow them to specify CSS declarations for a specific node and
prevent them from fiddling with other elements, use this directive.
It accepts any valid CSS selector, and will prepend this to any
CSS declaration extracted from the document. For example, if this
directive is set to <code>#user-content</code> and a user uses the
selector <code>a:hover</code>, the final selector will be
<code>#user-content a:hover</code>.
</p>
<p>
The comma shorthand may be used; consider the above example, with
<code>#user-content, #user-content2</code>, the final selector will
be <code>#user-content a:hover, #user-content2 a:hover</code>.
</p>
<p>
<strong>Warning:</strong> It is possible for users to bypass this measure
using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
Purifier, and I am working to get it fixed. Until then, HTML Purifier
performs a basic check to prevent this.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Filter.ExtractStyleBlocks.TidyImpl
TYPE: mixed/null
VERSION: 3.1.0
DEFAULT: NULL
ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
--DESCRIPTION--
<p>
If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
class to use for internal cleaning. This will usually be good enough.
</p>
<p>
However, for trusted user input, you can set this to <code>false</code> to
disable cleaning. In addition, you can supply your own concrete implementation
of Tidy's interface to use, although I don't know why you'd want to do that.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,74 @@
Filter.ExtractStyleBlocks
TYPE: bool
VERSION: 3.1.0
DEFAULT: false
EXTERNAL: CSSTidy
--DESCRIPTION--
<p>
This directive turns on the style block extraction filter, which removes
<code>style</code> blocks from input HTML, cleans them up with CSSTidy,
and places them in the <code>StyleBlocks</code> context variable, for further
use by you, usually to be placed in an external stylesheet, or a
<code>style</code> block in the <code>head</code> of your document.
</p>
<p>
Sample usage:
</p>
<pre><![CDATA[
<?php
header('Content-type: text/html; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';
?>
<!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" lang="en" xml:lang="en">
<head>
<title>Filter.ExtractStyleBlocks</title>
<?php
require_once '/path/to/library/HTMLPurifier.auto.php';
require_once '/path/to/csstidy.class.php';
$dirty = '<style>body {color:#F00;}</style> Some text';
$config = HTMLPurifier_Config::createDefault();
$config->set('Filter', 'ExtractStyleBlocks', true);
$purifier = new HTMLPurifier($config);
$html = $purifier->purify($dirty);
// This implementation writes the stylesheets to the styles/ directory.
// You can also echo the styles inside the document, but it's a bit
// more difficult to make sure they get interpreted properly by
// browsers; try the usual CSS armoring techniques.
$styles = $purifier->context->get('StyleBlocks');
$dir = 'styles/';
if (!is_dir($dir)) mkdir($dir);
$hash = sha1($_GET['html']);
foreach ($styles as $i => $style) {
file_put_contents($name = $dir . $hash . "_$i");
echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />';
}
?>
</head>
<body>
<div>
<?php echo $html; ?>
</div>
</b]]><![CDATA[ody>
</html>
]]></pre>
<p>
<strong>Warning:</strong> It is possible for a user to mount an
imagecrash attack using this CSS. Counter-measures are difficult;
it is not simply enough to limit the range of CSS lengths (using
relative lengths with many nesting levels allows for large values
to be attained without actually specifying them in the stylesheet),
and the flexible nature of selectors makes it difficult to selectively
disable lengths on image tags (HTML Purifier, however, does disable
CSS width and height in inline styling). There are probably two effective
counter measures: an explicit width and height set to auto in all
images in your document (unlikely) or the disabling of width and
height (somewhat reasonable). Whether or not these measures should be
used is left to the reader.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,16 @@
Filter.YouTube
TYPE: bool
VERSION: 3.1.0
DEFAULT: false
--DESCRIPTION--
<p>
<strong>Warning:</strong> Deprecated in favor of %HTML.SafeObject and
%Output.FlashCompat (turn both on to allow YouTube videos and other
Flash content).
</p>
<p>
This directive enables YouTube video embedding in HTML Purifier. Check
<a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document
on embedding videos</a> for more information on what this filter does.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,25 @@
HTML.Allowed
TYPE: itext/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--
<p>
This is a preferred convenience directive that combines
%HTML.AllowedElements and %HTML.AllowedAttributes.
Specify elements and attributes that are allowed using:
<code>element1[attr1|attr2],element2...</code>. For example,
if you would like to only allow paragraphs and links, specify
<code>a[href],p</code>. You can specify attributes that apply
to all elements using an asterisk, e.g. <code>*[lang]</code>.
You can also use newlines instead of commas to separate elements.
</p>
<p>
<strong>Warning</strong>:
All of the constraints on the component directives are still enforced.
The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code>
whitelist: directly copy-pasting it here will probably result in
broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
are set, this directive has no effect.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,19 @@
HTML.AllowedAttributes
TYPE: lookup/null
VERSION: 1.3.0
DEFAULT: NULL
--DESCRIPTION--
<p>
If HTML Purifier's attribute set is unsatisfactory, overload it!
The syntax is "tag.attr" or "*.attr" for the global attributes
(style, id, class, dir, lang, xml:lang).
</p>
<p>
<strong>Warning:</strong> If another directive conflicts with the
elements here, <em>that</em> directive will win and override. For
example, %HTML.EnableAttrID will take precedence over *.id in this
directive. You must set that directive to true before you can use
IDs at all.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,10 @@
HTML.AllowedComments
TYPE: lookup
VERSION: 4.4.0
DEFAULT: array()
--DESCRIPTION--
A whitelist which indicates what explicit comment bodies should be
allowed, modulo leading and trailing whitespace. See also %HTML.AllowedCommentsRegexp
(these directives are union'ed together, so a comment is considered
valid if any directive deems it valid.)
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,15 @@
HTML.AllowedCommentsRegexp
TYPE: string/null
VERSION: 4.4.0
DEFAULT: NULL
--DESCRIPTION--
A regexp, which if it matches the body of a comment, indicates that
it should be allowed. Trailing and leading spaces are removed prior
to running this regular expression.
<strong>Warning:</strong> Make sure you specify
correct anchor metacharacters <code>^regex$</code>, otherwise you may accept
comments that you did not mean to! In particular, the regex <code>/foo|bar/</code>
is probably not sufficiently strict, since it also allows <code>foobar</code>.
See also %HTML.AllowedComments (these directives are union'ed together,
so a comment is considered valid if any directive deems it valid.)
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,23 @@
HTML.AllowedElements
TYPE: lookup/null
VERSION: 1.3.0
DEFAULT: NULL
--DESCRIPTION--
<p>
If HTML Purifier's tag set is unsatisfactory for your needs, you can
overload it with your own list of tags to allow. If you change
this, you probably also want to change %HTML.AllowedAttributes; see
also %HTML.Allowed which lets you set allowed elements and
attributes at the same time.
</p>
<p>
If you attempt to allow an element that HTML Purifier does not know
about, HTML Purifier will raise an error. You will need to manually
tell HTML Purifier about this element by using the
<a href="http://htmlpurifier.org/docs/enduser-customize.html">advanced customization features.</a>
</p>
<p>
<strong>Warning:</strong> If another directive conflicts with the
elements here, <em>that</em> directive will win and override.
</p>
--# vim: et sw=4 sts=4

Some files were not shown because too many files have changed in this diff Show More