extraheader = array('js'=>'', 'css'=>'');
$this->footerjs = array();
}
/**
* Add javascript to an internal array which is output at the
* end of the page within a document.ready() function.
*
* @param string $script javascript to be added
*/
function addFooterJS($script) { /* {{{ */
$this->footerjs[] = $script;
} /* }}} */
function htmlStartPage($title="", $bodyClass="", $base="", $httpheader=array()) { /* {{{ */
if(1 || method_exists($this, 'js')) {
/* We still need unsafe-eval, because printDocumentChooserHtml and
* printFolderChooserHtml will include a javascript file with ajax
* which is evaled by jquery
* X-WebKit-CSP is deprecated, Chrome understands Content-Security-Policy
* since version 25+
* X-Content-Security-Policy is deprecated, Firefox understands
* Content-Security-Policy since version 23+
*/
$csp_rules = "script-src 'self' 'unsafe-eval';"; // style-src 'self';";
foreach (array("X-WebKit-CSP", "X-Content-Security-Policy", "Content-Security-Policy") as $csp) {
header($csp . ": " . $csp_rules);
}
}
if($httpheader) {
foreach($httpheader as $name=>$value) {
header($name . ": " . $value);
}
}
$hookObjs = $this->getHookObjects('SeedDMS_View_Bootstrap');
foreach($hookObjs as $hookObj) {
if (method_exists($hookObj, 'startPage')) {
$hookObj->startPage($this);
}
}
echo "\n";
echo "\n
\n";
$menuitems = array();
if (!$this->params['user']->isGuest())
$menuitems['addevent'] = array('link'=>"../out/out.AddEvent.php", 'label'=>'add_event');
/* Check if hook exists because otherwise callHook() will override $menuitems */
if($this->hasHook('calendarNavigationBar'))
$menuitems = $this->callHook('calendarNavigationBar', $menuitems);
self::showNavigationBar($menuitems);
echo "
\n";
echo "
\n";
return;
} /* }}} */
function pageList($pageNumber, $totalPages, $baseURI, $params) { /* {{{ */
$maxpages = 25; // skip pages when more than this is shown
$range = 5; // pages left and right of current page
if (!is_numeric($pageNumber) || !is_numeric($totalPages) || $totalPages<2) {
return;
}
// Construct the basic URI based on the $_GET array. One could use a
// regular expression to strip out the pg (page number) variable to
// achieve the same effect. This seems to be less haphazard though...
$resultsURI = $baseURI;
$first=true;
foreach ($params as $key=>$value) {
// Don't include the page number in the basic URI. This is added in
// during the list display loop.
if (!strcasecmp($key, "pg")) {
continue;
}
if (is_array($value)) {
foreach ($value as $subkey=>$subvalue) {
$resultsURI .= ($first ? "?" : "&").$key."%5B".$subkey."%5D=".$subvalue;
$first = false;
}
}
else {
$resultsURI .= ($first ? "?" : "&").$key."=".$value;
}
$first = false;
}
echo "
\n";
} /* }}} */
function ___exitError($pagetitle, $error, $noexit=false, $plain=false) { /* {{{ */
/* This is just a hack to prevent creation of js files in an error
* case, because they will contain this error page again. It would be much
* better, if there was extra error() function similar to show() and calling
* $view() after setting the action to 'error'. This would also allow to
* set separate error pages for each view.
*/
if(!$noexit && isset($_REQUEST['action'])) {
if(in_array($_REQUEST['action'], array('js', 'footerjs'))) {
exit;
}
if($_REQUEST['action'] == 'webrootjs') {
$this->webrootjs();
exit;
}
}
if(!$plain) {
$this->htmlStartPage($pagetitle);
$this->globalNavigation();
$this->contentStart();
}
print "
";
print "
".getMLText('error')."!
";
print htmlspecialchars($error);
print "
";
print "";
$this->contentEnd();
$this->htmlEndPage();
add_log_line(" UI::exitError error=".$error." pagetitle=".$pagetitle, PEAR_LOG_ERR);
if($noexit)
return;
exit;
} /* }}} */
function printNewTreeNavigation($folderid=0, $accessmode=M_READ, $showdocs=0, $formid='form1', $expandtree=0, $orderby='') { /* {{{ */
$this->printNewTreeNavigationHtml($folderid, $accessmode, $showdocs, $formid, $expandtree, $orderby);
?>
\n";
} /* }}} */
/**
* Create a tree of folders using jqtree.
*
* The tree can contain folders only or include documents.
*
* @param integer $folderid current folderid. If set the tree will be
* folded out and the all folders in the path will be visible
* @param integer $accessmode use this access mode when retrieving folders
* and documents shown in the tree
* @param boolean $showdocs set to true if tree shall contain documents
* as well.
*/
function printNewTreeNavigationJs($folderid=0, $accessmode=M_READ, $showdocs=0, $formid='form1', $expandtree=0, $orderby='') { /* {{{ */
function jqtree($path, $folder, $user, $accessmode, $showdocs=1, $expandtree=0, $orderby='') {
if($path || $expandtree) {
if($path)
$pathfolder = array_shift($path);
$subfolders = $folder->getSubFolders($orderby);
$subfolders = SeedDMS_Core_DMS::filterAccess($subfolders, $user, $accessmode);
$children = array();
foreach($subfolders as $subfolder) {
$node = array('label'=>$subfolder->getName(), 'id'=>$subfolder->getID(), 'load_on_demand'=>($subfolder->hasSubFolders() || ($subfolder->hasDocuments() && $showdocs)) ? true : false, 'is_folder'=>true);
if($expandtree || $pathfolder->getID() == $subfolder->getID()) {
if($showdocs) {
$documents = $folder->getDocuments($orderby);
$documents = SeedDMS_Core_DMS::filterAccess($documents, $user, $accessmode);
foreach($documents as $document) {
$node2 = array('label'=>$document->getName(), 'id'=>$document->getID(), 'load_on_demand'=>false, 'is_folder'=>false);
$children[] = $node2;
}
}
$node['children'] = jqtree($path, $subfolder, $user, $accessmode, $showdocs, $expandtree, $orderby);
}
$children[] = $node;
}
return $children;
} else {
$subfolders = $folder->getSubFolders($orderby);
$subfolders = SeedDMS_Core_DMS::filterAccess($subfolders, $user, $accessmode);
$children = array();
foreach($subfolders as $subfolder) {
$node = array('label'=>$subfolder->getName(), 'id'=>$subfolder->getID(), 'load_on_demand'=>($subfolder->hasSubFolders() || ($subfolder->hasDocuments() && $showdocs)) ? true : false, 'is_folder'=>true);
$children[] = $node;
}
return $children;
}
return array();
}
if($folderid) {
$folder = $this->params['dms']->getFolder($folderid);
$path = $folder->getPath();
$folder = array_shift($path);
$node = array('label'=>$folder->getName(), 'id'=>$folder->getID(), 'load_on_demand'=>true, 'is_folder'=>true);
if(!$folder->hasSubFolders()) {
$node['load_on_demand'] = false;
$node['children'] = array();
} else {
$node['children'] = jqtree($path, $folder, $this->params['user'], $accessmode, $showdocs, $expandtree, $orderby);
if($showdocs) {
$documents = $folder->getDocuments($orderby);
$documents = SeedDMS_Core_DMS::filterAccess($documents, $this->params['user'], $accessmode);
foreach($documents as $document) {
$node2 = array('label'=>$document->getName(), 'id'=>$document->getID(), 'load_on_demand'=>false, 'is_folder'=>false);
$node['children'][] = $node2;
}
}
}
/* Nasty hack to remove the highest folder */
if(isset($this->params['remove_root_from_tree']) && $this->params['remove_root_from_tree']) {
foreach($node['children'] as $n)
$tree[] = $n;
} else {
$tree[] = $node;
}
} else {
$root = $this->params['dms']->getFolder($this->params['rootfolderid']);
$tree = array(array('label'=>$root->getName(), 'id'=>$root->getID(), 'load_on_demand'=>true, 'is_folder'=>true));
}
?>
var data = ;
$(function() {
$('#jqtree').tree({
saveState: true,
data: data,
saveState: 'jqtree',
openedIcon: '',
closedIcon: '',
_onCanSelectNode: function(node) {
if(node.is_folder) {
folderSelected(node.id, node.name);
} else
documentSelected(node.id, node.name);
},
autoOpen: true,
drapAndDrop: true,
onCreateLi: function(node, $li) {
// Add 'icon' span before title
if(node.is_folder)
$li.find('.jqtree-title').before(' ').attr('rel', 'folder_' + node.id).attr('formtoken', '');
else
$li.find('.jqtree-title').before(' ');
}
});
// Unfold tree if folder is opened
$('#jqtree').tree('openNode', $('#jqtree').tree('getNodeById', ), false);
$('#jqtree').bind(
'tree.click',
function(event) {
var node = event.node;
$('#jqtree').tree('openNode', node);
// event.preventDefault();
if(node.is_folder) {
folderSelected(node.id, node.name);
} else
documentSelected(node.id, node.name);
}
);
});
contentHeading("", true);
$this->contentContainerStart();
?>
printNewTreeNavigation($folderid, M_READ, 0, '');
$this->contentContainerEnd();
} else {
$this->contentHeading("", true);
}
} /* }}} */
/**
* Print clipboard in div container
*
* @param array clipboard
*/
function printClipboard($clipboard, $previewer){ /* {{{ */
$this->contentHeading(getMLText("clipboard"), true);
echo "
\n";
?>
\n";
} /* }}} */
/**
* Print button with link for deleting a document
*
* This button is used in document listings (e.g. on the ViewFolder page)
* for deleting a document. In seeddms version < 4.3.9 this was just a
* link to the out/out.RemoveDocument.php page which asks for confirmation
* an than calls op/op.RemoveDocument.php. Starting with version 4.3.9
* the button just opens a small popup asking for confirmation and than
* calls the ajax command 'deletedocument'. The ajax call is called
* in the click function of 'button.removedocument'. That button needs
* to have two attributes: 'rel' for the id of the document, and 'msg'
* for the message shown by notify if the document could be deleted.
*
* @param object $document document to be deleted
* @param string $msg message shown in case of successful deletion
* @param boolean $return return html instead of printing it
* @return string html content if $return is true, otherwise an empty string
*/
function printDeleteDocumentButton($document, $msg, $return=false){ /* {{{ */
$docid = $document->getID();
$content = '';
$content .= ' $document->getName())), ENT_QUOTES).'">';
if($return)
return $content;
else
echo $content;
return '';
} /* }}} */
function printDeleteDocumentButtonJs(){ /* {{{ */
echo "
$(document).ready(function () {
// $('.delete-document-btn').click(function(ev) {
$('body').on('click', 'a.delete-document-btn', function(ev){
id = $(ev.currentTarget).attr('rel');
confirmmsg = $(ev.currentTarget).attr('confirmmsg');
msg = $(ev.currentTarget).attr('msg');
formtoken = '".createFormKey('removedocument')."';
bootbox.dialog(confirmmsg, [{
\"label\" : \" ".getMLText("rm_document")."\",
\"class\" : \"btn-danger\",
\"callback\": function() {
$.get('../op/op.Ajax.php',
{ command: 'deletedocument', id: id, formtoken: formtoken },
function(data) {
if(data.success) {
$('#table-row-document-'+id).hide('slow');
noty({
text: msg,
type: 'success',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 1500,
});
} else {
noty({
text: data.message,
type: 'error',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 3500,
});
}
},
'json'
);
}
}, {
\"label\" : \"".getMLText("cancel")."\",
\"class\" : \"btn-cancel\",
\"callback\": function() {
}
}]);
});
});
";
} /* }}} */
/**
* Print button with link for deleting a folder
*
* This button works like document delete button
* {@link SeedDMS_Bootstrap_Style::printDeleteDocumentButton()}
*
* @param object $folder folder to be deleted
* @param string $msg message shown in case of successful deletion
* @param boolean $return return html instead of printing it
* @return string html content if $return is true, otherwise an empty string
*/
function printDeleteFolderButton($folder, $msg, $return=false){ /* {{{ */
$folderid = $folder->getID();
$content = '';
$content .= ' $folder->getName())), ENT_QUOTES).'">';
if($return)
return $content;
else
echo $content;
return '';
} /* }}} */
function printDeleteFolderButtonJs(){ /* {{{ */
echo "
$(document).ready(function () {
// $('.delete-folder-btn').click(function(ev) {
$('body').on('click', 'a.delete-folder-btn', function(ev){
id = $(ev.currentTarget).attr('rel');
confirmmsg = $(ev.currentTarget).attr('confirmmsg');
msg = $(ev.currentTarget).attr('msg');
formtoken = '".createFormKey('removefolder')."';
bootbox.dialog(confirmmsg, [{
\"label\" : \" ".getMLText("rm_folder")."\",
\"class\" : \"btn-danger\",
\"callback\": function() {
$.get('../op/op.Ajax.php',
{ command: 'deletefolder', id: id, formtoken: formtoken },
function(data) {
if(data.success) {
$('#table-row-folder-'+id).hide('slow');
noty({
text: msg,
type: 'success',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 1500,
});
} else {
noty({
text: data.message,
type: 'error',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 3500,
});
}
},
'json'
);
}
}, {
\"label\" : \"".getMLText("cancel")."\",
\"class\" : \"btn-cancel\",
\"callback\": function() {
}
}]);
});
});
";
} /* }}} */
function printLockButton($document, $msglock, $msgunlock, $return=false) { /* {{{ */
$docid = $document->getID();
if($document->isLocked()) {
$icon = 'unlock';
$msg = $msgunlock;
$title = 'unlock_document';
} else {
$icon = 'lock';
$msg = $msglock;
$title = 'lock_document';
}
$content = '';
$content .= '';
if($return)
return $content;
else
echo $content;
return '';
} /* }}} */
/**
* Output left-arrow with link which takes over a number of ids into
* a select box.
*
* Clicking in the button will preset the comma seperated list of ids
* in data-ref as options in the select box with name $name
*
* @param string $name id of select box
* @param array $ids list of option values
*/
function printSelectPresetButtonHtml($name, $ids) { /* {{{ */
?>
" data-ref="" data-ids="">
$(document).ready( function() {
$('.selectpreset_btn').click(function(ev){
ev.preventDefault();
if (typeof $(ev.currentTarget).data('ids') != 'undefined') {
target = $(ev.currentTarget).data('ref');
// Use attr() instead of data() because data() converts to int which cannot be split
items = $(ev.currentTarget).attr('data-ids');
arr = items.split(",");
for(var i in arr) {
$("#"+target+" option[value='"+arr[i]+"']").attr("selected", "selected");
}
// $("#"+target).trigger("chosen:updated");
$("#"+target).trigger("change");
}
});
});
" data-ref="" data-text="">
$(document).ready( function() {
$('.inputpreset_btn').click(function(ev){
ev.preventDefault();
if (typeof $(ev.currentTarget).data('text') != 'undefined') {
target = $(ev.currentTarget).data('ref');
value = $(ev.currentTarget).data('text');
sep = $(ev.currentTarget).data('sep');
if(sep) {
// Use attr() instead of data() because data() converts to int which cannot be split
arr = value.split(sep);
for(var i in arr) {
$("#"+target+" option[value='"+arr[i]+"']").attr("selected", "selected");
}
} else {
$("#"+target).val(value);
}
}
});
});
" data-ref="" data-text="">
$(document).ready( function() {
$('.checkboxpreset_btn').click(function(ev){
ev.preventDefault();
if (typeof $(ev.currentTarget).data('text') != 'undefined') {
target = $(ev.currentTarget).data('ref');
value = $(ev.currentTarget).data('text');
if(value) {
$("#"+target).attr('checked', '');
} else {
$("#"+target).removeAttribute('checked');
}
}
});
});
getID().'" msg="'.getMLText($msg).'" attrvalue="'.htmlspecialchars($value, ENT_QUOTES).'" confirmmsg="'.htmlspecialchars(getMLText("confirm_rm_attr_value", array ("attrdefname" => $attrdef->getName())), ENT_QUOTES).'">';
if($return)
return $content;
else
echo $content;
return '';
} /* }}} */
function printDeleteAttributeValueButtonJs(){ /* {{{ */
echo "
$(document).ready(function () {
// $('.delete-attribute-value-btn').click(function(ev) {
$('body').on('click', 'a.delete-attribute-value-btn', function(ev){
id = $(ev.currentTarget).attr('rel');
confirmmsg = $(ev.currentTarget).attr('confirmmsg');
attrvalue = $(ev.currentTarget).attr('attrvalue');
msg = $(ev.currentTarget).attr('msg');
formtoken = '".createFormKey('removeattrvalue')."';
bootbox.dialog(confirmmsg, [{
\"label\" : \" ".getMLText("rm_attr_value")."\",
\"class\" : \"btn-danger\",
\"callback\": function() {
$.post('../op/op.AttributeMgr.php',
{ action: 'removeattrvalue', attrdefid: id, attrvalue: attrvalue, formtoken: formtoken },
function(data) {
if(data.success) {
$('#table-row-attrvalue-'+id).hide('slow');
noty({
text: msg,
type: 'success',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 1500,
});
} else {
noty({
text: data.message,
type: 'error',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 3500,
});
}
},
'json'
);
}
}, {
\"label\" : \"".getMLText("cancel")."\",
\"class\" : \"btn-cancel\",
\"callback\": function() {
}
}]);
});
});
";
} /* }}} */
/**
* Return HTML of a single row in the document list table
*
* @param object $document
* @param object $previewer
* @param boolean $skipcont set to true if embrasing tr shall be skipped
*/
function documentListRow($document, $previewer, $skipcont=false, $version=0) { /* {{{ */
$dms = $this->params['dms'];
$user = $this->params['user'];
$showtree = $this->params['showtree'];
$workflowmode = $this->params['workflowmode'];
$previewwidth = $this->params['previewWidthList'];
$enableClipboard = $this->params['enableclipboard'];
$content = '';
$owner = $document->getOwner();
$comment = $document->getComment();
if (strlen($comment) > 150) $comment = substr($comment, 0, 147) . "...";
$docID = $document->getID();
if(!$skipcont)
$content .= "
";
if($enableRecursiveCount) {
if($user->isAdmin()) {
/* No need to check for access rights in countChildren() for
* admin. So pass 0 as the limit.
*/
$cc = $subFolder->countChildren($user, 0);
$content .= $cc['folder_count']." ".getMLText("folders")." ".$cc['document_count']." ".getMLText("documents");
} else {
$cc = $subFolder->countChildren($user, $maxRecursiveCount);
if($maxRecursiveCount > 5000)
$rr = 100.0;
else
$rr = 10.0;
$content .= (!$cc['folder_precise'] ? '~'.(round($cc['folder_count']/$rr)*$rr) : $cc['folder_count'])." ".getMLText("folders")." ".(!$cc['document_precise'] ? '~'.(round($cc['document_count']/$rr)*$rr) : $cc['document_count'])." ".getMLText("documents");
}
} else {
/* FIXME: the following is very inefficient for just getting the number of
* subfolders and documents. Making it more efficient is difficult, because
* the access rights need to be checked.
*/
$subsub = $subFolder->getSubFolders();
$subsub = SeedDMS_Core_DMS::filterAccess($subsub, $user, M_READ);
$subdoc = $subFolder->getDocuments();
$subdoc = SeedDMS_Core_DMS::filterAccess($subdoc, $user, M_READ);
$content .= count($subsub)." ".getMLText("folders")." ".count($subdoc)." ".getMLText("documents");
}
$content .= "
\n";
return $content;
} /* }}} */
/**
* Output HTML Code for jumploader
*
* @param string $uploadurl URL where post data is send
* @param integer $folderid id of folder where document is saved
* @param integer $maxfiles maximum number of files allowed to upload
* @param array $fields list of post fields
*/
function printUploadApplet($uploadurl, $attributes, $maxfiles=0, $fields=array()){ /* {{{ */
?>
";
print "";
$this->contentEnd();
$this->htmlEndPage();
add_log_line(" UI::exitError error=".$errormsg." pagetitle=".$pagetitle, PEAR_LOG_ERR);
if($noexit)
return;
exit;
} /* }}} */
/**
* Return HTML Template for jumploader
*
* @param string $uploadurl URL where post data is send
* @param integer $folderid id of folder where document is saved
* @param integer $maxfiles maximum number of files allowed to upload
* @param array $fields list of post fields
*/
function getFineUploaderTemplate() { /* {{{ */
return '
';
} /* }}} */
/**
* Output HTML Code for Fine Uploader
*
* @param string $uploadurl URL where post data is send
* @param integer $folderid id of folder where document is saved
* @param integer $maxfiles maximum number of files allowed to upload
* @param array $fields list of post fields
*/
function printFineUploaderHtml($prefix='userfile') { /* {{{ */
echo self::getFineUploaderHtml($prefix);
} /* }}} */
/**
* Get HTML Code for Fine Uploader
*
* @param string $uploadurl URL where post data is send
* @param integer $folderid id of folder where document is saved
* @param integer $maxfiles maximum number of files allowed to upload
* @param array $fields list of post fields
*/
function getFineUploaderHtml($prefix='userfile') { /* {{{ */
$html = '
';
return $html;
} /* }}} */
/**
* Output Javascript Code for fine uploader
*
* @param string $uploadurl URL where post data is send
* @param integer $folderid id of folder where document is saved
* @param integer $maxfiles maximum number of files allowed to upload
* @param array $fields list of post fields
*/
function printFineUploaderJs($uploadurl, $partsize=0, $maxuploadsize=0, $multiple=true, $prefix='userfile') { /* {{{ */
?>
$(document).ready(function() {
uploader = new qq.FineUploader({
debug: false,
autoUpload: false,
multiple: ,
element: $('#-fine-uploader')[0],
template: 'qq-template',
request: {
endpoint: ''
},
0 ? '
validation: {
sizeLimit: '.$maxuploadsize.'
},
' : ''); ?>
chunking: {
enabled: true,
mandatory: true
},
messages: {
sizeError: '{file} is too large, maximum file size is {sizeLimit}.'
},
callbacks: {
onComplete: function(id, name, json, xhr) {
},
onAllComplete: function(succeeded, failed) {
var uuids = Array();
var names = Array();
for (var i = 0; i < succeeded.length; i++) {
uuids.push(this.getUuid(succeeded[i]))
names.push(this.getName(succeeded[i]))
}
$('#-fine-uploader-uuids').val(uuids.join(';'));
$('#-fine-uploader-names').val(names.join(';'));
/* Run upload only if all files could be uploaded */
if(succeeded.length > 0 && failed.length == 0)
document.getElementById('form1').submit();
},
onError: function(id, name, reason, xhr) {
noty({
text: reason,
type: 'error',
dismissQueue: true,
layout: 'topRight',
theme: 'defaultTheme',
timeout: 3500,
});
}
}
});
});
params['dms'];
$document = $latestContent->getDocument();
?>
,
getReviewStatus(10);
break;
case "approval":
$statusList = $latestContent->getApprovalStatus(10);
break;
default:
$statusList = array();
}
foreach($statusList as $rec) {
echo "
";
echo "
";
switch ($rec["type"]) {
case 0: // individual.
$required = $dms->getUser($rec["required"]);
if (!is_object($required)) {
$reqName = getMLText("unknown_user")." '".$rec["required"]."'";
} else {
$reqName = htmlspecialchars($required->getFullName()." (".$required->getLogin().")");
}
break;
case 1: // Approver is a group.
$required = $dms->getGroup($rec["required"]);
if (!is_object($required)) {
$reqName = getMLText("unknown_group")." '".$rec["required"]."'";
}
else {
$reqName = "".htmlspecialchars($required->getName())."";
}
break;
}
echo $reqName;
echo "