Skip to content
Snippets Groups Projects
Commit 6dc05fba authored by Jared Hancock's avatar Jared Hancock
Browse files

Allow custom logo integration on client site

Administrators are allowed to upload one or more logos and then select from
the uploaded logos to set one for the client site. Logos can also be deleted
on settings->pages submission
parent c8536fe6
No related branches found
No related tags found
No related merge requests found
...@@ -856,6 +856,20 @@ class OsticketConfig extends Config { ...@@ -856,6 +856,20 @@ class OsticketConfig extends Config {
)); ));
} }
function getLogo($site) {
$id = $this->get("{$site}_logo_id", false);
return ($id) ? AttachmentFile::lookup($id) : null;
}
function getClientLogo() {
return $this->getLogo('client');
}
function getLogoId($site) {
return $this->get("{$site}_logo_id", false);
}
function getClientLogoId() {
return $this->getLogoId('client');
}
function updatePagesSettings($vars, &$errors) { function updatePagesSettings($vars, &$errors) {
$f=array(); $f=array();
...@@ -863,13 +877,33 @@ class OsticketConfig extends Config { ...@@ -863,13 +877,33 @@ class OsticketConfig extends Config {
$f['offline_page_id'] = array('type'=>'int', 'required'=>1, 'error'=>'required'); $f['offline_page_id'] = array('type'=>'int', 'required'=>1, 'error'=>'required');
$f['thank-you_page_id'] = array('type'=>'int', 'required'=>1, 'error'=>'required'); $f['thank-you_page_id'] = array('type'=>'int', 'required'=>1, 'error'=>'required');
if ($_FILES['logo']) {
$error = false;
list($logo) = AttachmentFile::format($_FILES['logo']);
if (!$logo)
; // Pass
elseif ($logo['error'])
$errors['logo'] = $logo['error'];
elseif (!($id = AttachmentFile::uploadLogo($logo, $error)))
$errors['logo'] = 'Unable to upload logo image. '.$error;
}
if(!Validator::process($f, $vars, $errors) || $errors) if(!Validator::process($f, $vars, $errors) || $errors)
return false; return false;
if (isset($vars['delete-logo']))
foreach ($vars['delete-logo'] as $id)
if (($vars['selected-logo'] != $id)
&& ($f = AttachmentFile::lookup($id)))
$f->delete();
return $this->updateAll(array( return $this->updateAll(array(
'landing_page_id' => $vars['landing_page_id'], 'landing_page_id' => $vars['landing_page_id'],
'offline_page_id' => $vars['offline_page_id'], 'offline_page_id' => $vars['offline_page_id'],
'thank-you_page_id' => $vars['thank-you_page_id'], 'thank-you_page_id' => $vars['thank-you_page_id'],
'client_logo_id' => (
(is_numeric($vars['selected-logo']) && $vars['selected-logo'])
? $vars['selected-logo'] : false),
)); ));
} }
......
...@@ -91,6 +91,18 @@ class AttachmentFile { ...@@ -91,6 +91,18 @@ class AttachmentFile {
return $this->ht['hash']; return $this->ht['hash'];
} }
function lastModified() {
return $this->ht['created'];
}
/**
* Retrieve a hash that can be sent to scp/file.php?h= in order to
* download this file
*/
function getDownloadHash() {
return strtolower($this->getHash() . md5($this->getId().session_id().$this->getHash()));
}
function open() { function open() {
return new AttachmentChunkedData($this->id); return new AttachmentChunkedData($this->id);
} }
...@@ -126,7 +138,19 @@ class AttachmentFile { ...@@ -126,7 +138,19 @@ class AttachmentFile {
function display() { function display() {
// Thanks, http://stackoverflow.com/a/1583753/1025836
$last_modified = strtotime($this->lastModified());
header("Last-Modified: ".gmdate(DATE_RFC822, $last_modified)." GMT", false);
header('ETag: "'.$this->getHash().'"');
header('Cache-Control: private, max-age=3600');
header('Expires: ' . date(DATE_RFC822, time() + 3600) . ' GMT');
header('Pragma: private');
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified ||
@trim($_SERVER['HTTP_IF_NONE_MATCH']) == $this->getHash()) {
header("HTTP/1.1 304 Not Modified");
exit();
}
header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream')); header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream'));
header('Content-Length: '.$this->getSize()); header('Content-Length: '.$this->getSize());
...@@ -141,7 +165,7 @@ class AttachmentFile { ...@@ -141,7 +165,7 @@ class AttachmentFile {
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public'); header('Cache-Control: public');
header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream')); header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream'));
$filename=basename($this->getName()); $filename=basename($this->getName());
$user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']); $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']);
if ((is_integer(strpos($user_agent,'msie'))) && (is_integer(strpos($user_agent,'win')))) { if ((is_integer(strpos($user_agent,'msie'))) && (is_integer(strpos($user_agent,'win')))) {
...@@ -149,7 +173,7 @@ class AttachmentFile { ...@@ -149,7 +173,7 @@ class AttachmentFile {
}else{ }else{
header('Content-Disposition: attachment; filename='.$filename.';' ); header('Content-Disposition: attachment; filename='.$filename.';' );
} }
header('Content-Transfer-Encoding: binary'); header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$this->getSize()); header('Content-Length: '.$this->getSize());
$this->sendData(); $this->sendData();
...@@ -157,12 +181,13 @@ class AttachmentFile { ...@@ -157,12 +181,13 @@ class AttachmentFile {
} }
/* Function assumes the files types have been validated */ /* Function assumes the files types have been validated */
function upload($file) { function upload($file, $ft='T') {
if(!$file['name'] || $file['error'] || !is_uploaded_file($file['tmp_name'])) if(!$file['name'] || $file['error'] || !is_uploaded_file($file['tmp_name']))
return false; return false;
$info=array('type'=>$file['type'], $info=array('type'=>$file['type'],
'filetype'=>$ft,
'size'=>$file['size'], 'size'=>$file['size'],
'name'=>$file['name'], 'name'=>$file['name'],
'hash'=>MD5(MD5_FILE($file['tmp_name']).time()), 'hash'=>MD5(MD5_FILE($file['tmp_name']).time()),
...@@ -172,15 +197,49 @@ class AttachmentFile { ...@@ -172,15 +197,49 @@ class AttachmentFile {
return AttachmentFile::save($info); return AttachmentFile::save($info);
} }
function uploadLogo($file, &$error, $aspect_ratio=3) {
/* Borrowed in part from
* http://salman-w.blogspot.com/2009/04/crop-to-fit-image-using-aspphp.html
*/
if (!extension_loaded('gd'))
return self::upload($file, 'L');
$source_path = $file['tmp_name'];
list($source_width, $source_height, $source_type) = getimagesize($source_path);
switch ($source_type) {
case IMAGETYPE_GIF:
case IMAGETYPE_JPEG:
case IMAGETYPE_PNG:
break;
default:
// TODO: Return an error
$error = 'Invalid image file type';
return false;
}
$source_aspect_ratio = $source_width / $source_height;
if ($source_aspect_ratio >= $aspect_ratio)
return self::upload($file, 'L');
$error = 'Image is too square. Upload a wider image';
return false;
}
function save($file) { function save($file) {
if(!$file['hash']) if(!$file['hash'])
$file['hash']=MD5(MD5($file['data']).time()); $file['hash']=MD5(MD5($file['data']).time());
if(!$file['size']) if(!$file['size'])
$file['size']=strlen($file['data']); $file['size']=strlen($file['data']);
if(!$file['filetype'])
$file['filetype'] = 'T';
$sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() ' $sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() '
.',type='.db_input($file['type']) .',type='.db_input($file['type'])
.',ft='.db_input($file['filetype'])
.',size='.db_input($file['size']) .',size='.db_input($file['size'])
.',name='.db_input(Format::file_name($file['name'])) .',name='.db_input(Format::file_name($file['name']))
.',hash='.db_input($file['hash']); .',hash='.db_input($file['hash']);
...@@ -208,11 +267,11 @@ class AttachmentFile { ...@@ -208,11 +267,11 @@ class AttachmentFile {
function lookup($id) { function lookup($id) {
$id = is_numeric($id)?$id:AttachmentFile::getIdByHash($id); $id = is_numeric($id)?$id:AttachmentFile::getIdByHash($id);
return ($id && ($file = new AttachmentFile($id)) && $file->getId()==$id)?$file:null; return ($id && ($file = new AttachmentFile($id)) && $file->getId()==$id)?$file:null;
} }
/* /*
Method formats http based $_FILE uploads - plus basic validation. Method formats http based $_FILE uploads - plus basic validation.
@restrict - make sure file type & size are allowed. @restrict - make sure file type & size are allowed.
*/ */
...@@ -234,7 +293,7 @@ class AttachmentFile { ...@@ -234,7 +293,7 @@ class AttachmentFile {
foreach($attachments as $i => &$file) { foreach($attachments as $i => &$file) {
//skip no file upload "error" - why PHP calls it an error is beyond me. //skip no file upload "error" - why PHP calls it an error is beyond me.
if($file['error'] && $file['error']==UPLOAD_ERR_NO_FILE) { if($file['error'] && $file['error']==UPLOAD_ERR_NO_FILE) {
unset($attachments[$i]); unset($attachments[$i]);
continue; continue;
} }
...@@ -263,7 +322,7 @@ class AttachmentFile { ...@@ -263,7 +322,7 @@ class AttachmentFile {
* canned-response, or faq point to any more. * canned-response, or faq point to any more.
*/ */
/* static */ function deleteOrphans() { /* static */ function deleteOrphans() {
$sql = 'DELETE FROM '.FILE_TABLE.' WHERE id NOT IN (' $sql = 'DELETE FROM '.FILE_TABLE.' WHERE id NOT IN ('
# DISTINCT implies sort and may not be necessary # DISTINCT implies sort and may not be necessary
.'SELECT DISTINCT(file_id) FROM (' .'SELECT DISTINCT(file_id) FROM ('
...@@ -273,16 +332,27 @@ class AttachmentFile { ...@@ -273,16 +332,27 @@ class AttachmentFile {
.' UNION ALL ' .' UNION ALL '
.'SELECT file_id FROM '.FAQ_ATTACHMENT_TABLE .'SELECT file_id FROM '.FAQ_ATTACHMENT_TABLE
.') still_loved' .') still_loved'
.')'; .') AND `ft` = "T"';
db_query($sql); db_query($sql);
//Delete orphaned chuncked data! //Delete orphaned chuncked data!
AttachmentChunkedData::deleteOrphans(); AttachmentChunkedData::deleteOrphans();
return true; return true;
} }
/* static */
function allLogos() {
$sql = 'SELECT id FROM '.FILE_TABLE.' WHERE ft="L"
ORDER BY created';
$logos = array();
$res = db_query($sql);
while (list($id) = db_fetch_row($res))
$logos[] = AttachmentFile::lookup($id);
return $logos;
}
} }
/** /**
...@@ -328,11 +398,11 @@ class AttachmentChunkedData { ...@@ -328,11 +398,11 @@ class AttachmentChunkedData {
} }
function deleteOrphans() { function deleteOrphans() {
$sql = 'DELETE c.* FROM '.FILE_CHUNK_TABLE.' c ' $sql = 'DELETE c.* FROM '.FILE_CHUNK_TABLE.' c '
. ' LEFT JOIN '.FILE_TABLE.' f ON(f.id=c.file_id) ' . ' LEFT JOIN '.FILE_TABLE.' f ON(f.id=c.file_id) '
. ' WHERE f.id IS NULL'; . ' WHERE f.id IS NULL';
return db_query($sql)?db_affected_rows():0; return db_query($sql)?db_affected_rows():0;
} }
} }
......
...@@ -20,7 +20,10 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); ...@@ -20,7 +20,10 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
<body> <body>
<div id="container"> <div id="container">
<div id="header"> <div id="header">
<a id="logo" href="<?php echo ROOT_PATH; ?>index.php" title="Support Center"><img src="<?php echo ASSETS_PATH; ?>images/logo.png" border=0 alt="Support Center"></a> <a id="logo" href="<?php echo ROOT_PATH; ?>index.php"
title="Support Center"><img src="logo.php" border=0 alt="<?php
echo $ost->getConfig()->getTitle(); ?>"
style="height: 5em"></a>
<p> <p>
<?php <?php
if($thisclient && is_object($thisclient) && $thisclient->isValid()) { if($thisclient && is_object($thisclient) && $thisclient->isValid()) {
......
...@@ -3,7 +3,8 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) ...@@ -3,7 +3,8 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
$pages = Page::getPages(); $pages = Page::getPages();
?> ?>
<h2>Site Pages</h2> <h2>Site Pages</h2>
<form action="settings.php?t=pages" method="post" id="save"> <form action="settings.php?t=pages" method="post" id="save"
enctype="multipart/form-data">
<?php csrf_token(); ?> <?php csrf_token(); ?>
<input type="hidden" name="t" value="pages" > <input type="hidden" name="t" value="pages" >
<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2"> <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
...@@ -66,8 +67,117 @@ $pages = Page::getPages(); ...@@ -66,8 +67,117 @@ $pages = Page::getPages();
</tr> </tr>
</tbody> </tbody>
</table> </table>
<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
<thead>
<tr>
<th colspan="2">
<h4>Logos</h4>
<em>System Default Logo</em>
</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">
<label style="display:block">
<input type="radio" name="selected-logo" value="0"
style="margin-left: 1em"
<?php if (!$ost->getConfig()->getClientLogoId())
echo 'checked="checked"'; ?>/>
<img src="../assets/default/images/logo.png"
alt="Default Logo" valign="middle"
style="box-shadow: 0 0 0.5em rgba(0,0,0,0.5);
margin: 0.5em; height: 5em"/>
</label>
</td></tr>
<tr>
<th colspan="2">
<em>Use a custom logo &mdash; Use a delete checkbox to
remove the logo from the system</em>
</th>
</tr>
<tr><td colspan="2">
<?php
$current = $ost->getConfig()->getClientLogoId();
foreach (AttachmentFile::allLogos() as $logo) { ?>
<div>
<label>
<input type="radio" name="selected-logo"
style="margin-left: 1em" value="<?php
echo $logo->getId(); ?>" <?php
if ($logo->getId() == $current)
echo 'checked="checked"'; ?>/>
<img src="image.php?h=<?php echo $logo->getDownloadHash(); ?>"
alt="Custom Logo" valign="middle"
style="box-shadow: 0 0 0.5em rgba(0,0,0,0.5);
margin: 0.5em; height: 5em;"/>
</label>
<?php if ($logo->getId() != $current) { ?>
<label>
<input type="checkbox" name="delete-logo[]" value="<?php
echo $logo->getId(); ?>"/> Delete
</label>
<?php } ?>
</div>
<?php } ?>
<br/>
<b>Upload a new logo:</b>
<input type="file" name="logo[]" size="30" value="" />
<font class="error"><br/><?php echo $errors['logo']; ?></font>
</td>
</tr>
</tbody>
</table>
<p style="padding-left:250px;"> <p style="padding-left:250px;">
<input class="button" type="submit" name="submit" value="Save Changes"> <input class="button" type="submit" name="submit-button" value="Save Changes">
<input class="button" type="reset" name="reset" value="Reset Changes"> <input class="button" type="reset" name="reset" value="Reset Changes">
</p> </p>
</form> </form>
<div style="display:none;" class="dialog" id="confirm-action">
<h3>Please Confirm</h3>
<a class="close" href="">&times;</a>
<hr/>
<p class="confirm-action" id="delete-confirm">
<font color="red"><strong>Are you sure you want to DELETE selected
logos?</strong></font>
<br/><br/>Deleted logos CANNOT be recovered.
</p>
<div>Please confirm to continue.</div>
<hr style="margin-top:1em"/>
<p class="full-width">
<span class="buttons" style="float:left">
<input type="button" value="No, Cancel" class="close">
</span>
<span class="buttons" style="float:right">
<input type="button" value="Yes, Do it!" class="confirm">
</span>
</p>
<div class="clear"></div>
</div>
<script type="text/javascript">
$(function() {
$('#save input:submit.button').bind('click', function(e) {
var formObj = $('#save');
if ($('input:checkbox:checked', formObj).length) {
e.preventDefault();
$('.dialog#confirm-action').undelegate('.confirm');
$('.dialog#confirm-action').delegate('input.confirm', 'click', function(e) {
e.preventDefault();
$('.dialog#confirm-action').hide();
$('#overlay').hide();
formObj.submit();
return false;
});
$('#overlay').show();
$('.dialog#confirm-action .confirm-action').hide();
$('.dialog#confirm-action p#delete-confirm')
.show()
.parent('div').show().trigger('click');
return false;
}
else return true;
});
});
</script>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/********************************************************************* /*********************************************************************
login.php login.php
Client Login Client Login
Peter Rotich <peter@osticket.com> Peter Rotich <peter@osticket.com>
Copyright (c) 2006-2013 osTicket Copyright (c) 2006-2013 osTicket
......
logo.php 0 → 100644
<?php
/*********************************************************************
logo.php
Simple logo to facilitate serving a customized client-side logo from
osTicet. The logo is configurable in Admin Panel -> Settings -> Pages
Peter Rotich <peter@osticket.com>
Jared Hancock <jared@osticket.com>
Copyright (c) 2006-2013 osTicket
http://www.osticket.com
Released under the GNU General Public License WITHOUT ANY WARRANTY.
See LICENSE.TXT for details.
vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
// Don't update the session for inline image fetches
if (!function_exists('noop')) { function noop() {} }
session_set_save_handler('noop','noop','noop','noop','noop','noop');
define('DISABLE_SESSION', true);
require('client.inc.php');
if (($logo = $ost->getConfig()->getClientLogo())) {
$logo->display();
} else {
header('Location: '.ASSETS_PATH.'images/logo.png');
}
?>
<?php
/*********************************************************************
image.php
Simply downloads the file...on hash validation as follows;
* Hash must be 64 chars long.
* First 32 chars is the perm. file hash
* Next 32 chars is md5(file_id.session_id().file_hash)
Peter Rotich <peter@osticket.com>
Copyright (c) 2006-2013 osTicket
http://www.osticket.com
Released under the GNU General Public License WITHOUT ANY WARRANTY.
See LICENSE.TXT for details.
vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
require('staff.inc.php');
require_once(INCLUDE_DIR.'class.file.php');
$h=trim($_GET['h']);
//basic checks
if(!$h || strlen($h)!=64 //32*2
|| !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
|| strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
die('Unknown or invalid file. #'.Format::htmlchars($_GET['h']));
$file->display();
?>
...@@ -20,6 +20,7 @@ require_once('client.inc.php'); ...@@ -20,6 +20,7 @@ require_once('client.inc.php');
//Client Login page: Ajax interface can pre-declare the function to trap logins. //Client Login page: Ajax interface can pre-declare the function to trap logins.
if(!function_exists('clientLoginPage')) { if(!function_exists('clientLoginPage')) {
function clientLoginPage($msg ='') { function clientLoginPage($msg ='') {
global $ost;
require('./login.php'); require('./login.php');
exit; exit;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment