mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
(HTML) Formatted Case Logs, Description and Notifications with inline images uploaded as Attachments. Beta Version !!
SVN:trunk[3916]
This commit is contained in:
321
application/Html2Text.php
Normal file
321
application/Html2Text.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
namespace Html2Text;
|
||||
|
||||
/**
|
||||
* Replace all occurrences of the search string with the replacement string.
|
||||
*
|
||||
* @author Sean Murphy <sean@iamseanmurphy.com>
|
||||
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
|
||||
* @license http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* @link http://php.net/manual/function.str-replace.php
|
||||
*
|
||||
* @param mixed $search
|
||||
* @param mixed $replace
|
||||
* @param mixed $subject
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
function mb_str_replace($search, $replace, $subject, &$count = 0) {
|
||||
if (!is_array($subject)) {
|
||||
// Normalize $search and $replace so they are both arrays of the same length
|
||||
$searches = is_array($search) ? array_values($search) : array($search);
|
||||
$replacements = is_array($replace) ? array_values($replace) : array($replace);
|
||||
$replacements = array_pad($replacements, count($searches), '');
|
||||
foreach ($searches as $key => $search) {
|
||||
$parts = mb_split(preg_quote($search), $subject);
|
||||
$count += count($parts) - 1;
|
||||
$subject = implode($replacements[$key], $parts);
|
||||
}
|
||||
} else {
|
||||
// Call mb_str_replace for each subject in array, recursively
|
||||
foreach ($subject as $key => $value) {
|
||||
$subject[$key] = mb_str_replace($search, $replace, $value, $count);
|
||||
}
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2010 Jevon Wright and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* or
|
||||
*
|
||||
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Jevon Wright - initial API and implementation
|
||||
* Denis Flaven - some fixes for properly handling UTF-8 characters
|
||||
****************************************************************************/
|
||||
|
||||
class Html2Text {
|
||||
|
||||
/**
|
||||
* Tries to convert the given HTML into a plain text format - best suited for
|
||||
* e-mail display, etc.
|
||||
*
|
||||
* <p>In particular, it tries to maintain the following features:
|
||||
* <ul>
|
||||
* <li>Links are maintained, with the 'href' copied over
|
||||
* <li>Information in the <head> is lost
|
||||
* </ul>
|
||||
*
|
||||
* @param string html the input HTML
|
||||
* @return string the HTML converted, as best as possible, to text
|
||||
* @throws Html2TextException if the HTML could not be loaded as a {@link DOMDocument}
|
||||
*/
|
||||
static function convert($html) {
|
||||
// replace with spaces
|
||||
|
||||
$html = str_replace(" ", " ", $html);
|
||||
$html = mb_str_replace("\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
|
||||
$html = static::fixNewlines($html);
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
if (!@$doc->loadHTML('<?xml encoding="UTF-8">'.$html)) // Forces the UTF-8 character set for HTML fragments
|
||||
{
|
||||
throw new Html2TextException("Could not load HTML - badly formed?", $html);
|
||||
}
|
||||
|
||||
$output = static::iterateOverNode($doc);
|
||||
|
||||
// remove leading and trailing spaces on each line
|
||||
$output = preg_replace("/[ \t]*\n[ \t]*/im", "\n", $output);
|
||||
$output = preg_replace("/ *\t */im", "\t", $output);
|
||||
|
||||
// remove unnecessary empty lines
|
||||
$output = preg_replace("/\n\n\n*/im", "\n\n", $output);
|
||||
|
||||
// remove leading and trailing whitespace
|
||||
$output = trim($output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unify newlines; in particular, \r\n becomes \n, and
|
||||
* then \r becomes \n. This means that all newlines (Unix, Windows, Mac)
|
||||
* all become \ns.
|
||||
*
|
||||
* @param string text text with any number of \r, \r\n and \n combinations
|
||||
* @return string the fixed text
|
||||
*/
|
||||
static function fixNewlines($text) {
|
||||
// replace \r\n to \n
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
// remove \rs
|
||||
$text = str_replace("\r", "\n", $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
static function nextChildName($node) {
|
||||
// get the next child
|
||||
$nextNode = $node->nextSibling;
|
||||
while ($nextNode != null) {
|
||||
if ($nextNode instanceof \DOMElement) {
|
||||
break;
|
||||
}
|
||||
$nextNode = $nextNode->nextSibling;
|
||||
}
|
||||
$nextName = null;
|
||||
if ($nextNode instanceof \DOMElement && $nextNode != null) {
|
||||
$nextName = strtolower($nextNode->nodeName);
|
||||
}
|
||||
|
||||
return $nextName;
|
||||
}
|
||||
|
||||
static function prevChildName($node) {
|
||||
// get the previous child
|
||||
$nextNode = $node->previousSibling;
|
||||
while ($nextNode != null) {
|
||||
if ($nextNode instanceof \DOMElement) {
|
||||
break;
|
||||
}
|
||||
$nextNode = $nextNode->previousSibling;
|
||||
}
|
||||
$nextName = null;
|
||||
if ($nextNode instanceof \DOMElement && $nextNode != null) {
|
||||
$nextName = strtolower($nextNode->nodeName);
|
||||
}
|
||||
|
||||
return $nextName;
|
||||
}
|
||||
|
||||
static function iterateOverNode($node) {
|
||||
if ($node instanceof \DOMText) {
|
||||
// Replace whitespace characters with a space (equivilant to \s)
|
||||
return preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $node->wholeText);
|
||||
}
|
||||
if ($node instanceof \DOMDocumentType) {
|
||||
// ignore
|
||||
return "";
|
||||
}
|
||||
|
||||
$nextName = static::nextChildName($node);
|
||||
$prevName = static::prevChildName($node);
|
||||
|
||||
$name = strtolower($node->nodeName);
|
||||
|
||||
// start whitespace
|
||||
switch ($name) {
|
||||
case "hr":
|
||||
return "---------------------------------------------------------------\n";
|
||||
|
||||
case "style":
|
||||
case "head":
|
||||
case "title":
|
||||
case "meta":
|
||||
case "script":
|
||||
// ignore these tags
|
||||
return "";
|
||||
|
||||
case "h1":
|
||||
case "h2":
|
||||
case "h3":
|
||||
case "h4":
|
||||
case "h5":
|
||||
case "h6":
|
||||
case "ol":
|
||||
case "ul":
|
||||
// add two newlines, second line is added below
|
||||
$output = "\n";
|
||||
break;
|
||||
|
||||
case "td":
|
||||
case "th":
|
||||
// add tab char to separate table fields
|
||||
$output = "\t";
|
||||
break;
|
||||
|
||||
case "tr":
|
||||
case "p":
|
||||
case "div":
|
||||
// add one line
|
||||
$output = "\n";
|
||||
break;
|
||||
|
||||
case "li":
|
||||
$output = "- ";
|
||||
break;
|
||||
|
||||
default:
|
||||
// print out contents of unknown tags
|
||||
$output = "";
|
||||
break;
|
||||
}
|
||||
|
||||
// debug
|
||||
//$output .= "[$name,$nextName]";
|
||||
|
||||
if (isset($node->childNodes)) {
|
||||
for ($i = 0; $i < $node->childNodes->length; $i++) {
|
||||
$n = $node->childNodes->item($i);
|
||||
|
||||
$text = static::iterateOverNode($n);
|
||||
|
||||
$output .= $text;
|
||||
}
|
||||
}
|
||||
|
||||
// end whitespace
|
||||
switch ($name) {
|
||||
case "h1":
|
||||
case "h2":
|
||||
case "h3":
|
||||
case "h4":
|
||||
case "h5":
|
||||
case "h6":
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "p":
|
||||
case "br":
|
||||
// add one line
|
||||
if ($nextName != "div")
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "div":
|
||||
// add one line only if the next child isn't a div
|
||||
if ($nextName != "div" && $nextName != null)
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
case "a":
|
||||
// links are returned in [text](link) format
|
||||
$href = $node->getAttribute("href");
|
||||
|
||||
$output = trim($output);
|
||||
|
||||
// remove double [[ ]] s from linking images
|
||||
if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") {
|
||||
$output = substr($output, 1, strlen($output) - 2);
|
||||
|
||||
// for linking images, the title of the <a> overrides the title of the <img>
|
||||
if ($node->getAttribute("title")) {
|
||||
$output = $node->getAttribute("title");
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no link text, but a title attr
|
||||
if (!$output && $node->getAttribute("title")) {
|
||||
$output = $node->getAttribute("title");
|
||||
}
|
||||
|
||||
if ($href == null) {
|
||||
// it doesn't link anywhere
|
||||
if ($node->getAttribute("name") != null) {
|
||||
$output = "[$output]";
|
||||
}
|
||||
} else {
|
||||
if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") {
|
||||
// link to the same address: just use link
|
||||
$output;
|
||||
} else {
|
||||
// replace it
|
||||
if ($output) {
|
||||
$output = "[$output]($href)";
|
||||
} else {
|
||||
// empty string
|
||||
$output = $href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// does the next node require additional whitespace?
|
||||
switch ($nextName) {
|
||||
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
|
||||
$output .= "\n";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "img":
|
||||
if ($node->getAttribute("title")) {
|
||||
$output = "[" . $node->getAttribute("title") . "]";
|
||||
} elseif ($node->getAttribute("alt")) {
|
||||
$output = "[" . $node->getAttribute("alt") . "]";
|
||||
} else {
|
||||
$output = "";
|
||||
}
|
||||
break;
|
||||
|
||||
case "li":
|
||||
$output .= "\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
28
application/Html2TextException.php
Normal file
28
application/Html2TextException.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2010 Jevon Wright and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* or
|
||||
*
|
||||
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Jevon Wright - initial API and implementation
|
||||
****************************************************************************/
|
||||
|
||||
namespace Html2Text;
|
||||
|
||||
class Html2TextException extends \Exception {
|
||||
var $more_info;
|
||||
|
||||
public function __construct($message = "", $more_info = "") {
|
||||
parent::__construct($message);
|
||||
$this->more_info = $more_info;
|
||||
}
|
||||
}
|
||||
@@ -1825,7 +1825,7 @@ EOF
|
||||
$sPreviousLog = is_object($value) ? $value->GetAsHTML($oPage, true /* bEditMode */, array('AttributeText', 'RenderWikiHtml')) : '';
|
||||
$iEntriesCount = is_object($value) ? count($value->GetIndex()) : 0;
|
||||
$sHidden = "<input type=\"hidden\" id=\"{$iId}_count\" value=\"$iEntriesCount\"/>"; // To know how many entries the case log already contains
|
||||
$sHTMLValue = "<div class=\"caselog\" $sStyle><table style=\"width:100%;\"><tr><td>$sHeader<textarea style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".htmlentities($sEditValue, ENT_QUOTES, 'UTF-8')."</textarea>$sPreviousLog</td><td>{$sValidationField}</td></tr></table>$sHidden</div>";
|
||||
$sHTMLValue = "<div class=\"caselog\" $sStyle><table style=\"width:100%;\"><tr><td>$sHeader<textarea class=\"htmlEditor\" style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".htmlentities($sEditValue, ENT_QUOTES, 'UTF-8')."</textarea>$sPreviousLog</td><td>{$sValidationField}</td></tr></table>$sHidden</div>";
|
||||
$oPage->add_ready_script("$('#$iId').bind('keyup change validate', function(evt, sFormId) { return ValidateCaseLogField('$iId', $bMandatory, sFormId) } );"); // Custom validation function
|
||||
break;
|
||||
|
||||
@@ -3364,9 +3364,23 @@ EOF
|
||||
{
|
||||
$sHTMLValue = '<span>'.$sComment.'</span><br/>';
|
||||
}
|
||||
$sHTMLValue .= "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
||||
$sHTMLValue .= "<span style=\"font-family:Tahoma,Verdana,Arial,Helvetica;font-size:12px;\" id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
|
||||
// Replace the text area with CKEditor
|
||||
// To change the default settings of the editor,
|
||||
// a) edit the file /js/ckeditor/config.js
|
||||
// b) or override some of the configuration settings, using the second parameter of ckeditor()
|
||||
$aConfig = array();
|
||||
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
||||
$aConfig['font_style'] = $sLanguage;
|
||||
$aConfig['language'] = $sLanguage;
|
||||
$aConfig['contentsLanguage'] = $sLanguage;
|
||||
$aConfig['extraPlugins'] = 'disabler';
|
||||
$sConfigJS = json_encode($aConfig);
|
||||
|
||||
$oPage->add_ready_script("$('#$sInputId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
|
||||
|
||||
}
|
||||
//$aVal = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos);
|
||||
$oPage->add('<fieldset><legend>'.$oAttDef->GetLabel().'</legend>');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* DisplayBlock and derived class
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -1289,8 +1289,34 @@ class HistoryBlock extends DisplayBlock
|
||||
else
|
||||
{
|
||||
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
|
||||
}
|
||||
}
|
||||
$sMaxWidth = MetaModel::GetModuleSetting('itop-attachment', 'inline_image_max_width', '450px');
|
||||
|
||||
$oPage->add_ready_script("$('.case-log-history-entry-toggle').on('click', function () { $(this).closest('.case-log-history-entry').toggleClass('expanded');});");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('.history_entry').each(function() {
|
||||
var jMe = $(this)
|
||||
jMe.find('img[data-att-id]').each(function() {
|
||||
if ('$sMaxWidth' != '')
|
||||
{
|
||||
$(this).css({'max-width': '$sMaxWidth', width: '', height: '', 'max-height': ''});
|
||||
}
|
||||
$(this).addClass('inline-image');
|
||||
$(this).attr('href', $(this).attr('src'));
|
||||
}).magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
|
||||
var oContent = $(this).find('.history_html_content');
|
||||
if (jMe.height() < oContent.height())
|
||||
{
|
||||
jMe.prepend('<span class="history_truncated_toggler"></span>');
|
||||
jMe.find('.history_truncated_toggler').on('click', function() {
|
||||
jMe.toggleClass('history_entry_truncated');
|
||||
});
|
||||
}
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -1319,7 +1345,7 @@ class HistoryBlock extends DisplayBlock
|
||||
}
|
||||
$aAttribs = array('date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')),
|
||||
'userinfo' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')),
|
||||
'log' => array('label' => Dict::S('UI:History:Changes'), 'description' => Dict::S('UI:History:Changes+')),
|
||||
'log' => array('label' => Dict::S('UI:History:Changes').'<span style="display:block;float:right">Expand All / Collapse All</span>', 'description' => Dict::S('UI:History:Changes+')),
|
||||
);
|
||||
$aValues = array();
|
||||
foreach($aChanges as $aChange)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class iTopWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -416,7 +416,7 @@ EOF
|
||||
ShowDebug();
|
||||
$('#logOffBtn>ul').popupmenu();
|
||||
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
|
||||
|
||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
use Html2Text\Html2Text;
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
@@ -26,11 +27,14 @@
|
||||
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
require_once(APPROOT.'/application/transaction.class.inc.php');
|
||||
require_once(APPROOT.'application/Html2Text.php');
|
||||
require_once(APPROOT.'application/Html2TextException.php');
|
||||
|
||||
define('ITOP_CONFIG_FILE', 'config-itop.php');
|
||||
define('ITOP_DEFAULT_CONFIG_FILE', APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE);
|
||||
|
||||
define('SERVER_NAME_PLACEHOLDER', '$SERVER_NAME$');
|
||||
define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.render.php?operation=download_document&class=Attachment&field=contents&id=');
|
||||
|
||||
class FileUploadException extends Exception
|
||||
{
|
||||
@@ -1134,4 +1138,65 @@ class utils
|
||||
asort($aPossibleEncodings);
|
||||
return $aPossibleEncodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing some (valid) HTML markup to plain text
|
||||
* @param string $sHtml
|
||||
* @return string
|
||||
*/
|
||||
public static function HtmlToText($sHtml)
|
||||
{
|
||||
try
|
||||
{
|
||||
//return '<?xml encoding="UTF-8">'.$sHtml;
|
||||
return \Html2Text\Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert (?) plain text to some HTML markup by replacing newlines by </br> tags
|
||||
* and escaping HTML entities
|
||||
* @param string $sText
|
||||
* @return string
|
||||
*/
|
||||
public static function TextToHtml($sText)
|
||||
{
|
||||
$sText = str_replace("\r\n", "\n", $sText);
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
return str_replace("\n", '</br>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the supplied HTML fragment to rebuild the attribute src="" for images
|
||||
* that refer to an attachment (detected via the attribute data-att-id="") so that
|
||||
* the URL is consistent with the current URL of the application.
|
||||
* @param string $sHtml The HTML fragment to process
|
||||
* @return string The modified HTML
|
||||
*/
|
||||
public static function FixInlineAttachments($sHtml)
|
||||
{
|
||||
$aNeedles = array();
|
||||
$aReplacements = array();
|
||||
// Find img tags with an attribute data-att-id
|
||||
if (preg_match_all('/<img ([^>]*)data-att-id="([0-9]+)"([^>]*)>/i', $sHtml, $aMatches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL;
|
||||
foreach($aMatches as $aImgInfo)
|
||||
{
|
||||
$sImgTag = $aImgInfo[0][0];
|
||||
$sAttId = $aImgInfo[2][0];
|
||||
|
||||
$sNewImgTag = preg_replace('/src="[^"]+"/', 'src="'.$sUrl.$sAttId.'"', $sImgTag); // preserve other attributes
|
||||
$aNeedles[] = $sImgTag;
|
||||
$aReplacements[] = $sNewImgTag;
|
||||
}
|
||||
$sHtml = str_replace($aNeedles, $aReplacements, $sHtml);
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Persistent classes (internal): user defined actions
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -157,7 +157,7 @@ class ActionEmail extends ActionNotification
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values"=>new ValueSetEnum('low,normal,high'), "sql"=>"importance", "default_value"=>'normal', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
|
||||
@@ -30,6 +30,7 @@ require_once('ormdocument.class.inc.php');
|
||||
require_once('ormstopwatch.class.inc.php');
|
||||
require_once('ormpassword.class.inc.php');
|
||||
require_once('ormcaselog.class.inc.php');
|
||||
require_once('htmlsanitizer.class.inc.php');
|
||||
|
||||
/**
|
||||
* MissingColumnException - sent if an attribute is being created but the column is missing in the row
|
||||
@@ -436,6 +437,15 @@ abstract class AttributeDefinition
|
||||
{
|
||||
return (string)$sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* For fields containing a potential markup, return the value without this markup
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText($sValue, $oHostObj = null)
|
||||
{
|
||||
return (string) $this->GetEditValue($sValue, $oHostObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get a value that will be JSON encoded
|
||||
@@ -476,7 +486,7 @@ abstract class AttributeDefinition
|
||||
/**
|
||||
* Override to escape the value when read by DBObject::GetAsCSV()
|
||||
*/
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
return (string)$sValue;
|
||||
}
|
||||
@@ -499,6 +509,7 @@ abstract class AttributeDefinition
|
||||
'' => 'Plain text (unlocalized) representation',
|
||||
'html' => 'HTML representation',
|
||||
'label' => 'Localized representation',
|
||||
'text' => 'Plain text representation (without any markup)',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -524,6 +535,10 @@ abstract class AttributeDefinition
|
||||
case 'label':
|
||||
return $this->GetEditValue($value);
|
||||
|
||||
case 'text':
|
||||
return $this->GetAsPlainText($value);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObj));
|
||||
}
|
||||
@@ -780,7 +795,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
|
||||
$sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
|
||||
@@ -1676,7 +1691,7 @@ class AttributeBoolean extends AttributeInteger
|
||||
{
|
||||
return $sValue ? '1' : '0';
|
||||
}
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
return $sValue ? '1' : '0';
|
||||
}
|
||||
@@ -1806,7 +1821,7 @@ class AttributeString extends AttributeDBField
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -1972,7 +1987,7 @@ class AttributeFinalClass extends AttributeString
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
if ($bLocalize && $value != '')
|
||||
{
|
||||
@@ -1982,7 +1997,7 @@ class AttributeFinalClass extends AttributeString
|
||||
{
|
||||
$sRawValue = $value;
|
||||
}
|
||||
return parent::GetAsCSV($sRawValue, $sSeparator, $sTextQualifier, null, false);
|
||||
return parent::GetAsCSV($sRawValue, $sSeparator, $sTextQualifier, null, false, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
@@ -2162,9 +2177,43 @@ define('WIKI_OBJECT_REGEXP', '/\[\[(.+):(.+)\]\]/U');
|
||||
*/
|
||||
class AttributeText extends AttributeString
|
||||
{
|
||||
public function GetEditClass() {return "Text";}
|
||||
public function GetEditClass() {return ($this->GetFormat() == 'text') ? 'Text' : "HTML";}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false) {return "TEXT";}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->Get('sql')] = $this->GetSQLCol($bFullSpec);
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Add the extra column only if the property 'format' is specified for the attribute
|
||||
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')";
|
||||
if ($bFullSpec)
|
||||
{
|
||||
$aColumns[$this->Get('sql').'_format'].= " DEFAULT 'text'"; // default 'text' is for migrating old records
|
||||
}
|
||||
}
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->Get('sql');
|
||||
}
|
||||
$aColumns = array();
|
||||
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
|
||||
$aColumns[''] = $sPrefix;
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Add the extra column only if the property 'format' is specified for the attribute
|
||||
$aColumns['_format'] = $sPrefix.'_format';
|
||||
}
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
// Is there a way to know the current limitation for mysql?
|
||||
@@ -2224,8 +2273,6 @@ class AttributeText extends AttributeString
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
$aStyles = array();
|
||||
if ($this->GetWidth() != '')
|
||||
{
|
||||
@@ -2241,44 +2288,83 @@ class AttributeText extends AttributeString
|
||||
$aStyles[] = 'overflow:auto';
|
||||
$sStyle = 'style="'.implode(';', $aStyles).'"';
|
||||
}
|
||||
return "<div $sStyle>".str_replace("\n", "<br>\n", $sValue).'</div>';
|
||||
|
||||
if ($this->GetFormat() == 'text')
|
||||
{
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
return "<div $sStyle>".str_replace("\n", "<br>\n", $sValue).'</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return "<div class=\"HTML\" $sStyle>".utils::FixInlineAttachments($sValue).'</div>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
if ($this->GetFormat() == 'text')
|
||||
{
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
{
|
||||
$sClass = $aMatches[1];
|
||||
$sName = $aMatches[2];
|
||||
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sValue = str_replace($aMatches[0], "[[$sClassLabel:$sName]]", $sValue);
|
||||
$sClass = $aMatches[1];
|
||||
$sName = $aMatches[2];
|
||||
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sValue = str_replace($aMatches[0], "[[$sClassLabel:$sName]]", $sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* For fields containing a potential markup, return the value without this markup
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText($sValue, $oHostObj = null)
|
||||
{
|
||||
if ($this->GetFormat() == 'html')
|
||||
{
|
||||
return (string) utils::HtmlToText($this->GetEditValue($sValue, $oHostObj));
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::GetAsPlainText($sValue, $oHostObj);
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$sValue = $proposedValue;
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
switch ($this->GetFormat())
|
||||
{
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
case 'html':
|
||||
$sValue = HTMLSanitizer::Sanitize($sValue);
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
default:
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
|
||||
{
|
||||
$sClassLabel = $aMatches[1];
|
||||
$sName = $aMatches[2];
|
||||
|
||||
if (!MetaModel::IsValidClass($sClassLabel))
|
||||
foreach($aAllMatches as $iPos => $aMatches)
|
||||
{
|
||||
$sClass = MetaModel::GetClassFromLabel($sClassLabel);
|
||||
if ($sClass)
|
||||
$sClassLabel = $aMatches[1];
|
||||
$sName = $aMatches[2];
|
||||
|
||||
if (!MetaModel::IsValidClass($sClassLabel))
|
||||
{
|
||||
$sValue = str_replace($aMatches[0], "[[$sClass:$sName]]", $sValue);
|
||||
$sClass = MetaModel::GetClassFromLabel($sClassLabel);
|
||||
if ($sClass)
|
||||
{
|
||||
$sValue = str_replace($aMatches[0], "[[$sClass:$sName]]", $sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2300,6 +2386,92 @@ class AttributeText extends AttributeString
|
||||
{
|
||||
return $this->GetOptional('height', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual formatting of the field: either text (=plain text) or html (= text with HTML markup)
|
||||
* @return string
|
||||
*/
|
||||
public function GetFormat()
|
||||
{
|
||||
return $this->GetOptional('format', 'text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value from the row returned by the SQL query and transorms it to the appropriate
|
||||
* internal format (either text or html)
|
||||
* @see AttributeDBFieldVoid::FromSQLToValue()
|
||||
*/
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$value = $aCols[$sPrefix.''];
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Read from the extra column only if the property 'format' is specified for the attribute
|
||||
$sFormat = $aCols[$sPrefix.'_format'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormat = $this->GetFormat();
|
||||
}
|
||||
|
||||
switch($sFormat)
|
||||
{
|
||||
case 'text':
|
||||
if ($this->GetFormat() == 'html')
|
||||
{
|
||||
$value = utils::TextToHtml($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
if ($this->GetFormat() == 'text')
|
||||
{
|
||||
$value = utils::HtmlToText($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = utils::FixInlineAttachments((string)$value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown format ??
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$aValues = array();
|
||||
$aValues[$this->Get("sql")] = $this->ScalarToSQL($value);
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Add the extra column only if the property 'format' is specified for the attribute
|
||||
$aValues[$this->Get("sql").'_format'] = $this->GetFormat();
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
switch($this->GetFormat())
|
||||
{
|
||||
case 'html':
|
||||
if ($bConvertToPlainText)
|
||||
{
|
||||
$sValue = utils::HtmlToText((string)$sValue);
|
||||
}
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
default:
|
||||
return parent::GetAsCSV($sValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2358,7 +2530,25 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
return $sValue->GetModifiedEntry();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For fields containing a potential markup, return the value without this markup
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText($value, $oHostObj = null)
|
||||
{
|
||||
$value = $oObj->Get($sAttCode);
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
|
||||
return $value->GetAsPlainText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetDefaultValue() {return new ormCaseLog();}
|
||||
public function Equals($val1, $val2) {return ($val1->GetText() == $val2->GetText());}
|
||||
|
||||
@@ -2402,7 +2592,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
{
|
||||
if (strlen($proposedValue) > 0)
|
||||
{
|
||||
$oCaseLog->AddLogEntry(parent::MakeRealValue($proposedValue, $oHostObj));
|
||||
$oCaseLog->AddLogEntry($proposedValue);
|
||||
}
|
||||
}
|
||||
$ret = $oCaseLog;
|
||||
@@ -2414,7 +2604,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->GetCode();
|
||||
$sPrefix = $this->Get('sql');
|
||||
}
|
||||
$aColumns = array();
|
||||
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
|
||||
@@ -2502,11 +2692,11 @@ class AttributeCaseLog extends AttributeLongText
|
||||
return "<div class=\"caselog\" $sStyle>".$sContent.'</div>'; }
|
||||
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
return parent::GetAsCSV($value->GetText(), $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
|
||||
return parent::GetAsCSV($value->GetText($bConvertToPlainText), $sSeparator, $sTextQualifier, $oHostObject, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2615,6 +2805,15 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
return $sFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual formatting of the text: either text (=plain text) or html (= text with HTML markup)
|
||||
* @return string
|
||||
*/
|
||||
public function GetFormat()
|
||||
{
|
||||
return $this->GetOptional('format', 'html'); // default format for case logs is now HTML
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2624,11 +2823,29 @@ class AttributeCaseLog extends AttributeLongText
|
||||
*/
|
||||
class AttributeHTML extends AttributeLongText
|
||||
{
|
||||
public function GetEditClass() {return "HTML";}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
return $sValue;
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = $this->GetSQLCol();
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Add the extra column only if the property 'format' is specified for the attribute
|
||||
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')";
|
||||
if ($bFullSpec)
|
||||
{
|
||||
$aColumns[$this->Get('sql').'_format'].= " DEFAULT 'html'"; // default 'html' is for migrating old records
|
||||
}
|
||||
}
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual formatting of the text: either text (=plain text) or html (= text with HTML markup)
|
||||
* @return string
|
||||
*/
|
||||
public function GetFormat()
|
||||
{
|
||||
return $this->GetOptional('format', 'html'); // Defaults to HTML
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2706,11 +2923,29 @@ class AttributeTemplateText extends AttributeText
|
||||
*/
|
||||
class AttributeTemplateHTML extends AttributeText
|
||||
{
|
||||
public function GetEditClass() {return "HTML";}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
return $sValue;
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = $this->GetSQLCol();
|
||||
if ($this->GetOptional('format', null) != null )
|
||||
{
|
||||
// Add the extra column only if the property 'format' is specified for the attribute
|
||||
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')";
|
||||
if ($bFullSpec)
|
||||
{
|
||||
$aColumns[$this->Get('sql').'_format'].= " DEFAULT 'html'"; // default 'html' is for migrating old records
|
||||
}
|
||||
}
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual formatting of the text: either text (=plain text) or html (= text with HTML markup)
|
||||
* @return string
|
||||
*/
|
||||
public function GetFormat()
|
||||
{
|
||||
return $this->GetOptional('format', 'html'); // Defaults to HTML
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2885,7 +3120,7 @@ class AttributeEnum extends AttributeString
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
if (is_null($sValue))
|
||||
{
|
||||
@@ -3257,7 +3492,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -3818,7 +4053,7 @@ class AttributeExternalField extends AttributeDefinition
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
return array('' => $this->GetCode());
|
||||
return array('' => $this->GetCode()); // Warning: Use GetCode() since AttributeExternalField does not have any 'sql' property
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4013,10 +4248,10 @@ class AttributeExternalField extends AttributeDefinition
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsXML($value, null, $bLocalize);
|
||||
}
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier, null, $bLocalize);
|
||||
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier, null, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint() { return false; }
|
||||
@@ -4257,7 +4492,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
return ''; // Not exportable in CSV !
|
||||
}
|
||||
@@ -4379,7 +4614,7 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->GetCode();
|
||||
$sPrefix = $this->GetCode(); // Warning: a stopwatch does not have any 'sql' property, so its SQL column is equal to its attribute code !!
|
||||
}
|
||||
$aColumns = array();
|
||||
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
|
||||
@@ -4551,7 +4786,7 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
@@ -4773,7 +5008,7 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function GetSubItemAsCSV($sItemCode, $value, $sSeparator = ',', $sTextQualifier = '"')
|
||||
public function GetSubItemAsCSV($sItemCode, $value, $sSeparator = ',', $sTextQualifier = '"', $bConvertToPlainText = false)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -5026,10 +5261,10 @@ class AttributeSubItem extends AttributeDefinition
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsCSV($this->Get('item_code'), $value, $sSeparator, $sTextQualifier);
|
||||
$res = $oParent->GetSubItemAsCSV($this->Get('item_code'), $value, $sSeparator, $sTextQualifier, $bConvertToPlainText);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -5087,7 +5322,7 @@ class AttributeOneWayPassword extends AttributeDefinition
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->GetCode();
|
||||
$sPrefix = $this->GetCode(); // Warning: AttributeOneWayPassword does not have any sql property so code = sql !
|
||||
}
|
||||
$aColumns = array();
|
||||
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
|
||||
@@ -5196,7 +5431,7 @@ class AttributeOneWayPassword extends AttributeDefinition
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
return ''; // Not exportable in CSV
|
||||
}
|
||||
@@ -5300,7 +5535,7 @@ class AttributeTable extends AttributeDBField
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
// Not implemented
|
||||
return '';
|
||||
@@ -5372,7 +5607,7 @@ class AttributePropertySet extends AttributeTable
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
if (count($value) == 0)
|
||||
{
|
||||
@@ -5451,7 +5686,7 @@ class AttributeComputedFieldVoid extends AttributeDefinition
|
||||
{
|
||||
if ($sPrefix == '')
|
||||
{
|
||||
$sPrefix = $this->GetCode();
|
||||
$sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property
|
||||
}
|
||||
return array('' => $sPrefix);
|
||||
}
|
||||
@@ -5602,7 +5837,7 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
|
||||
return Str::pure2html((string)$sValue);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
@@ -5769,7 +6004,7 @@ class AttributeRedundancySettings extends AttributeDBField
|
||||
return sprintf($this->GetUserOptionFormat($sCurrentOption), $this->GetMinUpValue($sValue), MetaModel::GetName($sClass));
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Persistent classes (internal) : cmdbChangeOp and derived
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -529,9 +529,6 @@ class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$sResult = '';
|
||||
$oTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
@@ -560,6 +557,66 @@ class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a multiline string (text) containing some HTML markup
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_html",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
|
||||
}
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
$sResult = '';
|
||||
$oTargetObjectClass = $this->Get('objclass');
|
||||
$oTargetObjectKey = $this->Get('objkey');
|
||||
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
|
||||
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
|
||||
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sTextView = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$this->Get('prevdata').'</div></div>';
|
||||
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a caselog (text)
|
||||
* since the caselog itself stores the history
|
||||
@@ -622,27 +679,8 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
||||
$oObj = $oMonoObjectSet->Fetch();
|
||||
$oCaseLog = $oObj->Get($this->Get('attcode'));
|
||||
$iMaxVisibleLength = MetaModel::getConfig()->Get('max_history_case_log_entry_length', 0);
|
||||
$sTextEntry = $oCaseLog->GetEntryAt($this->Get('lastentry'));
|
||||
if (($iMaxVisibleLength > 0) && (strlen($sTextEntry) > $iMaxVisibleLength))
|
||||
{
|
||||
if (function_exists('mb_strcut'))
|
||||
{
|
||||
// Safe with multi-byte strings
|
||||
$sBefore = $this->ToHtml(mb_strcut($sTextEntry, 0, $iMaxVisibleLength, 'UTF-8'));
|
||||
$sAfter = $this->ToHtml(mb_strcut($sTextEntry, $iMaxVisibleLength, null, 'UTF-8'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's hope we have no multi-byte characters around the cuttting point...
|
||||
$sBefore = $this->ToHtml(substr($sTextEntry, 0, $iMaxVisibleLength));
|
||||
$sAfter = $this->ToHtml(substr($sTextEntry, $iMaxVisibleLength));
|
||||
}
|
||||
$sTextEntry = '<span class="case-log-history-entry">'.$sBefore.'<span class="case-log-history-entry-end">'.$sAfter.'<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-minus"></span></span><span class="case-log-history-entry-more">...<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-plus"></span></span></span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTextEntry = $this->ToHtml($sTextEntry);
|
||||
}
|
||||
$sTextEntry = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$oCaseLog->GetEntryAt($this->Get('lastentry')).'</div></div>';
|
||||
|
||||
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName, $sTextEntry);
|
||||
}
|
||||
return $sResult;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class cmdbObject
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -337,7 +337,14 @@ abstract class CMDBObject extends DBObject
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
@@ -352,7 +359,14 @@ abstract class CMDBObject extends DBObject
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -30,7 +30,7 @@ define('ACCESS_READONLY', 0);
|
||||
/**
|
||||
* Configuration read/write
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -875,6 +875,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'html_sanitizer' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
|
||||
'default' => 'HTMLDOMSanitizer',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
// Copyright (C) 2015-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Bulk export: CSV export
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$oP->p(" *\tcharset: (optional) character set for encoding the result (default is 'UTF-8').");
|
||||
$oP->p(" *\ttext-qualifier: (optional) character to be used around text strings (default is '\"').");
|
||||
$oP->p(" *\tno_localize: set to 1 to retrieve non-localized values (for instance for ENUM values). Default is 0 (= localized values)");
|
||||
$oP->p(" *\tformatted_text: set to 1 to export case logs and formatted text fields with their HTML markup. Default is 0 (= plain text)");
|
||||
}
|
||||
|
||||
public function ReadParameters()
|
||||
@@ -55,6 +56,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
}
|
||||
|
||||
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
|
||||
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +81,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize') ,'interactive_fields_csv' => array('interactive_fields_csv')));
|
||||
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize', 'formatted_text') ,'interactive_fields_csv' => array('interactive_fields_csv')));
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
@@ -157,6 +159,10 @@ class CSVBulkExport extends TabularBulkExport
|
||||
}
|
||||
$oP->add('</select>');
|
||||
|
||||
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
|
||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:TextFormat').'</h3>');
|
||||
$oP->add('<input type="checkbox" id="csv_formatted_text" name="formatted_text" value="1"'.$sChecked.'><label for="csv_formatted_text"> '.Dict::S('Core:BulkExport:OptionFormattedText').'</label>');
|
||||
|
||||
$oP->add('</td></tr></table>');
|
||||
|
||||
$oP->add('</fieldset>');
|
||||
@@ -182,7 +188,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
break;
|
||||
|
||||
default:
|
||||
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
|
||||
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
@@ -251,7 +257,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
break;
|
||||
|
||||
default:
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput);
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
|
||||
}
|
||||
}
|
||||
if ($this->aStatusInfo['charset'] != 'UTF-8')
|
||||
|
||||
@@ -754,10 +754,10 @@ abstract class DBObject implements iDisplay
|
||||
return $oAtt->GetAsXML($this->Get($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true)
|
||||
public function GetAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize);
|
||||
return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public function GetOriginalAsHTML($sAttCode, $bLocalize = true)
|
||||
@@ -772,10 +772,10 @@ abstract class DBObject implements iDisplay
|
||||
return $oAtt->GetAsXML($this->GetOriginal($sAttCode), $this, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetOriginalAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true)
|
||||
public function GetOriginalAsCSV($sAttCode, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsCSV($this->GetOriginal($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize);
|
||||
return $oAtt->GetAsCSV($this->GetOriginal($sAttCode), $sSeparator, $sTextQualifier, $this, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Send an email (abstraction for synchronous/asynchronous modes)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -157,6 +157,9 @@ class EMail
|
||||
|
||||
protected function SendSynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
// If the body of the message is in HTML, embed all images based on attachments
|
||||
$this->EmbedInlineImages();
|
||||
|
||||
$this->LoadConfig();
|
||||
|
||||
$sTransport = self::$m_oConfig->Get('email_transport');
|
||||
@@ -208,9 +211,46 @@ class EMail
|
||||
return EMAIL_SEND_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reprocess the body of the message (if it is an HTML message)
|
||||
* to replace the URL of images based on attachments by a link
|
||||
* to an embedded image (i.e. cid:....)
|
||||
*/
|
||||
protected function EmbedInlineImages()
|
||||
{
|
||||
if ($this->m_aData['body']['mimeType'] == 'text/html')
|
||||
{
|
||||
$oDOMDoc = new DOMDocument();
|
||||
$oDOMDoc->preserveWhitespace = true;
|
||||
@$oDOMDoc->loadHTML('<?xml encoding="UTF-8"?>'.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
$oXPath = new DOMXPath($oDOMDoc);
|
||||
$sXPath = "//img[@data-att-id]";
|
||||
$oImagesList = $oXPath->query($sXPath);
|
||||
|
||||
if ($oImagesList->length != 0)
|
||||
{
|
||||
foreach($oImagesList as $oImg)
|
||||
{
|
||||
$iAttId = $oImg->getAttribute('data-att-id');
|
||||
$oAttachment = MetaModel::GetObject('Attachment', $iAttId, false, true /* Allow All Data */);
|
||||
if ($oAttachment)
|
||||
{
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
$oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType());
|
||||
$sCid = $this->m_oMessage->embed($oSwiftImage);
|
||||
$oImg->setAttribute('src', $sCid);
|
||||
}
|
||||
}
|
||||
}
|
||||
$sHtmlBody = $oDOMDoc->saveHTML();
|
||||
$this->m_oMessage->setBody($sHtmlBody, 'text/html', 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
|
||||
{
|
||||
{
|
||||
if ($bForceSynchronous)
|
||||
{
|
||||
return $this->SendSynchronous($aIssues, $oLog);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015 Combodo SARL
|
||||
// Copyright (C) 2015-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Bulk export: Excel (xlsx) export
|
||||
*
|
||||
* @copyright Copyright (C) 2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -46,12 +46,18 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
{
|
||||
$oP->p(" * xlsx format options:");
|
||||
$oP->p(" *\tfields: the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
|
||||
$oP->p(" *\tformatted_text: set to 1 to export case logs and formatted text fields with their HTML markup. Default is 0 (= plain text)");
|
||||
}
|
||||
|
||||
|
||||
public function ReadParameters()
|
||||
{
|
||||
parent::ReadParameters();
|
||||
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
|
||||
}
|
||||
|
||||
public function EnumFormParts()
|
||||
{
|
||||
return array_merge(parent::EnumFormParts(), array('interactive_fields_xlsx' => array('interactive_fields_xlsx')));
|
||||
return array_merge(parent::EnumFormParts(), array('xlsx_options' => array('formatted_text') ,'interactive_fields_xlsx' => array('interactive_fields_xlsx')));
|
||||
}
|
||||
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
@@ -62,7 +68,20 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_xlsx');
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'xlsx_options':
|
||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:XLSXOptions').'</legend>');
|
||||
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
||||
|
||||
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
|
||||
$oP->add('<h3>'.Dict::S('Core:BulkExport:TextFormat').'</h3>');
|
||||
$oP->add('<input type="checkbox" id="xlsx_formatted_text" name="formatted_text" value="1"'.$sChecked.'><label for="xlsx_formatted_text"> '.Dict::S('Core:BulkExport:OptionFormattedText').'</label>');
|
||||
|
||||
$oP->add('</td></tr></table>');
|
||||
|
||||
$oP->add('</fieldset>');
|
||||
break;
|
||||
|
||||
default:
|
||||
return parent:: DisplayFormPart($oP, $sPartId);
|
||||
}
|
||||
}
|
||||
@@ -103,8 +122,16 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
$value = $oObj->Get($sAttCode);
|
||||
if ($value instanceOf ormCaseLog)
|
||||
{
|
||||
if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
|
||||
{
|
||||
$sText = $value->GetText();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sText = $value->GetAsPlainText();
|
||||
}
|
||||
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
|
||||
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
|
||||
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $sText));
|
||||
}
|
||||
else if ($value instanceOf DBObjectSet)
|
||||
{
|
||||
@@ -114,7 +141,14 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetEditValue($value, $oObj);
|
||||
if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
|
||||
{
|
||||
$sRet = $oAttDef->GetEditValue($value, $oObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRet = $oAttDef->GetAsPlainText($value, $oObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sRet;
|
||||
|
||||
338
core/htmlsanitizer.class.inc.php
Normal file
338
core/htmlsanitizer.class.inc.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
/**
|
||||
* Base class for all possible implementations of HTML Sanitization
|
||||
*/
|
||||
abstract class HTMLSanitizer
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Do nothing..
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the given HTML document
|
||||
* @param string $sHTML
|
||||
* @return string
|
||||
*/
|
||||
abstract public function DoSanitize($sHTML);
|
||||
|
||||
/**
|
||||
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
|
||||
* @param string $sHTML
|
||||
* @return string
|
||||
*/
|
||||
public static function Sanitize($sHTML)
|
||||
{
|
||||
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
|
||||
if(!class_exists($sSanitizerClass))
|
||||
{
|
||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
|
||||
{
|
||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$oSanitizer = new $sSanitizerClass();
|
||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
if($sSanitizerClass != 'HTMLDOMSanitizer')
|
||||
{
|
||||
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
|
||||
// try again with the HTMLDOMSanitizer
|
||||
$oSanitizer = new HTMLDOMSanitizer();
|
||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
|
||||
IssueLog::Error('The HTML will NOT be sanitized.');
|
||||
$sCleanHTML = $sHTML;
|
||||
}
|
||||
}
|
||||
return $sCleanHTML;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy HTMLSanitizer which does nothing at all!
|
||||
* Can be used if HTML Sanitization is not important
|
||||
* (for example when importing "safe" data during an on-boarding)
|
||||
* and performance is at stake
|
||||
*
|
||||
*/
|
||||
class HTMLNullSanitizer extends HTMLSanitizer
|
||||
{
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see HTMLSanitizer::Sanitize()
|
||||
*/
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
|
||||
* Complete but quite slow
|
||||
* http://htmlpurifier.org
|
||||
*/
|
||||
/*
|
||||
class HTMLPurifierSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected static $oPurifier = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (self::$oPurifier == null)
|
||||
{
|
||||
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
|
||||
if (!file_exists($sLibPath))
|
||||
{
|
||||
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
|
||||
}
|
||||
require_once($sLibPath);
|
||||
|
||||
$oPurifierConfig = HTMLPurifier_Config::createDefault();
|
||||
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
|
||||
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
|
||||
$oPurifierConfig->set('URI.AllowedSchemes', array (
|
||||
'http' => true,
|
||||
'https' => true,
|
||||
'data' => true, // This one is not present by default
|
||||
));
|
||||
$sPurifierCache = APPROOT.'data/HTMLPurifier';
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
mkdir($sPurifierCache);
|
||||
}
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
throw new Exception("Could not create the cache directory '$sPurifierCache'");
|
||||
}
|
||||
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
|
||||
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$sCleanHtml = self::$oPurifier->purify($sHTML);
|
||||
return $sCleanHtml;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected $oDoc;
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
'body' => array(),
|
||||
'a' => array('href', 'name', 'style'),
|
||||
'p' => array('style'),
|
||||
'br' => array(),
|
||||
'span' => array('style'),
|
||||
'div' => array('style'),
|
||||
'b' => array(),
|
||||
'i' => array(),
|
||||
'em' => array(),
|
||||
'strong' => array(),
|
||||
'img' => array('src','style'),
|
||||
'ul' => array('style'),
|
||||
'ol' => array('style'),
|
||||
'li' => array('style'),
|
||||
'h1' => array('style'),
|
||||
'h2' => array('style'),
|
||||
'h3' => array('style'),
|
||||
'h4' => array('style'),
|
||||
'nav' => array('style'),
|
||||
'section' => array('style'),
|
||||
'code' => array('style'),
|
||||
'table' => array('style', 'width'),
|
||||
'thead' => array('style'),
|
||||
'tbody' => array('style'),
|
||||
'tr' => array('style'),
|
||||
'td' => array('style', 'colspan'),
|
||||
'th' => array('style'),
|
||||
'fieldset' => array('style'),
|
||||
'legend' => array('style'),
|
||||
'font' => array('face', 'color', 'style', 'size'),
|
||||
'big' => array(),
|
||||
'small' => array(),
|
||||
'tt' => array(),
|
||||
'code' => array(),
|
||||
'kbd' => array(),
|
||||
'samp' => array(),
|
||||
'var' => array(),
|
||||
'del' => array(),
|
||||
's' => array(), // strikethrough
|
||||
'ins' => array(),
|
||||
'cite' => array(),
|
||||
'q' => array(),
|
||||
'hr' => array('style'),
|
||||
'pre' => array(),
|
||||
'center' => array(),
|
||||
);
|
||||
|
||||
protected static $aAttrsWhiteList = array(
|
||||
'href' => '/^(http:|https:)/i',
|
||||
'src' => '/^(http:|https:|data:)/i',
|
||||
);
|
||||
|
||||
protected static $aStylesWhiteList = array(
|
||||
'background-color', 'color', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height',
|
||||
);
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
$oXPath = new DOMXPath($this->oDoc);
|
||||
$sXPath = "//body";
|
||||
$oNodesList = $oXPath->query($sXPath);
|
||||
|
||||
if ($oNodesList->length == 0)
|
||||
{
|
||||
// No body, save the whole document
|
||||
$sCleanHtml = $this->oDoc->saveHTML();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Export only the content of the body tag
|
||||
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
||||
// remove the body tag itself
|
||||
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
|
||||
}
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
|
||||
protected function CleanNode(DOMNode $oElement)
|
||||
{
|
||||
$aAttrToRemove = array();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes())
|
||||
{
|
||||
foreach($oElement->attributes as $oAttr)
|
||||
{
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
|
||||
{
|
||||
// Forbidden (or unknown) attribute
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if ($sAttr == 'style')
|
||||
{
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '')
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode)
|
||||
{
|
||||
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else if ($oNode instanceof DOMComment)
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
|
||||
{
|
||||
$this->ProcessImage($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extra attribute data-att-id for images which are based on an actual attachment
|
||||
* so that we can later reconstruct the full "src" URL when needed
|
||||
* @param DOMNode $oElement
|
||||
*/
|
||||
protected function ProcessImage(DOMNode $oElement)
|
||||
{
|
||||
$sSrc = $oElement->getAttribute('src');
|
||||
$sDownloadUrl = str_replace(array('.', '?'), array('\.', '\?'), ATTACHMENT_DOWNLOAD_URL); // Escape . and ?
|
||||
$sUrlPattern = '|'.$sDownloadUrl.'([0-9]+)|';
|
||||
if (preg_match($sUrlPattern, $sSrc, $aMatches))
|
||||
{
|
||||
$oElement->setAttribute('data-att-id', $aMatches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
{
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach($aItems as $sItem)
|
||||
{
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
|
||||
{
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(';', $aAllowedStyles);
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
{
|
||||
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
|
||||
{
|
||||
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3538,7 +3538,6 @@ abstract class MetaModel
|
||||
{
|
||||
// Skip this attribute if not originaly defined in this class
|
||||
if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue;
|
||||
|
||||
foreach($oAttDef->GetSQLColumns(true) as $sField => $sDBFieldSpec)
|
||||
{
|
||||
// Keep track of columns used by iTop
|
||||
@@ -4893,9 +4892,9 @@ abstract class MetaModel
|
||||
{
|
||||
// Expand the parameters for the object
|
||||
$sName = substr($sSearch, 0, $iPos);
|
||||
if (preg_match_all('/\\$'.$sName.'->([^\\$]+)\\$/', $sInput, $aMatches))
|
||||
if (preg_match_all('/\\$'.$sName.'-(>|>)([^\\$]+)\\$/', $sInput, $aMatches)) // Support both syntaxes: $this->xxx$ or $this->xxx$ for HTML compatibility
|
||||
{
|
||||
foreach($aMatches[1] as $sPlaceholderAttCode)
|
||||
foreach($aMatches[2] as $idx => $sPlaceholderAttCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -4903,7 +4902,7 @@ abstract class MetaModel
|
||||
if ($sReplacement !== null)
|
||||
{
|
||||
$aReplacements[] = $sReplacement;
|
||||
$aSearches[] = '$'.$sName.'->'.$sPlaceholderAttCode.'$';
|
||||
$aSearches[] = '$'.$sName.'-'.$aMatches[1][$idx].$sPlaceholderAttCode.'$';
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
@@ -43,9 +43,17 @@ class ormCaseLog {
|
||||
$this->m_bModified = false;
|
||||
}
|
||||
|
||||
public function GetText()
|
||||
public function GetText($bConvertToPlainText = false)
|
||||
{
|
||||
return $this->m_sLog;
|
||||
if ($bConvertToPlainText)
|
||||
{
|
||||
// Rebuild the log, but filtering any HTML markup for the all 'html' entries in the log
|
||||
return $this->GetAsPlainText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->m_sLog;
|
||||
}
|
||||
}
|
||||
|
||||
public static function FromJSON($oJson)
|
||||
@@ -97,11 +105,24 @@ class ormCaseLog {
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : 'text';
|
||||
switch($sFormat)
|
||||
{
|
||||
case 'text':
|
||||
$sHtmlEntry = utils::TextToHtml($sTextEntry);
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
$sHtmlEntry = $sTextEntry;
|
||||
$sTextEntry = utils::HtmlToText($sHtmlEntry);
|
||||
break;
|
||||
}
|
||||
$aEntries[] = array(
|
||||
'date' => $sDate,
|
||||
'user_login' => $this->m_aIndex[$index]['user_name'],
|
||||
'user_id' => $this->m_aIndex[$index]['user_id'],
|
||||
'message' => $sTextEntry
|
||||
'message' => $sTextEntry,
|
||||
'message_html' => $sHtmlEntry,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,7 +134,8 @@ class ormCaseLog {
|
||||
$aEntries[] = array(
|
||||
'date' => '',
|
||||
'user_login' => '',
|
||||
'message' => $sTextEntry
|
||||
'message' => $sTextEntry,
|
||||
'message_html' => utils::TextToHtml($sTextEntry),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,6 +144,22 @@ class ormCaseLog {
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "plain text" version of the log (equivalent to $this->m_sLog) where all the HTML markup from the 'html' entries have been removed
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText()
|
||||
{
|
||||
$sPlainText = '';
|
||||
$aJSON = $this->GetForJSON();
|
||||
foreach($aJSON['entries'] as $aData)
|
||||
{
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
|
||||
$sPlainText .= $sSeparator.$aData['message'];
|
||||
}
|
||||
return $sPlainText;
|
||||
}
|
||||
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->m_aIndex;
|
||||
@@ -152,7 +190,16 @@ class ormCaseLog {
|
||||
{
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$sCSSClass = 'caselog_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
{
|
||||
$sCSSClass = 'caselog_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTextEntry = utils::FixInlineAttachments($sTextEntry);
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
|
||||
@@ -180,7 +227,7 @@ class ormCaseLog {
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sHtml = $sHtml.$sEntry;
|
||||
@@ -227,7 +274,20 @@ class ormCaseLog {
|
||||
{
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
$sCSSClass = 'case_log_simple_html_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
{
|
||||
$sCSSClass = 'case_log_simple_html_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTextEntry = utils::FixInlineAttachments($sTextEntry);
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<li>';
|
||||
@@ -254,7 +314,7 @@ class ormCaseLog {
|
||||
}
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
|
||||
$sEntry .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= '<div class="'.$sCSSClass.'" style="'.$sStyleCaseLogEntry.'">';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '</li>';
|
||||
@@ -317,10 +377,19 @@ class ormCaseLog {
|
||||
}
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
$sCSSClass= 'caselog_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
{
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
$sCSSClass= 'caselog_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
{
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTextEntry = utils::FixInlineAttachments($sTextEntry);
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
@@ -349,7 +418,7 @@ class ormCaseLog {
|
||||
}
|
||||
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), $sDate, $aIndex[$index]['user_name']);
|
||||
$sEntry .= '</div>';
|
||||
$sEntry .= '<div class="caselog_entry"'.$sDisplay.'>';
|
||||
$sEntry .= '<div class="'.$sCSSClass.'"'.$sDisplay.'>';
|
||||
$sEntry .= $sTextEntry;
|
||||
$sEntry .= '</div>';
|
||||
$sHtml = $sHtml.$sEntry;
|
||||
@@ -358,6 +427,7 @@ class ormCaseLog {
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
// In this case the format is always "text"
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
@@ -402,6 +472,7 @@ class ormCaseLog {
|
||||
*/
|
||||
public function AddLogEntry($sText, $sOnBehalfOf = '')
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
$bMergeEntries = false;
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'));
|
||||
if ($sOnBehalfOf == '')
|
||||
@@ -439,7 +510,8 @@ class ormCaseLog {
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $aLatestEntry['text_length'] + $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
|
||||
}
|
||||
@@ -455,6 +527,7 @@ class ormCaseLog {
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
}
|
||||
$this->m_bModified = true;
|
||||
@@ -463,7 +536,7 @@ class ormCaseLog {
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
$sText = HTMLSanitizer::Sanitize(isset($oJson->message) ? $oJson->message : '');
|
||||
|
||||
if (isset($oJson->user_id))
|
||||
{
|
||||
@@ -505,6 +578,16 @@ class ormCaseLog {
|
||||
{
|
||||
$iDate = time();
|
||||
}
|
||||
if (isset($oJson->format))
|
||||
{
|
||||
$sFormat = $oJson->format;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: what is the default format ? text ?
|
||||
$sFormat = 'html';
|
||||
}
|
||||
|
||||
$sDate = date(Dict::S('UI:CaseLog:DateFormat'), $iDate);
|
||||
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
@@ -516,7 +599,8 @@ class ormCaseLog {
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => $sFormat,
|
||||
);
|
||||
|
||||
$this->m_bModified = true;
|
||||
|
||||
@@ -280,16 +280,33 @@ legend.transparent {
|
||||
}
|
||||
|
||||
|
||||
.ui-widget-content td a.cke_toolbox_collapser {
|
||||
.ui-widget-content td a.cke_button, .ui-widget-content td a.cke_combo_button, .ui-widget-content td a.cke_toolbox_collapser, cke_dialog a {
|
||||
padding-left: 0;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
|
||||
p a:hover, td a:hover {
|
||||
.ui-widget-content td a:hover, p a:hover, td a:hover {
|
||||
text-decoration: underline;
|
||||
color: #e87c1e;
|
||||
padding-left: 14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
|
||||
|
||||
.cke_reset_all *:hover {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
table.cke_dialog_contents a.cke_dialog_ui_button_ok {
|
||||
color: black;
|
||||
border-color: #e87c1e;
|
||||
background: #e87c1e;
|
||||
}
|
||||
|
||||
|
||||
.cke_notifications_area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -2154,3 +2171,33 @@ span.refresh-button {
|
||||
}
|
||||
|
||||
|
||||
.history_entry {
|
||||
position: relative !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.history_entry_truncated {
|
||||
max-height: 7em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.history_truncated_toggler {
|
||||
position: absolute !important;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(ui-lightness/images/ui-icons_222222_256x240.png);
|
||||
background-position: -16px -192px;
|
||||
}
|
||||
|
||||
|
||||
.history_entry_truncated .history_truncated_toggler {
|
||||
background-position: 0 -192px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -224,16 +224,27 @@ legend.transparent {
|
||||
padding-left:14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
.ui-widget-content td a.cke_toolbox_collapser {
|
||||
.ui-widget-content td a.cke_button, .ui-widget-content td a.cke_toolbox_collapser, .ui-widget-content td a.cke_combo_button, cke_dialog a {
|
||||
padding-left: 0;
|
||||
}
|
||||
p a:hover, td a:hover {
|
||||
text-decoration:underline;
|
||||
color:$highlight-color;
|
||||
padding-left:14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.ui-widget-content td a:hover, p a:hover, td a:hover {
|
||||
text-decoration:underline;
|
||||
color:$highlight-color;
|
||||
}
|
||||
.cke_reset_all *:hover {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
table.cke_dialog_contents a.cke_dialog_ui_button_ok {
|
||||
color: #000;
|
||||
border-color: $highlight-color;
|
||||
background: $highlight-color;
|
||||
}
|
||||
.cke_notifications_area {
|
||||
display: none;
|
||||
}
|
||||
td a.no-arrow, td a.no-arrow:visited, .SearchDrawer a.no-arrow, .SearchDrawer a.no-arrow:visited {
|
||||
text-decoration:none;
|
||||
color:#000000;
|
||||
@@ -1583,3 +1594,25 @@ span.refresh-button {
|
||||
.printable-tab .case-log-history-entry .case-log-history-entry-toggle {
|
||||
display: none;
|
||||
}
|
||||
.history_entry {
|
||||
position: relative !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
.history_entry_truncated {
|
||||
max-height: 7em;
|
||||
overflow: hidden;
|
||||
}
|
||||
.history_truncated_toggler {
|
||||
position: absolute !important;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(ui-lightness/images/ui-icons_222222_256x240.png);
|
||||
background-position: -16px -192px;
|
||||
}
|
||||
.history_entry_truncated .history_truncated_toggler {
|
||||
background-position: 0 -192px;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -288,7 +288,7 @@ EOF
|
||||
{
|
||||
$('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
|
||||
}
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg]);
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg, false /* not an inline image */]);
|
||||
|
||||
//alert(data.msg);
|
||||
}
|
||||
@@ -342,7 +342,7 @@ EOF
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="RemoveNewAttachment('.$iAttId.');"/> </div>');
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."']);");
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."', false /* not an line image */]);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Handles various ajax requests
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -97,6 +97,118 @@ try
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'cke_img_upload':
|
||||
// Image uploaded via CKEditor
|
||||
$aResult = array(
|
||||
'uploaded' => 0,
|
||||
'fileName' => '',
|
||||
'url' => '',
|
||||
'icon' => '',
|
||||
'msg' => '',
|
||||
'att_id' => 0,
|
||||
'preview' => 'false',
|
||||
);
|
||||
|
||||
$sObjClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class'));
|
||||
$sTempId = utils::ReadParam('temp_id', '');
|
||||
if (empty($sObjClass))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'obj_class'";
|
||||
}
|
||||
elseif (empty($sTempId))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'temp_id'";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$oDoc = utils::ReadPostedDocument('upload');
|
||||
$oAttachment = MetaModel::NewObject('Attachment');
|
||||
$oAttachment->Set('expire', time() + 3600); // one hour...
|
||||
$oAttachment->Set('temp_id', $sTempId);
|
||||
$oAttachment->Set('item_class', $sObjClass);
|
||||
$oAttachment->SetDefaultOrgId();
|
||||
$oAttachment->Set('contents', $oDoc);
|
||||
$iAttId = $oAttachment->DBInsert();
|
||||
|
||||
$aResult['uploaded'] = 1;
|
||||
$aResult['msg'] = $oDoc->GetFileName();
|
||||
$aResult['fileName'] = $oDoc->GetFileName();
|
||||
$aResult['url'] = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$aResult['icon'] = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($oDoc->GetFileName());
|
||||
$aResult['att_id'] = $iAttId;
|
||||
$aResult['preview'] = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
}
|
||||
catch (FileUploadException $e)
|
||||
{
|
||||
$aResult['error'] = $e->GetMessage();
|
||||
}
|
||||
}
|
||||
$oPage->add(json_encode($aResult));
|
||||
break;
|
||||
|
||||
case 'cke_browse':
|
||||
$oPage = new NiceWebPage('Browse for image...');
|
||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-attachments/css/magnific-popup.css');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlModulesRoot().'itop-attachments/js/jquery.magnific-popup.min.js');
|
||||
$sImgUrl = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL;
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
// Helper function to get parameters from the query string.
|
||||
function getUrlParam( paramName ) {
|
||||
var reParam = new RegExp( '(?:[\?&]|&)' + paramName + '=([^&]+)', 'i' );
|
||||
var match = window.location.search.match( reParam );
|
||||
|
||||
return ( match && match.length > 1 ) ? match[1] : null;
|
||||
}
|
||||
// Simulate user action of selecting a file to be returned to CKEditor.
|
||||
function returnFileUrl(iAttId, sAltText) {
|
||||
|
||||
var funcNum = getUrlParam( 'CKEditorFuncNum' );
|
||||
var fileUrl = '$sImgUrl'+iAttId;
|
||||
window.opener.CKEDITOR.tools.callFunction( funcNum, fileUrl, function() {
|
||||
// Get the reference to a dialog window.
|
||||
var dialog = this.getDialog();
|
||||
// Check if this is the Image Properties dialog window.
|
||||
if ( dialog.getName() == 'image' ) {
|
||||
// Get the reference to a text field that stores the "alt" attribute.
|
||||
var element = dialog.getContentElement( 'info', 'txtAlt' );
|
||||
// Assign the new value.
|
||||
if ( element )
|
||||
element.setValue(sAltText);
|
||||
}
|
||||
// Return "false" to stop further execution. In such case CKEditor will ignore the second argument ("fileUrl")
|
||||
// and the "onSelect" function assigned to the button that called the file manager (if defined).
|
||||
// return false;
|
||||
} );
|
||||
window.close();
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('.img-picker').magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
EOF
|
||||
);
|
||||
$sTempId = utils::ReadParam('temp_id');
|
||||
$sClass = utils::ReadParam('obj_class', '', false, 'class');
|
||||
$iObjectId = utils::ReadParam('obj_key', 0, false, 'integer');
|
||||
$sOQL = "SELECT Attachment WHERE ((temp_id = :temp_id) OR (item_class = :obj_class AND item_id = :obj_id))";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('temp_id' => $sTempId, 'obj_class' => $sClass, 'obj_id' => $iObjectId));
|
||||
while($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
if ($oDoc->GetMainMimeType() == 'image')
|
||||
{
|
||||
$sDocName = addslashes(htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8'));
|
||||
$iAttId = $oAttachment->GetKey();
|
||||
$oPage->add("<div style=\"float:left;margin:1em;text-align:center;\"><img class=\"img-picker\" style=\"max-width:300px;cursor:zoom-in\" href=\"{$sImgUrl}{$iAttId}\" alt=\"$sDocName\" title=\"$sDocName\" src=\"{$sImgUrl}{$iAttId}\"><br/><button onclick=\"returnFileUrl($iAttId, '$sDocName')\">Insert</button></div>");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$oPage->p("Missing argument 'operation'");
|
||||
}
|
||||
|
||||
374
datamodels/2.x/itop-attachments/css/magnific-popup.css
Normal file
374
datamodels/2.x/itop-attachments/css/magnific-popup.css
Normal file
@@ -0,0 +1,374 @@
|
||||
/* Magnific Popup CSS */
|
||||
.mfp-bg {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1042;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
background: #0b0b0b;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80); }
|
||||
|
||||
.mfp-wrap {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1043;
|
||||
position: fixed;
|
||||
outline: none !important;
|
||||
-webkit-backface-visibility: hidden; }
|
||||
|
||||
.mfp-container {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0 8px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
|
||||
.mfp-container:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle; }
|
||||
|
||||
.mfp-align-top .mfp-container:before {
|
||||
display: none; }
|
||||
|
||||
.mfp-content {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
z-index: 1045; }
|
||||
|
||||
.mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
|
||||
width: 100%;
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-ajax-cur {
|
||||
cursor: progress; }
|
||||
|
||||
.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
|
||||
cursor: -moz-zoom-out;
|
||||
cursor: -webkit-zoom-out;
|
||||
cursor: zoom-out; }
|
||||
|
||||
.mfp-zoom {
|
||||
cursor: pointer;
|
||||
cursor: -webkit-zoom-in;
|
||||
cursor: -moz-zoom-in;
|
||||
cursor: zoom-in; }
|
||||
|
||||
.mfp-auto-cursor .mfp-content {
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none; }
|
||||
|
||||
.mfp-loading.mfp-figure {
|
||||
display: none; }
|
||||
|
||||
.mfp-hide {
|
||||
display: none !important; }
|
||||
|
||||
.mfp-preloader {
|
||||
color: #CCC;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: auto;
|
||||
text-align: center;
|
||||
margin-top: -0.8em;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
z-index: 1044; }
|
||||
.mfp-preloader a {
|
||||
color: #CCC; }
|
||||
.mfp-preloader a:hover {
|
||||
color: #FFF; }
|
||||
|
||||
.mfp-s-ready .mfp-preloader {
|
||||
display: none; }
|
||||
|
||||
.mfp-s-error .mfp-content {
|
||||
display: none; }
|
||||
|
||||
button.mfp-close, button.mfp-arrow {
|
||||
overflow: visible;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
z-index: 1046;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none; }
|
||||
button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0; }
|
||||
|
||||
.mfp-close {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
padding: 0 0 18px 10px;
|
||||
color: #FFF;
|
||||
font-style: normal;
|
||||
font-size: 28px;
|
||||
font-family: Arial, Baskerville, monospace; }
|
||||
.mfp-close:hover, .mfp-close:focus {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100); }
|
||||
.mfp-close:active {
|
||||
top: 1px; }
|
||||
|
||||
.mfp-close-btn-in .mfp-close {
|
||||
color: #333; }
|
||||
|
||||
.mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
|
||||
color: #FFF;
|
||||
right: -6px;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
width: 100%; }
|
||||
|
||||
.mfp-counter {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: #CCC;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap; }
|
||||
|
||||
.mfp-arrow {
|
||||
position: absolute;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
margin: 0;
|
||||
top: 50%;
|
||||
margin-top: -55px;
|
||||
padding: 0;
|
||||
width: 90px;
|
||||
height: 110px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
|
||||
.mfp-arrow:active {
|
||||
margin-top: -54px; }
|
||||
.mfp-arrow:hover, .mfp-arrow:focus {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100); }
|
||||
.mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin-top: 35px;
|
||||
margin-left: 35px;
|
||||
border: medium inset transparent; }
|
||||
.mfp-arrow:after, .mfp-arrow .mfp-a {
|
||||
border-top-width: 13px;
|
||||
border-bottom-width: 13px;
|
||||
top: 8px; }
|
||||
.mfp-arrow:before, .mfp-arrow .mfp-b {
|
||||
border-top-width: 21px;
|
||||
border-bottom-width: 21px;
|
||||
opacity: 0.7; }
|
||||
|
||||
.mfp-arrow-left {
|
||||
left: 0; }
|
||||
.mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
|
||||
border-right: 17px solid #FFF;
|
||||
margin-left: 31px; }
|
||||
.mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
|
||||
margin-left: 25px;
|
||||
border-right: 27px solid #3F3F3F; }
|
||||
|
||||
.mfp-arrow-right {
|
||||
right: 0; }
|
||||
.mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
|
||||
border-left: 17px solid #FFF;
|
||||
margin-left: 39px; }
|
||||
.mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
|
||||
border-left: 27px solid #3F3F3F; }
|
||||
|
||||
.mfp-iframe-holder {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px; }
|
||||
.mfp-iframe-holder .mfp-content {
|
||||
line-height: 0;
|
||||
width: 100%;
|
||||
max-width: 900px; }
|
||||
.mfp-iframe-holder .mfp-close {
|
||||
top: -40px; }
|
||||
|
||||
.mfp-iframe-scaler {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding-top: 56.25%; }
|
||||
.mfp-iframe-scaler iframe {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
|
||||
background: #000; }
|
||||
|
||||
/* Main image in popup */
|
||||
img.mfp-img {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
line-height: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 40px 0 40px;
|
||||
margin: 0 auto; }
|
||||
|
||||
/* The shadow behind the image */
|
||||
.mfp-figure {
|
||||
line-height: 0; }
|
||||
.mfp-figure:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 40px;
|
||||
bottom: 40px;
|
||||
display: block;
|
||||
right: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
|
||||
background: #444; }
|
||||
.mfp-figure small {
|
||||
color: #BDBDBD;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 14px; }
|
||||
.mfp-figure figure {
|
||||
margin: 0; }
|
||||
|
||||
.mfp-bottom-bar {
|
||||
margin-top: -36px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-title {
|
||||
text-align: left;
|
||||
line-height: 18px;
|
||||
color: #F3F3F3;
|
||||
word-wrap: break-word;
|
||||
padding-right: 36px; }
|
||||
|
||||
.mfp-image-holder .mfp-content {
|
||||
max-width: 100%; }
|
||||
|
||||
.mfp-gallery .mfp-image-holder .mfp-figure {
|
||||
cursor: pointer; }
|
||||
|
||||
@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
|
||||
/**
|
||||
* Remove all paddings around the image on small screen
|
||||
*/
|
||||
.mfp-img-mobile .mfp-image-holder {
|
||||
padding-left: 0;
|
||||
padding-right: 0; }
|
||||
.mfp-img-mobile img.mfp-img {
|
||||
padding: 0; }
|
||||
.mfp-img-mobile .mfp-figure:after {
|
||||
top: 0;
|
||||
bottom: 0; }
|
||||
.mfp-img-mobile .mfp-figure small {
|
||||
display: inline;
|
||||
margin-left: 5px; }
|
||||
.mfp-img-mobile .mfp-bottom-bar {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
top: auto;
|
||||
padding: 3px 5px;
|
||||
position: fixed;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
.mfp-img-mobile .mfp-bottom-bar:empty {
|
||||
padding: 0; }
|
||||
.mfp-img-mobile .mfp-counter {
|
||||
right: 5px;
|
||||
top: 3px; }
|
||||
.mfp-img-mobile .mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
padding: 0; }
|
||||
}
|
||||
|
||||
@media all and (max-width: 900px) {
|
||||
.mfp-arrow {
|
||||
-webkit-transform: scale(0.75);
|
||||
transform: scale(0.75); }
|
||||
|
||||
.mfp-arrow-left {
|
||||
-webkit-transform-origin: 0;
|
||||
transform-origin: 0; }
|
||||
|
||||
.mfp-arrow-right {
|
||||
-webkit-transform-origin: 100%;
|
||||
transform-origin: 100%; }
|
||||
|
||||
.mfp-container {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px; }
|
||||
}
|
||||
|
||||
.mfp-ie7 .mfp-img {
|
||||
padding: 0; }
|
||||
.mfp-ie7 .mfp-bottom-bar {
|
||||
width: 600px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
margin-top: 5px;
|
||||
padding-bottom: 5px; }
|
||||
.mfp-ie7 .mfp-container {
|
||||
padding: 0; }
|
||||
.mfp-ie7 .mfp-content {
|
||||
padding-top: 44px; }
|
||||
.mfp-ie7 .mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-top: 0; }
|
||||
2060
datamodels/2.x/itop-attachments/js/jquery.magnific-popup.js
Normal file
2060
datamodels/2.x/itop-attachments/js/jquery.magnific-popup.js
Normal file
File diff suppressed because it is too large
Load Diff
4
datamodels/2.x/itop-attachments/js/jquery.magnific-popup.min.js
vendored
Normal file
4
datamodels/2.x/itop-attachments/js/jquery.magnific-popup.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension
|
||||
{
|
||||
protected static $m_bIsModified = false;
|
||||
@@ -204,6 +203,7 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
$sTitle = ($oSet->Count() > 0)? Dict::Format('Attachments:TabTitle_Count', $oSet->Count()) : Dict::S('Attachments:EmptyTabTitle');
|
||||
$oPage->SetCurrentTab($sTitle);
|
||||
}
|
||||
$sMaxWidth = MetaModel::GetModuleSetting('itop-attachment', 'inline_image_max_width', '450px');
|
||||
$oPage->add_style(
|
||||
<<<EOF
|
||||
.attachment {
|
||||
@@ -233,6 +233,9 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
padding: 0;
|
||||
float: none;
|
||||
}
|
||||
.inline-image {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oPage->add('<fieldset>');
|
||||
@@ -243,15 +246,24 @@ EOF
|
||||
$sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false';
|
||||
$iTransactionId = $oPage->GetTransactionId();
|
||||
$sClass = get_class($oObject);
|
||||
$iObjectId = $oObject->Getkey();
|
||||
$sTempId = session_id().'_'.$iTransactionId;
|
||||
$sDeleteBtn = Dict::S('Attachments:DeleteBtn');
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function RemoveAttachment(att_id)
|
||||
{
|
||||
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
|
||||
$('#display_attachment_'+att_id).hide();
|
||||
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
|
||||
var bDelete = true;
|
||||
if ($('#display_attachment_'+att_id).hasClass('image-in-use'))
|
||||
{
|
||||
bDelete = window.confirm('This image is used in a description. Delete it anyway?');
|
||||
}
|
||||
if (bDelete)
|
||||
{
|
||||
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
|
||||
$('#display_attachment_'+att_id).hide();
|
||||
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
|
||||
}
|
||||
return false; // Do not submit the form !
|
||||
}
|
||||
EOF
|
||||
@@ -264,7 +276,7 @@ EOF
|
||||
$sFileName = $oDoc->GetFileName();
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input id="attachment_'.$iAttId.'" type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment('.$iAttId.');"/> </div>');
|
||||
}
|
||||
|
||||
@@ -291,10 +303,10 @@ EOF
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
$sFileName = $oDoc->GetFileName();
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input id="attachment_'.$iAttId.'" type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment('.$iAttId.');"/> </div>');
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."']);");
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."', false /* not an line image */]);");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,9 +317,21 @@ EOF
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUpload);
|
||||
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
||||
|
||||
$oPage->add_ready_script(
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
||||
|
||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-attachments/css/magnific-popup.css');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlModulesRoot().'itop-attachments/js/jquery.magnific-popup.min.js');
|
||||
$maxWidth = MetaModel::GetModuleSetting('itop-standard-email-synchro', 'inline_image_max_width', '');
|
||||
if ($maxWidth !== '')
|
||||
{
|
||||
$sStyle = "style=\"max-width:{$maxWidth}px;cursor:zoom-in;\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sStyle = "style=\"cursor:zoom-in;\"";
|
||||
}
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL;
|
||||
$oPage->add_ready_script(
|
||||
<<< EOF
|
||||
$('#file').fileupload({
|
||||
url: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php',
|
||||
@@ -323,13 +347,13 @@ $oPage->add_ready_script(
|
||||
}
|
||||
else
|
||||
{
|
||||
var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.result.att_id+'&field=contents';
|
||||
var sDownloadLink = '$sDownloadLink'+data.result.att_id;
|
||||
$('#attachments').append('<div class="attachment" id="display_attachment_'+data.result.att_id+'"><a data-preview="'+data.result.preview+'" href="'+sDownloadLink+'"><img src="'+data.result.icon+'"><br/>'+data.result.msg+'<input id="attachment_'+data.result.att_id+'" type="hidden" name="attachments[]" value="'+data.result.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveAttachment('+data.result.att_id+');"/></div>');
|
||||
if($sIsDeleteEnabled)
|
||||
{
|
||||
$('#display_attachment_'+data.result.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
|
||||
}
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.result.msg]);
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.result.msg, false /* inline image */]);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -380,7 +404,65 @@ $oPage->add_ready_script(
|
||||
window.dropZoneTimeout = null;
|
||||
dropZone.removeClass('drag_in');
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
// Hook the file upload of all CKEditor instances
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
oEditor.config.extraPlugins = 'uploadimage';
|
||||
oEditor.config.uploadUrl = GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php';
|
||||
oEditor.config.filebrowserBrowseUrl = GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php?operation=cke_browse&temp_id=$sTempId&obj_class=$sClass&obj_key=$iObjectId';
|
||||
oEditor.on( 'fileUploadResponse', function( evt ) {
|
||||
// Get XHR and response.
|
||||
var data = evt.data,
|
||||
xhr = data.fileLoader.xhr,
|
||||
response = xhr.responseText.split( '|' );
|
||||
|
||||
var oValues = JSON.parse(response[0]);
|
||||
|
||||
var sDownloadLink = '$sDownloadLink'+oValues.att_id;
|
||||
$('#attachments').append('<div class="attachment" id="display_attachment_'+oValues.att_id+'"><a data-preview="'+oValues.preview+'" href="'+sDownloadLink+'"><img src="'+oValues.icon+'"><br/>'+oValues.msg+'<input id="attachment_'+oValues.att_id+'" type="hidden" name="attachments[]" value="'+oValues.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveAttachment('+oValues.att_id+');"/></div>');
|
||||
if(true)
|
||||
{
|
||||
$('#display_attachment_'+oValues.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
|
||||
}
|
||||
$('#attachment_plugin').trigger('add_attachment', [oValues.att_id, oValues.msg, true /* inline image */]);
|
||||
} );
|
||||
|
||||
oEditor.on( 'fileUploadRequest', function( evt ) {
|
||||
evt.data.fileLoader.uploadUrl += '?operation=cke_img_upload&temp_id=$sTempId&obj_class=$sClass';
|
||||
}, null, null, 4 ); // Listener with priority 4 will be executed before priority 5.
|
||||
|
||||
});
|
||||
|
||||
$('img[data-att-id]').each(function() {
|
||||
if ('$sMaxWidth' != '')
|
||||
{
|
||||
$(this).css({'max-width': '$sMaxWidth', width: '', height: '', 'max-height': ''});
|
||||
}
|
||||
$(this).addClass('inline-image').attr('href', $(this).attr('src'));
|
||||
}).magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
|
||||
// check if the attachments are used by inline images
|
||||
window.setTimeout( function() {
|
||||
$('.attachment a').each(function() {
|
||||
var sUrl = $(this).attr('href');
|
||||
if($('img[src="'+sUrl+'"]').length > 0)
|
||||
{
|
||||
$(this).addClass('image-in-use').find('img').wrap('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
}
|
||||
});
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
var sHtml = oEditor.getData();
|
||||
var jElement = $('<div/>').html(sHtml).contents();
|
||||
jElement.find('img').each(function() {
|
||||
var sSrc = $(this).attr('src');
|
||||
$('.attachment a[href="'+sSrc+'"]').parent().addClass('image-in-use').find('img').wrap('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
});
|
||||
});
|
||||
$('.image-in-use-wrapper').append('<div style="position:absolute;top:0;left:0;"><img src="../images/transp-lock.png"></div>');
|
||||
}, 200 );
|
||||
EOF
|
||||
);
|
||||
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
|
||||
@@ -406,7 +488,7 @@ EOF
|
||||
$sFileName = $oDoc->GetFileName();
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'</a><input type="hidden" name="attachments[]" value="'.$iAttId.'"/><br/> </div>');
|
||||
}
|
||||
}
|
||||
@@ -415,7 +497,24 @@ EOF
|
||||
$oPage->add('</fieldset>');
|
||||
$sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable'));
|
||||
$iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290);
|
||||
$oPage->add_ready_script("$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }}, content: function() { if ($(this).attr('data-preview') == 'true') { return('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}});");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$(document).tooltip({
|
||||
items: '.attachment a',
|
||||
position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }},
|
||||
content: function() { if ($(this).attr('data-preview') == 'true') { return('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}
|
||||
});
|
||||
|
||||
$('img[data-att-id]').each(function() {
|
||||
if ('$sMaxWidth' != '')
|
||||
{
|
||||
$(this).css({'max-width': '$sMaxWidth', width: '', height: '', 'max-height': ''});
|
||||
}
|
||||
$(this).addClass('inline-image');
|
||||
$(this).attr('href', $(this).attr('src'));
|
||||
}).magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
protected static function UpdateAttachments($oObject, $oChange = null)
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
<sql>description</sql>
|
||||
<default_value/>
|
||||
<is_null_allowed>false</is_null_allowed>
|
||||
<format>html</format>
|
||||
</field>
|
||||
<field id="start_date" xsi:type="AttributeDateTime">
|
||||
<always_load_in_tables>true</always_load_in_tables>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -857,6 +857,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Možnosti tabulky',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Nepřekládat hodnoty číselníků',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Zahrnout odkazované objekty',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definice objektů k exportu',
|
||||
'Core:BulkExportLabelOQLExpression' => 'Dotaz OQL:',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -2478,6 +2478,7 @@ Operators:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* @author Stephan Rosenke <stephan.rosenke@itomig.de>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -590,6 +590,7 @@ Operatoren:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet-Optionen',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Werte von Aufzählungsfeldern nicht lokalisieren',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve Textformatierung',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition der zu exportierenden Objekte',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL-Abfrage',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query-Bibliotheks-Eintrag:',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -842,6 +842,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -836,6 +836,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -700,6 +700,7 @@ Opérateurs :<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Options du format HTML pour Excel',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Ne pas traduire les valeurs (pour les champs de type "Enum")',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Inclure les objets liés',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Préserver le formatage du texte',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Définition des objets à exporter',
|
||||
'Core:BulkExportLabelOQLExpression' => 'Requête OQL:',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Entrée du livre des requêtes:',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -593,6 +593,7 @@ Operators:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -825,6 +825,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -615,6 +615,7 @@ Operators:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@
|
||||
* Linux & Open Source Professionals
|
||||
* http://www.linprofs.com
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -845,6 +845,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -838,6 +838,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -833,6 +833,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -21,7 +21,7 @@
|
||||
* Localized data
|
||||
*
|
||||
* @author Izzet Sirin <izzet.sirin@htr.com.tr>
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -765,6 +765,7 @@ Operators:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -21,7 +21,7 @@
|
||||
* Localized data
|
||||
*
|
||||
* @author Robert Deng <denglx@gmail.com>
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -764,6 +764,7 @@ Operators:<br/>
|
||||
'Core:BulkExport:SpreadsheetOptions' => 'Spreadsheet Options~~',
|
||||
'Core:BulkExport:OptionLinkSets' => 'Include linked objects~~',
|
||||
'Core:BulkExport:OptionNoLocalize' => 'Do not localize the values (for Enumerated fields)~~',
|
||||
'Core:BulkExport:OptionFormattedText' => 'Preserve text formatting~~',
|
||||
'Core:BulkExport:ScopeDefinition' => 'Definition of the objects to export~~',
|
||||
'Core:BulkExportLabelOQLExpression' => 'OQL Query:~~',
|
||||
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
|
||||
|
||||
@@ -249,6 +249,16 @@ function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
|
||||
else
|
||||
{
|
||||
sTextContent = oFormattedContents.contents().find("body").text();
|
||||
|
||||
if (sTextContent == '')
|
||||
{
|
||||
// No plain text, maybe there is just an image...
|
||||
var oImg = oFormattedContents.contents().find("body img");
|
||||
if (oImg.length != 0)
|
||||
{
|
||||
sTextContent = 'image';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bMandatory && (sTextContent == ''))
|
||||
|
||||
@@ -15,19 +15,22 @@ function sprintf(format, etc) {
|
||||
|
||||
function setPageSize(table,size, bReload) {
|
||||
var c = table.config;
|
||||
c.selectedSize = size;
|
||||
if (size == -1)
|
||||
if (c != undefined)
|
||||
{
|
||||
size = c.totalRows;
|
||||
c.selectedSize = size;
|
||||
if (size == -1)
|
||||
{
|
||||
size = c.totalRows;
|
||||
}
|
||||
c.size = size;
|
||||
c.totalPages = Math.ceil(c.totalRows / c.size);
|
||||
c.pagerPositionSet = false;
|
||||
if (bReload)
|
||||
{
|
||||
moveToPage(table);
|
||||
}
|
||||
fixPosition(table);
|
||||
}
|
||||
c.size = size;
|
||||
c.totalPages = Math.ceil(c.totalRows / c.size);
|
||||
c.pagerPositionSet = false;
|
||||
if (bReload)
|
||||
{
|
||||
moveToPage(table);
|
||||
}
|
||||
fixPosition(table);
|
||||
}
|
||||
|
||||
function fixPosition(table) {
|
||||
@@ -246,6 +249,8 @@ function sprintf(format, etc) {
|
||||
function applySelection(table)
|
||||
{
|
||||
var c = table.config;
|
||||
if (c == undefined) return;
|
||||
|
||||
if (c.selectionMode == 'negative')
|
||||
{
|
||||
$(table).find(':checkbox[name^=selectObj]').attr('checked', true);
|
||||
|
||||
@@ -834,7 +834,7 @@ $(function()
|
||||
jTab.find('span').html(sTabText+' <img style="vertical-align:bottom" src="../images/indicator.gif">');
|
||||
}
|
||||
$.post(sUrl, oParams, function(data) {
|
||||
var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents';
|
||||
var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&field=contents&id='+data.att_id;
|
||||
var sIcon = GetAbsoluteUrlModulesRoot()+'itop-attachments/icons/pdf.png';
|
||||
if (jTab != null)
|
||||
{
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.4 KiB |
@@ -1219,6 +1219,7 @@ EOF;
|
||||
// Added if present...
|
||||
//
|
||||
$aParameters['validation_pattern'] = $this->GetPropString($oField, 'validation_pattern');
|
||||
$aParameters['format'] = $this->GetPropString($oField, 'format');
|
||||
$aParameters['width'] = $this->GetPropString($oField, 'width');
|
||||
$aParameters['height'] = $this->GetPropString($oField, 'height');
|
||||
$aParameters['digits'] = $this->GetPropNumber($oField, 'digits');
|
||||
|
||||
Reference in New Issue
Block a user