mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-01 07:04:16 +01:00
Compare commits
13 Commits
feature/86
...
issue/7071
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73ce7e8dab | ||
|
|
d589d9d05a | ||
|
|
c0c9ea9287 | ||
|
|
fae2bcc6e9 | ||
|
|
53047d35fe | ||
|
|
909469ce97 | ||
|
|
dacb54285c | ||
|
|
5af93ca92a | ||
|
|
3fa500c9c1 | ||
|
|
f0adbbba29 | ||
|
|
bbdb30f421 | ||
|
|
897b5d452e | ||
|
|
fdd1479c8e |
@@ -419,7 +419,7 @@ class DisplayBlock
|
||||
$oSet->OptimizeColumnLoad(array($oSet->GetClassAlias() => array())); // No need to load all the columns just to get the id
|
||||
while($oObject = $oSet->Fetch())
|
||||
{
|
||||
$aKeys[] = $oObject->GetKey();
|
||||
$aKeys[] = $oObject->GetKey();
|
||||
}
|
||||
$oSet->Rewind();
|
||||
if (count($aKeys) > 0)
|
||||
@@ -772,10 +772,6 @@ class DisplayBlock
|
||||
$oBlock = $this->RenderList($aExtraParams, $oPage);
|
||||
break;
|
||||
|
||||
case 'links':
|
||||
$oBlock = $this->RenderLinks($oPage, $aExtraParams);
|
||||
break;
|
||||
|
||||
case static::ENUM_STYLE_ACTIONS:
|
||||
$oBlock = $this->RenderActions($aExtraParams);
|
||||
break;
|
||||
@@ -795,11 +791,11 @@ class DisplayBlock
|
||||
case static::ENUM_STYLE_CHART:
|
||||
$oBlock = $this->RenderChart($sId, $aQueryParams, $aExtraParams);
|
||||
break;
|
||||
|
||||
|
||||
case static::ENUM_STYLE_CHART_AJAX:
|
||||
$oBlock = $this->RenderChartAjax($aExtraParams);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Unsupported style, do nothing.
|
||||
$sHtml .= Dict::format('UI:Error:UnsupportedStyleOfBlock', $this->m_sStyle);
|
||||
@@ -897,7 +893,7 @@ JS
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$bConditionAdded = false;
|
||||
|
||||
|
||||
// If the condition is an external key with a class having a hierarchy, use a "below" criteria
|
||||
if (MetaModel::IsValidAttCode($sClass, $sFilterCode))
|
||||
{
|
||||
@@ -906,13 +902,13 @@ JS
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass());
|
||||
|
||||
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($oAttDef->GetTargetClass());
|
||||
if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$oFilter->AddConditionExpression(self::GetConditionIN($oFilter, 'id', $condition));
|
||||
$oFilter->AddConditionExpression(self::GetConditionIN($oFilter, 'id', $condition));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -935,21 +931,21 @@ JS
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In all other cases, just add the condition directly
|
||||
if (!$bConditionAdded)
|
||||
{
|
||||
$this->m_oFilter->AddCondition($sFilterCode, $condition, null); // Use the default 'loose' operator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static protected function GetConditionIN($oFilter, $sFilterCode, $condition)
|
||||
{
|
||||
$oField = new FieldExpression($sFilterCode, $oFilter->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($condition)).')';
|
||||
$sOQLCondition = $oField->RenderExpression()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
return $oNewCondition;
|
||||
return $oNewCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1565,54 +1561,6 @@ JS
|
||||
return $oContentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0 N°5957
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return \Combodo\iTop\Application\UI\Base\Component\Html\Html|\Combodo\iTop\Application\UI\Base\Layout\UIContentBlock|string
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function RenderLinks(WebPage $oPage, array $aExtraParams)
|
||||
{
|
||||
// Note: No deprecation ticket yet as we want to wait and see if people / code actually use this method, in which case we might keep it.
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('This method is most likely not used throughout the application and will be removed soon. If you ever see this message, please inform us.');
|
||||
|
||||
$oBlock = null;
|
||||
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
|
||||
$oBlock = cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams);
|
||||
} else {
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $this->m_aParams['target_attr']);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oBlock = new Html('<p>'.Dict::Format('UI:NoObject_Class_ToDisplay', MetaModel::GetName($sTargetClass)).'</p>');
|
||||
$bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true;
|
||||
if ($bDisplayMenu) {
|
||||
if ((UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
|
||||
$sDefaults = '';
|
||||
if (isset($this->m_aParams['default'])) {
|
||||
foreach ($this->m_aParams['default'] as $sName => $sValue) {
|
||||
$sDefaults .= '&'.urlencode($sName).'='.urlencode($sValue);
|
||||
}
|
||||
}
|
||||
$oBlock->AddHtml("<p><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=modify_links&class=$sClass&sParams&link_attr=".$aExtraParams['link_attr']."&id=".$aExtraParams['object_id']."&target_class=$sTargetClass&addObjects=true$sDefaults\">".Dict::Format('UI:ClickToCreateNew',
|
||||
Metamodel::GetName($sClass))."</a></p>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sChartId
|
||||
* @param array $aQueryParams
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,11 +42,6 @@ class CMDBSource
|
||||
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
||||
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
||||
|
||||
/**
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
|
||||
* @internal will be removed in a future version
|
||||
*/
|
||||
const MYSQL_DEFAULT_PORT = 3306;
|
||||
|
||||
/**
|
||||
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
|
||||
@@ -220,7 +215,7 @@ class CMDBSource
|
||||
* @param string $sServer server variable to update
|
||||
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
|
||||
*
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost.
|
||||
*
|
||||
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
|
||||
*/
|
||||
|
||||
@@ -495,8 +495,8 @@ class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sSourceQuery, $oRightField->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeExternalKey');
|
||||
$aObjKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], 'AttributeObjectKey');
|
||||
$aExtKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], \Combodo\iTop\Core\AttributeDefinition\AttributeExternalKey::class);
|
||||
$aObjKeys = $oModelReflection->ListAttributes($aAliases[$sFromClass], \Combodo\iTop\Core\AttributeDefinition\AttributeObjectKey::class);
|
||||
$aAllKeys = array_merge($aExtKeys, $aObjKeys);
|
||||
if (!array_key_exists($sExtKeyAttCode, $aAllKeys))
|
||||
{
|
||||
@@ -557,7 +557,7 @@ class OqlObjectQuery extends OqlQuery
|
||||
}
|
||||
$aAttList = $oModelReflection->ListAttributes($aAliases[$sFromClass]);
|
||||
$sAttType = $aAttList[$sExtKeyAttCode];
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !is_subclass_of($sAttType, 'AttributeHierarchicalKey') && ($sAttType != 'AttributeHierarchicalKey'))
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !is_subclass_of($sAttType, \Combodo\iTop\Core\AttributeDefinition\AttributeHierarchicalKey::class) && ($sAttType != \Combodo\iTop\Core\AttributeDefinition\AttributeHierarchicalKey::class))
|
||||
{
|
||||
throw new OqlNormalizeException("The specified tree operator $sOperator is not applicable to the key", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
|
||||
@@ -513,6 +513,15 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
{
|
||||
$bUpdateFromDelta = true;
|
||||
}
|
||||
} else {
|
||||
//@since 3.2.2 N°2364 - API : remove old linkedset persistance
|
||||
/* Goo pattern to use:
|
||||
* $oCISet = $oTicket->Get(‘functioncis_list’);
|
||||
* $oCISet->AddItem(MetaModel::NewObject(‘lnkFunctionCIToTicket’, array(‘ci_id’=> 12345));
|
||||
* $oCISet->RemoveItem(123456);
|
||||
* $oTicket->Set(‘functionalcis_list’, $oCISet);
|
||||
*/
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('old pattern - please get previous value of the linked set, modify it and set it back to the host object');
|
||||
}
|
||||
|
||||
if ($bUpdateFromDelta)
|
||||
@@ -528,7 +537,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
else
|
||||
{
|
||||
// For backward compatibility reasons, let's rebuild a delta...
|
||||
|
||||
// Reset the delta
|
||||
$this->iCursor = 0;
|
||||
$this->aAdded = array();
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
{% if aTilesRendering[brick.GetId] is defined %}
|
||||
{{ aTilesRendering[brick.GetId]|raw }}
|
||||
{% else %}
|
||||
{% include '' ~ brick.GetTemplatePath with {brick: brick} %}
|
||||
{% set twigPath = brick.GetTemplatePath('tile') %}
|
||||
{% include '' ~ twigPath with {brick: brick} %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -162,6 +162,8 @@
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal-clipboard.js'|add_itop_version }}"></script>
|
||||
{# User Preferences #}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/user_preferences.js'|add_itop_version }}"></script>
|
||||
{# Polyfill for custom elements #}
|
||||
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'node_modules/@ungap/custom-elements/es.js'|add_itop_version }}"></script>
|
||||
{# custom elements #}
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/custom_elements/base_element.js'|add_itop_version }}"></script>
|
||||
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/custom_elements/tile_element.js'|add_itop_version }}"></script>
|
||||
|
||||
467
js/date.js
467
js/date.js
@@ -1,467 +0,0 @@
|
||||
/*
|
||||
* Date prototype extensions. Doesn't depend on any
|
||||
* other code. Doens't overwrite existing methods.
|
||||
*
|
||||
* Adds dayNames, abbrDayNames, monthNames and abbrMonthNames static properties and isLeapYear,
|
||||
* isWeekend, isWeekDay, getDaysInMonth, getDayName, getMonthName, getDayOfYear, getWeekOfYear,
|
||||
* setDayOfYear, addYears, addMonths, addDays, addHours, addMinutes, addSeconds methods
|
||||
*
|
||||
* Copyright (c) 2006 Jörn Zaefferer and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
|
||||
*
|
||||
* Additional methods and properties added by Kelvin Luck: firstDayOfWeek, dateFormat, zeroTime, asString, fromString -
|
||||
* I've added my name to these methods so you know who to blame if they are broken!
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* An Array of day names starting with Sunday.
|
||||
*
|
||||
* @example dayNames[0]
|
||||
* @result 'Sunday'
|
||||
*
|
||||
* @name dayNames
|
||||
* @type Array
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
Date.dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
|
||||
/**
|
||||
* An Array of abbreviated day names starting with Sun.
|
||||
*
|
||||
* @example abbrDayNames[0]
|
||||
* @result 'Sun'
|
||||
*
|
||||
* @name abbrDayNames
|
||||
* @type Array
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
Date.abbrDayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
|
||||
/**
|
||||
* An Array of month names starting with Janurary.
|
||||
*
|
||||
* @example monthNames[0]
|
||||
* @result 'January'
|
||||
*
|
||||
* @name monthNames
|
||||
* @type Array
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
Date.monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
|
||||
/**
|
||||
* An Array of abbreviated month names starting with Jan.
|
||||
*
|
||||
* @example abbrMonthNames[0]
|
||||
* @result 'Jan'
|
||||
*
|
||||
* @name monthNames
|
||||
* @type Array
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
Date.abbrMonthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
/**
|
||||
* The first day of the week for this locale.
|
||||
*
|
||||
* @name firstDayOfWeek
|
||||
* @type Number
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
Date.firstDayOfWeek = 1;
|
||||
|
||||
/**
|
||||
* The format that string dates should be represented as (e.g. 'dd/mm/yyyy' for UK, 'mm/dd/yyyy' for US, 'yyyy-mm-dd' for Unicode etc).
|
||||
*
|
||||
* @name format
|
||||
* @type String
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
//Date.format = 'dd/mm/yyyy';
|
||||
//Date.format = 'mm/dd/yyyy';
|
||||
Date.format = 'yyyy-mm-dd';
|
||||
//Date.format = 'dd mmm yy';
|
||||
|
||||
/**
|
||||
* The first two numbers in the century to be used when decoding a two digit year. Since a two digit year is ambiguous (and date.setYear
|
||||
* only works with numbers < 99 and so doesn't allow you to set years after 2000) we need to use this to disambiguate the two digit year codes.
|
||||
*
|
||||
* @name format
|
||||
* @type String
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
Date.fullYearStart = '20';
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Adds a given method under the given name
|
||||
* to the Date prototype if it doesn't
|
||||
* currently exist.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function add(name, method) {
|
||||
if( !Date.prototype[name] ) {
|
||||
Date.prototype[name] = method;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the year is a leap year.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.isLeapYear();
|
||||
* @result true
|
||||
*
|
||||
* @name isLeapYear
|
||||
* @type Boolean
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("isLeapYear", function() {
|
||||
var y = this.getFullYear();
|
||||
return (y%4==0 && y%100!=0) || y%400==0;
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the day is a weekend day (Sat or Sun).
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.isWeekend();
|
||||
* @result false
|
||||
*
|
||||
* @name isWeekend
|
||||
* @type Boolean
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("isWeekend", function() {
|
||||
return this.getDay()==0 || this.getDay()==6;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the day is a day of the week (Mon-Fri)
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.isWeekDay();
|
||||
* @result false
|
||||
*
|
||||
* @name isWeekDay
|
||||
* @type Boolean
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("isWeekDay", function() {
|
||||
return !this.isWeekend();
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the number of days in the month.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getDaysInMonth();
|
||||
* @result 31
|
||||
*
|
||||
* @name getDaysInMonth
|
||||
* @type Number
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("getDaysInMonth", function() {
|
||||
return [31,(this.isLeapYear() ? 29:28),31,30,31,30,31,31,30,31,30,31][this.getMonth()];
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the name of the day.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getDayName();
|
||||
* @result 'Saturday'
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getDayName(true);
|
||||
* @result 'Sat'
|
||||
*
|
||||
* @param abbreviated Boolean When set to true the name will be abbreviated.
|
||||
* @name getDayName
|
||||
* @type String
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("getDayName", function(abbreviated) {
|
||||
return abbreviated ? Date.abbrDayNames[this.getDay()] : Date.dayNames[this.getDay()];
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the name of the month.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getMonthName();
|
||||
* @result 'Janurary'
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getMonthName(true);
|
||||
* @result 'Jan'
|
||||
*
|
||||
* @param abbreviated Boolean When set to true the name will be abbreviated.
|
||||
* @name getDayName
|
||||
* @type String
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("getMonthName", function(abbreviated) {
|
||||
return abbreviated ? Date.abbrMonthNames[this.getMonth()] : Date.monthNames[this.getMonth()];
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the number of the day of the year.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getDayOfYear();
|
||||
* @result 11
|
||||
*
|
||||
* @name getDayOfYear
|
||||
* @type Number
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("getDayOfYear", function() {
|
||||
var tmpdtm = new Date("1/1/" + this.getFullYear());
|
||||
return Math.floor((this.getTime() - tmpdtm.getTime()) / 86400000);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the number of the week of the year.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.getWeekOfYear();
|
||||
* @result 2
|
||||
*
|
||||
* @name getWeekOfYear
|
||||
* @type Number
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("getWeekOfYear", function() {
|
||||
return Math.ceil(this.getDayOfYear() / 7);
|
||||
});
|
||||
|
||||
/**
|
||||
* Set the day of the year.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.setDayOfYear(1);
|
||||
* dtm.toString();
|
||||
* @result 'Tue Jan 01 2008 00:00:00'
|
||||
*
|
||||
* @name setDayOfYear
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("setDayOfYear", function(day) {
|
||||
this.setMonth(0);
|
||||
this.setDate(day);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of years to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addYears(1);
|
||||
* dtm.toString();
|
||||
* @result 'Mon Jan 12 2009 00:00:00'
|
||||
*
|
||||
* @name addYears
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addYears", function(num) {
|
||||
this.setFullYear(this.getFullYear() + num);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of months to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addMonths(1);
|
||||
* dtm.toString();
|
||||
* @result 'Tue Feb 12 2008 00:00:00'
|
||||
*
|
||||
* @name addMonths
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addMonths", function(num) {
|
||||
var tmpdtm = this.getDate();
|
||||
|
||||
this.setMonth(this.getMonth() + num);
|
||||
|
||||
if (tmpdtm > this.getDate())
|
||||
this.addDays(-this.getDate());
|
||||
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of days to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addDays(1);
|
||||
* dtm.toString();
|
||||
* @result 'Sun Jan 13 2008 00:00:00'
|
||||
*
|
||||
* @name addDays
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addDays", function(num) {
|
||||
this.setDate(this.getDate() + num);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of hours to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addHours(24);
|
||||
* dtm.toString();
|
||||
* @result 'Sun Jan 13 2008 00:00:00'
|
||||
*
|
||||
* @name addHours
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addHours", function(num) {
|
||||
this.setHours(this.getHours() + num);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of minutes to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addMinutes(60);
|
||||
* dtm.toString();
|
||||
* @result 'Sat Jan 12 2008 01:00:00'
|
||||
*
|
||||
* @name addMinutes
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addMinutes", function(num) {
|
||||
this.setMinutes(this.getMinutes() + num);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a number of seconds to the date object.
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.addSeconds(60);
|
||||
* dtm.toString();
|
||||
* @result 'Sat Jan 12 2008 00:01:00'
|
||||
*
|
||||
* @name addSeconds
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
*/
|
||||
add("addSeconds", function(num) {
|
||||
this.setSeconds(this.getSeconds() + num);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Sets the time component of this Date to zero for cleaner, easier comparison of dates where time is not relevant.
|
||||
*
|
||||
* @example var dtm = new Date();
|
||||
* dtm.zeroTime();
|
||||
* dtm.toString();
|
||||
* @result 'Sat Jan 12 2008 00:01:00'
|
||||
*
|
||||
* @name zeroTime
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
add("zeroTime", function() {
|
||||
this.setMilliseconds(0);
|
||||
this.setSeconds(0);
|
||||
this.setMinutes(0);
|
||||
this.setHours(0);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a string representation of the date object according to Date.format.
|
||||
* (Date.toString may be used in other places so I purposefully didn't overwrite it)
|
||||
*
|
||||
* @example var dtm = new Date("01/12/2008");
|
||||
* dtm.asString();
|
||||
* @result '12/01/2008' // (where Date.format == 'dd/mm/yyyy'
|
||||
*
|
||||
* @name asString
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
add("asString", function() {
|
||||
var r = Date.format;
|
||||
return r
|
||||
.split('yyyy').join(this.getFullYear())
|
||||
.split('yy').join((this.getFullYear() + '').substring(2))
|
||||
.split('mmm').join(this.getMonthName(true))
|
||||
.split('mm').join(_zeroPad(this.getMonth()+1))
|
||||
.split('dd').join(_zeroPad(this.getDate()));
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a new date object created from the passed String according to Date.format or false if the attempt to do this results in an invalid date object
|
||||
* (We can't simple use Date.parse as it's not aware of locale and I chose not to overwrite it incase it's functionality is being relied on elsewhere)
|
||||
*
|
||||
* @example var dtm = Date.fromString("12/01/2008");
|
||||
* dtm.toString();
|
||||
* @result 'Sat Jan 12 2008 00:00:00' // (where Date.format == 'dd/mm/yyyy'
|
||||
*
|
||||
* @name fromString
|
||||
* @type Date
|
||||
* @cat Plugins/Methods/Date
|
||||
* @author Kelvin Luck
|
||||
*/
|
||||
Date.fromString = function(s)
|
||||
{
|
||||
var f = Date.format;
|
||||
var d = new Date('01/01/1977');
|
||||
var iY = f.indexOf('yyyy');
|
||||
if (iY > -1) {
|
||||
d.setFullYear(Number(s.substr(iY, 4)));
|
||||
} else {
|
||||
// TODO - this doesn't work very well - are there any rules for what is meant by a two digit year?
|
||||
d.setFullYear(Number(Date.fullYearStart + s.substr(f.indexOf('yy'), 2)));
|
||||
}
|
||||
var iM = f.indexOf('mmm');
|
||||
if (iM > -1) {
|
||||
var mStr = s.substr(iM, 3);
|
||||
for (var i=0; i<Date.abbrMonthNames.length; i++) {
|
||||
if (Date.abbrMonthNames[i] == mStr) break;
|
||||
}
|
||||
d.setMonth(i);
|
||||
} else {
|
||||
d.setMonth(Number(s.substr(f.indexOf('mm'), 2)) - 1);
|
||||
}
|
||||
d.setDate(Number(s.substr(f.indexOf('dd'), 2)));
|
||||
if (isNaN(d.getTime())) {
|
||||
return false;
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
// utility method
|
||||
var _zeroPad = function(num) {
|
||||
var s = '0'+num;
|
||||
return s.substring(s.length-2)
|
||||
//return ('0'+num).substring(-2); // doesn't work on IE :(
|
||||
};
|
||||
|
||||
})();
|
||||
458
js/hovertip.js
458
js/hovertip.js
@@ -1,458 +0,0 @@
|
||||
/**
|
||||
* Hovertip - easy and elegant tooltips
|
||||
*
|
||||
* By Dave Cohen <http://dave-cohen.com>
|
||||
* With ideas and and javascript code borrowed from many folks.
|
||||
* (See URLS in the comments)
|
||||
*
|
||||
* Licensed under GPL.
|
||||
* Requires jQuery.js. <http://jquery.com>,
|
||||
* which may be distributed under a different licence.
|
||||
*
|
||||
* $Date: 2006-09-15 12:49:19 -0700 (Fri, 15 Sep 2006) $
|
||||
* $Rev: $
|
||||
* $Id:$
|
||||
*
|
||||
* This plugin helps you create tooltips. It supports:
|
||||
*
|
||||
* hovertips - these appear under the mouse when mouse is over the target
|
||||
* element.
|
||||
*
|
||||
* clicktips - these appear in the document when the target element is
|
||||
* clicked.
|
||||
*
|
||||
* You may define behaviors for additional types of tooltips.
|
||||
*
|
||||
* There are a variety of ways to add tooltips. Each of the following is
|
||||
* supported:
|
||||
*
|
||||
* <p>blah blah blah
|
||||
* <span>important term</span>
|
||||
* <span class="tooltip">text that appears.</span>
|
||||
* blah blah blah</p>
|
||||
*
|
||||
* or,
|
||||
*
|
||||
* <p>blah blah blah
|
||||
* <span hovertip="termdefinition">important term</span>
|
||||
* blah blah blah</p>
|
||||
* <div id="termdefinition" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
|
||||
*
|
||||
* or,
|
||||
*
|
||||
* <p>blah blah blah
|
||||
* <span id="term">important term</span>
|
||||
* blah blah blah</p>
|
||||
* <div target="term" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
|
||||
*
|
||||
*
|
||||
* Hooks are available to customize both the behavior of activated tooltips,
|
||||
* and the syntax used to mark them up.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
//// mouse events ////
|
||||
/**
|
||||
* To make hovertips appear correctly we need the exact mouse position.
|
||||
* These functions make that possible.
|
||||
*/
|
||||
|
||||
// use globals to track mouse position
|
||||
var hovertipMouseX;
|
||||
var hovertipMouseY;
|
||||
function hovertipMouseUpdate(e) {
|
||||
var mouse = hovertipMouseXY(e);
|
||||
hovertipMouseX = mouse[0];
|
||||
hovertipMouseY = mouse[1];
|
||||
}
|
||||
|
||||
// http://www.howtocreate.co.uk/tutorials/javascript/eventinfo
|
||||
function hovertipMouseXY(e) {
|
||||
if( !e ) {
|
||||
if( window.event ) {
|
||||
//Internet Explorer
|
||||
e = window.event;
|
||||
} else {
|
||||
//total failure, we have no way of referencing the event
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( typeof( e.pageX ) == 'number' ) {
|
||||
//most browsers
|
||||
var xcoord = e.pageX;
|
||||
var ycoord = e.pageY;
|
||||
} else if( typeof( e.clientX ) == 'number' ) {
|
||||
//Internet Explorer and older browsers
|
||||
//other browsers provide this, but follow the pageX/Y branch
|
||||
var xcoord = e.clientX;
|
||||
var ycoord = e.clientY;
|
||||
var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
|
||||
( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
|
||||
( navigator.vendor == 'KDE' );
|
||||
if( !badOldBrowser ) {
|
||||
if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
|
||||
//IE 4, 5 & 6 (in non-standards compliant mode)
|
||||
xcoord += document.body.scrollLeft;
|
||||
ycoord += document.body.scrollTop;
|
||||
} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
|
||||
//IE 6 (in standards compliant mode)
|
||||
xcoord += document.documentElement.scrollLeft;
|
||||
ycoord += document.documentElement.scrollTop;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//total failure, we have no way of obtaining the mouse coordinates
|
||||
return;
|
||||
}
|
||||
return [xcoord, ycoord];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// target selectors ////
|
||||
|
||||
/**
|
||||
* These selectors find the targets for a given tooltip element.
|
||||
* Several methods are supported.
|
||||
*
|
||||
* You may write your own selector functions to customize.
|
||||
*/
|
||||
|
||||
/**
|
||||
* For this model:
|
||||
* <span hovertip="ht1">target term</span>...
|
||||
* <div class="hovertip" id="ht1">tooltip text</div>
|
||||
*/
|
||||
targetSelectById = function(el, config) {
|
||||
var id;
|
||||
var selector;
|
||||
if (id = el.getAttribute('id')) {
|
||||
selector = '*[@'+config.attribute+'=\''+id+'\']';
|
||||
return $(selector);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* For this model:
|
||||
* <span id="ht1">target term</span>...
|
||||
* <div class="hovertip" target="ht1">tooltip text</div>
|
||||
*/
|
||||
targetSelectByTargetAttribute = function(el, config) {
|
||||
target_list = el.getAttribute('target');
|
||||
if (target_list) {
|
||||
// use for attribute to specify targets
|
||||
target_ids = target_list.split(' ');
|
||||
var selector = '#' + target_ids.join(',#');
|
||||
return $(selector);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* For this model:
|
||||
* <span>target term</span><span class="hovertip">tooltip text</span>
|
||||
*/
|
||||
targetSelectByPrevious = function(el, config) {
|
||||
return $(el.previousSibling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make all siblings targets. Experimental.
|
||||
*/
|
||||
targetSelectBySiblings = function(el, config) {
|
||||
return $(el).siblings();
|
||||
}
|
||||
|
||||
//// prepare tip elements ////
|
||||
|
||||
/**
|
||||
* The tooltip element needs special preparation. You may define your own
|
||||
* prepare functions to cusomize the behavior.
|
||||
*/
|
||||
|
||||
// adds a close link to clicktips
|
||||
clicktipPrepareWithCloseLink = function(o, config) {
|
||||
return o.append("<a class='clicktip_close'><span>close</span></a>")
|
||||
.find('a.clicktip_close').click(function(e) {
|
||||
o.hide();
|
||||
return false;
|
||||
}).end();
|
||||
};
|
||||
|
||||
// ensure that hovertips do not disappear when the mouse is over them.
|
||||
// also position the hovertip as an absolutely positioned child of body.
|
||||
hovertipPrepare = function(o, config) {
|
||||
return o.hover(function() {
|
||||
hovertipHideCancel(this);
|
||||
}, function() {
|
||||
hovertipHideLater(this);
|
||||
}).css('position', 'absolute').each(hovertipPosition);
|
||||
};
|
||||
|
||||
// do not modify tooltips when preparing
|
||||
hovertipPrepareNoOp = function(o, config) {
|
||||
return o;
|
||||
}
|
||||
|
||||
//// manipulate tip elements /////
|
||||
/**
|
||||
* A variety of functions to modify tooltip elements
|
||||
*/
|
||||
|
||||
// move tooltips to body, so they are not descended from other absolutely
|
||||
// positioned elements.
|
||||
hovertipPosition = function(i) {
|
||||
document.body.appendChild(this);
|
||||
}
|
||||
|
||||
hovertipIsVisible = function(el) {
|
||||
return (jQuery.css(el, 'display') != 'none');
|
||||
}
|
||||
|
||||
// show the tooltip under the mouse.
|
||||
// Introduce a delay, so tip appears only if cursor rests on target for more than an instant.
|
||||
hovertipShowUnderMouse = function(el) {
|
||||
hovertipHideCancel(el);
|
||||
if (!hovertipIsVisible(el)) {
|
||||
el.ht.showing = // keep reference to timer
|
||||
window.setTimeout(function() {
|
||||
el.ht.tip.css({
|
||||
'position':'absolute',
|
||||
'top': hovertipMouseY + 'px',
|
||||
'left': hovertipMouseX + 'px'})
|
||||
.show();
|
||||
}, el.ht.config.showDelay);
|
||||
}
|
||||
};
|
||||
|
||||
// do not hide
|
||||
hovertipHideCancel = function(el) {
|
||||
if (el.ht.hiding) {
|
||||
window.clearTimeout(el.ht.hiding);
|
||||
el.ht.hiding = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Hide a tooltip, but only after a delay.
|
||||
// The delay allow the tip to remain when user moves mouse from target to tooltip
|
||||
hovertipHideLater = function(el) {
|
||||
if (el.ht.showing) {
|
||||
window.clearTimeout(el.ht.showing);
|
||||
el.ht.showing = null;
|
||||
}
|
||||
if (el.ht.hiding) {
|
||||
window.clearTimeout(el.ht.hiding);
|
||||
el.ht.hiding = null;
|
||||
}
|
||||
el.ht.hiding =
|
||||
window.setTimeout(function() {
|
||||
if (el.ht.hiding) {
|
||||
// fadeOut, slideUp do not work on Konqueror
|
||||
el.ht.tip.hide();
|
||||
}
|
||||
}, el.ht.config.hideDelay);
|
||||
};
|
||||
|
||||
|
||||
//// prepare target elements ////
|
||||
/**
|
||||
* As we prepared the tooltip elements, the targets also need preparation.
|
||||
*
|
||||
* You may define your own custom behavior.
|
||||
*/
|
||||
|
||||
// when clicked on target, toggle visibilty of tooltip
|
||||
clicktipTargetPrepare = function(o, el, config) {
|
||||
return o.addClass(config.attribute + '_target')
|
||||
.click(function() {
|
||||
el.ht.tip.toggle();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
// when hover over target, make tooltip appear
|
||||
hovertipTargetPrepare = function(o, el, config) {
|
||||
return o.addClass(config.attribute + '_target')
|
||||
.hover(function() {
|
||||
// show tip when mouse over target
|
||||
hovertipShowUnderMouse(el);
|
||||
},
|
||||
function() {
|
||||
// hide the tip
|
||||
// add a delay so user can move mouse from the target to the tip
|
||||
hovertipHideLater(el);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* hovertipActivate() is our jQuery plugin function. It turns on hovertip or
|
||||
* clicktip behavior for a set of elements.
|
||||
*
|
||||
* @param config
|
||||
* controls aspects of tooltip behavior. Be sure to define
|
||||
* 'attribute', 'showDelay' and 'hideDelay'.
|
||||
*
|
||||
* @param targetSelect
|
||||
* function finds the targets of a given tooltip element.
|
||||
*
|
||||
* @param tipPrepare
|
||||
* function alters the tooltip to display and behave properly
|
||||
*
|
||||
* @param targetPrepare
|
||||
* function alters the target to display and behave properly.
|
||||
*/
|
||||
jQuery.fn.hovertipActivate = function(config, targetSelect, tipPrepare, targetPrepare) {
|
||||
//alert('activating ' + this.size());
|
||||
// unhide so jquery show/hide will work.
|
||||
return this.css('display', 'block')
|
||||
.hide() // don't show it until click
|
||||
.each(function() {
|
||||
if (!this.ht)
|
||||
this.ht = new Object();
|
||||
this.ht.config = config;
|
||||
|
||||
// find our targets
|
||||
var targets = targetSelect(this, config);
|
||||
if (targets && targets.size()) {
|
||||
if (!this.ht.targets)
|
||||
this.ht.targets = targetPrepare(targets, this, config);
|
||||
else
|
||||
this.ht.targets.add(targetPrepare(targets, this, config));
|
||||
|
||||
// listen to mouse move events so we know exatly where to place hovetips
|
||||
targets.mousemove(hovertipMouseUpdate);
|
||||
|
||||
// prepare the tooltip element
|
||||
// is it bad form to call $(this) here?
|
||||
if (!this.ht.tip)
|
||||
this.ht.tip = tipPrepare($(this), config);
|
||||
}
|
||||
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
* Here's an example ready function which shows how to enable tooltips.
|
||||
*
|
||||
* You can make this considerably shorter by choosing only the markup style(s)
|
||||
* you will use.
|
||||
*
|
||||
* You may also remove the code that wraps hovertips to produce drop-shadow FX
|
||||
*
|
||||
* Invoke this function or one like it from your $(document).ready().
|
||||
*
|
||||
* Here, we break the action up into several timout callbacks, to avoid
|
||||
* locking up browsers.
|
||||
*/
|
||||
function hovertipInit() {
|
||||
// specify the attribute name we use for our clicktips
|
||||
var clicktipConfig = {'attribute':'clicktip'};
|
||||
|
||||
/**
|
||||
* To enable this style of markup (id on tooltip):
|
||||
* <span clicktip="foo">target</span>...
|
||||
* <div id="foo" class="clicktip">blah blah</div>
|
||||
*/
|
||||
window.setTimeout(function() {
|
||||
$('.clicktip').hovertipActivate(clicktipConfig,
|
||||
targetSelectById,
|
||||
clicktipPrepareWithCloseLink,
|
||||
clicktipTargetPrepare);
|
||||
}, 0);
|
||||
|
||||
/**
|
||||
* To enable this style of markup (id on target):
|
||||
* <span id="foo">target</span>...
|
||||
* <div target="foo" class="clicktip">blah blah</div>
|
||||
*/
|
||||
window.setTimeout(function() {
|
||||
$('.clicktip').hovertipActivate(clicktipConfig,
|
||||
targetSelectByTargetAttribute,
|
||||
clicktipPrepareWithCloseLink,
|
||||
clicktipTargetPrepare);
|
||||
}, 0);
|
||||
|
||||
// specify our configuration for hovertips, including delay times (millisec)
|
||||
var hovertipConfig = {'attribute':'hovertip',
|
||||
'showDelay': 300,
|
||||
'hideDelay': 700};
|
||||
|
||||
// use <div class='hovertip'>blah blah</div>
|
||||
var hovertipSelect = 'div.hovertip';
|
||||
|
||||
// OPTIONAL: here we wrap each hovertip to apply special effect. (i.e. drop shadow):
|
||||
$(hovertipSelect).css('display', 'block').addClass('hovertip_wrap3').
|
||||
wrap("<div class='hovertip_wrap0'><div class='hovertip_wrap1'><div class='hovertip_wrap2'>" +
|
||||
"</div></div></div>").each(function() {
|
||||
// fix class and attributes for newly wrapped elements
|
||||
var tooltip = this.parentNode.parentNode.parentNode;
|
||||
if (this.getAttribute('target'))
|
||||
tooltip.setAttribute('target', this.getAttribute('target'));
|
||||
if (this.getAttribute('id')) {
|
||||
var id = this.getAttribute('id');
|
||||
this.removeAttribute('id');
|
||||
tooltip.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
hovertipSelect = 'div.hovertip_wrap0';
|
||||
|
||||
// end optional FX section
|
||||
|
||||
/**
|
||||
* To enable this style of markup (id on tooltip):
|
||||
* <span hovertip="foo">target</span>...
|
||||
* <div id="foo" class="hovertip">blah blah</div>
|
||||
*/
|
||||
window.setTimeout(function() {
|
||||
$(hovertipSelect).hovertipActivate(hovertipConfig,
|
||||
targetSelectById,
|
||||
hovertipPrepare,
|
||||
hovertipTargetPrepare);
|
||||
}, 0);
|
||||
|
||||
/**
|
||||
* To enable this style of markup (id on target):
|
||||
* <span id="foo">target</span>...
|
||||
* <div target="foo" class="hovertip">blah blah</div>
|
||||
*/
|
||||
window.setTimeout(function() {
|
||||
$(hovertipSelect).hovertipActivate(hovertipConfig,
|
||||
targetSelectByTargetAttribute,
|
||||
hovertipPrepare,
|
||||
hovertipTargetPrepare);
|
||||
}, 0);
|
||||
|
||||
/**
|
||||
* This next section enables this style of markup:
|
||||
* <foo><span>target</span><span class="hovertip">blah blah</span></foo>
|
||||
*
|
||||
* With drop shadow effect.
|
||||
*
|
||||
*/
|
||||
var hovertipSpanSelect = 'span.hovertip';
|
||||
// activate hovertips with wrappers for FX (drop shadow):
|
||||
$(hovertipSpanSelect).css('display', 'block').addClass('hovertip_wrap3').
|
||||
wrap("<span class='hovertip_wrap0'><span class='hovertip_wrap1'><span class='hovertip_wrap2'>" +
|
||||
"</span></span></span>").each(function() {
|
||||
// fix class and attributes for newly wrapped elements
|
||||
var tooltip = this.parentNode.parentNode.parentNode;
|
||||
if (this.getAttribute('target'))
|
||||
tooltip.setAttribute('target', this.getAttribute('target'));
|
||||
if (this.getAttribute('id')) {
|
||||
var id = this.getAttribute('id');
|
||||
this.removeAttribute('id');
|
||||
tooltip.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
hovertipSpanSelect = 'span.hovertip_wrap0';
|
||||
|
||||
window.setTimeout(function() {
|
||||
$(hovertipSpanSelect)
|
||||
.hovertipActivate(hovertipConfig,
|
||||
targetSelectByPrevious,
|
||||
hovertipPrepare,
|
||||
hovertipTargetPrepare);
|
||||
}, 0);
|
||||
}
|
||||
142
js/jquery.layout.min.js
vendored
142
js/jquery.layout.min.js
vendored
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
jquery.layout 1.3.0 - Release Candidate 30.79
|
||||
$Date: 2013-01-01 08:00:00 (Tue, 1 Jan 2013) $
|
||||
$Rev: 303007 $
|
||||
|
||||
Copyright (c) 2013 Kevin Dalman (http://allpro.net)
|
||||
Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net)
|
||||
|
||||
Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
|
||||
and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
|
||||
|
||||
SEE: http://layout.jquery-dev.net/LICENSE.txt
|
||||
|
||||
Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.79
|
||||
|
||||
Docs: http://layout.jquery-dev.net/documentation.html
|
||||
Tips: http://layout.jquery-dev.net/tips.html
|
||||
Help: http://groups.google.com/group/jquery-ui-layout
|
||||
*/
|
||||
(function(b){var a=Math.min,d=Math.max,c=Math.floor,f=function(a){return"string"===b.type(a)},j=function(a,d){if(b.isArray(d))for(var c=0,j=d.length;c<j;c++){var h=d[c];try{f(h)&&(h=eval(h)),b.isFunction(h)&&h(a)}catch(k){}}};b.layout={version:"1.3.rc30.79",revision:0.033007,browser:{},effects:{slide:{all:{duration:"fast"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},west:{direction:"left"}},drop:{all:{duration:"slow"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},
|
||||
west:{direction:"left"}},scale:{all:{duration:"fast"}},blind:{},clip:{},explode:{},fade:{},fold:{},puff:{},size:{all:{easing:"swing"}}},config:{optionRootKeys:"effects panes north south west east center".split(" "),allPanes:["north","south","west","east","center"],borderPanes:["north","south","west","east"],oppositeEdge:{north:"south",south:"north",east:"west",west:"east"},offscreenCSS:{left:"-99999px",right:"auto"},offscreenReset:"offscreenReset",hidden:{visibility:"hidden"},visible:{visibility:"visible"},
|
||||
resizers:{cssReq:{position:"absolute",padding:0,margin:0,fontSize:"1px",textAlign:"left",overflow:"hidden"},cssDemo:{background:"#DDD",border:"none"}},togglers:{cssReq:{position:"absolute",display:"block",padding:0,margin:0,overflow:"hidden",textAlign:"center",fontSize:"1px",cursor:"pointer",zIndex:1},cssDemo:{background:"#AAA"}},content:{cssReq:{position:"relative"},cssDemo:{overflow:"auto",padding:"10px"},cssDemoPane:{overflow:"hidden",padding:0}},panes:{cssReq:{position:"absolute",margin:0},cssDemo:{padding:"10px",
|
||||
background:"#FFF",border:"1px solid #BBB",overflow:"auto"}},north:{side:"top",sizeType:"Height",dir:"horz",cssReq:{top:0,bottom:"auto",left:0,right:0,width:"auto"}},south:{side:"bottom",sizeType:"Height",dir:"horz",cssReq:{top:"auto",bottom:0,left:0,right:0,width:"auto"}},east:{side:"right",sizeType:"Width",dir:"vert",cssReq:{left:"auto",right:0,top:"auto",bottom:"auto",height:"auto"}},west:{side:"left",sizeType:"Width",dir:"vert",cssReq:{left:0,right:"auto",top:"auto",bottom:"auto",height:"auto"}},
|
||||
center:{dir:"center",cssReq:{left:"auto",right:"auto",top:"auto",bottom:"auto",height:"auto",width:"auto"}}},callbacks:{},getParentPaneElem:function(a){a=b(a);if(a=a.data("layout")||a.data("parentLayout")){a=a.container;if(a.data("layoutPane"))return a;a=a.closest("."+b.layout.defaults.panes.paneClass);if(a.data("layoutPane"))return a}return null},getParentPaneInstance:function(a){return(a=b.layout.getParentPaneElem(a))?a.data("layoutPane"):null},getParentLayoutInstance:function(a){return(a=b.layout.getParentPaneElem(a))?
|
||||
a.data("parentLayout"):null},getEventObject:function(b){return"object"===typeof b&&b.stopPropagation?b:null},parsePaneName:function(a){var d=b.layout.getEventObject(a);d&&(d.stopPropagation(),a=b(this).data("layoutEdge"));a&&!/^(west|east|north|south|center)$/.test(a)&&(b.layout.msg('LAYOUT ERROR - Invalid pane-name: "'+a+'"'),a="error");return a},plugins:{draggable:!!b.fn.draggable,effects:{core:!!b.effects,slide:b.effects&&(b.effects.slide||b.effects.effect&&b.effects.effect.slide)}},onCreate:[],
|
||||
onLoad:[],onReady:[],onDestroy:[],onUnload:[],afterOpen:[],afterClose:[],scrollbarWidth:function(){return window.scrollbarWidth||b.layout.getScrollbarSize("width")},scrollbarHeight:function(){return window.scrollbarHeight||b.layout.getScrollbarSize("height")},getScrollbarSize:function(a){var d=b('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body"),c={width:d.css("width")-d[0].clientWidth,height:d.height()-d[0].clientHeight};
|
||||
d.remove();window.scrollbarWidth=c.width;window.scrollbarHeight=c.height;return a.match(/^(width|height)$/)?c[a]:c},showInvisibly:function(b,a){if(b&&b.length&&(a||"none"===b.css("display"))){var d=b[0].style,d={display:d.display||"",visibility:d.visibility||""};b.css({display:"block",visibility:"hidden"});return d}return{}},getElementDimensions:function(a,c){var f={css:{},inset:{}},j=f.css,h={bottom:0},k=b.layout.cssNum,p=a.offset(),O,R,D;f.offsetLeft=p.left;f.offsetTop=p.top;c||(c={});b.each(["Left",
|
||||
"Right","Top","Bottom"],function(d,k){O=j["border"+k]=b.layout.borderWidth(a,k);R=j["padding"+k]=b.layout.cssNum(a,"padding"+k);D=k.toLowerCase();f.inset[D]=0<=c[D]?c[D]:R;h[D]=f.inset[D]+O});j.width=a.width();j.height=a.height();j.top=k(a,"top",!0);j.bottom=k(a,"bottom",!0);j.left=k(a,"left",!0);j.right=k(a,"right",!0);f.outerWidth=a.outerWidth();f.outerHeight=a.outerHeight();f.innerWidth=d(0,f.outerWidth-h.left-h.right);f.innerHeight=d(0,f.outerHeight-h.top-h.bottom);f.layoutWidth=a.innerWidth();
|
||||
f.layoutHeight=a.innerHeight();return f},getElementStyles:function(b,a){var d={},c=b[0].style,f=a.split(","),k=["Top","Bottom","Left","Right"],j=["Color","Style","Width"],h,p,D,x,A,r;for(x=0;x<f.length;x++)if(h=f[x],h.match(/(border|padding|margin)$/))for(A=0;4>A;A++)if(p=k[A],"border"===h)for(r=0;3>r;r++)D=j[r],d[h+p+D]=c[h+p+D];else d[h+p]=c[h+p];else d[h]=c[h];return d},cssWidth:function(a,c){if(0>=c)return 0;var f=!b.layout.browser.boxModel?"border-box":b.support.boxSizing?a.css("boxSizing"):
|
||||
"content-box",j=b.layout.borderWidth,h=b.layout.cssNum,k=c;"border-box"!==f&&(k-=j(a,"Left")+j(a,"Right"));"content-box"===f&&(k-=h(a,"paddingLeft")+h(a,"paddingRight"));return d(0,k)},cssHeight:function(a,c){if(0>=c)return 0;var f=!b.layout.browser.boxModel?"border-box":b.support.boxSizing?a.css("boxSizing"):"content-box",j=b.layout.borderWidth,h=b.layout.cssNum,k=c;"border-box"!==f&&(k-=j(a,"Top")+j(a,"Bottom"));"content-box"===f&&(k-=h(a,"paddingTop")+h(a,"paddingBottom"));return d(0,k)},cssNum:function(a,
|
||||
d,c){a.jquery||(a=b(a));var f=b.layout.showInvisibly(a);d=b.css(a[0],d,!0);c=c&&"auto"==d?d:Math.round(parseFloat(d)||0);a.css(f);return c},borderWidth:function(a,d){a.jquery&&(a=a[0]);var c="border"+d.substr(0,1).toUpperCase()+d.substr(1);return"none"===b.css(a,c+"Style",!0)?0:Math.round(parseFloat(b.css(a,c+"Width",!0))||0)},isMouseOverElem:function(a,d){var c=b(d||this),f=c.offset(),j=f.top,f=f.left,k=f+c.outerWidth(),c=j+c.outerHeight(),h=a.pageX,p=a.pageY;return b.layout.browser.msie&&0>h&&0>
|
||||
p||h>=f&&h<=k&&p>=j&&p<=c},msg:function(a,d,c,f){b.isPlainObject(a)&&window.debugData?("string"===typeof d?(f=c,c=d):"object"===typeof c&&(f=c,c=null),c=c||"log( <object> )",f=b.extend({sort:!1,returnHTML:!1,display:!1},f),!0===d||f.display?debugData(a,c,f):window.console&&console.log(debugData(a,c,f))):d?alert(a):window.console?console.log(a):(d=b("#layoutLogger"),d.length||(d=b('<div id="layoutLogger" style="position: '+(b.support.fixedPosition?"fixed":"absolute")+'; top: 5px; z-index: 999999; max-width: 25%; overflow: hidden; border: 1px solid #000; border-radius: 5px; background: #FBFBFB; box-shadow: 0 2px 10px rgba(0,0,0,0.3);"><div style="font-size: 13px; font-weight: bold; padding: 5px 10px; background: #F6F6F6; border-radius: 5px 5px 0 0; cursor: move;"><span style="float: right; padding-left: 7px; cursor: pointer;" title="Remove Console" onclick="$(this).closest(\'#layoutLogger\').remove()">X</span>Layout console.log</div><ul style="font-size: 13px; font-weight: none; list-style: none; margin: 0; padding: 0 0 2px;"></ul></div>').appendTo("body"),
|
||||
d.css("left",b(window).width()-d.outerWidth()-5),b.ui.draggable&&d.draggable({handle:":first-child"})),d.children("ul").append('<li style="padding: 4px 10px; margin: 0; border-top: 1px solid #CCC;">'+a.replace(/\</g,"<").replace(/\>/g,">")+"</li>"))}};var h=navigator.userAgent.toLowerCase(),p=/(chrome)[ \/]([\w.]+)/.exec(h)||/(webkit)[ \/]([\w.]+)/.exec(h)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(h)||/(msie) ([\w.]+)/.exec(h)||0>h.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(h)||
|
||||
[],h=p[1]||"",p=p[2]||0,x="msie"===h;b.layout.browser={version:p,safari:"webkit"===h,webkit:"chrome"===h,msie:x,isIE6:x&&6==p,boxModel:!x||!1!==b.support.boxModel};h&&(b.layout.browser[h]=!0);x&&b(function(){b.layout.browser.boxModel=b.support.boxModel});b.layout.defaults={name:"",containerClass:"ui-layout-container",inset:null,scrollToBookmarkOnLoad:!0,resizeWithWindow:!0,resizeWithWindowDelay:200,resizeWithWindowMaxDelay:0,maskPanesEarly:!1,onresizeall_start:null,onresizeall_end:null,onload_start:null,
|
||||
onload_end:null,onunload_start:null,onunload_end:null,initPanes:!0,showErrorMessages:!0,showDebugMessages:!1,zIndex:null,zIndexes:{pane_normal:0,content_mask:1,resizer_normal:2,pane_sliding:100,pane_animate:1E3,resizer_drag:1E4},errors:{pane:"pane",selector:"selector",addButtonError:"Error Adding Button\nInvalid ",containerMissing:"UI Layout Initialization Error\nThe specified layout-container does not exist.",centerPaneMissing:"UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element.",
|
||||
noContainerHeight:"UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!",callbackError:"UI Layout Callback Error\nThe EVENT callback is not a valid function."},panes:{applyDemoStyles:!1,closable:!0,resizable:!0,slidable:!0,initClosed:!1,initHidden:!1,contentSelector:".ui-layout-content",contentIgnoreSelector:".ui-layout-ignore",findNestedContent:!1,paneClass:"ui-layout-pane",resizerClass:"ui-layout-resizer",togglerClass:"ui-layout-toggler",
|
||||
buttonClass:"ui-layout-button",minSize:0,maxSize:0,spacing_open:6,spacing_closed:6,togglerLength_open:50,togglerLength_closed:50,togglerAlign_open:"center",togglerAlign_closed:"center",togglerContent_open:"",togglerContent_closed:"",resizerDblClickToggle:!0,autoResize:!0,autoReopen:!0,resizerDragOpacity:1,maskContents:!1,maskObjects:!1,maskZindex:null,resizingGrid:!1,livePaneResizing:!1,liveContentResizing:!1,liveResizingTolerance:1,sliderCursor:"pointer",slideTrigger_open:"click",slideTrigger_close:"mouseleave",
|
||||
slideDelay_open:300,slideDelay_close:300,hideTogglerOnSlide:!1,preventQuickSlideClose:b.layout.browser.webkit,preventPrematureSlideClose:!1,tips:{Open:"Open",Close:"Close",Resize:"Resize",Slide:"Slide Open",Pin:"Pin",Unpin:"Un-Pin",noRoomToOpen:"Not enough room to show this panel.",minSizeWarning:"Panel has reached its minimum size",maxSizeWarning:"Panel has reached its maximum size"},showOverflowOnHover:!1,enableCursorHotkey:!0,customHotkeyModifier:"SHIFT",fxName:"slide",fxSpeed:null,fxSettings:{},
|
||||
fxOpacityFix:!0,animatePaneSizing:!1,children:null,containerSelector:"",initChildren:!0,destroyChildren:!0,resizeChildren:!0,triggerEventsOnLoad:!1,triggerEventsDuringLiveResize:!0,onshow_start:null,onshow_end:null,onhide_start:null,onhide_end:null,onopen_start:null,onopen_end:null,onclose_start:null,onclose_end:null,onresize_start:null,onresize_end:null,onsizecontent_start:null,onsizecontent_end:null,onswap_start:null,onswap_end:null,ondrag_start:null,ondrag_end:null},north:{paneSelector:".ui-layout-north",
|
||||
size:"auto",resizerCursor:"n-resize",customHotkey:""},south:{paneSelector:".ui-layout-south",size:"auto",resizerCursor:"s-resize",customHotkey:""},east:{paneSelector:".ui-layout-east",size:200,resizerCursor:"e-resize",customHotkey:""},west:{paneSelector:".ui-layout-west",size:200,resizerCursor:"w-resize",customHotkey:""},center:{paneSelector:".ui-layout-center",minWidth:0,minHeight:0}};b.layout.optionsMap={layout:"name instanceKey stateManagement effects inset zIndexes errors zIndex scrollToBookmarkOnLoad showErrorMessages maskPanesEarly outset resizeWithWindow resizeWithWindowDelay resizeWithWindowMaxDelay onresizeall onresizeall_start onresizeall_end onload onload_start onload_end onunload onunload_start onunload_end".split(" "),
|
||||
center:"paneClass contentSelector contentIgnoreSelector findNestedContent applyDemoStyles triggerEventsOnLoad showOverflowOnHover maskContents maskObjects liveContentResizing containerSelector children initChildren resizeChildren destroyChildren onresize onresize_start onresize_end onsizecontent onsizecontent_start onsizecontent_end".split(" "),noDefault:["paneSelector","resizerCursor","customHotkey"]};b.layout.transformData=function(a,d){var c=d?{panes:{},center:{}}:{},f,j,k,h,p,x,D;if("object"!==
|
||||
typeof a)return c;for(j in a){f=c;p=a[j];k=j.split("__");D=k.length-1;for(x=0;x<=D;x++)h=k[x],x===D?f[h]=b.isPlainObject(p)?b.layout.transformData(p):p:(f[h]||(f[h]={}),f=f[h])}return c};b.layout.backwardCompatibility={map:{applyDefaultStyles:"applyDemoStyles",childOptions:"children",initChildLayout:"initChildren",destroyChildLayout:"destroyChildren",resizeChildLayout:"resizeChildren",resizeNestedLayout:"resizeChildren",resizeWhileDragging:"livePaneResizing",resizeContentWhileDragging:"liveContentResizing",
|
||||
triggerEventsWhileDragging:"triggerEventsDuringLiveResize",maskIframesOnResize:"maskContents",useStateCookie:"stateManagement.enabled","cookie.autoLoad":"stateManagement.autoLoad","cookie.autoSave":"stateManagement.autoSave","cookie.keys":"stateManagement.stateKeys","cookie.name":"stateManagement.cookie.name","cookie.domain":"stateManagement.cookie.domain","cookie.path":"stateManagement.cookie.path","cookie.expires":"stateManagement.cookie.expires","cookie.secure":"stateManagement.cookie.secure",
|
||||
noRoomToOpenTip:"tips.noRoomToOpen",togglerTip_open:"tips.Close",togglerTip_closed:"tips.Open",resizerTip:"tips.Resize",sliderTip:"tips.Slide"},renameOptions:function(a){function d(b,c){for(var f=b.split("."),k=f.length-1,j={branch:a,key:f[k]},r=0,q;r<k;r++)q=f[r],j.branch=void 0==j.branch[q]?c?j.branch[q]={}:{}:j.branch[q];return j}var c=b.layout.backwardCompatibility.map,f,j,k,h;for(h in c)f=d(h),k=f.branch[f.key],void 0!==k&&(j=d(c[h],!0),j.branch[j.key]=k,delete f.branch[f.key])},renameAllOptions:function(a){var d=
|
||||
b.layout.backwardCompatibility.renameOptions;d(a);a.defaults&&("object"!==typeof a.panes&&(a.panes={}),b.extend(!0,a.panes,a.defaults),delete a.defaults);a.panes&&d(a.panes);b.each(b.layout.config.allPanes,function(b,c){a[c]&&d(a[c])});return a}};b.fn.layout=function(h){function p(e){if(!e)return!0;var w=e.keyCode;if(33>w)return!0;var m={38:"north",40:"south",37:"west",39:"east"},a=e.shiftKey,g=e.ctrlKey,t,n,d,c;g&&(37<=w&&40>=w)&&r[m[w]].enableCursorHotkey?c=m[w]:(g||a)&&b.each(k.borderPanes,function(e,
|
||||
b){t=r[b];n=t.customHotkey;d=t.customHotkeyModifier;if(a&&"SHIFT"==d||g&&"CTRL"==d||g&&a)if(n&&w===(isNaN(n)||9>=n?n.toUpperCase().charCodeAt(0):n))return c=b,!1});if(!c||!y[c]||!r[c].closable||q[c].isHidden)return!0;na(c);e.stopPropagation();return e.returnValue=!1}function x(e){if(H()){this&&this.tagName&&(e=this);var w;f(e)?w=y[e]:b(e).data("layoutRole")?w=b(e):b(e).parents().each(function(){if(b(this).data("layoutRole"))return w=b(this),!1});if(w&&w.length){var m=w.data("layoutEdge");e=q[m];e.cssSaved&&
|
||||
X(m);if(e.isSliding||e.isResizing||e.isClosed)e.cssSaved=!1;else{var a={zIndex:r.zIndexes.resizer_normal+1},g={},t=w.css("overflow"),n=w.css("overflowX"),d=w.css("overflowY");"visible"!=t&&(g.overflow=t,a.overflow="visible");n&&!n.match(/(visible|auto)/)&&(g.overflowX=n,a.overflowX="visible");d&&!d.match(/(visible|auto)/)&&(g.overflowY=n,a.overflowY="visible");e.cssSaved=g;w.css(a);b.each(k.allPanes,function(e,b){b!=m&&X(b)})}}}}function X(e){if(H()){this&&this.tagName&&(e=this);var w;f(e)?w=y[e]:
|
||||
b(e).data("layoutRole")?w=b(e):b(e).parents().each(function(){if(b(this).data("layoutRole"))return w=b(this),!1});if(w&&w.length){e=w.data("layoutEdge");e=q[e];var m=e.cssSaved||{};!e.isSliding&&!e.isResizing&&w.css("zIndex",r.zIndexes.pane_normal);w.css(m);e.cssSaved=!1}}}var G=b.layout.browser,k=b.layout.config,Q=b.layout.cssWidth,O=b.layout.cssHeight,R=b.layout.getElementDimensions,D=b.layout.getElementStyles,Ma=b.layout.getEventObject,A=b.layout.parsePaneName,r=b.extend(!0,{},b.layout.defaults);
|
||||
r.effects=b.extend(!0,{},b.layout.effects);var q={id:"layout"+b.now(),initialized:!1,paneResizing:!1,panesSliding:{},container:{innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0,layoutWidth:0,layoutHeight:0},north:{childIdx:0},south:{childIdx:0},east:{childIdx:0},west:{childIdx:0},center:{childIdx:0}},ba={north:null,south:null,east:null,west:null,center:null},M={data:{},set:function(e,b,m){M.clear(e);M.data[e]=setTimeout(b,m)},clear:function(e){var b=M.data;b[e]&&(clearTimeout(b[e]),delete b[e])}},
|
||||
ca=function(e,w,m){var a=r;(a.showErrorMessages&&!m||m&&a.showDebugMessages)&&b.layout.msg(a.name+" / "+e,!1!==w);return!1},C=function(e,w,m){var a=w&&f(w),g=a?q[w]:q,t=a?r[w]:r,n=r.name,d=e+(e.match(/_/)?"":"_end"),c=d.match(/_end$/)?d.substr(0,d.length-4):"",l=t[d]||t[c],h="NC",k=[];!a&&"boolean"===b.type(w)&&(m=w,w="");if(l)try{f(l)&&(l.match(/,/)?(k=l.split(","),l=eval(k[0])):l=eval(l)),b.isFunction(l)&&(h=k.length?l(k[1]):a?l(w,y[w],g,t,n):l(z,g,t,n))}catch(j){ca(r.errors.callbackError.replace(/EVENT/,
|
||||
b.trim((w||"")+" "+d)),!1),"string"===b.type(j)&&string.length&&ca("Exception: "+j,!1)}!m&&!1!==h&&(a?(m=y[w],t=r[w],g=q[w],m.triggerHandler("layoutpane"+d,[w,m,g,t,n]),c&&m.triggerHandler("layoutpane"+c,[w,m,g,t,n])):(u.triggerHandler("layout"+d,[z,g,t,n]),c&&u.triggerHandler("layout"+c,[z,g,t,n])));a&&"onresize_end"===e&&db(w+"",!0);return h},eb=function(e){if(!G.mozilla){var b=y[e];"IFRAME"===q[e].tagName?b.css(k.hidden).css(k.visible):b.find("IFRAME").css(k.hidden).css(k.visible)}},ya=function(e){var b=
|
||||
y[e];e=k[e].dir;b={minWidth:1001-Q(b,1E3),minHeight:1001-O(b,1E3)};"horz"===e&&(b.minSize=b.minHeight);"vert"===e&&(b.minSize=b.minWidth);return b},fa=function(e,w,m){m||(m=k[e].dir);f(w)&&w.match(/%/)&&(w="100%"===w?-1:parseInt(w,10)/100);if(0===w)return 0;if(1<=w)return parseInt(w,10);var a=r,g=0;"horz"==m?g=v.innerHeight-(y.north?a.north.spacing_open:0)-(y.south?a.south.spacing_open:0):"vert"==m&&(g=v.innerWidth-(y.west?a.west.spacing_open:0)-(y.east?a.east.spacing_open:0));if(-1===w)return g;
|
||||
if(0<w)return c(g*w);if("center"==e)return 0;m="horz"===m?"height":"width";a=y[e];e="height"===m?U[e]:!1;var g=b.layout.showInvisibly(a),t=a.css(m),n=e?e.css(m):0;a.css(m,"auto");e&&e.css(m,"auto");w="height"===m?a.outerHeight():a.outerWidth();a.css(m,t).css(g);e&&e.css(m,n);return w},ga=function(e,b){var a=y[e],E=r[e],g=q[e],t=b?E.spacing_open:0,E=b?E.spacing_closed:0;return!a||g.isHidden?0:g.isClosed||g.isSliding&&b?E:"horz"===k[e].dir?a.outerHeight()+t:a.outerWidth()+t},Y=function(e,b){if(H()){var m=
|
||||
r[e],E=q[e],g=k[e],t=g.dir;g.sizeType.toLowerCase();var g=void 0!=b?b:E.isSliding,n=m.spacing_open,c=k.oppositeEdge[e],f=q[c],l=y[c],h=!l||!1===f.isVisible||f.isSliding?0:"horz"==t?l.outerHeight():l.outerWidth(),c=(!l||f.isHidden?0:r[c][!1!==f.isClosed?"spacing_closed":"spacing_open"])||0,f="horz"==t?v.innerHeight:v.innerWidth,l=ya("center"),l="horz"==t?d(r.center.minHeight,l.minHeight):d(r.center.minWidth,l.minWidth),g=f-n-(g?0:fa("center",l,t)+h+c),t=E.minSize=d(fa(e,m.minSize),ya(e).minSize),g=
|
||||
E.maxSize=a(m.maxSize?fa(e,m.maxSize):1E5,g),E=E.resizerPosition={},n=v.inset.top,h=v.inset.left,c=v.innerWidth,f=v.innerHeight,m=m.spacing_open;switch(e){case "north":E.min=n+t;E.max=n+g;break;case "west":E.min=h+t;E.max=h+g;break;case "south":E.min=n+f-g-m;E.max=n+f-t-m;break;case "east":E.min=h+c-g-m,E.max=h+c-t-m}}},Na=function(e,a){var m=b(e),E=m.data("layoutRole"),g=m.data("layoutEdge"),t=r[g][E+"Class"],g="-"+g,n=m.hasClass(t+"-closed")?"-closed":"-open",d="-closed"===n?"-open":"-closed",n=
|
||||
t+"-hover "+(t+g+"-hover ")+(t+n+"-hover ")+(t+g+n+"-hover ");a&&(n+=t+d+"-hover "+(t+g+d+"-hover "));"resizer"==E&&m.hasClass(t+"-sliding")&&(n+=t+"-sliding-hover "+(t+g+"-sliding-hover "));return b.trim(n)},Oa=function(e,a){var m=b(a||this);e&&"toggler"===m.data("layoutRole")&&e.stopPropagation();m.addClass(Na(m))},da=function(e,a){var m=b(a||this);m.removeClass(Na(m,!0))},fb=function(){var e=b(this).data("layoutEdge"),a=q[e];!a.isClosed&&(!a.isResizing&&!q.paneResizing)&&(b.fn.disableSelection&&
|
||||
b("body").disableSelection(),r.maskPanesEarly&&va(e,{resizing:!0}))},gb=function(e,a){var m=a||this,E=b(m).data("layoutEdge"),g=E+"ResizerLeave";M.clear(E+"_openSlider");M.clear(g);a?q.paneResizing||(b.fn.enableSelection&&b("body").enableSelection(),r.maskPanesEarly&&za()):M.set(g,function(){gb(e,m)},200)},H=function(){return q.initialized||q.creatingLayout?!0:Aa()},Aa=function(e){var a=r;if(!u.is(":visible"))return!e&&(G.webkit&&"BODY"===u[0].tagName)&&setTimeout(function(){Aa(!0)},50),!1;if(!hb("center").length)return ca(a.errors.centerPaneMissing);
|
||||
q.creatingLayout=!0;b.extend(v,R(u,a.inset));A(void 0);b.each(k.allPanes,function(e,b){ib(b,!0)});Pa();b.each(k.borderPanes,function(e,b){y[b]&&q[b].isVisible&&(Y(b),ha(b))});ia("center");b.each(k.allPanes,function(e,b){jb(b)});a.scrollToBookmarkOnLoad&&(e=self.location,e.hash&&e.replace(e.hash));z.hasParentLayout?a.resizeWithWindow=!1:a.resizeWithWindow&&b(window).bind("resize."+K,Ab);delete q.creatingLayout;q.initialized=!0;j(z,b.layout.onReady);C("onload_end");return!0},Qa=function(e,a){var m=
|
||||
A.call(this,e),d=y[m];if(d){var g=U[m],t=q[m],n=r[m],c=r.stateManagement||{},n=a?n.children=a:n.children;if(b.isPlainObject(n))n=[n];else if(!n||!b.isArray(n))return;b.each(n,function(e,a){b.isPlainObject(a)&&(a.containerSelector?d.find(a.containerSelector):g||d).each(function(){var e=b(this),g=e.data("layout");if(!g){kb({container:e,options:a},t);if(c.includeChildren&&q.stateData[m]){var g=(q.stateData[m].children||{})[a.instanceKey],w=a.stateManagement||(a.stateManagement={autoLoad:!0});!0===w.autoLoad&&
|
||||
g&&(w.autoSave=!1,w.includeChildren=!0,w.autoLoad=b.extend(!0,{},g))}(g=e.layout(a))&&Ba(m,g)}})})}},kb=function(e,b){var a=e.container,d=e.options,g=d.stateManagement,t=d.instanceKey||a.data("layoutInstanceKey");t||(t=(g&&g.cookie?g.cookie.name:"")||d.name);t=t?t.replace(/[^\w-]/gi,"_").replace(/_{2,}/g,"_"):"layout"+ ++b.childIdx;d.instanceKey=t;a.data("layoutInstanceKey",t);return t},Ba=function(e,a){var m=y[e],d=ba[e],g=q[e];b.isPlainObject(d)&&(b.each(d,function(e,b){b.destroyed&&delete d[e]}),
|
||||
b.isEmptyObject(d)&&(d=ba[e]=null));!a&&!d&&(a=m.data("layout"));a&&(a.hasParentLayout=!0,m=a.options,kb(a,g),d||(d=ba[e]={}),d[m.instanceKey]=a.container.data("layout"));z[e].children=ba[e];a||Qa(e)},Ab=function(){var e=r,b=Number(e.resizeWithWindowDelay);10>b&&(b=100);M.clear("winResize");M.set("winResize",function(){M.clear("winResize");M.clear("winResizeRepeater");var b=R(u,e.inset);(b.innerWidth!==v.innerWidth||b.innerHeight!==v.innerHeight)&&oa()},b);M.data.winResizeRepeater||lb()},lb=function(){var e=
|
||||
Number(r.resizeWithWindowMaxDelay);0<e&&M.set("winResizeRepeater",function(){lb();oa()},e)},mb=function(){C("onunload_start");j(z,b.layout.onUnload);C("onunload_end")},nb=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,a){var d=r[a];if(d.enableCursorHotkey||d.customHotkey)return b(document).bind("keydown."+K,p),!1})},hb=function(e){e=r[e].paneSelector;if("#"===e.substr(0,1))return u.find(e).eq(0);var b=u.children(e).eq(0);return b.length?b:u.children("form:first").children(e).eq(0)},
|
||||
ib=function(e,b){if(b||H()){var m=r[e],c=q[e],g=k[e],t=g.dir,n="center"===e,f={},h=y[e],l,j;h?Ra(e,!1,!0,!1):U[e]=!1;h=y[e]=hb(e);if(h.length){h.data("layoutCSS")||h.data("layoutCSS",D(h,"position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border"));z[e]={name:e,pane:y[e],content:U[e],options:r[e],state:q[e],children:ba[e]};h.data({parentLayout:z,layoutPane:z[e],layoutEdge:e,layoutRole:"pane"}).css(g.cssReq).css("zIndex",r.zIndexes.pane_normal).css(m.applyDemoStyles?
|
||||
g.cssDemo:{}).addClass(m.paneClass+" "+m.paneClass+"-"+e).bind("mouseenter."+K,Oa).bind("mouseleave."+K,da);g={hide:"",show:"",toggle:"",close:"",open:"",slideOpen:"",slideClose:"",slideToggle:"",size:"sizePane",sizePane:"sizePane",sizeContent:"",sizeHandles:"",enableClosable:"",disableClosable:"",enableSlideable:"",disableSlideable:"",enableResizable:"",disableResizable:"",swapPanes:"swapPanes",swap:"swapPanes",move:"swapPanes",removePane:"removePane",remove:"removePane",createChildren:"",resizeChildren:"",
|
||||
resizeAll:"resizeAll",resizeLayout:"resizeAll"};for(j in g)h.bind("layoutpane"+j.toLowerCase()+"."+K,z[g[j]||j]);Sa(e,!1);n||(l=c.size=fa(e,m.size),n=fa(e,m.minSize)||1,j=fa(e,m.maxSize)||1E5,0<l&&(l=d(a(l,j),n)),c.autoResize=m.autoResize,c.isClosed=!1,c.isSliding=!1,c.isResizing=!1,c.isHidden=!1,c.pins||(c.pins=[]));c.tagName=h[0].tagName;c.edge=e;c.noRoom=!1;c.isVisible=!0;ob(e);"horz"===t?f.height=O(h,l):"vert"===t&&(f.width=Q(h,l));h.css(f);"horz"!=t&&ia(e,!0);q.initialized&&(Pa(e),nb(e));m.initClosed&&
|
||||
m.closable&&!m.initHidden?ja(e,!0,!0):m.initHidden||m.initClosed?Ta(e):c.noRoom||h.css("display","block");h.css("visibility","visible");m.showOverflowOnHover&&h.hover(x,X);q.initialized&&jb(e)}else y[e]=!1}},jb=function(e){var b=y[e],a=q[e],d=r[e];b&&(b.data("layout")&&Ba(e,b.data("layout")),a.isVisible&&(q.initialized?oa():pa(e),d.triggerEventsOnLoad?C("onresize_end",e):db(e,!0)),d.initChildren&&d.children&&Qa(e))},ob=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,b){var a=y[b],g=
|
||||
F[b],d=q[b],c=k[b].side,f={};if(a){switch(b){case "north":f.top=v.inset.top;f.left=v.inset.left;f.right=v.inset.right;break;case "south":f.bottom=v.inset.bottom;f.left=v.inset.left;f.right=v.inset.right;break;case "west":f.left=v.inset.left;break;case "east":f.right=v.inset.right}a.css(f);g&&d.isClosed?g.css(c,v.inset[c]):g&&!d.isHidden&&g.css(c,v.inset[c]+ga(b))}})},Pa=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,a){var d=y[a];F[a]=!1;P[a]=!1;if(d){var g=r[a],d=q[a],c="#"===g.paneSelector.substr(0,
|
||||
1)?g.paneSelector.substr(1):"",n=g.resizerClass,f=g.togglerClass,h="-"+a,l=z[a],j=l.resizer=F[a]=b("<div></div>"),l=l.toggler=g.closable?P[a]=b("<div></div>"):!1;!d.isVisible&&g.slidable&&j.attr("title",g.tips.Slide).css("cursor",g.sliderCursor);j.attr("id",c?c+"-resizer":"").data({parentLayout:z,layoutPane:z[a],layoutEdge:a,layoutRole:"resizer"}).css(k.resizers.cssReq).css("zIndex",r.zIndexes.resizer_normal).css(g.applyDemoStyles?k.resizers.cssDemo:{}).addClass(n+" "+n+h).hover(Oa,da).hover(fb,gb).appendTo(u);
|
||||
g.resizerDblClickToggle&&j.bind("dblclick."+K,na);l&&(l.attr("id",c?c+"-toggler":"").data({parentLayout:z,layoutPane:z[a],layoutEdge:a,layoutRole:"toggler"}).css(k.togglers.cssReq).css(g.applyDemoStyles?k.togglers.cssDemo:{}).addClass(f+" "+f+h).hover(Oa,da).bind("mouseenter",fb).appendTo(j),g.togglerContent_open&&b("<span>"+g.togglerContent_open+"</span>").data({layoutEdge:a,layoutRole:"togglerContent"}).data("layoutRole","togglerContent").data("layoutEdge",a).addClass("content content-open").css("display",
|
||||
"none").appendTo(l),g.togglerContent_closed&&b("<span>"+g.togglerContent_closed+"</span>").data({layoutEdge:a,layoutRole:"togglerContent"}).addClass("content content-closed").css("display","none").appendTo(l),pb(a));var g=a,B=b.layout.plugins.draggable,g=g?g.split(","):k.borderPanes;b.each(g,function(e,a){var g=r[a];if(!B||!y[a]||!g.resizable)return g.resizable=!1,!0;var m=q[a],w=r.zIndexes,d=k[a],c="horz"==d.dir?"top":"left",t=F[a],n=g.resizerClass,f=0,l,h,E=n+"-drag",j=n+"-"+a+"-drag",J=n+"-dragging",
|
||||
zb=n+"-"+a+"-dragging",cb=n+"-dragging-limit",v=n+"-"+a+"-dragging-limit",x=!1;m.isClosed||t.attr("title",g.tips.Resize).css("cursor",g.resizerCursor);t.draggable({containment:u[0],axis:"horz"==d.dir?"y":"x",delay:0,distance:1,grid:g.resizingGrid,helper:"clone",opacity:g.resizerDragOpacity,addClasses:!1,zIndex:w.resizer_drag,start:function(e,w){g=r[a];m=q[a];h=g.livePaneResizing;if(!1===C("ondrag_start",a))return!1;m.isResizing=!0;q.paneResizing=a;M.clear(a+"_closeSlider");Y(a);l=m.resizerPosition;
|
||||
f=w.position[c];t.addClass(E+" "+j);x=!1;b("body").disableSelection();va(a,{resizing:!0})},drag:function(e,b){x||(b.helper.addClass(J+" "+zb).css({right:"auto",bottom:"auto"}).children().css("visibility","hidden"),x=!0,m.isSliding&&y[a].css("zIndex",w.pane_sliding));var d=0;b.position[c]<l.min?(b.position[c]=l.min,d=-1):b.position[c]>l.max&&(b.position[c]=l.max,d=1);d?(b.helper.addClass(cb+" "+v),window.defaultStatus=0<d&&a.match(/(north|west)/)||0>d&&a.match(/(south|east)/)?g.tips.maxSizeWarning:
|
||||
g.tips.minSizeWarning):(b.helper.removeClass(cb+" "+v),window.defaultStatus="");h&&Math.abs(b.position[c]-f)>=g.liveResizingTolerance&&(f=b.position[c],p(e,b,a))},stop:function(e,g){b("body").enableSelection();window.defaultStatus="";t.removeClass(E+" "+j);m.isResizing=!1;q.paneResizing=!1;p(e,g,a,!0)}})});var p=function(b,e,a,g){var m=e.position,w=k[a];b=r[a];e=q[a];var d;switch(a){case "north":d=m.top;break;case "west":d=m.left;break;case "south":d=v.layoutHeight-m.top-b.spacing_open;break;case "east":d=
|
||||
v.layoutWidth-m.left-b.spacing_open}d-=v.inset[w.side];g?(!1!==C("ondrag_end",a)&&Ca(a,d,!1,!0),za(!0),e.isSliding&&va(a,{resizing:!0})):Math.abs(d-e.size)<b.liveResizingTolerance||(Ca(a,d,!1,!0),ea.each(qb))};d.isVisible?Ua(a):(Da(a),ma(a,!0))}});qa()},Sa=function(b,a){if(H()){var m=r[b],d=m.contentSelector,g=z[b],c=y[b],n;d&&(n=g.content=U[b]=m.findNestedContent?c.find(d).eq(0):c.children(d).eq(0));n&&n.length?(n.data("layoutRole","content"),n.data("layoutCSS")||n.data("layoutCSS",D(n,"height")),
|
||||
n.css(k.content.cssReq),m.applyDemoStyles&&(n.css(k.content.cssDemo),c.css(k.content.cssDemoPane)),c.css("overflowX").match(/(scroll|auto)/)&&c.css("overflow","hidden"),q[b].content={},!1!==a&&pa(b)):g.content=U[b]=!1}},qb=function(){var e=b(this),a=e.data("layoutMask"),a=q[a];"IFRAME"==a.tagName&&a.isVisible&&e.css({top:a.offsetTop,left:a.offsetLeft,width:a.outerWidth,height:a.outerHeight})},va=function(e,a){var m=k[e],d=["center"],g=r.zIndexes,c=b.extend({objectsOnly:!1,animation:!1,resizing:!0,
|
||||
sliding:q[e].isSliding},a),n,f;c.resizing&&d.push(e);c.sliding&&d.push(k.oppositeEdge[e]);"horz"===m.dir&&(d.push("west"),d.push("east"));b.each(d,function(e,a){f=q[a];n=r[a];if(f.isVisible&&(n.maskObjects||!c.objectsOnly&&n.maskContents)){for(var m=b([]),d,w=0,h=ea.length;w<h;w++)d=ea.eq(w),d.data("layoutMask")===a&&(m=m.add(d));if(!m.length){m=y[a];d=q[a];var w=r[a],h=r.zIndexes,j=b([]),E,k,v,p,x;if(w.maskContents||w.maskObjects)for(x=0;x<(w.maskObjects?2:1);x++)E=w.maskObjects&&0==x,k=document.createElement(E?
|
||||
"iframe":"div"),v=b(k).data("layoutMask",a),k.className="ui-layout-mask ui-layout-mask-"+a,p=k.style,p.display="block",p.position="absolute",p.background="#FFF",E&&(k.frameborder=0,k.src="about:blank",p.opacity=0,p.filter="Alpha(Opacity='0')",p.border=0),"IFRAME"==d.tagName?(p.zIndex=h.pane_normal+1,u.append(k)):(v.addClass("ui-layout-mask-inside-pane"),p.zIndex=w.maskZindex||h.content_mask,p.top=0,p.left=0,p.width="100%",p.height="100%",m.append(k)),j=j.add(k),ea=ea.add(k);m=j}m.each(function(){qb.call(this);
|
||||
this.style.zIndex=f.isSliding?g.pane_sliding+1:g.pane_normal+1;this.style.display="block"})}})},za=function(a){if(a||!q.paneResizing)ea.hide();else if(!a&&!b.isEmptyObject(q.panesSliding)){a=ea.length-1;for(var d,m;0<=a;a--)m=ea.eq(a),d=m.data("layoutMask"),r[d].maskObjects||m.hide()}},Ra=function(a,d,m,c){if(H()){a=A.call(this,a);var g=y[a],t=U[a],n=F[a],f=P[a];g&&b.isEmptyObject(g.data())&&(g=!1);t&&b.isEmptyObject(t.data())&&(t=!1);n&&b.isEmptyObject(n.data())&&(n=!1);f&&b.isEmptyObject(f.data())&&
|
||||
(f=!1);g&&g.stop(!0,!0);var h=r[a],l=ba[a],j=b.isPlainObject(l)&&!b.isEmptyObject(l);c=void 0!==c?c:h.destroyChildren;j&&c&&(b.each(l,function(a,b){b.destroyed||b.destroy(!0);b.destroyed&&delete l[a]}),b.isEmptyObject(l)&&(l=ba[a]=null,j=!1));g&&d&&!j?g.remove():g&&g[0]&&(d=h.paneClass,c=d+"-"+a,d=[d,d+"-open",d+"-closed",d+"-sliding",c,c+"-open",c+"-closed",c+"-sliding"],b.merge(d,Na(g,!0)),g.removeClass(d.join(" ")).removeData("parentLayout").removeData("layoutPane").removeData("layoutRole").removeData("layoutEdge").removeData("autoHidden").unbind("."+
|
||||
K),j&&t?(t.width(t.width()),b.each(l,function(a,b){b.resizeAll()})):t&&t.css(t.data("layoutCSS")).removeData("layoutCSS").removeData("layoutRole"),g.data("layout")||g.css(g.data("layoutCSS")).removeData("layoutCSS"));f&&f.remove();n&&n.remove();z[a]=y[a]=U[a]=F[a]=P[a]=!1;m||oa()}},Ea=function(a){var b=y[a],d=b[0].style;r[a].useOffscreenClose?(b.data(k.offscreenReset)||b.data(k.offscreenReset,{left:d.left,right:d.right}),b.css(k.offscreenCSS)):b.hide().removeData(k.offscreenReset)},rb=function(a){var b=
|
||||
y[a];a=r[a];var d=k.offscreenCSS,c=b.data(k.offscreenReset),g=b[0].style;b.show().removeData(k.offscreenReset);a.useOffscreenClose&&c&&(g.left==d.left&&(g.left=c.left),g.right==d.right&&(g.right=c.right))},Ta=function(a,b){if(H()){var d=A.call(this,a),c=r[d],g=q[d],t=F[d];y[d]&&!g.isHidden&&!(q.initialized&&!1===C("onhide_start",d))&&(g.isSliding=!1,delete q.panesSliding[d],t&&t.hide(),!q.initialized||g.isClosed?(g.isClosed=!0,g.isHidden=!0,g.isVisible=!1,q.initialized||Ea(d),ia("horz"===k[d].dir?
|
||||
"":"center"),(q.initialized||c.triggerEventsOnLoad)&&C("onhide_end",d)):(g.isHiding=!0,ja(d,!1,b)))}},Fa=function(a,b,d,c){if(H()){a=A.call(this,a);var g=q[a];y[a]&&g.isHidden&&!1!==C("onshow_start",a)&&(g.isShowing=!0,g.isSliding=!1,delete q.panesSliding[a],!1===b?ja(a,!0):ra(a,!1,d,c))}},na=function(a,b){if(H()){var d=Ma(a),c=A.call(this,a),g=q[c];d&&d.stopImmediatePropagation();g.isHidden?Fa(c):g.isClosed?ra(c,!!b):ja(c)}},ja=function(a,b,d,c){function g(){l.isMoving=!1;ma(t,!0);var a=k.oppositeEdge[t];
|
||||
q[a].noRoom&&(Y(a),ha(a));if(!c&&(q.initialized||h.triggerEventsOnLoad))p||C("onclose_end",t),p&&C("onshow_end",t),v&&C("onhide_end",t)}var t=A.call(this,a);if(!q.initialized&&y[t]){a=t;var n=q[a];Ea(a);n.isClosed=!0;n.isVisible=!1;Da(a)}else if(H()){var f=y[t],h=r[t],l=q[t],j,p,v;u.queue(function(a){if(!f||!h.closable&&!l.isShowing&&!l.isHiding||!b&&l.isClosed&&!l.isShowing)return a();var e=!l.isShowing&&!1===C("onclose_start",t);p=l.isShowing;v=l.isHiding;delete l.isShowing;delete l.isHiding;if(e)return a();
|
||||
j=!d&&!l.isClosed&&"none"!=h.fxName_close;l.isMoving=!0;l.isClosed=!0;l.isVisible=!1;v?l.isHidden=!0:p&&(l.isHidden=!1);l.isSliding?wa(t,!1):ia("horz"===k[t].dir?"":"center",!1);Da(t);j?(Ga(t,!0),f.hide(h.fxName_close,h.fxSettings_close,h.fxSpeed_close,function(){Ga(t,!1);l.isClosed&&g();a()})):(Ea(t),g(),a())})}},Da=function(a){if(F[a]){var d=F[a],c=P[a],f=r[a],g=k[a].side,t=f.resizerClass,n=f.togglerClass,h="-"+a;d.css(g,v.inset[g]).removeClass(t+"-open "+t+h+"-open").removeClass(t+"-sliding "+
|
||||
t+h+"-sliding").addClass(t+"-closed "+t+h+"-closed");f.resizable&&b.layout.plugins.draggable&&d.draggable("disable").removeClass("ui-state-disabled").css("cursor","default").attr("title","");c&&(c.removeClass(n+"-open "+n+h+"-open").addClass(n+"-closed "+n+h+"-closed").attr("title",f.tips.Open),c.children(".content-open").hide(),c.children(".content-closed").css("display","block"));Va(a,!1);q.initialized&&qa()}},ra=function(a,b,d,c){function g(){j.isMoving=!1;eb(f);j.isSliding||ia("vert"==k[f].dir?
|
||||
"center":"",!1);Ua(f)}if(H()){var f=A.call(this,a),n=y[f],h=r[f],j=q[f],l,p;u.queue(function(a){if(!n||!h.resizable&&!h.closable&&!j.isShowing||j.isVisible&&!j.isSliding)return a();if(j.isHidden&&!j.isShowing)a(),Fa(f,!0);else{j.autoResize&&j.size!=h.size?ka(f,h.size,!0,!0,!0):Y(f,b);var e=C("onopen_start",f);if("abort"===e)return a();"NC"!==e&&Y(f,b);if(j.minSize>j.maxSize)return Va(f,!1),!c&&h.tips.noRoomToOpen&&alert(h.tips.noRoomToOpen),a();b?wa(f,!0):j.isSliding?wa(f,!1):h.slidable&&ma(f,!1);
|
||||
j.noRoom=!1;ha(f);p=j.isShowing;delete j.isShowing;l=!d&&j.isClosed&&"none"!=h.fxName_open;j.isMoving=!0;j.isVisible=!0;j.isClosed=!1;p&&(j.isHidden=!1);l?(Ga(f,!0),n.show(h.fxName_open,h.fxSettings_open,h.fxSpeed_open,function(){Ga(f,!1);j.isVisible&&g();a()})):(rb(f),g(),a())}})}},Ua=function(a,d){var c=y[a],f=F[a],g=P[a],h=r[a],n=q[a],j=k[a].side,p=h.resizerClass,l=h.togglerClass,u="-"+a;f.css(j,v.inset[j]+ga(a)).removeClass(p+"-closed "+p+u+"-closed").addClass(p+"-open "+p+u+"-open");n.isSliding?
|
||||
f.addClass(p+"-sliding "+p+u+"-sliding"):f.removeClass(p+"-sliding "+p+u+"-sliding");da(0,f);h.resizable&&b.layout.plugins.draggable?f.draggable("enable").css("cursor",h.resizerCursor).attr("title",h.tips.Resize):n.isSliding||f.css("cursor","default");g&&(g.removeClass(l+"-closed "+l+u+"-closed").addClass(l+"-open "+l+u+"-open").attr("title",h.tips.Close),da(0,g),g.children(".content-closed").hide(),g.children(".content-open").css("display","block"));Va(a,!n.isSliding);b.extend(n,R(c));q.initialized&&
|
||||
(qa(),pa(a,!0));if(!d&&(q.initialized||h.triggerEventsOnLoad)&&c.is(":visible"))C("onopen_end",a),n.isShowing&&C("onshow_end",a),q.initialized&&C("onresize_end",a)},sb=function(a){function b(){g.isClosed?g.isMoving||ra(c,!0):wa(c,!0)}if(H()){var d=Ma(a),c=A.call(this,a),g=q[c];a=r[c].slideDelay_open;d&&d.stopImmediatePropagation();g.isClosed&&d&&"mouseenter"===d.type&&0<a?M.set(c+"_openSlider",b,a):b()}},Wa=function(a){function c(){g.isClosed?wa(f,!1):g.isMoving||ja(f)}if(H()){var m=Ma(a),f=A.call(this,
|
||||
a);a=r[f];var g=q[f],h=g.isMoving?1E3:300;!g.isClosed&&!g.isResizing&&("click"===a.slideTrigger_close?c():a.preventQuickSlideClose&&g.isMoving||a.preventPrematureSlideClose&&m&&b.layout.isMouseOverElem(m,y[f])||(m?M.set(f+"_closeSlider",c,d(a.slideDelay_close,h)):c()))}},Ga=function(a,b){var d=y[a],c=q[a],g=r[a],f=r.zIndexes;b?(va(a,{animation:!0,objectsOnly:!0}),d.css({zIndex:f.pane_animate}),"south"==a?d.css({top:v.inset.top+v.innerHeight-d.outerHeight()}):"east"==a&&d.css({left:v.inset.left+v.innerWidth-
|
||||
d.outerWidth()})):(za(),d.css({zIndex:c.isSliding?f.pane_sliding:f.pane_normal}),"south"==a?d.css({top:"auto"}):"east"==a&&!d.css("left").match(/\-99999/)&&d.css({left:"auto"}),G.msie&&(g.fxOpacityFix&&"slide"!=g.fxName_open&&d.css("filter")&&1==d.css("opacity"))&&d[0].style.removeAttribute("filter"))},ma=function(a,b){var d=r[a],c=F[a],g=d.slideTrigger_open.toLowerCase();if(c&&(!b||d.slidable)){g.match(/mouseover/)?g=d.slideTrigger_open="mouseenter":g.match(/(click|dblclick|mouseenter)/)||(g=d.slideTrigger_open=
|
||||
"click");if(d.resizerDblClickToggle&&g.match(/click/))c[b?"unbind":"bind"]("dblclick."+K,na);c[b?"bind":"unbind"](g+"."+K,sb).css("cursor",b?d.sliderCursor:"default").attr("title",b?d.tips.Slide:"")}},wa=function(a,b){function d(b){M.clear(a+"_closeSlider");b.stopPropagation()}var c=r[a],g=q[a],f=r.zIndexes,h=c.slideTrigger_close.toLowerCase(),j=b?"bind":"unbind",k=y[a],l=F[a];M.clear(a+"_closeSlider");b?(g.isSliding=!0,q.panesSliding[a]=!0,ma(a,!1)):(g.isSliding=!1,delete q.panesSliding[a]);k.css("zIndex",
|
||||
b?f.pane_sliding:f.pane_normal);l.css("zIndex",b?f.pane_sliding+2:f.resizer_normal);h.match(/(click|mouseleave)/)||(h=c.slideTrigger_close="mouseleave");l[j](h,Wa);"mouseleave"===h&&(k[j]("mouseleave."+K,Wa),l[j]("mouseenter."+K,d),k[j]("mouseenter."+K,d));b?"click"===h&&!c.resizable&&(l.css("cursor",b?c.sliderCursor:"default"),l.attr("title",b?c.tips.Close:"")):M.clear(a+"_closeSlider")},ha=function(a,d,c,f){d=r[a];var g=q[a],h=k[a],n=y[a],j=F[a],p="vert"===h.dir,l=!1;if("center"===a||p&&g.noVerticalRoom)(l=
|
||||
0<=g.maxHeight)&&g.noRoom?(rb(a),j&&j.show(),g.isVisible=!0,g.noRoom=!1,p&&(g.noVerticalRoom=!1),eb(a)):!l&&!g.noRoom&&(Ea(a),j&&j.hide(),g.isVisible=!1,g.noRoom=!0);"center"!==a&&(g.minSize<=g.maxSize?(g.size>g.maxSize?ka(a,g.maxSize,c,!0,f):g.size<g.minSize?ka(a,g.minSize,c,!0,f):j&&(g.isVisible&&n.is(":visible"))&&(c=g.size+v.inset[h.side],b.layout.cssNum(j,h.side)!=c&&j.css(h.side,c)),g.noRoom&&(g.wasOpen&&d.closable?d.autoReopen?ra(a,!1,!0,!0):g.noRoom=!1:Fa(a,g.wasOpen,!0,!0))):g.noRoom||(g.noRoom=
|
||||
!0,g.wasOpen=!g.isClosed&&!g.isSliding,g.isClosed||(d.closable?ja(a,!0,!0):Ta(a,!0))))},Ca=function(a,b,d,c,g){if(H()){a=A.call(this,a);var f=r[a],h=q[a];g=g||f.livePaneResizing&&!h.isResizing;h.autoResize=!1;ka(a,b,d,c,g)}},ka=function(e,c,f,h,g){function j(){for(var a="width"===ua?l.outerWidth():l.outerHeight(),a=[{pane:n,count:1,target:c,actual:a,correct:c===a,attempt:c,cssSize:D}],e=a[0],h={},t="Inaccurate size after resizing the "+n+"-pane.";!e.correct;){h={pane:n,count:e.count+1,target:c};h.attempt=
|
||||
e.actual>c?d(0,e.attempt-(e.actual-c)):d(0,e.attempt+(c-e.actual));h.cssSize=("horz"==k[n].dir?O:Q)(y[n],h.attempt);l.css(ua,h.cssSize);h.actual="width"==ua?l.outerWidth():l.outerHeight();h.correct=c===h.actual;1===a.length&&(ca(t,!1,!0),ca(e,!1,!0));ca(h,!1,!0);if(3<a.length)break;a.push(h);e=a[a.length-1]}J.size=c;b.extend(J,R(l));J.isVisible&&l.is(":visible")&&(x&&x.css(B,c+v.inset[B]),pa(n));!f&&(!Z&&q.initialized&&J.isVisible)&&C("onresize_end",n);f||(J.isSliding||ia("horz"==k[n].dir?"":"center",
|
||||
Z,g),qa());e=k.oppositeEdge[n];c<G&&q[e].noRoom&&(Y(e),ha(e,!1,f));1<a.length&&ca(t+"\nSee the Error Console for details.",!0,!0)}if(H()){var n=A.call(this,e),p=r[n],J=q[n],l=y[n],x=F[n],B=k[n].side,ua=k[n].sizeType.toLowerCase(),Z=J.isResizing&&!p.triggerEventsDuringLiveResize,z=!0!==h&&p.animatePaneSizing,G,D;u.queue(function(e){Y(n);G=J.size;c=fa(n,c);c=d(c,fa(n,p.minSize));c=a(c,J.maxSize);if(c<J.minSize)e(),ha(n,!1,f);else{if(!g&&c===G)return e();J.newSize=c;!f&&(q.initialized&&J.isVisible)&&
|
||||
C("onresize_start",n);D=("horz"==k[n].dir?O:Q)(y[n],c);if(z&&l.is(":visible")){var h=b.layout.effects.size[n]||b.layout.effects.size.all,h=p.fxSettings_size.easing||h.easing,v=r.zIndexes,u={};u[ua]=D+"px";J.isMoving=!0;l.css({zIndex:v.pane_animate}).show().animate(u,p.fxSpeed_size,h,function(){l.css({zIndex:J.isSliding?v.pane_sliding:v.pane_normal});J.isMoving=!1;delete J.newSize;j();e()})}else l.css(ua,D),delete J.newSize,l.is(":visible")?j():(J.size=c,b.extend(J,R(l))),e()}})}},ia=function(a,c,
|
||||
f){a=(a?a:"east,west,center").split(",");b.each(a,function(a,e){if(y[e]){var h=r[e],j=q[e],k=y[e],p=!0,l={},u=b.layout.showInvisibly(k),B={top:ga("north",!0),bottom:ga("south",!0),left:ga("west",!0),right:ga("east",!0),width:0,height:0};B.width=v.innerWidth-B.left-B.right;B.height=v.innerHeight-B.bottom-B.top;B.top+=v.inset.top;B.bottom+=v.inset.bottom;B.left+=v.inset.left;B.right+=v.inset.right;b.extend(j,R(k));if("center"===e){if(!f&&j.isVisible&&B.width===j.outerWidth&&B.height===j.outerHeight)return k.css(u),
|
||||
!0;b.extend(j,ya(e),{maxWidth:B.width,maxHeight:B.height});l=B;j.newWidth=l.width;j.newHeight=l.height;l.width=Q(k,l.width);l.height=O(k,l.height);p=0<=l.width&&0<=l.height;if(!q.initialized&&h.minWidth>B.width){var h=h.minWidth-j.outerWidth,B=r.east.minSize||0,x=r.west.minSize||0,Z=q.east.size,z=q.west.size,A=Z,D=z;0<h&&(q.east.isVisible&&Z>B)&&(A=d(Z-B,Z-h),h-=Z-A);0<h&&(q.west.isVisible&&z>x)&&(D=d(z-x,z-h),h-=z-D);if(0===h){Z&&Z!=B&&ka("east",A,!0,!0,f);z&&z!=x&&ka("west",D,!0,!0,f);ia("center",
|
||||
c,f);k.css(u);return}}}else{j.isVisible&&!j.noVerticalRoom&&b.extend(j,R(k),ya(e));if(!f&&!j.noVerticalRoom&&B.height===j.outerHeight)return k.css(u),!0;l.top=B.top;l.bottom=B.bottom;j.newSize=B.height;l.height=O(k,B.height);j.maxHeight=l.height;p=0<=j.maxHeight;p||(j.noVerticalRoom=!0)}p?(!c&&q.initialized&&C("onresize_start",e),k.css(l),"center"!==e&&qa(e),j.noRoom&&(!j.isClosed&&!j.isHidden)&&ha(e),j.isVisible&&(b.extend(j,R(k)),q.initialized&&pa(e))):!j.noRoom&&j.isVisible&&ha(e);k.css(u);delete j.newSize;
|
||||
delete j.newWidth;delete j.newHeight;if(!j.isVisible)return!0;"center"===e&&(j=G.isIE6||!G.boxModel,y.north&&(j||"IFRAME"==q.north.tagName)&&y.north.css("width",Q(y.north,v.innerWidth)),y.south&&(j||"IFRAME"==q.south.tagName)&&y.south.css("width",Q(y.south,v.innerWidth)));!c&&q.initialized&&C("onresize_end",e)}})},oa=function(a){A(a);if(u.is(":visible"))if(q.initialized){if(!0===a&&b.isPlainObject(r.outset)&&u.css(r.outset),b.extend(v,R(u,r.inset)),v.outerHeight){!0===a&&ob();if(!1===C("onresizeall_start"))return!1;
|
||||
var d,c,f;b.each(["south","north","east","west"],function(a,b){y[b]&&(c=r[b],f=q[b],f.autoResize&&f.size!=c.size?ka(b,c.size,!0,!0,!0):(Y(b),ha(b,!1,!0,!0)))});ia("",!0,!0);qa();b.each(k.allPanes,function(a,b){(d=y[b])&&q[b].isVisible&&C("onresize_end",b)});C("onresizeall_end")}}else Aa()},db=function(a,d){var c=A.call(this,a);r[c].resizeChildren&&(d||Ba(c),c=ba[c],b.isPlainObject(c)&&b.each(c,function(a,b){b.destroyed||b.resizeAll()}))},pa=function(a,c){if(H()){var h=A.call(this,a),h=h?h.split(","):
|
||||
k.allPanes;b.each(h,function(a,e){function h(a){return d(u.css.paddingBottom,parseInt(a.css("marginBottom"),10)||0)}function j(){var a=r[e].contentIgnoreSelector,a=p.nextAll().not(".ui-layout-mask").not(a||":lt(0)"),b=a.filter(":visible"),d=b.filter(":last");v={top:p[0].offsetTop,height:p.outerHeight(),numFooters:a.length,hiddenFooters:a.length-b.length,spaceBelow:0};v.spaceAbove=v.top;v.bottom=v.top+v.height;v.spaceBelow=d.length?d[0].offsetTop+d.outerHeight()-v.bottom+h(d):h(p)}var m=y[e],p=U[e],
|
||||
l=r[e],u=q[e],v=u.content;if(!m||!p||!m.is(":visible"))return!0;if(!p.length&&(Sa(e,!1),!p))return;if(!1!==C("onsizecontent_start",e)){if(!u.isMoving&&!u.isResizing||l.liveContentResizing||c||void 0==v.top)j(),0<v.hiddenFooters&&"hidden"===m.css("overflow")&&(m.css("overflow","visible"),j(),m.css("overflow","hidden"));m=u.innerHeight-(v.spaceAbove-u.css.paddingTop)-(v.spaceBelow-u.css.paddingBottom);if(!p.is(":visible")||v.height!=m){var x=p,l=x;f(x)?l=y[x]:x.jquery||(l=b(x));x=O(l,m);l.css({height:x,
|
||||
visibility:"visible"});0<x&&0<l.innerWidth()?l.data("autoHidden")&&(l.show().data("autoHidden",!1),G.mozilla||l.css(k.hidden).css(k.visible)):l.data("autoHidden")||l.hide().data("autoHidden",!0);v.height=m}q.initialized&&C("onsizecontent_end",e)}})}},qa=function(a){a=(a=A.call(this,a))?a.split(","):k.borderPanes;b.each(a,function(a,d){var e=r[d],g=q[d],h=y[d],j=F[d],p=P[d],u;if(h&&j){var l=k[d].dir,x=g.isClosed?"_closed":"_open",B=e["spacing"+x],z=e["togglerAlign"+x],x=e["togglerLength"+x],A;if(0===
|
||||
B)j.hide();else{!g.noRoom&&!g.isHidden&&j.show();"horz"===l?(A=v.innerWidth,g.resizerLength=A,h=b.layout.cssNum(h,"left"),j.css({width:Q(j,A),height:O(j,B),left:-9999<h?h:v.inset.left})):(A=h.outerHeight(),g.resizerLength=A,j.css({height:O(j,A),width:Q(j,B),top:v.inset.top+ga("north",!0)}));da(e,j);if(p){if(0===x||g.isSliding&&e.hideTogglerOnSlide){p.hide();return}p.show();if(!(0<x)||"100%"===x||x>A)x=A,z=0;else if(f(z))switch(z){case "top":case "left":z=0;break;case "bottom":case "right":z=A-x;break;
|
||||
default:z=c((A-x)/2)}else h=parseInt(z,10),z=0<=z?h:A-x+h;if("horz"===l){var D=Q(p,x);p.css({width:D,height:O(p,B),left:z,top:0});p.children(".content").each(function(){u=b(this);u.css("marginLeft",c((D-u.outerWidth())/2))})}else{var C=O(p,x);p.css({height:C,width:Q(p,B),top:z,left:0});p.children(".content").each(function(){u=b(this);u.css("marginTop",c((C-u.outerHeight())/2))})}da(0,p)}if(!q.initialized&&(e.initHidden||g.isHidden))j.hide(),p&&p.hide()}}})},pb=function(a){if(H()){var b=A.call(this,
|
||||
a);a=P[b];var d=r[b];a&&(d.closable=!0,a.bind("click."+K,function(a){a.stopPropagation();na(b)}).css("visibility","visible").css("cursor","pointer").attr("title",q[b].isClosed?d.tips.Open:d.tips.Close).show())}},Va=function(a,d){b.layout.plugins.buttons&&b.each(q[a].pins,function(c,f){b.layout.buttons.setPinState(z,b(f),a,d)})},u=b(this).eq(0);if(!u.length)return ca(r.errors.containerMissing);if(u.data("layoutContainer")&&u.data("layout"))return u.data("layout");var y={},U={},F={},P={},ea=b([]),v=
|
||||
q.container,K=q.id,z={options:r,state:q,container:u,panes:y,contents:U,resizers:F,togglers:P,hide:Ta,show:Fa,toggle:na,open:ra,close:ja,slideOpen:sb,slideClose:Wa,slideToggle:function(a){a=A.call(this,a);na(a,!0)},setSizeLimits:Y,_sizePane:ka,sizePane:Ca,sizeContent:pa,swapPanes:function(a,c){function f(a){var d=y[a],c=U[a];return!d?!1:{pane:a,P:d?d[0]:!1,C:c?c[0]:!1,state:b.extend(!0,{},q[a]),options:b.extend(!0,{},r[a])}}function h(a,c){if(a){var e=a.P,f=a.C,g=a.pane,j=k[c],m=b.extend(!0,{},q[c]),
|
||||
n=r[c],w={resizerCursor:n.resizerCursor};b.each(["fxName","fxSpeed","fxSettings"],function(a,b){w[b+"_open"]=n[b+"_open"];w[b+"_close"]=n[b+"_close"];w[b+"_size"]=n[b+"_size"]});y[c]=b(e).data({layoutPane:z[c],layoutEdge:c}).css(k.hidden).css(j.cssReq);U[c]=f?b(f):!1;r[c]=b.extend(!0,{},a.options,w);q[c]=b.extend(!0,{},a.state);e.className=e.className.replace(RegExp(n.paneClass+"-"+g,"g"),n.paneClass+"-"+c);Pa(c);j.dir!=k[g].dir?(e=p[c]||0,Y(c),e=d(e,q[c].minSize),Ca(c,e,!0,!0)):F[c].css(j.side,v.inset[j.side]+
|
||||
(q[c].isVisible?ga(c):0));a.state.isVisible&&!m.isVisible?Ua(c,!0):(Da(c),ma(c,!0));a=null}}if(H()){var g=A.call(this,a);q[g].edge=c;q[c].edge=g;if(!1===C("onswap_start",g)||!1===C("onswap_start",c))q[g].edge=g,q[c].edge=c;else{var j=f(g),n=f(c),p={};p[g]=j?j.state.size:0;p[c]=n?n.state.size:0;y[g]=!1;y[c]=!1;q[g]={};q[c]={};P[g]&&P[g].remove();P[c]&&P[c].remove();F[g]&&F[g].remove();F[c]&&F[c].remove();F[g]=F[c]=P[g]=P[c]=!1;h(j,c);h(n,g);j=n=p=null;y[g]&&y[g].css(k.visible);y[c]&&y[c].css(k.visible);
|
||||
oa();C("onswap_end",g);C("onswap_end",c)}}},showMasks:va,hideMasks:za,initContent:Sa,addPane:ib,removePane:Ra,createChildren:Qa,refreshChildren:Ba,enableClosable:pb,disableClosable:function(a,b){if(H()){var c=A.call(this,a),d=P[c];d&&(r[c].closable=!1,q[c].isClosed&&ra(c,!1,!0),d.unbind("."+K).css("visibility",b?"hidden":"visible").css("cursor","default").attr("title",""))}},enableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].slidable=!0,q[a].isClosed&&ma(a,
|
||||
!0))}},disableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&(r[a].slidable=!1,q[a].isSliding?ja(a,!1,!0):(ma(a,!1),b.css("cursor","default").attr("title",""),da(null,b[0])))}},enableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a],c=r[a];b&&b.data("draggable")&&(c.resizable=!0,b.draggable("enable"),q[a].isClosed||b.css("cursor",c.resizerCursor).attr("title",c.tips.Resize))}},disableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].resizable=
|
||||
!1,b.draggable("disable").css("cursor","default").attr("title",""),da(null,b[0]))}},allowOverflow:x,resetOverflow:X,destroy:function(a,c){b(window).unbind("."+K);b(document).unbind("."+K);"object"===typeof a?A(a):c=a;u.clearQueue().removeData("layout").removeData("layoutContainer").removeClass(r.containerClass).unbind("."+K);ea.remove();b.each(k.allPanes,function(a,b){Ra(b,!1,!0,c)});u.data("layoutCSS")&&!u.data("layoutRole")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");"BODY"===v.tagName&&
|
||||
(u=b("html")).data("layoutCSS")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");j(z,b.layout.onDestroy);mb();for(var d in z)d.match(/^(container|options)$/)||delete z[d];z.destroyed=!0;return z},initPanes:H,resizeAll:oa,runCallbacks:C,hasParentLayout:!1,children:ba,north:!1,south:!1,west:!1,east:!1,center:!1},Xa;var V,Ya,N,Ha,la,sa,W;h=b.layout.transformData(h,!0);h=b.layout.backwardCompatibility.renameAllOptions(h);if(!b.isEmptyObject(h.panes)){V=b.layout.optionsMap.noDefault;la=0;for(sa=V.length;la<
|
||||
sa;la++)N=V[la],delete h.panes[N];V=b.layout.optionsMap.layout;la=0;for(sa=V.length;la<sa;la++)N=V[la],delete h.panes[N]}V=b.layout.optionsMap.layout;var Bb=b.layout.config.optionRootKeys;for(N in h)Ha=h[N],0>b.inArray(N,Bb)&&0>b.inArray(N,V)&&(h.panes[N]||(h.panes[N]=b.isPlainObject(Ha)?b.extend(!0,{},Ha):Ha),delete h[N]);b.extend(!0,r,h);b.each(k.allPanes,function(a,c){k[c]=b.extend(!0,{},k.panes,k[c]);Ya=r.panes;W=r[c];if("center"===c){V=b.layout.optionsMap.center;a=0;for(sa=V.length;a<sa;a++)if(N=
|
||||
V[a],!h.center[N]&&(h.panes[N]||!W[N]))W[N]=Ya[N]}else{W=r[c]=b.extend(!0,{},Ya,W);var d=r[c],f=r.panes;d.fxSettings||(d.fxSettings={});f.fxSettings||(f.fxSettings={});b.each(["_open","_close","_size"],function(a,e){var h="fxName"+e,j="fxSpeed"+e,k="fxSettings"+e,l=d[h]=d[h]||f[h]||d.fxName||f.fxName||"none",p=b.effects&&(b.effects[l]||b.effects.effect&&b.effects.effect[l]);if("none"===l||!r.effects[l]||!p)l=d[h]="none";l=r.effects[l]||{};h=l.all||null;l=l[c]||null;d[j]=d[j]||f[j]||d.fxSpeed||f.fxSpeed||
|
||||
null;d[k]=b.extend(!0,{},h,l,f.fxSettings,d.fxSettings,f[k],d[k])});delete d.fxName;delete d.fxSpeed;delete d.fxSettings;W.resizerClass||(W.resizerClass="ui-layout-resizer");W.togglerClass||(W.togglerClass="ui-layout-toggler")}W.paneClass||(W.paneClass="ui-layout-pane")});var Ia=h.zIndex,xa=r.zIndexes;0<Ia&&(xa.pane_normal=Ia,xa.content_mask=d(Ia+1,xa.content_mask),xa.resizer_normal=d(Ia+2,xa.resizer_normal));delete r.panes;var Cb=r,tb=q;tb.creatingLayout=!0;j(z,b.layout.onCreate);if(!1===C("onload_start"))Xa=
|
||||
"cancel";else{var Za=u[0],$=b("html"),ub=v.tagName=Za.tagName,vb=v.id=Za.id,wb=v.className=Za.className,L=r,Ja=L.name,$a={},Ka=u.data("parentLayout"),La=u.data("layoutEdge"),ab=Ka&&La,ta=b.layout.cssNum,bb,aa;v.ref=(L.name?L.name+" layout / ":"")+ub+(vb?"#"+vb:wb?".["+wb+"]":"");v.isBody="BODY"===ub;!ab&&!v.isBody&&(bb=u.closest("."+b.layout.defaults.panes.paneClass),Ka=bb.data("parentLayout"),La=bb.data("layoutEdge"),ab=Ka&&La);u.data({layout:z,layoutContainer:K}).addClass(L.containerClass);
|
||||
var xb={destroy:"",initPanes:"",resizeAll:"resizeAll",resize:"resizeAll"};for(Ja in xb)u.bind("layout"+Ja.toLowerCase()+"."+K,z[xb[Ja]||Ja]);ab&&(z.hasParentLayout=!0,Ka.refreshChildren(La,z));u.data("layoutCSS")||(v.isBody?(u.data("layoutCSS",b.extend(D(u,"position,margin,padding,border"),{height:u.css("height"),overflow:u.css("overflow"),overflowX:u.css("overflowX"),overflowY:u.css("overflowY")})),$.data("layoutCSS",b.extend(D($,"padding"),{height:"auto",overflow:$.css("overflow"),overflowX:$.css("overflowX"),
|
||||
overflowY:$.css("overflowY")}))):u.data("layoutCSS",D(u,"position,margin,padding,border,top,bottom,left,right,width,height,overflow,overflowX,overflowY")));try{$a={overflow:"hidden",overflowX:"hidden",overflowY:"hidden"};u.css($a);L.inset&&!b.isPlainObject(L.inset)&&(aa=parseInt(L.inset,10)||0,L.inset={top:aa,bottom:aa,left:aa,right:aa});if(v.isBody)L.outset?b.isPlainObject(L.outset)||(aa=parseInt(L.outset,10)||0,L.outset={top:aa,bottom:aa,left:aa,right:aa}):L.outset={top:ta($,"paddingTop"),bottom:ta($,
|
||||
"paddingBottom"),left:ta($,"paddingLeft"),right:ta($,"paddingRight")},$.css($a).css({height:"100%",border:"none",padding:0,margin:0}),G.isIE6?(u.css({width:"100%",height:"100%",border:"none",padding:0,margin:0,position:"relative"}),L.inset||(L.inset=R(u).inset)):(u.css({width:"auto",height:"auto",margin:0,position:"absolute"}),u.css(L.outset)),b.extend(v,R(u,L.inset));else{var yb=u.css("position");(!yb||!yb.match(/(fixed|absolute|relative)/))&&u.css("position","relative");u.is(":visible")&&(b.extend(v,
|
||||
R(u,L.inset)),1>v.innerHeight&&ca(L.errors.noContainerHeight.replace(/CONTAINER/,v.ref)))}ta(u,"minWidth")&&u.parent().css("overflowX","auto");ta(u,"minHeight")&&u.parent().css("overflowY","auto")}catch(Db){}nb();b(window).bind("unload."+K,mb);j(z,b.layout.onLoad);Cb.initPanes&&Aa();delete tb.creatingLayout;Xa=q.initialized}return"cancel"===Xa?null:z}})(jQuery);
|
||||
(function(b){b.ui||(b.ui={});b.ui.cookie={acceptsCookies:!!navigator.cookieEnabled,read:function(a){for(var d=document.cookie,d=d?d.split(";"):[],c,f=0,j=d.length;f<j;f++)if(c=b.trim(d[f]).split("="),c[0]==a)return decodeURIComponent(c[1]);return null},write:function(a,d,c){var f="",j="",h=!1;c=c||{};var p=c.expires||null,x=b.type(p);"date"===x?j=p:"string"===x&&0<p&&(p=parseInt(p,10),x="number");"number"===x&&(j=new Date,0<p?j.setDate(j.getDate()+p):(j.setFullYear(1970),h=!0));j&&(f+=";expires="+
|
||||
j.toUTCString());c.path&&(f+=";path="+c.path);c.domain&&(f+=";domain="+c.domain);c.secure&&(f+=";secure");document.cookie=a+"="+(h?"":encodeURIComponent(d))+f},clear:function(a){b.ui.cookie.write(a,"",{expires:-1})}};b.cookie||(b.cookie=function(a,d,c){var f=b.ui.cookie;if(null===d)f.clear(a);else{if(void 0===d)return f.read(a);f.write(a,d,c)}});b.layout.plugins.stateManagement=!0;b.layout.config.optionRootKeys.push("stateManagement");b.layout.defaults.stateManagement={enabled:!1,autoSave:!0,autoLoad:!0,
|
||||
animateLoad:!0,includeChildren:!0,stateKeys:"north.size,south.size,east.size,west.size,north.isClosed,south.isClosed,east.isClosed,west.isClosed,north.isHidden,south.isHidden,east.isHidden,west.isHidden",cookie:{name:"",domain:"",path:"",expires:"",secure:!1}};b.layout.optionsMap.layout.push("stateManagement");b.layout.state={saveCookie:function(a,d,c){var f=a.options,j=f.stateManagement;c=b.extend(!0,{},j.cookie,c||null);a=a.state.stateData=a.readState(d||j.stateKeys);b.ui.cookie.write(c.name||f.name||
|
||||
"Layout",b.layout.state.encodeJSON(a),c);return b.extend(!0,{},a)},deleteCookie:function(a){a=a.options;b.ui.cookie.clear(a.stateManagement.cookie.name||a.name||"Layout")},readCookie:function(a){a=a.options;return(a=b.ui.cookie.read(a.stateManagement.cookie.name||a.name||"Layout"))?b.layout.state.decodeJSON(a):{}},loadCookie:function(a){var d=b.layout.state.readCookie(a);d&&(a.state.stateData=b.extend(!0,{},d),a.loadState(d));return d},loadState:function(a,d,c){if(b.isPlainObject(d)&&!b.isEmptyObject(d))if(d=
|
||||
a.state.stateData=b.layout.transformData(d),c=b.extend({animateLoad:!1,includeChildren:a.options.stateManagement.includeChildren},c),a.state.initialized){var f=!c.animateLoad,j,h,p,x;b.each(b.layout.config.borderPanes,function(c,G){S=d[G];b.isPlainObject(S)&&(s=S.size,j=S.initClosed,h=S.initHidden,ar=S.autoResize,p=a.state[G],x=p.isVisible,ar&&(p.autoResize=ar),x||a._sizePane(G,s,!1,!1,!1),!0===h?a.hide(G,f):!0===j?a.close(G,!1,f):!1===j?a.open(G,!1,f):!1===h&&a.show(G,!1,f),x&&a._sizePane(G,s,!1,
|
||||
!1,f))});if(c.includeChildren){var I,T;b.each(a.children,function(a,c){(I=d[a]?d[a].children:0)&&c&&b.each(c,function(a,b){T=I[a];b&&T&&b.loadState(T)})})}}else{var S=b.extend(!0,{},d);b.each(b.layout.config.allPanes,function(a,b){S[b]&&delete S[b].children});b.extend(!0,a.options,S)}},readState:function(a,d){"string"===b.type(d)&&(d={keys:d});d||(d={});var c=a.options.stateManagement,f=d.includeChildren,f=void 0!==f?f:c.includeChildren,c=d.stateKeys||c.stateKeys,j={isClosed:"initClosed",isHidden:"initHidden"},
|
||||
h=a.state,p=b.layout.config.allPanes,x={},I,T,S,X,G,k;b.isArray(c)&&(c=c.join(","));for(var c=c.replace(/__/g,".").split(","),Q=0,O=c.length;Q<O;Q++)I=c[Q].split("."),T=I[0],I=I[1],0>b.inArray(T,p)||(S=h[T][I],void 0!=S&&("isClosed"==I&&h[T].isSliding&&(S=!0),(x[T]||(x[T]={}))[j[I]?j[I]:I]=S));f&&b.each(p,function(c,d){G=a.children[d];X=h.stateData[d];b.isPlainObject(G)&&!b.isEmptyObject(G)&&(k=x[d]||(x[d]={}),k.children||(k.children={}),b.each(G,function(a,c){c.state.initialized?k.children[a]=b.layout.state.readState(c):
|
||||
X&&(X.children&&X.children[a])&&(k.children[a]=b.extend(!0,{},X.children[a]))}))});return x},encodeJSON:function(a){function d(a){var f=[],j=0,h,p,x,I=b.isArray(a);for(h in a)p=a[h],x=typeof p,"string"==x?p='"'+p+'"':"object"==x&&(p=d(p)),f[j++]=(!I?'"'+h+'":':"")+p;return(I?"[":"{")+f.join(",")+(I?"]":"}")}return d(a)},decodeJSON:function(a){try{return b.parseJSON?b.parseJSON(a):window.eval("("+a+")")||{}}catch(d){return{}}},_create:function(a){var d=b.layout.state,c=a.options.stateManagement;b.extend(a,
|
||||
{readCookie:function(){return d.readCookie(a)},deleteCookie:function(){d.deleteCookie(a)},saveCookie:function(b,c){return d.saveCookie(a,b,c)},loadCookie:function(){return d.loadCookie(a)},loadState:function(b,c){d.loadState(a,b,c)},readState:function(b){return d.readState(a,b)},encodeJSON:d.encodeJSON,decodeJSON:d.decodeJSON});a.state.stateData={};if(c.autoLoad)if(b.isPlainObject(c.autoLoad))b.isEmptyObject(c.autoLoad)||a.loadState(c.autoLoad);else if(c.enabled)if(b.isFunction(c.autoLoad)){var f=
|
||||
{};try{f=c.autoLoad(a,a.state,a.options,a.options.name||"")}catch(j){}f&&(b.isPlainObject(f)&&!b.isEmptyObject(f))&&a.loadState(f)}else a.loadCookie()},_unload:function(a){var d=a.options.stateManagement;if(d.enabled&&d.autoSave)if(b.isFunction(d.autoSave))try{d.autoSave(a,a.state,a.options,a.options.name||"")}catch(c){}else a.saveCookie()}};b.layout.onCreate.push(b.layout.state._create);b.layout.onUnload.push(b.layout.state._unload);b.layout.plugins.buttons=!0;b.layout.defaults.autoBindCustomButtons=
|
||||
!1;b.layout.optionsMap.layout.push("autoBindCustomButtons");b.layout.buttons={init:function(a){var d=a.options.name||"",c;b.each("toggle open close pin toggle-slide open-slide".split(" "),function(f,j){b.each(b.layout.config.borderPanes,function(f,p){b(".ui-layout-button-"+j+"-"+p).each(function(){c=b(this).data("layoutName")||b(this).attr("layoutName");(void 0==c||c===d)&&a.bindButton(this,j,p)})})})},get:function(a,d,c,f){var j=b(d);a=a.options;var h=a.errors.addButtonError;j.length?0>b.inArray(c,
|
||||
b.layout.config.borderPanes)?(b.layout.msg(h+" "+a.errors.pane+": "+c,!0),j=b("")):(d=a[c].buttonClass+"-"+f,j.addClass(d+" "+d+"-"+c).data("layoutName",a.name)):b.layout.msg(h+" "+a.errors.selector+": "+d,!0);return j},bind:function(a,d,c,f){var j=b.layout.buttons;switch(c.toLowerCase()){case "toggle":j.addToggle(a,d,f);break;case "open":j.addOpen(a,d,f);break;case "close":j.addClose(a,d,f);break;case "pin":j.addPin(a,d,f);break;case "toggle-slide":j.addToggle(a,d,f,!0);break;case "open-slide":j.addOpen(a,
|
||||
d,f,!0)}return a},addToggle:function(a,d,c,f){b.layout.buttons.get(a,d,c,"toggle").click(function(b){a.toggle(c,!!f);b.stopPropagation()});return a},addOpen:function(a,d,c,f){b.layout.buttons.get(a,d,c,"open").attr("title",a.options[c].tips.Open).click(function(b){a.open(c,!!f);b.stopPropagation()});return a},addClose:function(a,d,c){b.layout.buttons.get(a,d,c,"close").attr("title",a.options[c].tips.Close).click(function(b){a.close(c);b.stopPropagation()});return a},addPin:function(a,d,c){var f=b.layout.buttons,
|
||||
j=f.get(a,d,c,"pin");if(j.length){var h=a.state[c];j.click(function(d){f.setPinState(a,b(this),c,h.isSliding||h.isClosed);h.isSliding||h.isClosed?a.open(c):a.close(c);d.stopPropagation()});f.setPinState(a,j,c,!h.isClosed&&!h.isSliding);h.pins.push(d)}return a},setPinState:function(a,b,c,f){var j=b.attr("pin");if(!(j&&f===("down"==j))){a=a.options[c];var j=a.buttonClass+"-pin",h=j+"-"+c;c=j+"-up "+h+"-up";j=j+"-down "+h+"-down";b.attr("pin",f?"down":"up").attr("title",f?a.tips.Unpin:a.tips.Pin).removeClass(f?
|
||||
c:j).addClass(f?j:c)}},syncPinBtns:function(a,d,c){b.each(a.state[d].pins,function(f,j){b.layout.buttons.setPinState(a,b(j),d,c)})},_load:function(a){var d=b.layout.buttons;b.extend(a,{bindButton:function(b,c,h){return d.bind(a,b,c,h)},addToggleBtn:function(b,c,h){return d.addToggle(a,b,c,h)},addOpenBtn:function(b,c,h){return d.addOpen(a,b,c,h)},addCloseBtn:function(b,c){return d.addClose(a,b,c)},addPinBtn:function(b,c){return d.addPin(a,b,c)}});for(var c=0;4>c;c++)a.state[b.layout.config.borderPanes[c]].pins=
|
||||
[];a.options.autoBindCustomButtons&&d.init(a)},_unload:function(){}};b.layout.onLoad.push(b.layout.buttons._load);b.layout.plugins.browserZoom=!0;b.layout.defaults.browserZoomCheckInterval=1E3;b.layout.optionsMap.layout.push("browserZoomCheckInterval");b.layout.browserZoom={_init:function(a){!1!==b.layout.browserZoom.ratio()&&b.layout.browserZoom._setTimer(a)},_setTimer:function(a){if(!a.destroyed){var d=a.options,c=a.state,f=a.hasParentLayout?5E3:Math.max(d.browserZoomCheckInterval,100);setTimeout(function(){if(!a.destroyed&&
|
||||
d.resizeWithWindow){var f=b.layout.browserZoom.ratio();f!==c.browserZoom&&(c.browserZoom=f,a.resizeAll());b.layout.browserZoom._setTimer(a)}},f)}},ratio:function(){function a(a,b){return(100*(parseInt(a,10)/parseInt(b,10))).toFixed()}var d=window,c=screen,f=document,j=f.documentElement||f.body,h=b.layout.browser,p=h.version,x,I,T;return h.msie&&8<p||!h.msie?!1:c.deviceXDPI&&c.systemXDPI?a(c.deviceXDPI,c.systemXDPI):h.webkit&&(x=f.body.getBoundingClientRect)?a(x.left-x.right,f.body.offsetWidth):h.webkit&&
|
||||
(I=d.outerWidth)?a(I,d.innerWidth):(I=c.width)&&(T=j.clientWidth)?a(I,T):!1}};b.layout.onReady.push(b.layout.browserZoom._init)})(jQuery);
|
||||
@@ -14,7 +14,10 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException($err);
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
@@ -27,64 +27,6 @@ return array(
|
||||
'AsyncSendEmail' => $baseDir . '/core/asynctask.class.inc.php',
|
||||
'AsyncSendNewsroom' => $baseDir . '/core/asynctask.class.inc.php',
|
||||
'AsyncTask' => $baseDir . '/core/asynctask.class.inc.php',
|
||||
'AttributeApplicationLanguage' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeArchiveDate' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeArchiveFlag' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeBlob' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeBoolean' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeCaseLog' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeClass' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeClassAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeClassState' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeCustomFields' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDBField' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDBFieldVoid' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDashboard' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDate' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDateTime' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDeadline' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDecimal' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDefinition' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeDuration' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeEmailAddress' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeEncryptedString' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeEnum' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeEnumSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeExternalField' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeExternalKey' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeFinalClass' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeFriendlyName' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeHTML' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeHierarchicalKey' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeIPAddress' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeImage' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeInteger' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeLinkedSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeLinkedSetIndirect' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeLongText' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeMetaEnum' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeOQL' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeObjectKey' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeObsolescenceDate' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeObsolescenceFlag' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeOneWayPassword' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributePassword' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributePercentage' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributePhoneNumber' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributePropertySet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeQueryAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeRedundancySettings' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeStopWatch' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeString' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeSubItem' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeTable' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeTagSet' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateHTML' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateString' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateText' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeText' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AttributeURL' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'AuditCategory' => $baseDir . '/application/audit.category.class.inc.php',
|
||||
'AuditDomain' => $baseDir . '/application/audit.domain.class.inc.php',
|
||||
'AuditRule' => $baseDir . '/application/audit.rule.class.inc.php',
|
||||
@@ -405,6 +347,66 @@ return array(
|
||||
'Combodo\\iTop\\Controller\\TemporaryObjects\\TemporaryObjectController' => $baseDir . '/sources/Controller/TemporaryObjects/TemporaryObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\WelcomePopupController' => $baseDir . '/sources/Controller/WelcomePopupController.php',
|
||||
'Combodo\\iTop\\Controller\\iController' => $baseDir . '/sources/Controller/iController.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeApplicationLanguage' => $baseDir . '/sources/Core/AttributeDefinition/AttributeApplicationLanguage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeArchiveDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeArchiveDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeArchiveFlag' => $baseDir . '/sources/Core/AttributeDefinition/AttributeArchiveFlag.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeBlob' => $baseDir . '/sources/Core/AttributeDefinition/AttributeBlob.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeBoolean' => $baseDir . '/sources/Core/AttributeDefinition/AttributeBoolean.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeCaseLog' => $baseDir . '/sources/Core/AttributeDefinition/AttributeCaseLog.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClass' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClass.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClassAttCodeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClassState' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClassState.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeCustomFields' => $baseDir . '/sources/Core/AttributeDefinition/AttributeCustomFields.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDBField' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDBField.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDBFieldVoid' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDBFieldVoid.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDashboard' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDashboard.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDateTime' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDateTime.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDeadline' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDeadline.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDecimal' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDecimal.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDefinition' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDefinition.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDuration' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDuration.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEmailAddress' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEmailAddress.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEncryptedString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEncryptedString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEnum' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEnum.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEnumSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEnumSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeExternalField' => $baseDir . '/sources/Core/AttributeDefinition/AttributeExternalField.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeExternalKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeExternalKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeFinalClass' => $baseDir . '/sources/Core/AttributeDefinition/AttributeFinalClass.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeFriendlyName' => $baseDir . '/sources/Core/AttributeDefinition/AttributeFriendlyName.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeHTML' => $baseDir . '/sources/Core/AttributeDefinition/AttributeHTML.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeHierarchicalKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeHierarchicalKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeIPAddress' => $baseDir . '/sources/Core/AttributeDefinition/AttributeIPAddress.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeImage' => $baseDir . '/sources/Core/AttributeDefinition/AttributeImage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeInteger' => $baseDir . '/sources/Core/AttributeDefinition/AttributeInteger.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLinkedSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLinkedSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLinkedSetIndirect' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLongText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLongText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeMetaEnum' => $baseDir . '/sources/Core/AttributeDefinition/AttributeMetaEnum.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeOQL' => $baseDir . '/sources/Core/AttributeDefinition/AttributeOQL.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObjectKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObjectKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObsolescenceDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObsolescenceDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObsolescenceFlag' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeOneWayPassword' => $baseDir . '/sources/Core/AttributeDefinition/AttributeOneWayPassword.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePassword' => $baseDir . '/sources/Core/AttributeDefinition/AttributePassword.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePercentage' => $baseDir . '/sources/Core/AttributeDefinition/AttributePercentage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePhoneNumber' => $baseDir . '/sources/Core/AttributeDefinition/AttributePhoneNumber.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePropertySet' => $baseDir . '/sources/Core/AttributeDefinition/AttributePropertySet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeQueryAttCodeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeRedundancySettings' => $baseDir . '/sources/Core/AttributeDefinition/AttributeRedundancySettings.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeStopWatch' => $baseDir . '/sources/Core/AttributeDefinition/AttributeStopWatch.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeSubItem' => $baseDir . '/sources/Core/AttributeDefinition/AttributeSubItem.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTable' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTable.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTagSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTagSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateHTML' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateHTML.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeURL' => $baseDir . '/sources/Core/AttributeDefinition/AttributeURL.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\MissingColumnException' => $baseDir . '/sources/Core/AttributeDefinition/MissingColumnException.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\iAttributeNoGroupBy' => $baseDir . '/sources/Core/AttributeDefinition/iAttributeNoGroupBy.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => $baseDir . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAbstract' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAbstract.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAzure' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAzure.php',
|
||||
@@ -1116,7 +1118,6 @@ return array(
|
||||
'MenuGroup' => $baseDir . '/application/menunode.class.inc.php',
|
||||
'MenuNode' => $baseDir . '/application/menunode.class.inc.php',
|
||||
'MetaModel' => $baseDir . '/core/metamodel.class.php',
|
||||
'MissingColumnException' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'MissingQueryArgument' => $baseDir . '/core/oql/expression.class.inc.php',
|
||||
'ModelReflection' => $baseDir . '/core/modelreflection.class.inc.php',
|
||||
'ModelReflectionRuntime' => $baseDir . '/core/modelreflection.class.inc.php',
|
||||
@@ -3161,8 +3162,6 @@ return array(
|
||||
'Twig\\Source' => $vendorDir . '/twig/twig/src/Source.php',
|
||||
'Twig\\Template' => $vendorDir . '/twig/twig/src/Template.php',
|
||||
'Twig\\TemplateWrapper' => $vendorDir . '/twig/twig/src/TemplateWrapper.php',
|
||||
'Twig\\Test\\IntegrationTestCase' => $vendorDir . '/twig/twig/src/Test/IntegrationTestCase.php',
|
||||
'Twig\\Test\\NodeTestCase' => $vendorDir . '/twig/twig/src/Test/NodeTestCase.php',
|
||||
'Twig\\Token' => $vendorDir . '/twig/twig/src/Token.php',
|
||||
'Twig\\TokenParser\\AbstractTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
|
||||
'Twig\\TokenParser\\ApplyTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ApplyTokenParser.php',
|
||||
@@ -3232,7 +3231,6 @@ return array(
|
||||
'cmdbAbstractObject' => $baseDir . '/application/cmdbabstract.class.inc.php',
|
||||
'cmdbDataGenerator' => $baseDir . '/core/data.generator.class.inc.php',
|
||||
'iApplicationUIExtension' => $baseDir . '/application/applicationextension/backoffice/iApplicationUIExtension.php',
|
||||
'iAttributeNoGroupBy' => $baseDir . '/core/attributedef.class.inc.php',
|
||||
'iBackgroundProcess' => $baseDir . '/core/backgroundprocess.inc.php',
|
||||
'iBackofficeDictEntriesExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeDictEntriesExtension.php',
|
||||
'iBackofficeDictEntriesPrefixesExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeDictEntriesPrefixesExtension.php',
|
||||
|
||||
@@ -55,7 +55,7 @@ return array(
|
||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'Pelago\\Emogrifier\\' => array($vendorDir . '/pelago/emogrifier/src'),
|
||||
'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-google/src', $vendorDir . '/league/oauth2-client/src'),
|
||||
'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src', $vendorDir . '/league/oauth2-google/src'),
|
||||
'Laminas\\Validator\\' => array($vendorDir . '/laminas/laminas-validator/src'),
|
||||
'Laminas\\Stdlib\\' => array($vendorDir . '/laminas/laminas-stdlib/src'),
|
||||
'Laminas\\ServiceManager\\' => array($vendorDir . '/laminas/laminas-servicemanager/src'),
|
||||
|
||||
@@ -311,8 +311,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
),
|
||||
'League\\OAuth2\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/oauth2-google/src',
|
||||
1 => __DIR__ . '/..' . '/league/oauth2-client/src',
|
||||
0 => __DIR__ . '/..' . '/league/oauth2-client/src',
|
||||
1 => __DIR__ . '/..' . '/league/oauth2-google/src',
|
||||
),
|
||||
'Laminas\\Validator\\' =>
|
||||
array (
|
||||
@@ -399,64 +399,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'AsyncSendEmail' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
|
||||
'AsyncSendNewsroom' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
|
||||
'AsyncTask' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
|
||||
'AttributeApplicationLanguage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeArchiveDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeArchiveFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeBlob' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeBoolean' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeCaseLog' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeClassAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeClassState' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeCustomFields' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDBField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDBFieldVoid' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDashboard' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDateTime' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDeadline' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDecimal' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDefinition' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeDuration' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeEmailAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeEncryptedString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeEnumSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeExternalField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeExternalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeFinalClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeFriendlyName' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeHierarchicalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeIPAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeImage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeInteger' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeLinkedSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeLinkedSetIndirect' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeLongText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeMetaEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeOQL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeObjectKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeObsolescenceDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeObsolescenceFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeOneWayPassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributePassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributePercentage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributePhoneNumber' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributePropertySet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeQueryAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeRedundancySettings' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeStopWatch' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeSubItem' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeTable' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeTagSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeTemplateText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AttributeURL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'AuditCategory' => __DIR__ . '/../..' . '/application/audit.category.class.inc.php',
|
||||
'AuditDomain' => __DIR__ . '/../..' . '/application/audit.domain.class.inc.php',
|
||||
'AuditRule' => __DIR__ . '/../..' . '/application/audit.rule.class.inc.php',
|
||||
@@ -777,6 +719,66 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Controller\\TemporaryObjects\\TemporaryObjectController' => __DIR__ . '/../..' . '/sources/Controller/TemporaryObjects/TemporaryObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\WelcomePopupController' => __DIR__ . '/../..' . '/sources/Controller/WelcomePopupController.php',
|
||||
'Combodo\\iTop\\Controller\\iController' => __DIR__ . '/../..' . '/sources/Controller/iController.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeApplicationLanguage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeApplicationLanguage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeArchiveDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeArchiveDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeArchiveFlag' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeArchiveFlag.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeBlob' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeBlob.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeBoolean' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeBoolean.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeCaseLog' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeCaseLog.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClass' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClass.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClassAttCodeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeClassState' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClassState.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeCustomFields' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeCustomFields.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDBField' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDBField.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDBFieldVoid' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDBFieldVoid.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDashboard' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDashboard.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDateTime' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDateTime.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDeadline' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDeadline.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDecimal' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDecimal.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDefinition' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDefinition.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeDuration' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDuration.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEmailAddress' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEmailAddress.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEncryptedString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEncryptedString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEnum' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEnum.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeEnumSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEnumSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeExternalField' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeExternalField.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeExternalKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeExternalKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeFinalClass' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeFinalClass.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeFriendlyName' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeFriendlyName.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeHTML' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeHTML.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeHierarchicalKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeHierarchicalKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeIPAddress' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeIPAddress.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeImage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeImage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeInteger' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeInteger.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLinkedSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLinkedSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLinkedSetIndirect' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeLongText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLongText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeMetaEnum' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeMetaEnum.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeOQL' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeOQL.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObjectKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObjectKey.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObsolescenceDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObsolescenceDate.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeObsolescenceFlag' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeOneWayPassword' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeOneWayPassword.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePassword' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePassword.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePercentage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePercentage.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePhoneNumber' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePhoneNumber.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributePropertySet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePropertySet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeQueryAttCodeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeRedundancySettings' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeRedundancySettings.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeStopWatch' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeStopWatch.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeSubItem' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeSubItem.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTable' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTable.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTagSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTagSet.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateHTML' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateHTML.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateString.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeTemplateText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeText.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\AttributeURL' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeURL.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\MissingColumnException' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/MissingColumnException.php',
|
||||
'Combodo\\iTop\\Core\\AttributeDefinition\\iAttributeNoGroupBy' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/iAttributeNoGroupBy.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAbstract' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAbstract.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAzure' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAzure.php',
|
||||
@@ -1488,7 +1490,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'MenuGroup' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
|
||||
'MenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
|
||||
'MetaModel' => __DIR__ . '/../..' . '/core/metamodel.class.php',
|
||||
'MissingColumnException' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'MissingQueryArgument' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
|
||||
'ModelReflection' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
|
||||
'ModelReflectionRuntime' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
|
||||
@@ -3533,8 +3534,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Twig\\Source' => __DIR__ . '/..' . '/twig/twig/src/Source.php',
|
||||
'Twig\\Template' => __DIR__ . '/..' . '/twig/twig/src/Template.php',
|
||||
'Twig\\TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/src/TemplateWrapper.php',
|
||||
'Twig\\Test\\IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/IntegrationTestCase.php',
|
||||
'Twig\\Test\\NodeTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/NodeTestCase.php',
|
||||
'Twig\\Token' => __DIR__ . '/..' . '/twig/twig/src/Token.php',
|
||||
'Twig\\TokenParser\\AbstractTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
|
||||
'Twig\\TokenParser\\ApplyTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ApplyTokenParser.php',
|
||||
@@ -3604,7 +3603,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'cmdbAbstractObject' => __DIR__ . '/../..' . '/application/cmdbabstract.class.inc.php',
|
||||
'cmdbDataGenerator' => __DIR__ . '/../..' . '/core/data.generator.class.inc.php',
|
||||
'iApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iApplicationUIExtension.php',
|
||||
'iAttributeNoGroupBy' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
|
||||
'iBackgroundProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
|
||||
'iBackofficeDictEntriesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeDictEntriesExtension.php',
|
||||
'iBackofficeDictEntriesPrefixesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeDictEntriesPrefixesExtension.php',
|
||||
|
||||
@@ -36,7 +36,8 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
6
node_modules/.package-lock.json
generated
vendored
6
node_modules/.package-lock.json
generated
vendored
@@ -22,6 +22,12 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/custom-elements": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/custom-elements/-/custom-elements-1.3.0.tgz",
|
||||
"integrity": "sha512-f4q/s76+8nOy+fhrNHyetuoPDR01lmlZB5czfCG+OOnBw/Wf+x48DcCDPmMQY7oL8xYFL8qfenMoiS8DUkKBUw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ace-builds": {
|
||||
"version": "1.32.7",
|
||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.7.tgz",
|
||||
|
||||
15
node_modules/@ungap/custom-elements/LICENSE
generated
vendored
Normal file
15
node_modules/@ungap/custom-elements/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Andrea Giammarchi, @WebReflection
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
50
node_modules/@ungap/custom-elements/README.md
generated
vendored
Normal file
50
node_modules/@ungap/custom-elements/README.md
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Custom Elements Polyfill
|
||||
|
||||
This module provides the [Custom Elements V1 API](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-api) as defined by standards, including the ability to [extend builtin elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example), all in ~2K _minified_ and _gzipped_ / _brotlied_.
|
||||
|
||||
|
||||
## Compatibility
|
||||
|
||||
The polyfill gracefully enhances the following minimum versions of at least these browsers, up to their latest version:
|
||||
|
||||
* Chrome 38
|
||||
* Firefox 14
|
||||
* Opera 25
|
||||
* Internet Explorer 11 and Edge 12
|
||||
* Safari 8 and WebKit based
|
||||
* Samsung Internet 3
|
||||
|
||||
|
||||
## How To
|
||||
|
||||
Either install this module via `npm i @ungap/custom-elements`, and include it in your project, or use a CDN such as [unpkg.com](https://unpkg.com/@ungap/custom-elements) to obtain the _minified_ version of this module.
|
||||
|
||||
```html
|
||||
<!-- this should be on top of your HTML <head> scripts -->
|
||||
<script src="//unpkg.com/@ungap/custom-elements"></script>
|
||||
```
|
||||
|
||||
If targeted browsers are ES2015 compatible, the `es.js` file would provide the same polyfill, just lighter, as no transpilation is used.
|
||||
|
||||
```html
|
||||
<script src="//unpkg.com/@ungap/custom-elements/es.js"></script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
If installed as module, please remember to include it on top of your main JS file.
|
||||
|
||||
```js
|
||||
// ESM
|
||||
import '@ungap/custom-elements';
|
||||
|
||||
// CJS
|
||||
require('@ungap/custom-elements');
|
||||
```
|
||||
|
||||
The module will incrementally patch the global `window`/`self` reference, adding a `customElements` object that is compatible with the API.
|
||||
|
||||
|
||||
## Source Code
|
||||
|
||||
This module simply provides [@webreflection/custom-elements](https://github.com/WebReflection/custom-elements#readme) module under the [ungap](https://ungap.github.io/) umbrella.
|
||||
2
node_modules/@ungap/custom-elements/es.js
generated
vendored
Normal file
2
node_modules/@ungap/custom-elements/es.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
554
node_modules/@ungap/custom-elements/index.js
generated
vendored
Normal file
554
node_modules/@ungap/custom-elements/index.js
generated
vendored
Normal file
@@ -0,0 +1,554 @@
|
||||
/*! (c) Andrea Giammarchi @webreflection ISC */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var attributesObserver = (function (whenDefined, MutationObserver) {
|
||||
var attributeChanged = function attributeChanged(records) {
|
||||
for (var i = 0, length = records.length; i < length; i++) dispatch(records[i]);
|
||||
};
|
||||
var dispatch = function dispatch(_ref) {
|
||||
var target = _ref.target,
|
||||
attributeName = _ref.attributeName,
|
||||
oldValue = _ref.oldValue;
|
||||
target.attributeChangedCallback(attributeName, oldValue, target.getAttribute(attributeName));
|
||||
};
|
||||
return function (target, is) {
|
||||
var attributeFilter = target.constructor.observedAttributes;
|
||||
if (attributeFilter) {
|
||||
whenDefined(is).then(function () {
|
||||
new MutationObserver(attributeChanged).observe(target, {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
attributeFilter: attributeFilter
|
||||
});
|
||||
for (var i = 0, length = attributeFilter.length; i < length; i++) {
|
||||
if (target.hasAttribute(attributeFilter[i])) dispatch({
|
||||
target: target,
|
||||
attributeName: attributeFilter[i],
|
||||
oldValue: null
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return target;
|
||||
};
|
||||
});
|
||||
|
||||
function _unsupportedIterableToArray(o, minLen) {
|
||||
if (!o) return;
|
||||
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
||||
var n = Object.prototype.toString.call(o).slice(8, -1);
|
||||
if (n === "Object" && o.constructor) n = o.constructor.name;
|
||||
if (n === "Map" || n === "Set") return Array.from(o);
|
||||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
||||
}
|
||||
function _arrayLikeToArray(arr, len) {
|
||||
if (len == null || len > arr.length) len = arr.length;
|
||||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
||||
return arr2;
|
||||
}
|
||||
function _createForOfIteratorHelper(o, allowArrayLike) {
|
||||
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
|
||||
if (!it) {
|
||||
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
|
||||
if (it) o = it;
|
||||
var i = 0;
|
||||
var F = function () {};
|
||||
return {
|
||||
s: F,
|
||||
n: function () {
|
||||
if (i >= o.length) return {
|
||||
done: true
|
||||
};
|
||||
return {
|
||||
done: false,
|
||||
value: o[i++]
|
||||
};
|
||||
},
|
||||
e: function (e) {
|
||||
throw e;
|
||||
},
|
||||
f: F
|
||||
};
|
||||
}
|
||||
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
||||
}
|
||||
var normalCompletion = true,
|
||||
didErr = false,
|
||||
err;
|
||||
return {
|
||||
s: function () {
|
||||
it = it.call(o);
|
||||
},
|
||||
n: function () {
|
||||
var step = it.next();
|
||||
normalCompletion = step.done;
|
||||
return step;
|
||||
},
|
||||
e: function (e) {
|
||||
didErr = true;
|
||||
err = e;
|
||||
},
|
||||
f: function () {
|
||||
try {
|
||||
if (!normalCompletion && it.return != null) it.return();
|
||||
} finally {
|
||||
if (didErr) throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*! (c) Andrea Giammarchi - ISC */
|
||||
var TRUE = true,
|
||||
FALSE = false,
|
||||
QSA$1 = 'querySelectorAll';
|
||||
|
||||
/**
|
||||
* Start observing a generic document or root element.
|
||||
* @param {(node:Element, connected:boolean) => void} callback triggered per each dis/connected element
|
||||
* @param {Document|Element} [root=document] by default, the global document to observe
|
||||
* @param {Function} [MO=MutationObserver] by default, the global MutationObserver
|
||||
* @param {string[]} [query=['*']] the selectors to use within nodes
|
||||
* @returns {MutationObserver}
|
||||
*/
|
||||
var notify = function notify(callback) {
|
||||
var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;
|
||||
var MO = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : MutationObserver;
|
||||
var query = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ['*'];
|
||||
var loop = function loop(nodes, selectors, added, removed, connected, pass) {
|
||||
var _iterator = _createForOfIteratorHelper(nodes),
|
||||
_step;
|
||||
try {
|
||||
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
||||
var node = _step.value;
|
||||
if (pass || QSA$1 in node) {
|
||||
if (connected) {
|
||||
if (!added.has(node)) {
|
||||
added.add(node);
|
||||
removed["delete"](node);
|
||||
callback(node, connected);
|
||||
}
|
||||
} else if (!removed.has(node)) {
|
||||
removed.add(node);
|
||||
added["delete"](node);
|
||||
callback(node, connected);
|
||||
}
|
||||
if (!pass) loop(node[QSA$1](selectors), selectors, added, removed, connected, TRUE);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator.e(err);
|
||||
} finally {
|
||||
_iterator.f();
|
||||
}
|
||||
};
|
||||
var mo = new MO(function (records) {
|
||||
if (query.length) {
|
||||
var selectors = query.join(',');
|
||||
var added = new Set(),
|
||||
removed = new Set();
|
||||
var _iterator2 = _createForOfIteratorHelper(records),
|
||||
_step2;
|
||||
try {
|
||||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
||||
var _step2$value = _step2.value,
|
||||
addedNodes = _step2$value.addedNodes,
|
||||
removedNodes = _step2$value.removedNodes;
|
||||
loop(removedNodes, selectors, added, removed, FALSE, FALSE);
|
||||
loop(addedNodes, selectors, added, removed, TRUE, FALSE);
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator2.e(err);
|
||||
} finally {
|
||||
_iterator2.f();
|
||||
}
|
||||
}
|
||||
});
|
||||
var observe = mo.observe;
|
||||
(mo.observe = function (node) {
|
||||
return observe.call(mo, node, {
|
||||
subtree: TRUE,
|
||||
childList: TRUE
|
||||
});
|
||||
})(root);
|
||||
return mo;
|
||||
};
|
||||
|
||||
var QSA = 'querySelectorAll';
|
||||
var _self$1 = self,
|
||||
document$2 = _self$1.document,
|
||||
Element$1 = _self$1.Element,
|
||||
MutationObserver$2 = _self$1.MutationObserver,
|
||||
Set$2 = _self$1.Set,
|
||||
WeakMap$1 = _self$1.WeakMap;
|
||||
var elements = function elements(element) {
|
||||
return QSA in element;
|
||||
};
|
||||
var filter = [].filter;
|
||||
var qsaObserver = (function (options) {
|
||||
var live = new WeakMap$1();
|
||||
var drop = function drop(elements) {
|
||||
for (var i = 0, length = elements.length; i < length; i++) live["delete"](elements[i]);
|
||||
};
|
||||
var flush = function flush() {
|
||||
var records = observer.takeRecords();
|
||||
for (var i = 0, length = records.length; i < length; i++) {
|
||||
parse(filter.call(records[i].removedNodes, elements), false);
|
||||
parse(filter.call(records[i].addedNodes, elements), true);
|
||||
}
|
||||
};
|
||||
var matches = function matches(element) {
|
||||
return element.matches || element.webkitMatchesSelector || element.msMatchesSelector;
|
||||
};
|
||||
var notifier = function notifier(element, connected) {
|
||||
var selectors;
|
||||
if (connected) {
|
||||
for (var q, m = matches(element), i = 0, length = query.length; i < length; i++) {
|
||||
if (m.call(element, q = query[i])) {
|
||||
if (!live.has(element)) live.set(element, new Set$2());
|
||||
selectors = live.get(element);
|
||||
if (!selectors.has(q)) {
|
||||
selectors.add(q);
|
||||
options.handle(element, connected, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (live.has(element)) {
|
||||
selectors = live.get(element);
|
||||
live["delete"](element);
|
||||
selectors.forEach(function (q) {
|
||||
options.handle(element, connected, q);
|
||||
});
|
||||
}
|
||||
};
|
||||
var parse = function parse(elements) {
|
||||
var connected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
||||
for (var i = 0, length = elements.length; i < length; i++) notifier(elements[i], connected);
|
||||
};
|
||||
var query = options.query;
|
||||
var root = options.root || document$2;
|
||||
var observer = notify(notifier, root, MutationObserver$2, query);
|
||||
var attachShadow = Element$1.prototype.attachShadow;
|
||||
if (attachShadow) Element$1.prototype.attachShadow = function (init) {
|
||||
var shadowRoot = attachShadow.call(this, init);
|
||||
observer.observe(shadowRoot);
|
||||
return shadowRoot;
|
||||
};
|
||||
if (query.length) parse(root[QSA](query));
|
||||
return {
|
||||
drop: drop,
|
||||
flush: flush,
|
||||
observer: observer,
|
||||
parse: parse
|
||||
};
|
||||
});
|
||||
|
||||
var _self = self,
|
||||
document$1 = _self.document,
|
||||
Map = _self.Map,
|
||||
MutationObserver$1 = _self.MutationObserver,
|
||||
Object$1 = _self.Object,
|
||||
Set$1 = _self.Set,
|
||||
WeakMap = _self.WeakMap,
|
||||
Element = _self.Element,
|
||||
HTMLElement = _self.HTMLElement,
|
||||
Node = _self.Node,
|
||||
Error = _self.Error,
|
||||
TypeError$1 = _self.TypeError,
|
||||
Reflect = _self.Reflect;
|
||||
var defineProperty = Object$1.defineProperty,
|
||||
keys = Object$1.keys,
|
||||
getOwnPropertyNames = Object$1.getOwnPropertyNames,
|
||||
setPrototypeOf = Object$1.setPrototypeOf;
|
||||
var legacy = !self.customElements;
|
||||
var expando = function expando(element) {
|
||||
var key = keys(element);
|
||||
var value = [];
|
||||
var ignore = new Set$1();
|
||||
var length = key.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
value[i] = element[key[i]];
|
||||
try {
|
||||
delete element[key[i]];
|
||||
} catch (SafariTP) {
|
||||
ignore.add(i);
|
||||
}
|
||||
}
|
||||
return function () {
|
||||
for (var _i = 0; _i < length; _i++) ignore.has(_i) || (element[key[_i]] = value[_i]);
|
||||
};
|
||||
};
|
||||
if (legacy) {
|
||||
var HTMLBuiltIn = function HTMLBuiltIn() {
|
||||
var constructor = this.constructor;
|
||||
if (!classes.has(constructor)) throw new TypeError$1('Illegal constructor');
|
||||
var is = classes.get(constructor);
|
||||
if (override) return augment(override, is);
|
||||
var element = createElement.call(document$1, is);
|
||||
return augment(setPrototypeOf(element, constructor.prototype), is);
|
||||
};
|
||||
var createElement = document$1.createElement;
|
||||
var classes = new Map();
|
||||
var defined = new Map();
|
||||
var prototypes = new Map();
|
||||
var registry = new Map();
|
||||
var query = [];
|
||||
var handle = function handle(element, connected, selector) {
|
||||
var proto = prototypes.get(selector);
|
||||
if (connected && !proto.isPrototypeOf(element)) {
|
||||
var redefine = expando(element);
|
||||
override = setPrototypeOf(element, proto);
|
||||
try {
|
||||
new proto.constructor();
|
||||
} finally {
|
||||
override = null;
|
||||
redefine();
|
||||
}
|
||||
}
|
||||
var method = "".concat(connected ? '' : 'dis', "connectedCallback");
|
||||
if (method in proto) element[method]();
|
||||
};
|
||||
var _qsaObserver = qsaObserver({
|
||||
query: query,
|
||||
handle: handle
|
||||
}),
|
||||
parse = _qsaObserver.parse;
|
||||
var override = null;
|
||||
var whenDefined = function whenDefined(name) {
|
||||
if (!defined.has(name)) {
|
||||
var _,
|
||||
$ = new Promise(function ($) {
|
||||
_ = $;
|
||||
});
|
||||
defined.set(name, {
|
||||
$: $,
|
||||
_: _
|
||||
});
|
||||
}
|
||||
return defined.get(name).$;
|
||||
};
|
||||
var augment = attributesObserver(whenDefined, MutationObserver$1);
|
||||
self.customElements = {
|
||||
define: function define(is, Class) {
|
||||
if (registry.has(is)) throw new Error("the name \"".concat(is, "\" has already been used with this registry"));
|
||||
classes.set(Class, is);
|
||||
prototypes.set(is, Class.prototype);
|
||||
registry.set(is, Class);
|
||||
query.push(is);
|
||||
whenDefined(is).then(function () {
|
||||
parse(document$1.querySelectorAll(is));
|
||||
});
|
||||
defined.get(is)._(Class);
|
||||
},
|
||||
get: function get(is) {
|
||||
return registry.get(is);
|
||||
},
|
||||
whenDefined: whenDefined
|
||||
};
|
||||
defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {
|
||||
value: HTMLBuiltIn
|
||||
});
|
||||
self.HTMLElement = HTMLBuiltIn;
|
||||
document$1.createElement = function (name, options) {
|
||||
var is = options && options.is;
|
||||
var Class = is ? registry.get(is) : registry.get(name);
|
||||
return Class ? new Class() : createElement.call(document$1, name);
|
||||
};
|
||||
// in case ShadowDOM is used through a polyfill, to avoid issues
|
||||
// with builtin extends within shadow roots
|
||||
if (!('isConnected' in Node.prototype)) defineProperty(Node.prototype, 'isConnected', {
|
||||
configurable: true,
|
||||
get: function get() {
|
||||
return !(this.ownerDocument.compareDocumentPosition(this) & this.DOCUMENT_POSITION_DISCONNECTED);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
legacy = !self.customElements.get('extends-br');
|
||||
if (legacy) {
|
||||
try {
|
||||
var BR = function BR() {
|
||||
return self.Reflect.construct(HTMLBRElement, [], BR);
|
||||
};
|
||||
BR.prototype = HTMLLIElement.prototype;
|
||||
var is = 'extends-br';
|
||||
self.customElements.define('extends-br', BR, {
|
||||
'extends': 'br'
|
||||
});
|
||||
legacy = document$1.createElement('br', {
|
||||
is: is
|
||||
}).outerHTML.indexOf(is) < 0;
|
||||
var _self$customElements = self.customElements,
|
||||
get = _self$customElements.get,
|
||||
_whenDefined = _self$customElements.whenDefined;
|
||||
self.customElements.whenDefined = function (is) {
|
||||
var _this = this;
|
||||
return _whenDefined.call(this, is).then(function (Class) {
|
||||
return Class || get.call(_this, is);
|
||||
});
|
||||
};
|
||||
} catch (o_O) {}
|
||||
}
|
||||
}
|
||||
if (legacy) {
|
||||
var _parseShadow = function _parseShadow(element) {
|
||||
var root = shadowRoots.get(element);
|
||||
_parse(root.querySelectorAll(this), element.isConnected);
|
||||
};
|
||||
var customElements = self.customElements;
|
||||
var _createElement = document$1.createElement;
|
||||
var define = customElements.define,
|
||||
_get = customElements.get,
|
||||
upgrade = customElements.upgrade;
|
||||
var _ref = Reflect || {
|
||||
construct: function construct(HTMLElement) {
|
||||
return HTMLElement.call(this);
|
||||
}
|
||||
},
|
||||
construct = _ref.construct;
|
||||
var shadowRoots = new WeakMap();
|
||||
var shadows = new Set$1();
|
||||
var _classes = new Map();
|
||||
var _defined = new Map();
|
||||
var _prototypes = new Map();
|
||||
var _registry = new Map();
|
||||
var shadowed = [];
|
||||
var _query = [];
|
||||
var getCE = function getCE(is) {
|
||||
return _registry.get(is) || _get.call(customElements, is);
|
||||
};
|
||||
var _handle = function _handle(element, connected, selector) {
|
||||
var proto = _prototypes.get(selector);
|
||||
if (connected && !proto.isPrototypeOf(element)) {
|
||||
var redefine = expando(element);
|
||||
_override = setPrototypeOf(element, proto);
|
||||
try {
|
||||
new proto.constructor();
|
||||
} finally {
|
||||
_override = null;
|
||||
redefine();
|
||||
}
|
||||
}
|
||||
var method = "".concat(connected ? '' : 'dis', "connectedCallback");
|
||||
if (method in proto) element[method]();
|
||||
};
|
||||
var _qsaObserver2 = qsaObserver({
|
||||
query: _query,
|
||||
handle: _handle
|
||||
}),
|
||||
_parse = _qsaObserver2.parse;
|
||||
var _qsaObserver3 = qsaObserver({
|
||||
query: shadowed,
|
||||
handle: function handle(element, connected) {
|
||||
if (shadowRoots.has(element)) {
|
||||
if (connected) shadows.add(element);else shadows["delete"](element);
|
||||
if (_query.length) _parseShadow.call(_query, element);
|
||||
}
|
||||
}
|
||||
}),
|
||||
parseShadowed = _qsaObserver3.parse;
|
||||
// qsaObserver also patches attachShadow
|
||||
// be sure this runs *after* that
|
||||
var attachShadow = Element.prototype.attachShadow;
|
||||
if (attachShadow) Element.prototype.attachShadow = function (init) {
|
||||
var root = attachShadow.call(this, init);
|
||||
shadowRoots.set(this, root);
|
||||
return root;
|
||||
};
|
||||
var _whenDefined2 = function _whenDefined2(name) {
|
||||
if (!_defined.has(name)) {
|
||||
var _,
|
||||
$ = new Promise(function ($) {
|
||||
_ = $;
|
||||
});
|
||||
_defined.set(name, {
|
||||
$: $,
|
||||
_: _
|
||||
});
|
||||
}
|
||||
return _defined.get(name).$;
|
||||
};
|
||||
var _augment = attributesObserver(_whenDefined2, MutationObserver$1);
|
||||
var _override = null;
|
||||
getOwnPropertyNames(self).filter(function (k) {
|
||||
return /^HTML.*Element$/.test(k);
|
||||
}).forEach(function (k) {
|
||||
var HTMLElement = self[k];
|
||||
function HTMLBuiltIn() {
|
||||
var constructor = this.constructor;
|
||||
if (!_classes.has(constructor)) throw new TypeError$1('Illegal constructor');
|
||||
var _classes$get = _classes.get(constructor),
|
||||
is = _classes$get.is,
|
||||
tag = _classes$get.tag;
|
||||
if (is) {
|
||||
if (_override) return _augment(_override, is);
|
||||
var element = _createElement.call(document$1, tag);
|
||||
element.setAttribute('is', is);
|
||||
return _augment(setPrototypeOf(element, constructor.prototype), is);
|
||||
} else return construct.call(this, HTMLElement, [], constructor);
|
||||
}
|
||||
|
||||
defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {
|
||||
value: HTMLBuiltIn
|
||||
});
|
||||
defineProperty(self, k, {
|
||||
value: HTMLBuiltIn
|
||||
});
|
||||
});
|
||||
document$1.createElement = function (name, options) {
|
||||
var is = options && options.is;
|
||||
if (is) {
|
||||
var Class = _registry.get(is);
|
||||
if (Class && _classes.get(Class).tag === name) return new Class();
|
||||
}
|
||||
var element = _createElement.call(document$1, name);
|
||||
if (is) element.setAttribute('is', is);
|
||||
return element;
|
||||
};
|
||||
customElements.get = getCE;
|
||||
customElements.whenDefined = _whenDefined2;
|
||||
customElements.upgrade = function (element) {
|
||||
var is = element.getAttribute('is');
|
||||
if (is) {
|
||||
var _constructor = _registry.get(is);
|
||||
if (_constructor) {
|
||||
_augment(setPrototypeOf(element, _constructor.prototype), is);
|
||||
// apparently unnecessary because this is handled by qsa observer
|
||||
// if (element.isConnected && element.connectedCallback)
|
||||
// element.connectedCallback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
upgrade.call(customElements, element);
|
||||
};
|
||||
customElements.define = function (is, Class, options) {
|
||||
if (getCE(is)) throw new Error("'".concat(is, "' has already been defined as a custom element"));
|
||||
var selector;
|
||||
var tag = options && options["extends"];
|
||||
_classes.set(Class, tag ? {
|
||||
is: is,
|
||||
tag: tag
|
||||
} : {
|
||||
is: '',
|
||||
tag: is
|
||||
});
|
||||
if (tag) {
|
||||
selector = "".concat(tag, "[is=\"").concat(is, "\"]");
|
||||
_prototypes.set(selector, Class.prototype);
|
||||
_registry.set(is, Class);
|
||||
_query.push(selector);
|
||||
} else {
|
||||
define.apply(customElements, arguments);
|
||||
shadowed.push(selector = is);
|
||||
}
|
||||
_whenDefined2(is).then(function () {
|
||||
if (tag) {
|
||||
_parse(document$1.querySelectorAll(selector));
|
||||
shadows.forEach(_parseShadow, [selector]);
|
||||
} else parseShadowed(document$1.querySelectorAll(selector));
|
||||
});
|
||||
_defined.get(is)._(Class);
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
||||
3
node_modules/@ungap/custom-elements/min.js
generated
vendored
Normal file
3
node_modules/@ungap/custom-elements/min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
30
node_modules/@ungap/custom-elements/package.json
generated
vendored
Normal file
30
node_modules/@ungap/custom-elements/package.json
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@ungap/custom-elements",
|
||||
"version": "1.3.0",
|
||||
"description": "All inclusive customElements polyfill for every browser",
|
||||
"main": "index.js",
|
||||
"module": "index.js",
|
||||
"unpkg": "min.js",
|
||||
"scripts": {
|
||||
"build": "cp ./node_modules/@webreflection/custom-elements/*.js ./"
|
||||
},
|
||||
"keywords": [
|
||||
"custom",
|
||||
"elements",
|
||||
"polyfill",
|
||||
"customElements"
|
||||
],
|
||||
"author": "Andrea Giammarchi",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@webreflection/custom-elements": "^1.3.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ungap/custom-elements.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ungap/custom-elements/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ungap/custom-elements#readme"
|
||||
}
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"@cityssm/fa5-power-transforms-css": "github:cityssm/fa5-power-transforms-css",
|
||||
"@fontsource/raleway": "^4.5.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@ungap/custom-elements": "^1.3.0",
|
||||
"ace-builds": "^1.32.7",
|
||||
"blueimp-file-upload": "^10.32.0",
|
||||
"bulma-scss": "^0.9.4",
|
||||
@@ -56,6 +57,12 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/custom-elements": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/custom-elements/-/custom-elements-1.3.0.tgz",
|
||||
"integrity": "sha512-f4q/s76+8nOy+fhrNHyetuoPDR01lmlZB5czfCG+OOnBw/Wf+x48DcCDPmMQY7oL8xYFL8qfenMoiS8DUkKBUw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ace-builds": {
|
||||
"version": "1.32.7",
|
||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.7.tgz",
|
||||
@@ -320,6 +327,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
|
||||
},
|
||||
"@ungap/custom-elements": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/custom-elements/-/custom-elements-1.3.0.tgz",
|
||||
"integrity": "sha512-f4q/s76+8nOy+fhrNHyetuoPDR01lmlZB5czfCG+OOnBw/Wf+x48DcCDPmMQY7oL8xYFL8qfenMoiS8DUkKBUw=="
|
||||
},
|
||||
"ace-builds": {
|
||||
"version": "1.32.7",
|
||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.7.tgz",
|
||||
@@ -368,7 +380,7 @@
|
||||
},
|
||||
"ckeditor5-itop-build": {
|
||||
"version": "git+ssh://git@github.com/Combodo/ckeditor5-itop-build.git#303d2091805b12fce8807019f599b0a9601c9cfa",
|
||||
"from": "ckeditor5-itop-build@https://github.com/Combodo/ckeditor5-itop-build.git"
|
||||
"from": "ckeditor5-itop-build@github:Combodo/ckeditor5-itop-build"
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.11",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"@cityssm/fa5-power-transforms-css": "github:cityssm/fa5-power-transforms-css",
|
||||
"@fontsource/raleway": "^4.5.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@ungap/custom-elements": "^1.3.0",
|
||||
"ace-builds": "^1.32.7",
|
||||
"blueimp-file-upload": "^10.32.0",
|
||||
"bulma-scss": "^0.9.4",
|
||||
|
||||
@@ -617,7 +617,7 @@ EOF;
|
||||
}
|
||||
|
||||
if (is_null($iPort)) {
|
||||
$iPort = CMDBSource::MYSQL_DEFAULT_PORT;
|
||||
return '';
|
||||
}
|
||||
$sPortOption = self::GetMysqliCliSingleOption('port', $iPort);
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once(APPROOT.'/setup/parameters.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/setup/modulediscovery.class.inc.php');
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once(APPROOT.'setup/modulediscovery/ModuleFileReader.php');
|
||||
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Setup\ModuleDiscovery;
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use CoreException;
|
||||
use Exception;
|
||||
use ParseError;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\ParserFactory;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use \PhpParser\Node\Stmt\ElseIf_;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Arg;
|
||||
|
||||
require_once __DIR__ . '/ModuleFileReaderException.php';
|
||||
require_once APPROOT . 'sources/PhpParser/Evaluation/PhpExpressionEvaluator.php';
|
||||
@@ -47,16 +62,16 @@ class ModuleFileReader {
|
||||
{
|
||||
try
|
||||
{
|
||||
$oParser = (new \PhpParser\ParserFactory())->createForNewestSupportedVersion();
|
||||
$oParser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||
$aNodes = $oParser->parse(file_get_contents($sModuleFilePath));
|
||||
}
|
||||
catch (PhpParser\Error $e) {
|
||||
throw new \ModuleFileReaderException($e->getMessage(), 0, $e, $sModuleFilePath);
|
||||
catch (Error $e) {
|
||||
throw new ModuleFileReaderException($e->getMessage(), 0, $e, $sModuleFilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($aNodes as $sKey => $oNode) {
|
||||
if ($oNode instanceof \PhpParser\Node\Stmt\Expression) {
|
||||
if ($oNode instanceof Expression) {
|
||||
$aModuleInfo = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oNode);
|
||||
if (! is_null($aModuleInfo)){
|
||||
$this->CompleteModuleInfoWithFilePath($aModuleInfo);
|
||||
@@ -64,7 +79,7 @@ class ModuleFileReader {
|
||||
}
|
||||
}
|
||||
|
||||
if ($oNode instanceof PhpParser\Node\Stmt\If_) {
|
||||
if ($oNode instanceof If_) {
|
||||
$aModuleInfo = $this->GetModuleInformationFromIf($sModuleFilePath, $oNode);
|
||||
if (! is_null($aModuleInfo)){
|
||||
$this->CompleteModuleInfoWithFilePath($aModuleInfo);
|
||||
@@ -186,17 +201,17 @@ class ModuleFileReader {
|
||||
* @param \PhpParser\Node\Expr\Assign $oAssignation
|
||||
*
|
||||
* @return array|null
|
||||
* @throws \ModuleFileReaderException
|
||||
* @throws ModuleFileReaderException
|
||||
*/
|
||||
private function GetModuleInformationFromAddModuleCall(string $sModuleFilePath, \PhpParser\Node\Stmt\Expression $oExpression) : ?array
|
||||
{
|
||||
/** @var Assign $oAssignation */
|
||||
$oAssignation = $oExpression->expr;
|
||||
if (false === ($oAssignation instanceof PhpParser\Node\Expr\StaticCall)) {
|
||||
if (false === ($oAssignation instanceof StaticCall)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var PhpParser\Node\Expr\StaticCall $oAssignation */
|
||||
/** @var StaticCall $oAssignation */
|
||||
|
||||
if ("SetupWebPage" !== $oAssignation?->class?->name) {
|
||||
return null;
|
||||
@@ -212,24 +227,24 @@ class ModuleFileReader {
|
||||
}
|
||||
|
||||
$oModuleId = $aArgs[1];
|
||||
if (false === ($oModuleId instanceof PhpParser\Node\Arg)) {
|
||||
if (false === ($oModuleId instanceof Arg)) {
|
||||
throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleId), 0, null, $sModuleFilePath);
|
||||
}
|
||||
|
||||
/** @var PhpParser\Node\Arg $oModuleId */
|
||||
if (false === ($oModuleId->value instanceof PhpParser\Node\Scalar\String_)) {
|
||||
/** @var Arg $oModuleId */
|
||||
if (false === ($oModuleId->value instanceof String_)) {
|
||||
throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule not a string: " . get_class($oModuleId->value), 0, null, $sModuleFilePath);
|
||||
}
|
||||
|
||||
$sModuleId = $this->oPhpExpressionEvaluator->EvaluateExpression($oModuleId->value);
|
||||
|
||||
$oModuleConfigInfo = $aArgs[2];
|
||||
if (false === ($oModuleConfigInfo instanceof PhpParser\Node\Arg)) {
|
||||
if (false === ($oModuleConfigInfo instanceof Arg)) {
|
||||
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleConfigInfo), 0, null, $sModuleFilePath);
|
||||
}
|
||||
|
||||
/** @var PhpParser\Node\Arg $oModuleConfigInfo */
|
||||
if (false === ($oModuleConfigInfo->value instanceof PhpParser\Node\Expr\Array_)) {
|
||||
/** @var Arg $oModuleConfigInfo */
|
||||
if (false === ($oModuleConfigInfo->value instanceof Array_)) {
|
||||
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath);
|
||||
}
|
||||
|
||||
@@ -251,14 +266,14 @@ class ModuleFileReader {
|
||||
* @param \PhpParser\Node\Stmt\If_ $oNode
|
||||
*
|
||||
* @return array|null
|
||||
* @throws \ModuleFileReaderException
|
||||
* @throws ModuleFileReaderException
|
||||
*/
|
||||
private function GetModuleInformationFromIf(string $sModuleFilePath, \PhpParser\Node\Stmt\If_ $oNode) : ?array
|
||||
{
|
||||
$bCondition = $this->oPhpExpressionEvaluator->EvaluateExpression($oNode->cond);
|
||||
if ($bCondition) {
|
||||
foreach ($oNode->stmts as $oSubNode) {
|
||||
if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) {
|
||||
if ($oSubNode instanceof Expression) {
|
||||
$aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode);
|
||||
if (!is_null($aModuleConfig)) {
|
||||
return $aModuleConfig;
|
||||
@@ -271,7 +286,7 @@ class ModuleFileReader {
|
||||
|
||||
if (! is_null($oNode->elseifs)) {
|
||||
foreach ($oNode->elseifs as $oElseIfSubNode) {
|
||||
/** @var \PhpParser\Node\Stmt\ElseIf_ $oElseIfSubNode */
|
||||
/** @var ElseIf_ $oElseIfSubNode */
|
||||
$bCondition = $this->oPhpExpressionEvaluator->EvaluateExpression($oElseIfSubNode->cond);
|
||||
if ($bCondition) {
|
||||
return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oElseIfSubNode->stmts);
|
||||
@@ -289,7 +304,7 @@ class ModuleFileReader {
|
||||
private function GetModuleConfigurationFromStatement(string $sModuleFilePath, array $aStmts) : ?array
|
||||
{
|
||||
foreach ($aStmts as $oSubNode) {
|
||||
if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) {
|
||||
if ($oSubNode instanceof Expression) {
|
||||
$aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode);
|
||||
if (!is_null($aModuleConfig)) {
|
||||
return $aModuleConfig;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Setup\ModuleDiscovery;
|
||||
use Exception;
|
||||
use SetupLog;
|
||||
|
||||
class ModuleFileReaderException extends Exception
|
||||
{
|
||||
/**
|
||||
@@ -11,7 +15,7 @@ class ModuleFileReaderException extends Exception
|
||||
*/
|
||||
public function __construct($sMessage, $iHttpCode = 0, Exception $oPrevious = null, $sModuleFile = null)
|
||||
{
|
||||
$e = new \Exception("");
|
||||
$e = new Exception("");
|
||||
|
||||
$aContext = ['previous' => $oPrevious?->getMessage(), 'stack' => $e->getTraceAsString()];
|
||||
if (!is_null($sModuleFile)) {
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once APPROOT."setup/modulediscovery.class.inc.php";
|
||||
require_once APPROOT.'setup/modelfactory.class.inc.php';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
require_once(APPROOT.'setup/setuputils.class.inc.php');
|
||||
require_once(APPROOT.'setup/parameters.class.inc.php');
|
||||
|
||||
@@ -55,7 +55,7 @@ class UIBlockParser extends AbstractTokenParser
|
||||
|
||||
$sType = $oStream->expect(Token::NAME_TYPE)->getValue();
|
||||
|
||||
$oParams = $this->parser->getExpressionParser()->parseExpression();
|
||||
$oParams = $this->parser->parseExpression();
|
||||
|
||||
$oStream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ abstract class AbstractInput extends UIBlock
|
||||
/**@var string */
|
||||
protected $sPlaceholder;
|
||||
|
||||
public function GetName(): string
|
||||
public function GetName(): ?string
|
||||
{
|
||||
return $this->sName;
|
||||
}
|
||||
|
||||
@@ -70,8 +70,6 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
||||
];
|
||||
/** @inheritDoc */
|
||||
protected const COMPATIBILITY_DEPRECATED_LINKED_SCRIPTS_REL_PATH = [
|
||||
'js/date.js',
|
||||
'js/jquery.layout.min.js',
|
||||
];
|
||||
/** @inheritDoc */
|
||||
protected const COMPATIBILITY_MOVED_LINKED_STYLESHEETS_REL_PATH = [
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
use ValueSetEnum;
|
||||
|
||||
/**
|
||||
* An attibute that matches one of the language codes availables in the dictionnary
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeApplicationLanguage extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
}
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$aAvailableLanguages = Dict::GetLanguages();
|
||||
$aLanguageCodes = array();
|
||||
foreach ($aAvailableLanguages as $sLangCode => $aInfo) {
|
||||
$aLanguageCodes[$sLangCode] = $aInfo['description'].' ('.$aInfo['localized_description'].')';
|
||||
}
|
||||
|
||||
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
|
||||
// - Additional languages can be added on the fly even though it is not recommended
|
||||
// - Formatting is done at run time (just above)
|
||||
natcasesort($aLanguageCodes);
|
||||
|
||||
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
}
|
||||
44
sources/Core/AttributeDefinition/AttributeArchiveDate.php
Normal file
44
sources/Core/AttributeDefinition/AttributeArchiveDate.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
use Exception;
|
||||
|
||||
class AttributeArchiveDate extends AttributeDate
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeArchiveDate/Label', $sDefault);
|
||||
|
||||
return parent::GetLabel($sDefault);
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeArchiveDate/Label+', $sDefault);
|
||||
|
||||
return parent::GetDescription($sDefault);
|
||||
}
|
||||
}
|
||||
57
sources/Core/AttributeDefinition/AttributeArchiveFlag.php
Normal file
57
sources/Core/AttributeDefinition/AttributeArchiveFlag.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
|
||||
class AttributeArchiveFlag extends AttributeBoolean
|
||||
{
|
||||
public function __construct($sCode)
|
||||
{
|
||||
parent::__construct($sCode, array(
|
||||
"allowed_values" => null,
|
||||
"sql" => $sCode,
|
||||
"default_value" => false,
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
));
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function CopyOnAllTables()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsMagic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeArchiveFlag/Label', $sDefault);
|
||||
|
||||
return parent::GetLabel($sDefault);
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeArchiveFlag/Label+', $sDefault);
|
||||
|
||||
return parent::GetDescription($sDefault);
|
||||
}
|
||||
}
|
||||
386
sources/Core/AttributeDefinition/AttributeBlob.php
Normal file
386
sources/Core/AttributeDefinition/AttributeBlob.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeBlob;
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use ormDocument;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* A blob is an ormDocument, it is stored as several columns in the database
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeBlob extends AttributeDefinition
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("depends_on"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Document";
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return new ormDocument('', '', '');
|
||||
}
|
||||
|
||||
public function IsNullAllowed(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->GetOptional("is_null_allowed", false);
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see AttributeDefinition::MakeRealValue()
|
||||
*
|
||||
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
|
||||
*
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if ($proposedValue === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_object($proposedValue)) {
|
||||
$proposedValue = clone $proposedValue;
|
||||
} else {
|
||||
try {
|
||||
// Read the file from iTop, an URL (or the local file system - for admins only)
|
||||
$proposedValue = utils::FileGetContentsAndMIMEType($proposedValue);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Warning(get_class($this)."::MakeRealValue - ".$e->getMessage());
|
||||
// Not a real document !! store is as text !!! (This was the default behavior before)
|
||||
$proposedValue = new ormDocument($e->getMessage()." \n".$proposedValue, 'text/plain');
|
||||
}
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '') {
|
||||
$sPrefix = $this->GetCode();
|
||||
}
|
||||
$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.'_mimetype';
|
||||
$aColumns['_data'] = $sPrefix.'_data';
|
||||
$aColumns['_filename'] = $sPrefix.'_filename';
|
||||
$aColumns['_downloads_count'] = $sPrefix.'_downloads_count';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
if (!array_key_exists($sPrefix, $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
|
||||
}
|
||||
$sMimeType = isset($aCols[$sPrefix]) ? $aCols[$sPrefix] : '';
|
||||
|
||||
if (!array_key_exists($sPrefix.'_data', $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '".$sPrefix."_data' from {$sAvailable}");
|
||||
}
|
||||
$data = isset($aCols[$sPrefix.'_data']) ? $aCols[$sPrefix.'_data'] : null;
|
||||
|
||||
if (!array_key_exists($sPrefix.'_filename', $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '".$sPrefix."_filename' from {$sAvailable}");
|
||||
}
|
||||
$sFileName = isset($aCols[$sPrefix.'_filename']) ? $aCols[$sPrefix.'_filename'] : '';
|
||||
|
||||
if (!array_key_exists($sPrefix.'_downloads_count', $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '".$sPrefix."_downloads_count' from {$sAvailable}");
|
||||
}
|
||||
$iDownloadsCount = isset($aCols[$sPrefix.'_downloads_count']) ? $aCols[$sPrefix.'_downloads_count'] : ormDocument::DEFAULT_DOWNLOADS_COUNT;
|
||||
|
||||
$value = new ormDocument($data, $sMimeType, $sFileName, $iDownloadsCount);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
// #@# Optimization: do not load blobs anytime
|
||||
// As per mySQL doc, selecting blob columns will prevent mySQL from
|
||||
// using memory in case a temporary table has to be created
|
||||
// (temporary tables created on disk)
|
||||
// We will have to remove the blobs from the list of attributes when doing the select
|
||||
// then the use of Get() should finalize the load
|
||||
if ($value instanceof ormDocument) {
|
||||
$aValues = array();
|
||||
if (!$value->IsEmpty()) {
|
||||
$aValues[$this->GetCode().'_data'] = $value->GetData();
|
||||
} else {
|
||||
$aValues[$this->GetCode().'_data'] = '';
|
||||
}
|
||||
$aValues[$this->GetCode().'_mimetype'] = $value->GetMimeType();
|
||||
$aValues[$this->GetCode().'_filename'] = $value->GetFileName();
|
||||
$aValues[$this->GetCode().'_downloads_count'] = $value->GetDownloadsCount();
|
||||
} else {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode().'_data'] = '';
|
||||
$aValues[$this->GetCode().'_mimetype'] = '';
|
||||
$aValues[$this->GetCode().'_filename'] = '';
|
||||
$aValues[$this->GetCode().'_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode().'_data'] = 'LONGBLOB'; // 2^32 (4 Gb)
|
||||
$aColumns[$this->GetCode().'_mimetype'] = 'VARCHAR(255)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
$aColumns[$this->GetCode().'_filename'] = 'VARCHAR(255)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
$aColumns[$this->GetCode().'_downloads_count'] = 'INT(11) UNSIGNED';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return 'true';
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
return $value->GetAsHTML();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$sAttCode = $this->GetCode();
|
||||
if ($sValue instanceof ormDocument && !$sValue->IsEmpty()) {
|
||||
return $sValue->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $sAttCode);
|
||||
}
|
||||
|
||||
return ''; // Not exportable in CSV !
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sRet = '';
|
||||
if (is_object($value)) {
|
||||
/** @var ormDocument $value */
|
||||
if (!$value->IsEmpty()) {
|
||||
$sRet = '<mimetype>'.$value->GetMimeType().'</mimetype>';
|
||||
$sRet .= '<filename>'.$value->GetFileName().'</filename>';
|
||||
$sRet .= '<data>'.base64_encode($value->GetData()).'</data>';
|
||||
$sRet .= '<downloads_count>'.$value->GetDownloadsCount().'</downloads_count>';
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
if ($value instanceof ormDocument) {
|
||||
$aValues = array();
|
||||
$aValues['data'] = base64_encode($value->GetData());
|
||||
$aValues['mimetype'] = $value->GetMimeType();
|
||||
$aValues['filename'] = $value->GetFileName();
|
||||
$aValues['downloads_count'] = $value->GetDownloadsCount();
|
||||
} else {
|
||||
$aValues = null;
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
if (isset($json->data)) {
|
||||
$data = base64_decode($json->data);
|
||||
$value = new ormDocument($data, $json->mimetype, $json->filename, $json->downloads_count);
|
||||
} else {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function Fingerprint($value)
|
||||
{
|
||||
$sFingerprint = '';
|
||||
if ($value instanceof ormDocument) {
|
||||
$sFingerprint = $value->GetSignature();
|
||||
}
|
||||
|
||||
return $sFingerprint;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\BlobField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
/** @var $oFormField BlobField */
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
// Note: As of today we want this field to always be read-only
|
||||
$oFormField->SetReadOnly(true);
|
||||
|
||||
// Calling parent before so current value is set, then proceed
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// Setting current value correctly as the default method returns an empty string when there is no file yet.
|
||||
/** @var ormDocument $value */
|
||||
$value = $oObject->Get($this->GetCode());
|
||||
if (!is_object($value)) {
|
||||
$oFormField->SetCurrentValue(new ormDocument());
|
||||
}
|
||||
|
||||
// Generating urls
|
||||
if (is_object($value) && !$value->IsEmpty()) {
|
||||
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
|
||||
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
|
||||
}
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
if (false === ($proposedValue instanceof ormDocument)) {
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
// Empty file (no content, just a filename) are supported since PR {@link https://github.com/Combodo/combodo-email-synchro/pull/17}, so we check for both empty content and empty filename to determine that a document has no value
|
||||
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormDocument $original
|
||||
* @param ormDocument $value
|
||||
*
|
||||
* @since N°6502
|
||||
*/
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
// N°6502 Don't record history if only the download count has changed
|
||||
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::RecordAttChange($oObject, $original, $value);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
if (is_null($original)) {
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeBlob::class;
|
||||
}
|
||||
}
|
||||
253
sources/Core/AttributeDefinition/AttributeBoolean.php
Normal file
253
sources/Core/AttributeDefinition/AttributeBoolean.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOpSetAttributeScalar;
|
||||
use Combodo\iTop\Form\Field\SelectField;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
|
||||
/**
|
||||
* Map a boolean column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeBoolean extends AttributeInteger
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Integer";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "TINYINT(1)".($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
if ($proposedValue === '') {
|
||||
return null;
|
||||
}
|
||||
if ((int)$proposedValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if ($value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function GetValueLabel($bValue)
|
||||
{
|
||||
if (is_null($bValue)) {
|
||||
$sLabel = Dict::S('Core:'.get_class($this).'/Value:null');
|
||||
} else {
|
||||
$sValue = $bValue ? 'yes' : 'no';
|
||||
$sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue);
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, true /*user lang*/);
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetValueDescription($bValue)
|
||||
{
|
||||
if (is_null($bValue)) {
|
||||
$sDescription = Dict::S('Core:'.get_class($this).'/Value:null+');
|
||||
} else {
|
||||
$sValue = $bValue ? 'yes' : 'no';
|
||||
$sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue.'+');
|
||||
$sDescription = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue.'+', $sDefault,
|
||||
true /*user lang*/);
|
||||
}
|
||||
|
||||
return $sDescription;
|
||||
}
|
||||
|
||||
public function GetAsHTML($bValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_null($bValue)) {
|
||||
$sRes = '';
|
||||
} elseif ($bLocalize) {
|
||||
$sLabel = $this->GetValueLabel($bValue);
|
||||
$sDescription = $this->GetValueDescription($bValue);
|
||||
// later, we could imagine a detailed description in the title
|
||||
$sRes = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
|
||||
} else {
|
||||
$sRes = $bValue ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsXML($bValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_null($bValue)) {
|
||||
$sFinalValue = '';
|
||||
} elseif ($bLocalize) {
|
||||
$sFinalValue = $this->GetValueLabel($bValue);
|
||||
} else {
|
||||
$sFinalValue = $bValue ? 'yes' : 'no';
|
||||
}
|
||||
$sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$bValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if (is_null($bValue)) {
|
||||
$sFinalValue = '';
|
||||
} elseif ($bLocalize) {
|
||||
$sFinalValue = $this->GetValueLabel($bValue);
|
||||
} else {
|
||||
$sFinalValue = $bValue ? 'yes' : 'no';
|
||||
}
|
||||
$sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\SelectField';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param SelectField $oFormField
|
||||
*
|
||||
* @return SelectField
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
$oFormField->SetChoices(array('yes' => $this->GetValueLabel(true), 'no' => $this->GetValueLabel(false)));
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return '';
|
||||
} else {
|
||||
return $this->GetValueLabel($value);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
return (bool)$value;
|
||||
}
|
||||
|
||||
public function MakeValueFromString(
|
||||
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
|
||||
$sAttributeQualifier = null
|
||||
)
|
||||
{
|
||||
$sInput = mb_strtolower(trim($sProposedValue));
|
||||
if ($bLocalizedValue) {
|
||||
switch ($sInput) {
|
||||
case '1': // backward compatibility
|
||||
case $this->GetValueLabel(true):
|
||||
$value = true;
|
||||
break;
|
||||
case '0': // backward compatibility
|
||||
case 'no':
|
||||
case $this->GetValueLabel(false):
|
||||
$value = false;
|
||||
break;
|
||||
default:
|
||||
$value = null;
|
||||
}
|
||||
} else {
|
||||
switch ($sInput) {
|
||||
case '1': // backward compatibility
|
||||
case 'yes':
|
||||
$value = true;
|
||||
break;
|
||||
case '0': // backward compatibility
|
||||
case 'no':
|
||||
$value = false;
|
||||
break;
|
||||
default:
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
parent::RecordAttChange($oObject, $original ? 1 : 0, $value ? 1 : 0);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeScalar::class;
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = ''): array
|
||||
{
|
||||
return [
|
||||
0 => $this->GetValueLabel(false),
|
||||
1 => $this->GetValueLabel(true),
|
||||
];
|
||||
}
|
||||
|
||||
public function GetDisplayStyle()
|
||||
{
|
||||
return $this->GetOptional('display_style', 'select');
|
||||
}
|
||||
}
|
||||
413
sources/Core/AttributeDefinition/AttributeCaseLog.php
Normal file
413
sources/Core/AttributeDefinition/AttributeCaseLog.php
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeCaseLog;
|
||||
use CMDBSource;
|
||||
use CoreWarning;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use ormCaseLog;
|
||||
use stdClass;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* An attibute that stores a case log (i.e journal)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeCaseLog extends AttributeLongText
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
if (!($proposedValue instanceof ormCaseLog)) {
|
||||
return ($proposedValue == '');
|
||||
}
|
||||
|
||||
return ($proposedValue->GetText() == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormCaseLog $proposedValue
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Protection against wrong value type
|
||||
if (false === ($proposedValue instanceof ormCaseLog)) {
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
// We test if there is at least 1 entry in the log, not if the user is adding one
|
||||
return $proposedValue->GetEntryCount() > 0;
|
||||
}
|
||||
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (!is_string($value) && !is_null($value)) {
|
||||
throw new CoreWarning('Expected the attribute value to be a string', array(
|
||||
'found_type' => gettype($value),
|
||||
'value' => $value,
|
||||
'class' => $this->GetCode(),
|
||||
'attribute' => $this->GetHostClass(),
|
||||
));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "CaseLog";
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
if (!($sValue instanceof ormCaseLog)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $sValue->GetModifiedEntry();
|
||||
}
|
||||
|
||||
/**
|
||||
* For fields containing a potential markup, return the value without this markup
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param \DBObject $oHostObj
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText($value, $oHostObj = null)
|
||||
{
|
||||
if ($value instanceof ormCaseLog) {
|
||||
/** ormCaseLog $value */
|
||||
return $value->GetAsPlainText();
|
||||
} else {
|
||||
return (string)$value;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return new ormCaseLog();
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
return ($val1->GetText() == $val2->GetText());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Facilitate things: allow the user to Set the value from a string
|
||||
*
|
||||
* @param $proposedValue
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @return mixed|null|ormCaseLog|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if ($proposedValue instanceof ormCaseLog) {
|
||||
// Passthrough
|
||||
$ret = clone $proposedValue;
|
||||
} else {
|
||||
// Append the new value if an instance of the object is supplied
|
||||
//
|
||||
$oPreviousLog = null;
|
||||
if ($oHostObj != null) {
|
||||
$oPreviousLog = $oHostObj->Get($this->GetCode());
|
||||
if (!is_object($oPreviousLog)) {
|
||||
$oPreviousLog = $oHostObj->GetOriginal($this->GetCode());;
|
||||
}
|
||||
|
||||
}
|
||||
if (is_object($oPreviousLog)) {
|
||||
$oCaseLog = clone($oPreviousLog);
|
||||
} else {
|
||||
$oCaseLog = new ormCaseLog();
|
||||
}
|
||||
|
||||
if ($proposedValue instanceof stdClass) {
|
||||
$oCaseLog->AddLogEntryFromJSON($proposedValue);
|
||||
} else {
|
||||
if (utils::StrLen($proposedValue) > 0) {
|
||||
//N°5135 - add impersonation information in caselog
|
||||
if (UserRights::IsImpersonated()) {
|
||||
$sOnBehalfOf = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName());
|
||||
$oCaseLog->AddLogEntry($proposedValue, $sOnBehalfOf, UserRights::GetConnectedUserId());
|
||||
} else {
|
||||
$oCaseLog->AddLogEntry($proposedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
$ret = $oCaseLog;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
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;
|
||||
$aColumns['_index'] = $sPrefix.'_index';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aCols
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @return ormCaseLog
|
||||
* @throws MissingColumnException
|
||||
*/
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
if (!array_key_exists($sPrefix, $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
|
||||
}
|
||||
$sLog = $aCols[$sPrefix];
|
||||
|
||||
if (isset($aCols[$sPrefix.'_index'])) {
|
||||
$sIndex = $aCols[$sPrefix.'_index'];
|
||||
} else {
|
||||
// For backward compatibility, allow the current state to be: 1 log, no index
|
||||
$sIndex = '';
|
||||
}
|
||||
|
||||
if (strlen($sIndex) > 0) {
|
||||
$aIndex = unserialize($sIndex);
|
||||
$value = new ormCaseLog($sLog, $aIndex);
|
||||
} else {
|
||||
$value = new ormCaseLog($sLog);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
if (!($value instanceof ormCaseLog)) {
|
||||
$value = new ormCaseLog('');
|
||||
}
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode()] = $value->GetText();
|
||||
$aValues[$this->GetCode().'_index'] = serialize($value->GetIndex());
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'LONGTEXT' // 2^32 (4 Gb)
|
||||
.CMDBSource::GetSqlStringColumnDefinition();
|
||||
$aColumns[$this->GetCode().'_index'] = 'BLOB';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceof ormCaseLog) {
|
||||
$sContent = $value->GetAsHTML(null, false, array(__class__, 'RenderWikiHtml'));
|
||||
} else {
|
||||
$sContent = '';
|
||||
}
|
||||
$aStyles = array();
|
||||
if ($this->GetWidth() != '') {
|
||||
$aStyles[] = 'width:'.$this->GetWidth();
|
||||
}
|
||||
if ($this->GetHeight() != '') {
|
||||
$aStyles[] = 'height:'.$this->GetHeight();
|
||||
}
|
||||
$sStyle = '';
|
||||
if (count($aStyles) > 0) {
|
||||
$sStyle = 'style="'.implode(';', $aStyles).'"';
|
||||
}
|
||||
|
||||
return "<div class=\"caselog\" $sStyle>".$sContent.'</div>';
|
||||
}
|
||||
|
||||
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if ($value instanceof ormCaseLog) {
|
||||
return parent::GetAsCSV($value->GetText($bConvertToPlainText), $sSeparator, $sTextQualifier, $oHostObject,
|
||||
$bLocalize, $bConvertToPlainText);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceof ormCaseLog) {
|
||||
return parent::GetAsXML($value->GetText(), $oHostObject, $bLocalize);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available verbs for 'GetForTemplate'
|
||||
*/
|
||||
public function EnumTemplateVerbs()
|
||||
{
|
||||
return array(
|
||||
'' => 'Plain text representation of all the log entries',
|
||||
'head' => 'Plain text representation of the latest entry',
|
||||
'head_html' => 'HTML representation of the latest entry',
|
||||
'html' => 'HTML representation of all the log entries',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
switch ($sVerb) {
|
||||
case '':
|
||||
return $value->GetText(true);
|
||||
|
||||
case 'head':
|
||||
return $value->GetLatestEntry('text');
|
||||
|
||||
case 'head_html':
|
||||
return $value->GetLatestEntry('html');
|
||||
|
||||
case 'html':
|
||||
return $value->GetAsEmailHtml();
|
||||
|
||||
default:
|
||||
throw new \Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
|
||||
}
|
||||
}
|
||||
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
return $value->GetForJSON();
|
||||
}
|
||||
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
if (is_string($json)) {
|
||||
// Will be correctly handled in MakeRealValue
|
||||
$ret = $json;
|
||||
} else {
|
||||
if (isset($json->add_item)) {
|
||||
// Will be correctly handled in MakeRealValue
|
||||
$ret = $json->add_item;
|
||||
if (!isset($ret->message)) {
|
||||
throw new Exception("Missing mandatory entry: 'message'");
|
||||
}
|
||||
} else {
|
||||
$ret = ormCaseLog::FromJSON($json);
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function Fingerprint($value)
|
||||
{
|
||||
$sFingerprint = '';
|
||||
if ($value instanceof ormCaseLog) {
|
||||
$sFingerprint = $value->GetText();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\CaseLogField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
// First we call the parent so the field is build
|
||||
$oFormField = parent::MakeFormField($oObject, $oFormField);
|
||||
// Then only we set the value
|
||||
$oFormField->SetCurrentValue($this->GetEditValue($oObject->Get($this->GetCode())));
|
||||
// And we set the entries
|
||||
$oFormField->SetEntries($oObject->Get($this->GetCode())->GetAsArray());
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
/** @var ormCaseLog $value */
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeCaseLog::class;
|
||||
}
|
||||
}
|
||||
94
sources/Core/AttributeDefinition/AttributeClass.php
Normal file
94
sources/Core/AttributeDefinition/AttributeClass.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use MetaModel;
|
||||
use ValueSetEnumClasses;
|
||||
|
||||
/**
|
||||
* An attribute that matches an object class
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeClass extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('class_category', 'more_values'));
|
||||
}
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$aParams["allowed_values"] = new ValueSetEnumClasses($aParams['class_category'], $aParams['more_values']);
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$sDefault = parent::GetDefaultValue($oHostObject);
|
||||
if (!$this->IsNullAllowed() && $this->IsNull($sDefault)) {
|
||||
// For this kind of attribute specifying null as default value
|
||||
// is authorized even if null is not allowed
|
||||
|
||||
// Pick the first one...
|
||||
$aClasses = $this->GetAllowedValues();
|
||||
$sDefault = key($aClasses);
|
||||
}
|
||||
|
||||
return $sDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aArgs
|
||||
* @param string $sContains
|
||||
*
|
||||
* @return array|null
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
if (!$oValSetDef) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$aListClass = $oValSetDef->GetValues($aArgs, $sContains);
|
||||
/* @since 3.3.0 remove elements in class_exclusion_list */
|
||||
$sClassExclusionList = $this->GetOptional('class_exclusion_list', null);
|
||||
if (!empty($sClassExclusionList)) {
|
||||
foreach (explode(',', $sClassExclusionList) as $sClassName) {
|
||||
unset($aListClass[trim($sClassName)]);
|
||||
}
|
||||
}
|
||||
|
||||
return $aListClass;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return MetaModel::GetName($sValue);
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
}
|
||||
229
sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php
Normal file
229
sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CoreUnexpectedValue;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use ormSet;
|
||||
use utils;
|
||||
|
||||
class AttributeClassAttCodeSet extends AttributeSet
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
const DEFAULT_PARAM_INCLUDE_CHILD_CLASSES_ATTRIBUTES = false;
|
||||
|
||||
public function __construct($sCode, array $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-class-attcode-set';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('class_field', 'attribute_definition_list', 'attribute_definition_exclusion_list'));
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return max(255, 15 * $this->GetMaxItems());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aArgs
|
||||
* @param string $sContains
|
||||
*
|
||||
* @return array|null
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
if (!isset($aArgs['this'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$oHostObj = $aArgs['this'];
|
||||
$sTargetClass = $this->Get('class_field');
|
||||
$sRootClass = $oHostObj->Get($sTargetClass);
|
||||
$bIncludeChildClasses = $this->GetOptional('include_child_classes_attributes', static::DEFAULT_PARAM_INCLUDE_CHILD_CLASSES_ATTRIBUTES);
|
||||
|
||||
$aExcludeDefs = array();
|
||||
$sAttDefExclusionList = $this->Get('attribute_definition_exclusion_list');
|
||||
if (!empty($sAttDefExclusionList)) {
|
||||
foreach (explode(',', $sAttDefExclusionList) as $sAttDefName) {
|
||||
$sAttDefName = trim($sAttDefName);
|
||||
$aExcludeDefs[$sAttDefName] = $sAttDefName;
|
||||
}
|
||||
}
|
||||
|
||||
$aAllowedDefs = array();
|
||||
$sAttDefList = $this->Get('attribute_definition_list');
|
||||
if (!empty($sAttDefList)) {
|
||||
foreach (explode(',', $sAttDefList) as $sAttDefName) {
|
||||
$sAttDefName = trim($sAttDefName);
|
||||
$aAllowedDefs[$sAttDefName] = $sAttDefName;
|
||||
}
|
||||
}
|
||||
|
||||
$aAllAttributes = array();
|
||||
if (!empty($sRootClass)) {
|
||||
$aClasses = array($sRootClass);
|
||||
if ($bIncludeChildClasses === true) {
|
||||
$aClasses = $aClasses + MetaModel::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP);
|
||||
}
|
||||
|
||||
foreach ($aClasses as $sClass) {
|
||||
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
|
||||
// Add attribute only if not already there (can be in leaf classes but not the root)
|
||||
if (!array_key_exists($sAttCode, $aAllAttributes)) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
|
||||
// Skip excluded attdefs
|
||||
if (isset($aExcludeDefs[$sAttDefClass])) {
|
||||
continue;
|
||||
}
|
||||
// Skip not allowed attdefs only if list specified
|
||||
if (!empty($aAllowedDefs) && !isset($aAllowedDefs[$sAttDefClass])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aAllAttributes[$sAttCode] = array(
|
||||
'classes' => array($sClass),
|
||||
);
|
||||
} else {
|
||||
$aAllAttributes[$sAttCode]['classes'][] = $sClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aAllowedAttributes = array();
|
||||
foreach ($aAllAttributes as $sAttCode => $aAttData) {
|
||||
$iAttClassesCount = count($aAttData['classes']);
|
||||
$sAttFirstClass = $aAttData['classes'][0];
|
||||
$sAttLabel = MetaModel::GetLabel($sAttFirstClass, $sAttCode);
|
||||
|
||||
if ($sAttFirstClass === $sRootClass) {
|
||||
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass', $sAttCode, $sAttLabel);
|
||||
} elseif ($iAttClassesCount === 1) {
|
||||
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass', $sAttCode, $sAttLabel, MetaModel::GetName($sAttFirstClass));
|
||||
} else {
|
||||
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses', $sAttCode, $sAttLabel);
|
||||
}
|
||||
$aAllowedAttributes[$sAttCode] = $sLabel;
|
||||
}
|
||||
// N°6460 Always sort on the labels, not on the datamodel definition order
|
||||
natcasesort($aAllowedAttributes);
|
||||
|
||||
return $aAllowedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
|
||||
*
|
||||
* @param $proposedValue
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @param bool $bIgnoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
|
||||
{
|
||||
$oSet = new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
$aArgs = array();
|
||||
if (!empty($oHostObj)) {
|
||||
$aArgs['this'] = $oHostObj;
|
||||
}
|
||||
$aAllowedAttributes = $this->GetAllowedValues($aArgs);
|
||||
$aInvalidAttCodes = array();
|
||||
if (is_string($proposedValue) && !empty($proposedValue)) {
|
||||
$aJsonFromWidget = json_decode($proposedValue, true);
|
||||
if (is_null($aJsonFromWidget)) {
|
||||
$proposedValue = trim($proposedValue);
|
||||
$aProposedValues = $this->FromStringToArray($proposedValue);
|
||||
$aValues = array();
|
||||
foreach ($aProposedValues as $sValue) {
|
||||
$sAttCode = trim($sValue);
|
||||
if (empty($aAllowedAttributes) || isset($aAllowedAttributes[$sAttCode])) {
|
||||
$aValues[$sAttCode] = $sAttCode;
|
||||
} else {
|
||||
$aInvalidAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
$oSet->SetValues($aValues);
|
||||
}
|
||||
} elseif ($proposedValue instanceof ormSet) {
|
||||
$oSet = $proposedValue;
|
||||
}
|
||||
if (!empty($aInvalidAttCodes) && !$bIgnoreErrors) {
|
||||
$sTargetClass = $this->Get('class_field');
|
||||
$sClass = $oHostObj->Get($sTargetClass);
|
||||
throw new CoreUnexpectedValue("The attribute(s) ".implode(', ', $aInvalidAttCodes)." are invalid for class {$sClass}");
|
||||
}
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceof ormSet) {
|
||||
$value = $value->GetValues();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
if (!empty($oHostObject) && $bLocalize) {
|
||||
$sTargetClass = $this->Get('class_field');
|
||||
$sClass = $oHostObject->Get($sTargetClass);
|
||||
|
||||
$aLocalizedValues = array();
|
||||
foreach ($value as $sAttCode) {
|
||||
try {
|
||||
$sAttClass = $sClass;
|
||||
|
||||
// Look for the first class (current or children) that have this attcode
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
|
||||
if (MetaModel::IsValidAttCode($sChildClass, $sAttCode)) {
|
||||
$sAttClass = $sChildClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities(MetaModel::GetLabel($sAttClass, $sAttCode)." ($sAttCode)");
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// Ignore bad values
|
||||
}
|
||||
}
|
||||
$value = $aLocalizedValues;
|
||||
}
|
||||
$value = implode('', $value);
|
||||
}
|
||||
|
||||
return '<span class="'.implode(' ', $this->aCSSClasses).'">'.$value.'</span>';
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return (empty($proposedValue));
|
||||
}
|
||||
}
|
||||
87
sources/Core/AttributeDefinition/AttributeClassState.php
Normal file
87
sources/Core/AttributeDefinition/AttributeClassState.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* An attribute that matches a class state
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeClassState extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('class_field'));
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
if (isset($aArgs['this'])) {
|
||||
$oHostObj = $aArgs['this'];
|
||||
$sTargetClass = $this->Get('class_field');
|
||||
$sClass = $oHostObj->Get($sTargetClass);
|
||||
|
||||
$aAllowedStates = array();
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
|
||||
$aValues = MetaModel::EnumStates($sChildClass);
|
||||
foreach (array_keys($aValues) as $sState) {
|
||||
$aAllowedStates[$sState] = $sState.' ('.MetaModel::GetStateLabel($sChildClass, $sState).')';
|
||||
}
|
||||
}
|
||||
|
||||
return $aAllowedStates;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!empty($oHostObject)) {
|
||||
$sTargetClass = $this->Get('class_field');
|
||||
$sClass = $oHostObject->Get($sTargetClass);
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
|
||||
$aValues = MetaModel::EnumStates($sChildClass);
|
||||
if (in_array($sValue, $aValues)) {
|
||||
$sLabelForHtmlAttribute = utils::EscapeHtml($sValue.' ('.MetaModel::GetStateLabel($sChildClass, $sValue).')');
|
||||
$sHTML = '<span class="attribute-set-item" data-code="'.$sValue.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sValue.'</span>';
|
||||
|
||||
return $sHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
}
|
||||
471
sources/Core/AttributeDefinition/AttributeCustomFields.php
Normal file
471
sources/Core/AttributeDefinition/AttributeCustomFields.php
Normal file
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeCustomFields;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use ormCustomFieldsValue;
|
||||
use Str;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Custom fields managed by an external implementation
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeCustomFields extends AttributeDefinition
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("handler_class"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "CustomFields";
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function LoadFromClassTables()
|
||||
{
|
||||
return false;
|
||||
} // See ReadValue...
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $aValues
|
||||
*
|
||||
* @return TemplateFieldsHandler
|
||||
*/
|
||||
public function GetHandler($aValues = null)
|
||||
{
|
||||
$sHandlerClass = $this->Get('handler_class');
|
||||
/** @var TemplateFieldsHandler $oHandler */
|
||||
$oHandler = new $sHandlerClass($this->GetCode());
|
||||
if (!is_null($aValues)) {
|
||||
$oHandler->SetCurrentValues($aValues);
|
||||
}
|
||||
|
||||
return $oHandler;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
$sHandlerClass = $this->Get('handler_class');
|
||||
|
||||
return $sHandlerClass::GetPrerequisiteAttributes($sClass);
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return $this->GetForTemplate($sValue, '', $oHostObj, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the string representation out of the values given by the form defined in GetDisplayForm
|
||||
*/
|
||||
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
|
||||
{
|
||||
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
|
||||
if ($aRawData != null) {
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObject)
|
||||
{
|
||||
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue)) {
|
||||
if (false === $oHostObject->IsNew()) {
|
||||
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
|
||||
$aRequestTemplateValues = $proposedValue->GetValues();
|
||||
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
|
||||
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
|
||||
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
|
||||
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($proposedValue->GetHostObject())) {
|
||||
// the object might not be set : for example in \AttributeCustomFields::FromJSONToValue we don't have the object available :(
|
||||
$proposedValue->SetHostObject($oHostObject);
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
if (is_string($proposedValue)) {
|
||||
$aValues = json_decode($proposedValue, true);
|
||||
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
|
||||
}
|
||||
|
||||
if (is_array($proposedValue)) {
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $proposedValue);
|
||||
}
|
||||
|
||||
if (is_null($proposedValue)) {
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
|
||||
}
|
||||
|
||||
throw new Exception('Unexpected type for the value of a custom fields attribute: '.gettype($proposedValue));
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\SubFormField';
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to build the relevant form field
|
||||
*
|
||||
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
|
||||
* $oFormField is passed, MakeFormField behaves more like a Prepare.
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
$oFormField->SetForm($this->GetForm($oObject));
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oHostObject
|
||||
* @param null $sFormPrefix
|
||||
*
|
||||
* @return Combodo\iTop\Form\Form
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetForm(DBObject $oHostObject, $sFormPrefix = null)
|
||||
{
|
||||
try {
|
||||
$oValue = $oHostObject->Get($this->GetCode());
|
||||
$oHandler = $this->GetHandler($oValue->GetValues());
|
||||
$sFormId = utils::IsNullOrEmptyString($sFormPrefix) ? 'cf_'.$this->GetCode() : $sFormPrefix.'_cf_'.$this->GetCode();
|
||||
$oHandler->BuildForm($oHostObject, $sFormId);
|
||||
$oForm = $oHandler->GetForm();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oForm = new \Combodo\iTop\Form\Form('');
|
||||
$oField = new \Combodo\iTop\Form\Field\LabelField('');
|
||||
$oField->SetLabel('Custom field error: '.$e->getMessage());
|
||||
$oForm->AddField($oField);
|
||||
$oForm->Finalize();
|
||||
}
|
||||
|
||||
return $oForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the data from where it has been stored. This verb must be implemented as soon as LoadFromClassTables returns false
|
||||
* and LoadInObject returns true
|
||||
*
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
* @return mixed|null
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function ReadExternalValues(DBObject $oHostObject)
|
||||
{
|
||||
try {
|
||||
$oHandler = $this->GetHandler();
|
||||
$aValues = $oHandler->ReadValues($oHostObject);
|
||||
$oRet = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oRet = new ormCustomFieldsValue($oHostObject, $this->GetCode());
|
||||
}
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @since 3.1.0 N°6043 Move code contained in \AttributeCustomFields::WriteValue to this generic method
|
||||
*/
|
||||
public function WriteExternalValues(DBObject $oHostObject): void
|
||||
{
|
||||
$oValue = $oHostObject->Get($this->GetCode());
|
||||
if (!($oValue instanceof ormCustomFieldsValue)) {
|
||||
$oHandler = $this->GetHandler();
|
||||
$aValues = array();
|
||||
} else {
|
||||
// Pass the values through the form to make sure that they are correct
|
||||
$oHandler = $this->GetHandler($oValue->GetValues());
|
||||
$oHandler->BuildForm($oHostObject, '');
|
||||
$oForm = $oHandler->GetForm();
|
||||
$aValues = $oForm->GetCurrentValues();
|
||||
}
|
||||
|
||||
$oHandler->WriteValues($oHostObject, $aValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the current attribute in the object's signature, for the supplied value
|
||||
*
|
||||
* @param ormCustomFieldsValue $value The value of this attribute for the object
|
||||
*
|
||||
* @return string The "signature" for this field/attribute
|
||||
*/
|
||||
public function Fingerprint($value)
|
||||
{
|
||||
$oHandler = $this->GetHandler($value->GetValues());
|
||||
|
||||
return $oHandler->GetValueFingerprint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of the data
|
||||
*
|
||||
* @param DBObject $oHostObject
|
||||
* @param $value
|
||||
*
|
||||
* @return bool|string true or error message
|
||||
*/
|
||||
public function CheckValue(DBObject $oHostObject, $value)
|
||||
{
|
||||
try {
|
||||
$oHandler = $this->GetHandler($value->GetValues());
|
||||
$oHandler->BuildForm($oHostObject, '');
|
||||
$ret = $oHandler->Validate($oHostObject);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$ret = $e->getMessage();
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup data upon object deletion (object id still available here)
|
||||
*
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function DeleteExternalValues(DBObject $oHostObject): void
|
||||
{
|
||||
$oValue = $oHostObject->Get($this->GetCode());
|
||||
$oHandler = $this->GetHandler($oValue->GetValues());
|
||||
|
||||
$oHandler->DeleteValues($oHostObject);
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
try {
|
||||
/** @var ormCustomFieldsValue $value */
|
||||
$sRet = $value->GetAsHTML($bLocalize);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sRet = 'Custom field error: '.utils::EscapeHtml($e->getMessage());
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
try {
|
||||
$sRet = $value->GetAsXML($bLocalize);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sRet = Str::pure2xml('Custom field error: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormCustomFieldsValue $value
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return string
|
||||
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
|
||||
*/
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
try {
|
||||
$sRet = $value->GetAsCSV($sSeparator, $sTextQualifier, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, 'Custom field error: '.$e->getMessage());
|
||||
$sRet = $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available verbs for 'GetForTemplate'
|
||||
*/
|
||||
public function EnumTemplateVerbs()
|
||||
{
|
||||
$sHandlerClass = $this->Get('handler_class');
|
||||
|
||||
return $sHandlerClass::EnumTemplateVerbs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param $value mixed The current value of the field
|
||||
* @param $sVerb string The verb specifying the representation of the value
|
||||
* @param $oHostObject DBObject The object
|
||||
* @param $bLocalize bool Whether or not to localize the value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
try {
|
||||
$sRet = $value->GetForTemplate($sVerb, $bLocalize);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sRet = 'Custom field error: '.$e->getMessage();
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function MakeValueFromString(
|
||||
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
|
||||
$sAttributeQualifier = null
|
||||
)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormCustomFieldsValue $value
|
||||
*
|
||||
* @return string|array
|
||||
*
|
||||
* @since 3.1.0 N°1150 now returns the value (was always returning null before)
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
try {
|
||||
$sRet = $value->GetForJSON();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sRet = 'Custom field error: '.$e->getMessage();
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return ?ormCustomFieldsValue with empty host object as we don't have it here (most consumers don't have an object in their context, for example in \RestUtils::GetObjectSetFromKey)
|
||||
* The host object will be set in {@see MakeRealValue}
|
||||
* All the necessary checks will be done in {@see CheckValue}
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
return ormCustomFieldsValue::FromJSONToValue($json, $this);
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
try {
|
||||
$bEquals = $val1->Equals($val2);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$bEquals = false;
|
||||
}
|
||||
|
||||
return $bEquals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Protection against wrong value type
|
||||
if (false === ($proposedValue instanceof ormCustomFieldsValue)) {
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
return count($proposedValue->GetValues()) > 0;
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeCustomFields::class;
|
||||
}
|
||||
}
|
||||
32
sources/Core/AttributeDefinition/AttributeDBField.php
Normal file
32
sources/Core/AttributeDefinition/AttributeDBField.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
|
||||
/**
|
||||
* Base class for all kind of DB attributes, with the exception of external keys
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDBField extends AttributeDBFieldVoid
|
||||
{
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed"));
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->MakeRealValue($this->Get("default_value"), $oHostObject);
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return $this->Get("is_null_allowed");
|
||||
}
|
||||
}
|
||||
155
sources/Core/AttributeDefinition/AttributeDBFieldVoid.php
Normal file
155
sources/Core/AttributeDefinition/AttributeDBFieldVoid.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
|
||||
/**
|
||||
* Abstract class implementing default filters for a DB column
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDBFieldVoid extends AttributeDefinition
|
||||
{
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "sql"));
|
||||
}
|
||||
|
||||
// To be overriden, used in GetSQLColumns
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return 'VARCHAR(255)'
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
protected function GetSQLColSpec()
|
||||
{
|
||||
$default = $this->ScalarToSQL($this->GetDefaultValue());
|
||||
if (is_null($default)) {
|
||||
$sRet = '';
|
||||
} else {
|
||||
if (is_numeric($default)) {
|
||||
// Though it is a string in PHP, it will be considered as a numeric value in MySQL
|
||||
// Then it must not be quoted here, to preserve the compatibility with the value returned by CMDBSource::GetFieldSpec
|
||||
$sRet = " DEFAULT $default";
|
||||
} else {
|
||||
$sRet = " DEFAULT ".CMDBSource::Quote($default);
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return $this->Get("allowed_values");
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
return $this->Get("depends_on");
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return !$this->IsMagic();
|
||||
}
|
||||
|
||||
public function GetSQLExpr()
|
||||
{
|
||||
return $this->Get("sql");
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->MakeRealValue("", $oHostObject);
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
protected function ScalarToSQL($value)
|
||||
{
|
||||
return $value;
|
||||
} // format value as a valuable SQL literal (quoted outside)
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
$aColumns = array();
|
||||
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
|
||||
$aColumns[''] = $this->Get("sql");
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$aValues = array();
|
||||
$aValues[$this->Get("sql")] = $this->ScalarToSQL($value);
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->Get("sql")] = $this->GetSQLCol($bFullSpec);
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array("=" => "equals", "!=" => "differs from");
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = $sQValue";
|
||||
}
|
||||
}
|
||||
}
|
||||
110
sources/Core/AttributeDefinition/AttributeDashboard.php
Normal file
110
sources/Core/AttributeDefinition/AttributeDashboard.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use MetaModel;
|
||||
use RuntimeDashboard;
|
||||
use utils;
|
||||
|
||||
class AttributeDashboard extends AttributeDefinition
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(),
|
||||
array("definition_file", "is_user_editable"));
|
||||
}
|
||||
|
||||
public function GetDashboard()
|
||||
{
|
||||
$sAttCode = $this->GetCode();
|
||||
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $sAttCode);
|
||||
$sFilePath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$this->Get('definition_file');
|
||||
|
||||
return RuntimeDashboard::GetDashboard($sFilePath, $sClass.'__'.$sAttCode);
|
||||
}
|
||||
|
||||
public function IsUserEditable()
|
||||
{
|
||||
return $this->Get('is_user_editable');
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// if this verb returns false, then GetValue must be implemented
|
||||
public static function LoadInObject()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetValue($oHostObject)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Always return false for now, we don't consider a custom version of a dashboard
|
||||
return false;
|
||||
}
|
||||
}
|
||||
115
sources/Core/AttributeDefinition/AttributeDate.php
Normal file
115
sources/Core/AttributeDefinition/AttributeDate.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use DateTimeFormat;
|
||||
use DBObject;
|
||||
|
||||
/**
|
||||
* Map a date+time column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDate extends AttributeDateTime
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE;
|
||||
|
||||
public static $oDateFormat = null;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function GetFormat()
|
||||
{
|
||||
if (self::$oDateFormat == null) {
|
||||
AttributeDateTime::LoadFormatFromConfig();
|
||||
}
|
||||
|
||||
return self::$oDateFormat;
|
||||
}
|
||||
|
||||
public static function SetFormat(DateTimeFormat $oDateFormat)
|
||||
{
|
||||
self::$oDateFormat = $oDateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string used for the date & time stored in memory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function GetInternalFormat()
|
||||
{
|
||||
return 'Y-m-d';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string used for the date & time written to MySQL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function GetSQLFormat()
|
||||
{
|
||||
return 'Y-m-d';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Date";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "DATE";
|
||||
}
|
||||
|
||||
public function GetImportColumns()
|
||||
{
|
||||
// Allow an empty string to be a valid value (synonym for "reset")
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(10)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override to specify Field class
|
||||
*
|
||||
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
|
||||
* $oFormField is passed, MakeFormField behave more like a Prepare.
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
$oFormField = parent::MakeFormField($oObject, $oFormField);
|
||||
$oFormField->SetDateOnly(true);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
}
|
||||
519
sources/Core/AttributeDefinition/AttributeDateTime.php
Normal file
519
sources/Core/AttributeDefinition/AttributeDateTime.php
Normal file
@@ -0,0 +1,519 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use BinaryExpression;
|
||||
use CMDBSource;
|
||||
use CoreUnexpectedValue;
|
||||
use DateTime;
|
||||
use DateTimeFormat;
|
||||
use DateTimeImmutable;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use Expression;
|
||||
use FieldExpression;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use Str;
|
||||
use utils;
|
||||
use VariableExpression;
|
||||
|
||||
/**
|
||||
* Map a date+time column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDateTime extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE_TIME;
|
||||
|
||||
public static $oFormat = null;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return DateTimeFormat
|
||||
*/
|
||||
public static function GetFormat()
|
||||
{
|
||||
if (self::$oFormat == null) {
|
||||
static::LoadFormatFromConfig();
|
||||
}
|
||||
|
||||
return self::$oFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the 3 settings: date format, time format and data_time format from the configuration
|
||||
*/
|
||||
public static function LoadFormatFromConfig()
|
||||
{
|
||||
$aFormats = MetaModel::GetConfig()->Get('date_and_time_format');
|
||||
$sLang = Dict::GetUserLanguage();
|
||||
$sDateFormat = isset($aFormats[$sLang]['date']) ? $aFormats[$sLang]['date'] : (isset($aFormats['default']['date']) ? $aFormats['default']['date'] : 'Y-m-d');
|
||||
$sTimeFormat = isset($aFormats[$sLang]['time']) ? $aFormats[$sLang]['time'] : (isset($aFormats['default']['time']) ? $aFormats['default']['time'] : 'H:i:s');
|
||||
$sDateAndTimeFormat = isset($aFormats[$sLang]['date_time']) ? $aFormats[$sLang]['date_time'] : (isset($aFormats['default']['date_time']) ? $aFormats['default']['date_time'] : '$date $time');
|
||||
|
||||
$sFullFormat = str_replace(array('$date', '$time'), array($sDateFormat, $sTimeFormat), $sDateAndTimeFormat);
|
||||
|
||||
self::SetFormat(new DateTimeFormat($sFullFormat));
|
||||
AttributeDate::SetFormat(new DateTimeFormat($sDateFormat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string used for the date & time stored in memory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function GetInternalFormat()
|
||||
{
|
||||
return 'Y-m-d H:i:s';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string used for the date & time written to MySQL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function GetSQLFormat()
|
||||
{
|
||||
return 'Y-m-d H:i:s';
|
||||
}
|
||||
|
||||
public static function SetFormat(DateTimeFormat $oDateTimeFormat)
|
||||
{
|
||||
self::$oFormat = $oDateTimeFormat;
|
||||
}
|
||||
|
||||
public static function GetSQLTimeFormat()
|
||||
{
|
||||
return 'H:i:s';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a search string coming from user input
|
||||
*
|
||||
* @param string $sSearchString
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function ParseSearchString($sSearchString)
|
||||
{
|
||||
try {
|
||||
$oDateTime = $this->GetFormat()->Parse($sSearchString);
|
||||
$sSearchString = $oDateTime->format($this->GetInternalFormat());
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sFormatString = '!'.(string)AttributeDate::GetFormat(); // BEWARE: ! is needed to set non-parsed fields to zero !!!
|
||||
$oDateTime = DateTime::createFromFormat($sFormatString, $sSearchString);
|
||||
if ($oDateTime !== false) {
|
||||
$sSearchString = $oDateTime->format($this->GetInternalFormat());
|
||||
}
|
||||
}
|
||||
|
||||
return $sSearchString;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\DateTimeField';
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to specify Field class
|
||||
*
|
||||
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
|
||||
* $oFormField is passed, MakeFormField behave more like a Prepare.
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
$oFormField->SetPHPDateTimeFormat((string)$this->GetFormat());
|
||||
$oFormField->SetJSDateTimeFormat($this->GetFormat()->ToMomentJS());
|
||||
|
||||
$oFormField = parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// After call to the parent as it sets the current value
|
||||
$oValue = $oObject->Get($this->GetCode());
|
||||
if ($oValue === $this->GetNullValue()) {
|
||||
$oValue = $this->GetDefaultValue($oObject);
|
||||
}
|
||||
$oFormField->SetCurrentValue($this->GetFormat()->Format($oValue));
|
||||
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function EnumTemplateVerbs()
|
||||
{
|
||||
return array(
|
||||
'' => 'Formatted representation',
|
||||
'raw' => 'Not formatted representation',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
switch ($sVerb) {
|
||||
case '':
|
||||
case 'text':
|
||||
return static::GetFormat()->format($value);
|
||||
break;
|
||||
case 'html':
|
||||
// Note: Not passing formatted value as the method will format it.
|
||||
return $this->GetAsHTML($value);
|
||||
break;
|
||||
case 'raw':
|
||||
return $value;
|
||||
break;
|
||||
default:
|
||||
return parent::GetForTemplate($value, $sVerb, $oHostObject, $bLocalize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "DateTime";
|
||||
}
|
||||
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return (string)static::GetFormat()->format($sValue);
|
||||
}
|
||||
|
||||
public function GetValueLabel($sValue, $oHostObj = null)
|
||||
{
|
||||
return (string)static::GetFormat()->format($sValue);
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "DATETIME";
|
||||
}
|
||||
|
||||
public function GetImportColumns()
|
||||
{
|
||||
// Allow an empty string to be a valid value (synonym for "reset")
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(19)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public static function GetAsUnixSeconds($value)
|
||||
{
|
||||
$oDeadlineDateTime = new DateTime($value);
|
||||
$iUnixSeconds = $oDeadlineDateTime->format('U');
|
||||
|
||||
return $iUnixSeconds;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$sDefaultValue = $this->Get('default_value');
|
||||
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
|
||||
try {
|
||||
$sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
try {
|
||||
$sDefaultDate = Expression::FromOQL('"'.$sDefaultValue.'"')->Evaluate([]);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
|
||||
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$oDate = new DateTimeImmutable($sDefaultDate);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
|
||||
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
|
||||
return $oDate->format($this->GetInternalFormat());
|
||||
}
|
||||
|
||||
return $this->GetNullValue();
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return static::GetFormat()->ToRegExpr();
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"=" => "equals",
|
||||
"!=" => "differs from",
|
||||
"<" => "before",
|
||||
"<=" => "before",
|
||||
">" => "after (strictly)",
|
||||
">=" => "after",
|
||||
"SameDay" => "same day (strip time)",
|
||||
"SameMonth" => "same year/month",
|
||||
"SameYear" => "same year",
|
||||
"Today" => "today",
|
||||
">|" => "after today + N days",
|
||||
"<|" => "before today + N days",
|
||||
"=|" => "equals today + N days",
|
||||
);
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
// Unless we implement a "same xxx, depending on given precision" !
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
|
||||
switch ($sOpCode) {
|
||||
case '=':
|
||||
case '!=':
|
||||
case '<':
|
||||
case '<=':
|
||||
case '>':
|
||||
case '>=':
|
||||
return $this->GetSQLExpr()." $sOpCode $sQValue";
|
||||
case 'SameDay':
|
||||
return "DATE(".$this->GetSQLExpr().") = DATE($sQValue)";
|
||||
case 'SameMonth':
|
||||
return "DATE_FORMAT(".$this->GetSQLExpr().", '%Y-%m') = DATE_FORMAT($sQValue, '%Y-%m')";
|
||||
case 'SameYear':
|
||||
return "MONTH(".$this->GetSQLExpr().") = MONTH($sQValue)";
|
||||
case 'Today':
|
||||
return "DATE(".$this->GetSQLExpr().") = CURRENT_DATE()";
|
||||
case '>|':
|
||||
return "DATE(".$this->GetSQLExpr().") > DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
case '<|':
|
||||
return "DATE(".$this->GetSQLExpr().") < DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
case '=|':
|
||||
return "DATE(".$this->GetSQLExpr().") = DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
default:
|
||||
return $this->GetSQLExpr()." = $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|DateTime|string $proposedValue possible values :
|
||||
* - timestamp ({@see DateTime::getTimestamp())
|
||||
* - {@see \DateTime} PHP object
|
||||
* - string, following the {@see GetInternalFormat} format.
|
||||
*
|
||||
* @throws CoreUnexpectedValue if invalid value type or the string passed cannot be converted
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_numeric($proposedValue)) {
|
||||
return date(static::GetInternalFormat(), $proposedValue);
|
||||
}
|
||||
|
||||
if (is_object($proposedValue) && ($proposedValue instanceof DateTime)) {
|
||||
return $proposedValue->format(static::GetInternalFormat());
|
||||
}
|
||||
|
||||
if (is_string($proposedValue)) {
|
||||
if (($proposedValue === '') && $this->IsNullAllowed()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$oFormat = new DateTimeFormat(static::GetInternalFormat());
|
||||
$oFormat->Parse($proposedValue);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new CoreUnexpectedValue('Wrong format for date attribute '.$this->GetCode().', expecting "'.$this->GetInternalFormat().'" and got "'.$proposedValue.'"');
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
throw new CoreUnexpectedValue('Wrong format for date attribute '.$this->GetCode());
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html(static::GetFormat()->format($value));
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if (empty($sValue) || ($sValue === '0000-00-00 00:00:00') || ($sValue === '0000-00-00')) {
|
||||
return '';
|
||||
} else {
|
||||
if ((string)static::GetFormat() !== static::GetInternalFormat()) {
|
||||
// Format conversion
|
||||
$oDate = new DateTime($sValue);
|
||||
if ($oDate !== false) {
|
||||
$sValue = static::GetFormat()->format($oDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
|
||||
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string to find some smart search patterns and build the corresponding search/OQL condition
|
||||
* Each derived class is reponsible for defining and processing their own smart patterns, the base class
|
||||
* does nothing special, and just calls the default (loose) operator
|
||||
*
|
||||
* @param string $sSearchText The search string to analyze for smart patterns
|
||||
* @param FieldExpression $oField The FieldExpression representing the atttribute code in this OQL query
|
||||
* @param array $aParams Values of the query parameters
|
||||
* @param bool $bParseSearchString
|
||||
*
|
||||
* @return Expression The search condition to be added (AND) to the current search
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetSmartConditionExpression(
|
||||
$sSearchText, FieldExpression $oField, &$aParams, $bParseSearchString = false
|
||||
)
|
||||
{
|
||||
// Possible smart patterns
|
||||
$aPatterns = array(
|
||||
'between' => array('pattern' => '/^\[(.*),(.*)\]$/', 'operator' => 'n/a'),
|
||||
'greater than or equal' => array('pattern' => '/^>=(.*)$/', 'operator' => '>='),
|
||||
'greater than' => array('pattern' => '/^>(.*)$/', 'operator' => '>'),
|
||||
'less than or equal' => array('pattern' => '/^<=(.*)$/', 'operator' => '<='),
|
||||
'less than' => array('pattern' => '/^<(.*)$/', 'operator' => '<'),
|
||||
);
|
||||
|
||||
$sPatternFound = '';
|
||||
$aMatches = array();
|
||||
foreach ($aPatterns as $sPatName => $sPattern) {
|
||||
if (preg_match($sPattern['pattern'], $sSearchText, $aMatches)) {
|
||||
$sPatternFound = $sPatName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($sPatternFound) {
|
||||
case 'between':
|
||||
|
||||
$sParamName1 = $oField->GetParent().'_'.$oField->GetName().'_1';
|
||||
$oRightExpr = new VariableExpression($sParamName1);
|
||||
if ($bParseSearchString) {
|
||||
$aParams[$sParamName1] = $this->ParseSearchString($aMatches[1]);
|
||||
} else {
|
||||
$aParams[$sParamName1] = $aMatches[1];
|
||||
}
|
||||
$oCondition1 = new BinaryExpression($oField, '>=', $oRightExpr);
|
||||
|
||||
$sParamName2 = $oField->GetParent().'_'.$oField->GetName().'_2';
|
||||
$oRightExpr = new VariableExpression($sParamName2);
|
||||
if ($bParseSearchString) {
|
||||
$aParams[$sParamName2] = $this->ParseSearchString($aMatches[2]);
|
||||
} else {
|
||||
$aParams[$sParamName2] = $aMatches[2];
|
||||
}
|
||||
$oCondition2 = new BinaryExpression($oField, '<=', $oRightExpr);
|
||||
|
||||
$oNewCondition = new BinaryExpression($oCondition1, 'AND', $oCondition2);
|
||||
break;
|
||||
|
||||
case 'greater than':
|
||||
case 'greater than or equal':
|
||||
case 'less than':
|
||||
case 'less than or equal':
|
||||
$sSQLOperator = $aPatterns[$sPatternFound]['operator'];
|
||||
$sParamName = $oField->GetParent().'_'.$oField->GetName();
|
||||
$oRightExpr = new VariableExpression($sParamName);
|
||||
if ($bParseSearchString) {
|
||||
$aParams[$sParamName] = $this->ParseSearchString($aMatches[1]);
|
||||
} else {
|
||||
$aParams[$sParamName] = $aMatches[1];
|
||||
}
|
||||
$oNewCondition = new BinaryExpression($oField, $sSQLOperator, $oRightExpr);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams);
|
||||
|
||||
}
|
||||
|
||||
return $oNewCondition;
|
||||
}
|
||||
|
||||
|
||||
public function GetHelpOnSmartSearch()
|
||||
{
|
||||
$sDict = parent::GetHelpOnSmartSearch();
|
||||
|
||||
$oFormat = static::GetFormat();
|
||||
$sExample = $oFormat->Format(new DateTime('2015-07-19 18:40:00'));
|
||||
|
||||
return vsprintf($sDict, array($oFormat->ToPlaceholder(), $sExample));
|
||||
}
|
||||
}
|
||||
89
sources/Core/AttributeDefinition/AttributeDeadline.php
Normal file
89
sources/Core/AttributeDefinition/AttributeDeadline.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* A dead line stored as a date & time
|
||||
* The only difference with the DateTime attribute is the display:
|
||||
* relative to the current time
|
||||
*/
|
||||
class AttributeDeadline extends AttributeDateTime
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sResult = self::FormatDeadline($value);
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
public static function FormatDeadline($value)
|
||||
{
|
||||
$sResult = '';
|
||||
if ($value !== null) {
|
||||
$iValue = AttributeDateTime::GetAsUnixSeconds($value);
|
||||
$sDate = AttributeDateTime::GetFormat()->Format($value);
|
||||
$difference = $iValue - time();
|
||||
|
||||
if ($difference >= 0) {
|
||||
$sDifference = self::FormatDuration($difference);
|
||||
} else {
|
||||
$sDifference = Dict::Format('UI:DeadlineMissedBy_duration', self::FormatDuration(-$difference));
|
||||
}
|
||||
$sFormat = MetaModel::GetConfig()->Get('deadline_format');
|
||||
$sResult = str_replace(array('$date$', '$difference$'), array($sDate, $sDifference), $sFormat);
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
static function FormatDuration($duration)
|
||||
{
|
||||
$days = floor($duration / 86400);
|
||||
$hours = floor(($duration - (86400 * $days)) / 3600);
|
||||
$minutes = floor(($duration - (86400 * $days + 3600 * $hours)) / 60);
|
||||
|
||||
if ($duration < 60) {
|
||||
// Less than 1 min
|
||||
$sResult = Dict::S('UI:Deadline_LessThan1Min');
|
||||
} else {
|
||||
if ($duration < 3600) {
|
||||
// less than 1 hour, display it in minutes
|
||||
$sResult = Dict::Format('UI:Deadline_Minutes', $minutes);
|
||||
} else {
|
||||
if ($duration < 86400) {
|
||||
// Less that 1 day, display it in hours/minutes
|
||||
$sResult = Dict::Format('UI:Deadline_Hours_Minutes', $hours, $minutes);
|
||||
} else {
|
||||
// Less that 1 day, display it in hours/minutes
|
||||
$sResult = Dict::Format('UI:Deadline_Days_Hours_Minutes', $days, $hours, $minutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
169
sources/Core/AttributeDefinition/AttributeDecimal.php
Normal file
169
sources/Core/AttributeDefinition/AttributeDecimal.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use CoreException;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map a decimal value column (suitable for financial computations) to an attribute
|
||||
* internally in PHP such numbers are represented as string. Should you want to perform
|
||||
* a calculation on them, it is recommended to use the BC Math functions in order to
|
||||
* retain the precision
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDecimal extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('digits', 'decimals' /* including precision */));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "DECIMAL(".$this->Get('digits').",".$this->Get('decimals').")".($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
$iNbDigits = $this->Get('digits');
|
||||
$iPrecision = $this->Get('decimals');
|
||||
$iNbIntegerDigits = $iNbDigits - $iPrecision;
|
||||
|
||||
return "^[\-\+]?\d{1,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function CheckFormat($value)
|
||||
{
|
||||
$sRegExp = $this->GetValidationPattern();
|
||||
|
||||
return preg_match("/$sRegExp/", $value);
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"!=" => "differs from",
|
||||
"=" => "equals",
|
||||
">" => "greater (strict) than",
|
||||
">=" => "greater than",
|
||||
"<" => "less (strict) than",
|
||||
"<=" => "less than",
|
||||
"in" => "in",
|
||||
);
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
// Unless we implement an "equals approximately..." or "same order of magnitude"
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '>':
|
||||
return $this->GetSQLExpr()." > $sQValue";
|
||||
break;
|
||||
case '>=':
|
||||
return $this->GetSQLExpr()." >= $sQValue";
|
||||
break;
|
||||
case '<':
|
||||
return $this->GetSQLExpr()." < $sQValue";
|
||||
break;
|
||||
case '<=':
|
||||
return $this->GetSQLExpr()." <= $sQValue";
|
||||
break;
|
||||
case 'in':
|
||||
if (!is_array($value)) {
|
||||
throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')");
|
||||
}
|
||||
|
||||
return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')";
|
||||
break;
|
||||
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = \"$value\"";
|
||||
}
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return is_null($proposedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return utils::IsNotNullOrEmptyString($proposedValue);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
if ($proposedValue === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->ScalarToSQL($proposedValue);
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
assert(is_null($value) || preg_match('/'.$this->GetValidationPattern().'/', $value));
|
||||
|
||||
if (!is_null($value) && ($value !== '')) {
|
||||
$value = sprintf("%1.".$this->Get('decimals')."F", $value);
|
||||
}
|
||||
|
||||
return $value; // null or string
|
||||
}
|
||||
}
|
||||
1382
sources/Core/AttributeDefinition/AttributeDefinition.php
Normal file
1382
sources/Core/AttributeDefinition/AttributeDefinition.php
Normal file
File diff suppressed because it is too large
Load Diff
140
sources/Core/AttributeDefinition/AttributeDuration.php
Normal file
140
sources/Core/AttributeDefinition/AttributeDuration.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Str;
|
||||
|
||||
/**
|
||||
* Store a duration as a number of seconds
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeDuration extends AttributeInteger
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Duration";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "INT(11) UNSIGNED";
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return '0';
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
if (!is_numeric($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
if (((int)$proposedValue) < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html(self::FormatDuration($value));
|
||||
}
|
||||
|
||||
public static function FormatDuration($duration)
|
||||
{
|
||||
$aDuration = self::SplitDuration($duration);
|
||||
|
||||
if ($duration < 60) {
|
||||
// Less than 1 min
|
||||
$sResult = Dict::Format('Core:Duration_Seconds', $aDuration['seconds']);
|
||||
} else {
|
||||
if ($duration < 3600) {
|
||||
// less than 1 hour, display it in minutes/seconds
|
||||
$sResult = Dict::Format('Core:Duration_Minutes_Seconds', $aDuration['minutes'], $aDuration['seconds']);
|
||||
} else {
|
||||
if ($duration < 86400) {
|
||||
// Less than 1 day, display it in hours/minutes/seconds
|
||||
$sResult = Dict::Format('Core:Duration_Hours_Minutes_Seconds', $aDuration['hours'],
|
||||
$aDuration['minutes'], $aDuration['seconds']);
|
||||
} else {
|
||||
// more than 1 day, display it in days/hours/minutes/seconds
|
||||
$sResult = Dict::Format('Core:Duration_Days_Hours_Minutes_Seconds', $aDuration['days'],
|
||||
$aDuration['hours'], $aDuration['minutes'], $aDuration['seconds']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
static function SplitDuration($duration)
|
||||
{
|
||||
$duration = (int)$duration;
|
||||
$days = floor($duration / 86400);
|
||||
$hours = floor(($duration - (86400 * $days)) / 3600);
|
||||
$minutes = floor(($duration - (86400 * $days + 3600 * $hours)) / 60);
|
||||
$seconds = ($duration % 60); // modulo
|
||||
|
||||
return array('days' => $days, 'hours' => $hours, 'minutes' => $minutes, 'seconds' => $seconds);
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\DurationField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// Note : As of today, this attribute is -by nature- only supported in readonly mode, not edition
|
||||
$sAttCode = $this->GetCode();
|
||||
$oFormField->SetCurrentValue($oObject->Get($sAttCode));
|
||||
$oFormField->SetReadOnly(true);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
}
|
||||
55
sources/Core/AttributeDefinition/AttributeEmailAddress.php
Normal file
55
sources/Core/AttributeDefinition/AttributeEmailAddress.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Specialization of a string: email
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeEmailAddress extends AttributeString
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return $this->GetOptional('validation_pattern', '^'.utils::GetConfig()->Get('email_validation_pattern').'$');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\EmailField';
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$sUrlDecorationClass = utils::GetConfig()->Get('email_decoration_class');
|
||||
|
||||
return '<a class="mailto" href="mailto:'.$sValue.'"><span class="text_decoration '.$sUrlDecorationClass.'"></span>'.parent::GetAsHTML($sValue).'</a>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeEncrypted;
|
||||
use DBObject;
|
||||
use MetaModel;
|
||||
use SimpleCrypt;
|
||||
|
||||
/**
|
||||
* Map a text column (size < 255) to an attribute that is encrypted in the database
|
||||
* The encryption is based on a key set per iTop instance. Thus if you export your
|
||||
* database (in SQL) to someone else without providing the key at the same time
|
||||
* the encrypted fields will remain encrypted
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "TINYBLOB";
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string)$proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the value when reading from the database
|
||||
*
|
||||
* @param array $aCols
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$oSimpleCrypt = new SimpleCrypt(MetaModel::GetConfig()->GetEncryptionLibrary());
|
||||
$sValue = $oSimpleCrypt->Decrypt(MetaModel::GetConfig()->GetEncryptionKey(), $aCols[$sPrefix]);
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the value before storing it in the database
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$oSimpleCrypt = new SimpleCrypt(MetaModel::GetConfig()->GetEncryptionLibrary());
|
||||
$encryptedValue = $oSimpleCrypt->Encrypt(MetaModel::GetConfig()->GetEncryptionKey(), $value);
|
||||
|
||||
$aValues = array();
|
||||
$aValues[$this->Get("sql")] = $encryptedValue;
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
if (is_null($original)) {
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prevstring", $original);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeEncrypted::class;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
419
sources/Core/AttributeDefinition/AttributeEnum.php
Normal file
419
sources/Core/AttributeDefinition/AttributeEnum.php
Normal file
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use MySQLException;
|
||||
use ormStyle;
|
||||
|
||||
/**
|
||||
* Map a enum column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeEnum extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array('styled_values'));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sValue
|
||||
*
|
||||
* @return ormStyle|null
|
||||
*/
|
||||
public function GetStyle(?string $sValue): ?ormStyle
|
||||
{
|
||||
if ($this->IsParam('styled_values')) {
|
||||
$aStyles = $this->Get('styled_values');
|
||||
if (array_key_exists($sValue, $aStyles)) {
|
||||
return $aStyles[$sValue];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->IsParam('default_style')) {
|
||||
return $this->Get('default_style');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
// Get the definition of the column, including the actual values present in the table
|
||||
return $this->GetSQLColHelper($bFullSpec, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A more versatile version of GetSQLCol
|
||||
*
|
||||
* @param bool $bFullSpec
|
||||
* @param bool $bIncludeActualValues
|
||||
* @param string $sSQLTableName The table where to look for the actual values (may be useful for data synchro tables)
|
||||
*
|
||||
* @return string
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function GetSQLColHelper($bFullSpec = false, $bIncludeActualValues = false, $sSQLTableName = null)
|
||||
{
|
||||
$oValDef = $this->GetValuesDef();
|
||||
if ($oValDef) {
|
||||
$aValues = CMDBSource::Quote(array_keys($oValDef->GetValues(array(), "")), true);
|
||||
} else {
|
||||
$aValues = array();
|
||||
}
|
||||
|
||||
// Preserve the values already present in the database to ease migrations
|
||||
if ($bIncludeActualValues) {
|
||||
if ($sSQLTableName == null) {
|
||||
// No SQL table given, use the one of the attribute
|
||||
$sHostClass = $this->GetHostClass();
|
||||
$sSQLTableName = MetaModel::DBGetTable($sHostClass, $this->GetCode());
|
||||
}
|
||||
$aValues = array_unique(array_merge($aValues, $this->GetActualValuesInDB($sSQLTableName)));
|
||||
}
|
||||
|
||||
if (count($aValues) > 0) {
|
||||
// The syntax used here do matters
|
||||
// In particular, I had to remove unnecessary spaces to
|
||||
// make sure that this string will match the field type returned by the DB
|
||||
// (used to perform a comparison between the current DB format and the data model)
|
||||
return "ENUM(".implode(",", $aValues).")"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
} else {
|
||||
return "VARCHAR(255)"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? " DEFAULT ''" : ""); // ENUM() is not an allowed syntax!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AttributeDefinition::GetImportColumns()
|
||||
* @since 3.0.0
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function GetImportColumns()
|
||||
{
|
||||
// Note: this is used by the Data Synchro to build the "data" table
|
||||
// Right now the function is not passed the "target" SQL table, but if we improve this in the future
|
||||
// we may call $this->GetSQLColHelper(true, true, $sDBTable); to take into account the actual 'enum' values
|
||||
// in this table
|
||||
return array($this->GetCode() => $this->GetSQLColHelper(false, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the actual 'enum' values present in the database
|
||||
*
|
||||
* @return string[]
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function GetActualValuesInDB(string $sDBTable)
|
||||
{
|
||||
$aValues = array();
|
||||
try {
|
||||
$sSQL = "SELECT DISTINCT `".$this->GetSQLExpr()."` AS value FROM `$sDBTable`;";
|
||||
$aValuesInDB = CMDBSource::QueryToArray($sSQL);
|
||||
foreach ($aValuesInDB as $aRow) {
|
||||
if ($aRow['value'] !== null) {
|
||||
$aValues[] = $aRow['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySQLException $e) {
|
||||
// Never mind, maybe the table does not exist yet (new installation from scratch)
|
||||
// It seems more efficient to try and ignore errors than to test if the table & column really exists
|
||||
}
|
||||
|
||||
return CMDBSource::Quote($aValues);
|
||||
}
|
||||
|
||||
protected function GetSQLColSpec()
|
||||
{
|
||||
$default = $this->ScalarToSQL($this->GetDefaultValue());
|
||||
if (is_null($default)) {
|
||||
$sRet = '';
|
||||
} else {
|
||||
// ENUMs values are strings so the default value must be a string as well,
|
||||
// otherwise MySQL interprets the number as the zero-based index of the value in the list (i.e. the nth value in the list)
|
||||
$sRet = " DEFAULT ".CMDBSource::Quote($default);
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
// Note: for strings, the null value is an empty string and it is recorded as such in the DB
|
||||
// but that wasn't working for enums, because '' is NOT one of the allowed values
|
||||
// that's why a null value must be forced to a real null
|
||||
$value = parent::ScalarToSQL($value);
|
||||
if ($this->IsNull($value)) {
|
||||
return null;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
if (is_null($sValue)) {
|
||||
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
|
||||
$sLabel = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue,
|
||||
Dict::S('Enum:Undefined'));
|
||||
} else {
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, true /*user lang*/);
|
||||
if (is_null($sLabel)) {
|
||||
$sDefault = str_replace('_', ' ', $sValue);
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, false);
|
||||
}
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetValueDescription($sValue)
|
||||
{
|
||||
if (is_null($sValue)) {
|
||||
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+',
|
||||
Dict::S('Enum:Undefined'));
|
||||
} else {
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+',
|
||||
'', true /* user language only */);
|
||||
if (strlen($sDescription) == 0) {
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
if ($sParentClass) {
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode)) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sDescription = $oAttDef->GetValueDescription($sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sDescription;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($bLocalize) {
|
||||
$sLabel = $this->GetValueLabel($sValue);
|
||||
// $sDescription = $this->GetValueDescription($sValue);
|
||||
$oStyle = $this->GetStyle($sValue);
|
||||
// later, we could imagine a detailed description in the title
|
||||
// $sRes = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
|
||||
$oBadge = FieldBadgeUIBlockFactory::MakeForField($sLabel, $oStyle);
|
||||
$oRenderer = new BlockRenderer($oBadge);
|
||||
$sRes = $oRenderer->RenderHtml();
|
||||
} else {
|
||||
$sRes = parent::GetAsHtml($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
$sFinalValue = '';
|
||||
} elseif ($bLocalize) {
|
||||
$sFinalValue = $this->GetValueLabel($value);
|
||||
} else {
|
||||
$sFinalValue = $value;
|
||||
}
|
||||
$sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if (is_null($sValue)) {
|
||||
$sFinalValue = '';
|
||||
} elseif ($bLocalize) {
|
||||
$sFinalValue = $this->GetValueLabel($sValue);
|
||||
} else {
|
||||
$sFinalValue = $sValue;
|
||||
}
|
||||
$sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\SelectField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
$oFormField->SetChoices($this->GetAllowedValues($oObject->ToArgsForQuery()));
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
if (is_null($sValue)) {
|
||||
return '';
|
||||
} else {
|
||||
return $this->GetValueLabel($sValue);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$aRawValues = parent::GetAllowedValues($aArgs, $sContains);
|
||||
if (is_null($aRawValues)) {
|
||||
return null;
|
||||
}
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sKey => $sValue) {
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
|
||||
// Sort by label only if necessary
|
||||
// See N°1646 and {@see \MFCompiler::CompileAttributeEnumValues()} for complete information as for why sort on labels is done at runtime while other sorting are done at compile time
|
||||
/** @var \ValueSetEnum $oValueSetDef */
|
||||
$oValueSetDef = $this->GetValuesDef();
|
||||
if ($oValueSetDef->IsSortedByValues()) {
|
||||
asort($aLocalizedValues);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum can be localized
|
||||
*/
|
||||
public function MakeValueFromString(
|
||||
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
|
||||
$sAttributeQualifier = null
|
||||
)
|
||||
{
|
||||
if ($bLocalizedValue) {
|
||||
// Lookup for the value matching the input
|
||||
//
|
||||
$sFoundValue = null;
|
||||
$aRawValues = parent::GetAllowedValues();
|
||||
if (!is_null($aRawValues)) {
|
||||
foreach ($aRawValues as $sKey => $sValue) {
|
||||
$sRefValue = $this->GetValueLabel($sKey);
|
||||
if ($sProposedValue == $sRefValue) {
|
||||
$sFoundValue = $sKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($sFoundValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->MakeRealValue($sFoundValue, null);
|
||||
} else {
|
||||
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue,
|
||||
$sAttributeQualifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the input value to align it with the values supported
|
||||
* by this type of attribute. In this case: turns empty strings into nulls
|
||||
*
|
||||
* @param mixed $proposedValue The value to be set for the attribute
|
||||
*
|
||||
* @return mixed The actual value that will be set
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if ($proposedValue == '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::MakeRealValue($proposedValue, $oHostObj);
|
||||
}
|
||||
|
||||
public function GetOrderByHint()
|
||||
{
|
||||
$aValues = $this->GetAllowedValues();
|
||||
|
||||
return Dict::Format('UI:OrderByHint_Values', implode(', ', $aValues));
|
||||
}
|
||||
}
|
||||
243
sources/Core/AttributeDefinition/AttributeEnumSet.php
Normal file
243
sources/Core/AttributeDefinition/AttributeEnumSet.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use ormSet;
|
||||
use ValueSetEnumPadded;
|
||||
|
||||
/**
|
||||
* @since 2.7.0 N°985
|
||||
*/
|
||||
class AttributeEnumSet extends AttributeSet
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_TAG_SET;
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('possible_values', 'is_null_allowed', 'max_items'));
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
$aRawValues = $this->GetRawPossibleValues();
|
||||
$iMaxItems = $this->GetMaxItems();
|
||||
$aLengths = array();
|
||||
foreach (array_keys($aRawValues) as $sKey) {
|
||||
$aLengths[] = strlen($sKey);
|
||||
}
|
||||
rsort($aLengths, SORT_NUMERIC);
|
||||
$iMaxSize = 2;
|
||||
for ($i = 0; $i < min($iMaxItems, count($aLengths)); $i++) {
|
||||
$iMaxSize += $aLengths[$i] + 1;
|
||||
}
|
||||
|
||||
return max(255, $iMaxSize);
|
||||
}
|
||||
|
||||
private function GetRawPossibleValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
/** @var ValueSetEnumPadded $oValSetDef */
|
||||
$oValSetDef = $this->Get('possible_values');
|
||||
if (!$oValSetDef) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $oValSetDef->GetValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
public function GetPossibleValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$aRawValues = $this->GetRawPossibleValues($aArgs, $sContains);
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sKey => $sValue) {
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
if ($sValue instanceof ormSet) {
|
||||
$sValue = implode(', ', $sValue->GetValues());
|
||||
}
|
||||
|
||||
$aValues = $this->GetRawPossibleValues();
|
||||
if (is_array($aValues) && is_string($sValue) && isset($aValues[$sValue])) {
|
||||
$sValue = $aValues[$sValue];
|
||||
}
|
||||
|
||||
if (is_null($sValue)) {
|
||||
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
|
||||
$sLabel = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue,
|
||||
Dict::S('Enum:Undefined'));
|
||||
} else {
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, true /*user lang*/);
|
||||
if (is_null($sLabel)) {
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, false);
|
||||
if (is_null($sLabel)) {
|
||||
$sDefault = trim(str_replace('_', ' ', $sValue));
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sDefault, $sDefault, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetValueDescription($sValue)
|
||||
{
|
||||
if (is_null($sValue)) {
|
||||
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+',
|
||||
Dict::S('Enum:Undefined'));
|
||||
} else {
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+',
|
||||
'', true /* user language only */);
|
||||
if (strlen($sDescription) == 0) {
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
if ($sParentClass) {
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode)) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sDescription = $oAttDef->GetValueDescription($sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sDescription;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($bLocalize) {
|
||||
if ($value instanceof ormSet) {
|
||||
$sRes = $this->GenerateViewHtmlForValues($value->GetValues());
|
||||
} else {
|
||||
$sLabel = $this->GetValueLabel($value);
|
||||
$sDescription = $this->GetValueDescription($value);
|
||||
$sRes = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
|
||||
}
|
||||
} else {
|
||||
$sRes = parent::GetAsHtml($value, $oHostObject, $bLocalize);
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ormSet $value
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return mixed|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
if (is_object($value) && ($value instanceof ormSet)) {
|
||||
$aValues = $value->GetValues();
|
||||
if ($bLocalize) {
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aValues as $sValue) {
|
||||
$aLocalizedValues[] = $this->GetValueLabel($sValue);
|
||||
}
|
||||
$aValues = $aLocalizedValues;
|
||||
}
|
||||
$sRes = implode($sSepItem, $aValues);
|
||||
} else {
|
||||
$sRes = '';
|
||||
}
|
||||
|
||||
return "{$sTextQualifier}{$sRes}{$sTextQualifier}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value from a given string (plain text, CSV import)
|
||||
*
|
||||
* @param string $sProposedValue
|
||||
* @param bool $bLocalizedValue
|
||||
* @param string $sSepItem
|
||||
* @param string $sSepAttribute
|
||||
* @param string $sSepValue
|
||||
* @param string $sAttributeQualifier
|
||||
*
|
||||
* @return mixed null if no match could be found
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
if ($bLocalizedValue) {
|
||||
// Lookup for the values matching the input
|
||||
//
|
||||
$aValues = $this->FromStringToArray($sProposedValue);
|
||||
$aFoundValues = array();
|
||||
$aRawValues = $this->GetPossibleValues();
|
||||
foreach ($aValues as $sValue) {
|
||||
$bFound = false;
|
||||
foreach ($aRawValues as $sCode => $sRawValue) {
|
||||
if ($sValue == $sRawValue) {
|
||||
$aFoundValues[] = $sCode;
|
||||
$bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$bFound) {
|
||||
// Not found, break the import
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->MakeRealValue(implode(',', $aFoundValues), null);
|
||||
} else {
|
||||
return $this->MakeRealValue($sProposedValue, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $proposedValue Search string used for MATCHES
|
||||
*
|
||||
* @param string $sDefaultSepItem word separator to extract items
|
||||
*
|
||||
* @return array of EnumSet codes
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
|
||||
{
|
||||
$aValues = array();
|
||||
if (!empty($proposedValue)) {
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
// convert also other separators
|
||||
if ($sSepItem !== $sDefaultSepItem) {
|
||||
$proposedValue = str_replace($sDefaultSepItem, $sSepItem, $proposedValue);
|
||||
}
|
||||
foreach (explode($sSepItem, $proposedValue) as $sCode) {
|
||||
$sValue = trim($sCode);
|
||||
if (strlen($sValue) > 2) {
|
||||
$sLabel = $this->GetValueLabel($sValue);
|
||||
$aValues[$sLabel] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
return $val1->Equals($val2);
|
||||
}
|
||||
}
|
||||
525
sources/Core/AttributeDefinition/AttributeExternalField.php
Normal file
525
sources/Core/AttributeDefinition/AttributeExternalField.php
Normal file
@@ -0,0 +1,525 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Combodo\iTop\Form\Field\Field;
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* An attribute which corresponds to an external key (direct or indirect)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeExternalField extends AttributeDefinition
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search widget type corresponding to this attribute
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetSearchType()
|
||||
{
|
||||
// Not necessary the external key is already present
|
||||
if ($this->IsFriendlyName()) {
|
||||
return self::SEARCH_WIDGET_TYPE_RAW;
|
||||
}
|
||||
|
||||
try {
|
||||
$oRemoteAtt = $this->GetFinalAttDef();
|
||||
switch (true) {
|
||||
case ($oRemoteAtt instanceof AttributeString):
|
||||
return self::SEARCH_WIDGET_TYPE_EXTERNAL_FIELD;
|
||||
case ($oRemoteAtt instanceof AttributeExternalKey):
|
||||
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
|
||||
}
|
||||
}
|
||||
catch (CoreException $e) {
|
||||
}
|
||||
|
||||
return self::SEARCH_WIDGET_TYPE_RAW;
|
||||
}
|
||||
|
||||
function IsSearchable()
|
||||
{
|
||||
if ($this->IsFriendlyName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::IsSearchable();
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "ExtField";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AttributeDefinition
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetFinalAttDef()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetFinalAttDef();
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
// throw new CoreException("external attribute: does it make any sense to request its type ?");
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetSQLCol($bFullSpec);
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '') {
|
||||
return array('' => $this->GetCode()); // Warning: Use GetCode() since AttributeExternalField does not have any 'sql' property
|
||||
} else {
|
||||
return $sPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDefault
|
||||
*
|
||||
* @return string dict entry if defined, otherwise :
|
||||
* <ul>
|
||||
* <li>if field is a friendlyname then display the label of the ExternalKey
|
||||
* <li>the class hierarchy -> field name
|
||||
*
|
||||
* <p>For example, having this :
|
||||
*
|
||||
* <pre>
|
||||
* +---------------------+ +--------------------+ +--------------+
|
||||
* | Class A | | Class B | | Class C |
|
||||
* +---------------------+ +--------------------+ +--------------+
|
||||
* | foo <ExternalField>-------->c_id_friendly_name--------->friendlyname |
|
||||
* +---------------------+ +--------------------+ +--------------+
|
||||
* </pre>
|
||||
*
|
||||
* <p>The ExternalField foo points to a magical field that is brought by c_id ExternalKey in class B.
|
||||
*
|
||||
* <p>In the normal case the foo label would be : B -> C -> friendlyname<br>
|
||||
* But as foo is a friendlyname its label will be the same as the one on A.b_id field
|
||||
* This can be overrided with dict key Class:ClassA/Attribute:foo
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sLabelDefaultValue = '';
|
||||
$sLabel = parent::GetLabel($sLabelDefaultValue);
|
||||
if ($sLabelDefaultValue !== $sLabel) {
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
if ($this->IsFriendlyName() && ($this->Get("target_attcode") === "friendlyname")) {
|
||||
// This will be used even if we are pointing to a friendlyname in a distance > 1
|
||||
// For example we can link to a magic friendlyname (like org_id_friendlyname)
|
||||
// If a specific label is needed, use a Dict key !
|
||||
// See N°2174
|
||||
$sKeyAttCode = $this->Get("extkey_attcode");
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
|
||||
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
|
||||
$oKeyAtt = $this->GetKeyAttDef();
|
||||
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
|
||||
$sLabel = "{$sKeyLabel}->{$sLabel}";
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetLabelForSearchField()
|
||||
{
|
||||
$sLabel = parent::GetLabel('');
|
||||
if (strlen($sLabel) == 0) {
|
||||
$sKeyAttCode = $this->Get("extkey_attcode");
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
|
||||
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
|
||||
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
$sLabel .= '->'.$oRemoteAtt->GetLabel($this->m_sCode);
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sLabel = parent::GetDescription('');
|
||||
if (strlen($sLabel) == 0) {
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
$sLabel = $oRemoteAtt->GetDescription('');
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetHelpOnEdition($sDefault = null)
|
||||
{
|
||||
$sLabel = parent::GetHelpOnEdition('');
|
||||
if (strlen($sLabel) == 0) {
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
$sLabel = $oRemoteAtt->GetHelpOnEdition('');
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function IsExternalKey($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
switch ($iType) {
|
||||
case EXTKEY_ABSOLUTE:
|
||||
// see further
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
|
||||
return $oRemoteAtt->IsExternalKey($iType);
|
||||
|
||||
case EXTKEY_RELATIVE:
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw new CoreException("Unexpected value for argument iType: '$iType'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function IsFriendlyName()
|
||||
{
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
if ($oRemoteAtt instanceof AttributeExternalField) {
|
||||
$bRet = $oRemoteAtt->IsFriendlyName();
|
||||
} elseif ($oRemoteAtt instanceof AttributeFriendlyName) {
|
||||
$bRet = true;
|
||||
} else {
|
||||
$bRet = false;
|
||||
}
|
||||
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this->GetKeyAttDef($iType)->GetTargetClass();
|
||||
}
|
||||
|
||||
public static function IsExternalField()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetKeyAttCode()
|
||||
{
|
||||
return $this->Get("extkey_attcode");
|
||||
}
|
||||
|
||||
public function GetExtAttCode()
|
||||
{
|
||||
return $this->Get("target_attcode");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iType
|
||||
*
|
||||
* @return AttributeExternalKey
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
switch ($iType) {
|
||||
case EXTKEY_ABSOLUTE:
|
||||
// see further
|
||||
/** @var AttributeExternalKey $oRemoteAtt */
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
if ($oRemoteAtt->IsExternalField()) {
|
||||
return $oRemoteAtt->GetKeyAttDef(EXTKEY_ABSOLUTE);
|
||||
} else {
|
||||
if ($oRemoteAtt->IsExternalKey()) {
|
||||
return $oRemoteAtt;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->GetKeyAttDef(EXTKEY_RELATIVE); // which corresponds to the code hereafter !
|
||||
|
||||
case EXTKEY_RELATIVE:
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode"));
|
||||
|
||||
return $oAttDef;
|
||||
|
||||
default:
|
||||
throw new CoreException("Unexpected value for argument iType: '$iType'");
|
||||
}
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
return array($this->Get("extkey_attcode"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return AttributeExternalField
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetExtAttDef()
|
||||
{
|
||||
$oKeyAttDef = $this->GetKeyAttDef();
|
||||
/** @var AttributeExternalField $oExtAttDef */
|
||||
$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $this->Get("target_attcode"));
|
||||
if (!is_object($oExtAttDef)) {
|
||||
throw new CoreException("Invalid external field ".$this->GetCode()." in class ".$this->GetHostClass().". The class ".$oKeyAttDef->GetTargetClass()." has no attribute ".$this->Get("target_attcode"));
|
||||
}
|
||||
|
||||
return $oExtAttDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetSQLExpr()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetSQLExpr();
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetDefaultValue();
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->IsNullAllowed();
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetBasicFilterOperators();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetNullValue();
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->IsNull($proposedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
|
||||
*/
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetSQLValues($value);
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
// This one could be used in case of filtering only
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->ScalarToSQL($value);
|
||||
}
|
||||
|
||||
|
||||
// Do not overload GetSQLExpression here because this is handled in the joins
|
||||
//public function GetSQLExpressions($sPrefix = '') {return array();}
|
||||
|
||||
// Here, we get the data...
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->FromSQLToValue($aCols, $sPrefix);
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetAsHTML($value, null, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetAsXML($value, null, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier, null, $bLocalize, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\LabelField';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param Field $oFormField
|
||||
*
|
||||
* @return null
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
// Retrieving AttDef from the remote attribute
|
||||
$oRemoteAttDef = $this->GetExtAttDef();
|
||||
|
||||
if ($oFormField === null) {
|
||||
// ExternalField's FormField are actually based on the FormField from the target attribute.
|
||||
// Except for the AttributeExternalKey because we have no OQL and stuff
|
||||
if ($oRemoteAttDef instanceof AttributeExternalKey) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
} else {
|
||||
$sFormFieldClass = $oRemoteAttDef::GetFormFieldClass();
|
||||
}
|
||||
/** @var Field $oFormField */
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
switch ($sFormFieldClass) {
|
||||
case '\Combodo\iTop\Form\Field\SelectField':
|
||||
$oFormField->SetChoices($oRemoteAttDef->GetAllowedValues($oObject->ToArgsForQuery()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
if ($oFormField instanceof \Combodo\iTop\Form\Field\TextAreaField) {
|
||||
if (method_exists($oRemoteAttDef, 'GetFormat')) {
|
||||
/** @var TextAreaField $oFormField */
|
||||
$oFormField->SetFormat($oRemoteAttDef->GetFormat());
|
||||
}
|
||||
}
|
||||
|
||||
// Manually setting for remote ExternalKey, otherwise, the id would be displayed.
|
||||
if ($oRemoteAttDef instanceof AttributeExternalKey) {
|
||||
$oFormField->SetCurrentValue($oObject->Get($this->GetCode().'_friendlyname'));
|
||||
}
|
||||
|
||||
// Readonly field because we can't update external fields
|
||||
$oFormField->SetReadOnly(true);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetFormat()
|
||||
{
|
||||
$oRemoteAttDef = $this->GetExtAttDef();
|
||||
if (method_exists($oRemoteAttDef, 'GetFormat')) {
|
||||
/** @var TextAreaField $oFormField */
|
||||
return $oRemoteAttDef->GetFormat();
|
||||
}
|
||||
|
||||
return 'text';
|
||||
}
|
||||
}
|
||||
354
sources/Core/AttributeDefinition/AttributeExternalKey.php
Normal file
354
sources/Core/AttributeDefinition/AttributeExternalKey.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use TemporaryObjectDescriptor;
|
||||
use ValueSetObjects;
|
||||
|
||||
/**
|
||||
* Map a foreign key to an attribute
|
||||
* AttributeExternalKey and AttributeExternalField may be an external key
|
||||
* the difference is that AttributeExternalKey corresponds to a column into the defined table
|
||||
* where an AttributeExternalField corresponds to a column into another table (class)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search widget type corresponding to this attribute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetSearchType()
|
||||
{
|
||||
try {
|
||||
$oRemoteAtt = $this->GetFinalAttDef();
|
||||
$sTargetClass = $oRemoteAtt->GetTargetClass();
|
||||
if (MetaModel::IsHierarchicalClass($sTargetClass)) {
|
||||
return self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
|
||||
}
|
||||
|
||||
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
|
||||
}
|
||||
catch (CoreException $e) {
|
||||
}
|
||||
|
||||
return self::SEARCH_WIDGET_TYPE_RAW;
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "ExtKey";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "INT(11)".($bFullSpec ? " DEFAULT 0" : "");
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsExternalKey($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this->Get("targetclass");
|
||||
}
|
||||
|
||||
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function GetKeyAttCode()
|
||||
{
|
||||
return $this->GetCode();
|
||||
}
|
||||
|
||||
public function GetDisplayStyle()
|
||||
{
|
||||
return $this->GetOptional('display_style', 'select');
|
||||
}
|
||||
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('disable_mandatory_ext_keys')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->Get("is_null_allowed");
|
||||
}
|
||||
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return parent::GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
// overloaded here so that an ext key always have the answer to
|
||||
// "what are your possible values?"
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oValSetDef = $this->Get("allowed_values");
|
||||
if (!$oValSetDef) {
|
||||
// Let's propose every existing value
|
||||
$oValSetDef = new ValueSetObjects('SELECT '.$this->GetTargetClass());
|
||||
}
|
||||
|
||||
return $oValSetDef;
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
//throw new Exception("GetAllowedValues on ext key has been deprecated");
|
||||
try {
|
||||
return parent::GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// Some required arguments could not be found, enlarge to any existing value
|
||||
$oValSetDef = new ValueSetObjects('SELECT '.$this->GetTargetClass());
|
||||
|
||||
return $oValSetDef->GetValues($aArgs, $sContains);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAllowedValuesForSelect($aArgs = array(), $sContains = '')
|
||||
{
|
||||
//$this->GetValuesDef();
|
||||
$oValSetDef = new ValueSetObjects('SELECT '.$this->GetTargetClass());
|
||||
|
||||
return $oValSetDef->GetValuesForAutocomplete($aArgs, $sContains);
|
||||
}
|
||||
|
||||
|
||||
public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
return DBObjectSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
|
||||
}
|
||||
|
||||
public function GetDeletionPropagationOption()
|
||||
{
|
||||
return $this->Get("on_target_delete");
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return ($proposedValue == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return ((int)$proposedValue) !== 0;
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return 0;
|
||||
}
|
||||
if ($proposedValue === '') {
|
||||
return 0;
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue)) {
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
/** @inheritdoc @since 3.1 */
|
||||
public function WriteExternalValues(DBObject $oHostObject): void
|
||||
{
|
||||
$sTargetKey = $oHostObject->Get($this->GetCode());
|
||||
$oFilter = DBSearch::FromOQL('SELECT `'.TemporaryObjectDescriptor::class.'` WHERE item_class=:class AND item_id=:id');
|
||||
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
|
||||
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
|
||||
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
|
||||
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
|
||||
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
|
||||
$oTemporaryObjectDescriptor->DBUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMaximumComboLength()
|
||||
{
|
||||
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
|
||||
}
|
||||
|
||||
public function GetMinAutoCompleteChars()
|
||||
{
|
||||
return $this->GetOptional('min_autocomplete_chars', MetaModel::GetConfig()->Get('min_autocomplete_chars'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetMaxAutoCompleteResults(): int
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('max_autocomplete_results');
|
||||
}
|
||||
|
||||
public function AllowTargetCreation()
|
||||
{
|
||||
return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
*
|
||||
* @return null | AttributeDefinition
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
$oRet = null;
|
||||
$sRemoteClass = $this->GetTargetClass();
|
||||
foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
|
||||
if (!$oRemoteAttDef->IsLinkSet()) {
|
||||
continue;
|
||||
}
|
||||
if (!is_subclass_of($this->GetHostClass(),
|
||||
$oRemoteAttDef->GetLinkedClass()) && $oRemoteAttDef->GetLinkedClass() != $this->GetHostClass()) {
|
||||
continue;
|
||||
}
|
||||
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetCode()) {
|
||||
continue;
|
||||
}
|
||||
$oRet = $oRemoteAttDef;
|
||||
break;
|
||||
}
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\SelectObjectField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
|
||||
if ($oFormField === null) {
|
||||
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
// Setting params
|
||||
$oFormField->SetMaximumComboLength($this->GetMaximumComboLength());
|
||||
$oFormField->SetMinAutoCompleteChars($this->GetMinAutoCompleteChars());
|
||||
$oFormField->SetMaxAutoCompleteResults($this->GetMaxAutoCompleteResults());
|
||||
$oFormField->SetHierarchical(MetaModel::IsHierarchicalClass($this->GetTargetClass()));
|
||||
// Setting choices regarding the field dependencies
|
||||
$aFieldDependencies = $this->GetPrerequisiteAttributes();
|
||||
if (!empty($aFieldDependencies)) {
|
||||
$oTmpAttDef = $this;
|
||||
$oTmpField = $oFormField;
|
||||
$oFormField->SetOnFinalizeCallback(function () use ($oTmpField, $oTmpAttDef, $oObject) {
|
||||
/** @var $oTmpField \Combodo\iTop\Form\Field\Field */
|
||||
/** @var $oTmpAttDef \AttributeDefinition */
|
||||
/** @var $oObject \DBObject */
|
||||
|
||||
// We set search object only if it has not already been set (overrided)
|
||||
if ($oTmpField->GetSearch() === null) {
|
||||
$oSearch = DBSearch::FromOQL($oTmpAttDef->GetValuesDef()->GetFilterExpression());
|
||||
$oSearch->SetInternalParams(array('this' => $oObject));
|
||||
$oTmpField->SetSearch($oSearch);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
|
||||
$oSearch->SetInternalParams(array('this' => $oObject));
|
||||
$oFormField->SetSearch($oSearch);
|
||||
}
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_null($oHostObject)) {
|
||||
return $oHostObject->GetAsHTML($this->GetCode(), $oHostObject);
|
||||
}
|
||||
|
||||
return DBObject::MakeHyperLink($this->GetTargetClass(), $sValue);
|
||||
}
|
||||
}
|
||||
208
sources/Core/AttributeDefinition/AttributeFinalClass.php
Normal file
208
sources/Core/AttributeDefinition/AttributeFinalClass.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use MetaModel;
|
||||
use Str;
|
||||
|
||||
/**
|
||||
* The attribute dedicated to the finalclass automatic attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeFinalClass extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
public $m_sValue;
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$aParams["allowed_values"] = null;
|
||||
parent::__construct($sCode, $aParams);
|
||||
|
||||
$this->m_sValue = $this->Get("default_value");
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsMagic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SetFixedValue($sValue)
|
||||
{
|
||||
$this->m_sValue = $sValue;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
if ($bLocalize) {
|
||||
return MetaModel::GetName($sValue);
|
||||
} else {
|
||||
return $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum can be localized
|
||||
*
|
||||
* @param string $sProposedValue
|
||||
* @param bool $bLocalizedValue
|
||||
* @param string $sSepItem
|
||||
* @param string $sSepAttribute
|
||||
* @param string $sSepValue
|
||||
* @param string $sAttributeQualifier
|
||||
*
|
||||
* @return mixed|null|string
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function MakeValueFromString(
|
||||
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
|
||||
$sAttributeQualifier = null
|
||||
)
|
||||
{
|
||||
if ($bLocalizedValue) {
|
||||
// Lookup for the value matching the input
|
||||
//
|
||||
$sFoundValue = null;
|
||||
$aRawValues = self::GetAllowedValues();
|
||||
if (!is_null($aRawValues)) {
|
||||
foreach ($aRawValues as $sKey => $sValue) {
|
||||
if ($sProposedValue == $sValue) {
|
||||
$sFoundValue = $sKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($sFoundValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->MakeRealValue($sFoundValue, null);
|
||||
} else {
|
||||
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue,
|
||||
$sAttributeQualifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Because this is sometimes used to get a localized/string version of an attribute...
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return MetaModel::GetName($sValue);
|
||||
}
|
||||
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
// JSON values are NOT localized
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if ($bLocalize && $value != '') {
|
||||
$sRawValue = MetaModel::GetName($value);
|
||||
} else {
|
||||
$sRawValue = $value;
|
||||
}
|
||||
|
||||
return parent::GetAsCSV($sRawValue, $sSeparator, $sTextQualifier, null, false, $bConvertToPlainText);
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
if ($bLocalize) {
|
||||
$sRawValue = MetaModel::GetName($value);
|
||||
} else {
|
||||
$sRawValue = $value;
|
||||
}
|
||||
|
||||
return Str::pure2xml($sRawValue);
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return MetaModel::GetName($sValue);
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$aRawValues = MetaModel::EnumChildClasses($this->GetHostClass(), ENUM_CHILD_CLASSES_ALL);
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sClass) {
|
||||
$aLocalizedValues[$sClass] = MetaModel::GetName($sClass);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 2.7.0 N°2272 OQL perf finalclass in all intermediary tables
|
||||
*/
|
||||
public function CopyOnAllTables()
|
||||
{
|
||||
$sClass = self::GetHostClass();
|
||||
if (MetaModel::IsLeafClass($sClass)) {
|
||||
// Leaf class, no finalclass
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
211
sources/Core/AttributeDefinition/AttributeFriendlyName.php
Normal file
211
sources/Core/AttributeDefinition/AttributeFriendlyName.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use Str;
|
||||
|
||||
/**
|
||||
* The attribute dedicated to the friendly name automatic attribute (not written)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeFriendlyName extends AttributeDefinition
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
public $m_sValue;
|
||||
|
||||
public function __construct($sCode)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$aParams = array();
|
||||
$aParams["default_value"] = '';
|
||||
parent::__construct($sCode, $aParams);
|
||||
|
||||
$this->m_sValue = $this->Get("default_value");
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
// Code duplicated with AttributeObsolescenceFlag
|
||||
$aAttributes = $this->GetOptional("depends_on", array());
|
||||
$oExpression = $this->GetOQLExpression();
|
||||
foreach ($oExpression->ListRequiredFields() as $sAttCode) {
|
||||
if (!in_array($sAttCode, $aAttributes)) {
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '') {
|
||||
$sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property
|
||||
}
|
||||
|
||||
return array('' => $sPrefix);
|
||||
}
|
||||
|
||||
public static function IsBasedOnOQLExpression()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetOQLExpression()
|
||||
{
|
||||
return MetaModel::GetNameExpression($this->GetHostClass());
|
||||
}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sLabel = parent::GetLabel('');
|
||||
if (strlen($sLabel) == 0) {
|
||||
$sLabel = Dict::S('Core:FriendlyName-Label');
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sLabel = parent::GetDescription('');
|
||||
if (strlen($sLabel) == 0) {
|
||||
$sLabel = Dict::S('Core:FriendlyName-Description');
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$sValue = $aCols[$sPrefix];
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsMagic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function SetFixedValue($sValue)
|
||||
{
|
||||
$this->m_sValue = $sValue;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2html((string)$sValue);
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
|
||||
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\StringField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
$oFormField->SetReadOnly(true);
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
// Do not display friendly names in the history of change
|
||||
public function DescribeChangeAsHTML($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array("=" => "equals", "!=" => "differs from");
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "Contains";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '=':
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." $sOpCode $sQValue";
|
||||
case 'Contains':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value%");
|
||||
case 'NotLike':
|
||||
return $this->GetSQLExpr()." NOT LIKE $sQValue";
|
||||
case 'Like':
|
||||
default:
|
||||
return $this->GetSQLExpr()." LIKE $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
57
sources/Core/AttributeDefinition/AttributeHTML.php
Normal file
57
sources/Core/AttributeDefinition/AttributeHTML.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
/**
|
||||
* Map a text column (size > ?), containing HTML code, to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeHTML extends AttributeLongText
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->Get('sql')] = $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
|
||||
}
|
||||
}
|
||||
194
sources/Core/AttributeDefinition/AttributeHierarchicalKey.php
Normal file
194
sources/Core/AttributeDefinition/AttributeHierarchicalKey.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObjectSearch;
|
||||
|
||||
/**
|
||||
* Special kind of External Key to manage a hierarchy of objects
|
||||
*/
|
||||
class AttributeHierarchicalKey extends AttributeExternalKey
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
|
||||
|
||||
protected $m_sTargetClass;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
$aParams = parent::ListExpectedParams();
|
||||
$idx = array_search('targetclass', $aParams);
|
||||
unset($aParams[$idx]);
|
||||
$idx = array_search('jointype', $aParams);
|
||||
unset($aParams[$idx]);
|
||||
|
||||
return $aParams; // Later: mettre les bons parametres ici !!
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "ExtKey";
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The target class is the class for which the attribute has been defined first
|
||||
*/
|
||||
public function SetHostClass($sHostClass)
|
||||
{
|
||||
if (!isset($this->m_sTargetClass)) {
|
||||
$this->m_sTargetClass = $sHostClass;
|
||||
}
|
||||
parent::SetHostClass($sHostClass);
|
||||
}
|
||||
|
||||
public static function IsHierarchicalKey()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this->m_sTargetClass;
|
||||
}
|
||||
|
||||
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function GetKeyAttCode()
|
||||
{
|
||||
return $this->GetCode();
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return parent::GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'INT(11)'.($bFullSpec ? ' DEFAULT 0' : '');
|
||||
$aColumns[$this->GetSQLLeft()] = 'INT(11)'.($bFullSpec ? ' DEFAULT 0' : '');
|
||||
$aColumns[$this->GetSQLRight()] = 'INT(11)'.($bFullSpec ? ' DEFAULT 0' : '');
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetSQLRight()
|
||||
{
|
||||
return $this->GetCode().'_right';
|
||||
}
|
||||
|
||||
public function GetSQLLeft()
|
||||
{
|
||||
return $this->GetCode().'_left';
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
$aValues[$this->GetCode()] = $value;
|
||||
} else {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode()] = $value[$this->GetCode()];
|
||||
$aValues[$this->GetSQLRight()] = $value[$this->GetSQLRight()];
|
||||
$aValues[$this->GetSQLLeft()] = $value[$this->GetSQLLeft()];
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains);
|
||||
if ($oFilter) {
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
$oValSetDef->SetCondition($oFilter);
|
||||
|
||||
return $oValSetDef->GetValues($aArgs, $sContains);
|
||||
} else {
|
||||
return parent::GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
|
||||
if ($oFilter) {
|
||||
$oValSetDef->SetCondition($oFilter);
|
||||
}
|
||||
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
|
||||
if ($oFilter) {
|
||||
return $oFilter;
|
||||
}
|
||||
|
||||
return parent::GetAllowedValuesAsFilter($aArgs, $sContains, $iAdditionalValue);
|
||||
}
|
||||
|
||||
private function GetHierachicalFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||
{
|
||||
if (array_key_exists('this', $aArgs)) {
|
||||
// Hierarchical keys have one more constraint: the "parent value" cannot be
|
||||
// "under" themselves
|
||||
$iRootId = $aArgs['this']->GetKey();
|
||||
if ($iRootId > 0) // ignore objects that do no exist in the database...
|
||||
{
|
||||
$sClass = $this->m_sTargetClass;
|
||||
|
||||
return DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
*
|
||||
* @return null | AttributeDefinition
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
45
sources/Core/AttributeDefinition/AttributeIPAddress.php
Normal file
45
sources/Core/AttributeDefinition/AttributeIPAddress.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
/**
|
||||
* Specialization of a string: IP address
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeIPAddress extends AttributeString
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
$sNum = '(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])';
|
||||
|
||||
return "^($sNum\\.$sNum\\.$sNum\\.$sNum)$";
|
||||
}
|
||||
|
||||
public function GetOrderBySQLExpressions($sClassAlias)
|
||||
{
|
||||
// Note: This is the responsibility of this function to place backticks around column aliases
|
||||
return array('INET_ATON(`'.$sClassAlias.$this->GetCode().'`)');
|
||||
}
|
||||
}
|
||||
207
sources/Core/AttributeDefinition/AttributeImage.php
Normal file
207
sources/Core/AttributeDefinition/AttributeImage.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use HTMLSanitizer;
|
||||
use ormDocument;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* An image is a specific type of document, it is stored as several columns in the database
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeImage extends AttributeBlob
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function Get($sParamName)
|
||||
{
|
||||
$oParamValue = parent::Get($sParamName);
|
||||
|
||||
if ($sParamName === 'default_image') {
|
||||
/** @noinspection NestedPositiveIfStatementsInspection */
|
||||
if (!empty($oParamValue)) {
|
||||
return utils::GetAbsoluteUrlModulesRoot().$oParamValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $oParamValue;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Image";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see AttributeBlob::MakeRealValue()
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
|
||||
|
||||
if (($oDoc instanceof ormDocument)
|
||||
&& (false === $oDoc->IsEmpty())
|
||||
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
|
||||
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
|
||||
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
|
||||
}
|
||||
|
||||
// The validation of the MIME Type is done by CheckFormat below
|
||||
return $oDoc;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return new ormDocument('', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the supplied ormDocument actually contains an image
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see AttributeDefinition::CheckFormat()
|
||||
*/
|
||||
public function CheckFormat($value)
|
||||
{
|
||||
if ($value instanceof ormDocument && !$value->IsEmpty()) {
|
||||
return ($value->GetMainMimeType() == 'image');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see edit_image.js for JS generated markup in form edition
|
||||
*
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @param ormDocument $value
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sRet = '';
|
||||
$bIsCustomImage = false;
|
||||
|
||||
$iMaxWidth = $this->Get('display_max_width');
|
||||
$sMaxWidthPx = $iMaxWidth.'px';
|
||||
$iMaxHeight = $this->Get('display_max_height');
|
||||
$sMaxHeightPx = $iMaxHeight.'px';
|
||||
|
||||
$sDefaultImageUrl = $this->Get('default_image');
|
||||
if ($sDefaultImageUrl !== null) {
|
||||
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $sMaxWidthPx, $sMaxHeightPx);
|
||||
}
|
||||
|
||||
$sCustomImageUrl = $this->GetAttributeImageFileUrl($value, $oHostObject);
|
||||
if ($sCustomImageUrl !== null) {
|
||||
$bIsCustomImage = true;
|
||||
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $sMaxWidthPx, $sMaxHeightPx);
|
||||
}
|
||||
|
||||
$sCssClasses = 'ibo-input-image--image-view attribute-image';
|
||||
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
|
||||
|
||||
// Important: If you change this, mind updating edit_image.js as well
|
||||
return '<div class="'.$sCssClasses.'" style="max-width: min('.$sMaxWidthPx.',100%); max-height: min('.$sMaxHeightPx.',100%); aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sUrl
|
||||
* @param int $iMaxWidthPx
|
||||
* @param int $iMaxHeightPx
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 2.6.0 new private method
|
||||
* @since 2.7.0 change visibility to protected
|
||||
*/
|
||||
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx)
|
||||
{
|
||||
return '<img src="'.$sUrl.'" style="max-width: min('.$iMaxWidthPx.',100%); max-height: min('.$iMaxHeightPx.',100%)">';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormDocument $value
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
* @return null|string
|
||||
*
|
||||
* @since 2.6.0 new private method
|
||||
* @since 2.7.0 change visibility to protected
|
||||
*/
|
||||
protected function GetAttributeImageFileUrl($value, $oHostObject)
|
||||
{
|
||||
if (!is_object($value)) {
|
||||
return null;
|
||||
}
|
||||
if ($value->IsEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$bExistingImageModified = ($oHostObject->IsModified() && (array_key_exists($this->GetCode(), $oHostObject->ListChanges())));
|
||||
if ($oHostObject->IsNew() || ($bExistingImageModified)) {
|
||||
// If the object is modified (or not yet stored in the database) we must serve the content of the image directly inline
|
||||
// otherwise (if we just give an URL) the browser will be given the wrong content... and may cache it
|
||||
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
|
||||
}
|
||||
|
||||
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\ImageField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// Generating urls
|
||||
$value = $oObject->Get($this->GetCode());
|
||||
if (is_object($value) && !$value->IsEmpty()) {
|
||||
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
|
||||
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
|
||||
} else {
|
||||
$oDefaultImage = $this->Get('default_image');
|
||||
if (is_object($oDefaultImage) && !$oDefaultImage->IsEmpty()) {
|
||||
$oFormField->SetDownloadUrl($oDefaultImage);
|
||||
$oFormField->SetDisplayUrl($oDefaultImage);
|
||||
}
|
||||
}
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
}
|
||||
148
sources/Core/AttributeDefinition/AttributeInteger.php
Normal file
148
sources/Core/AttributeDefinition/AttributeInteger.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use CoreException;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map an integer column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeInteger extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "INT(11)".($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return "^[0-9]+$";
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"!=" => "differs from",
|
||||
"=" => "equals",
|
||||
">" => "greater (strict) than",
|
||||
">=" => "greater than",
|
||||
"<" => "less (strict) than",
|
||||
"<=" => "less than",
|
||||
"in" => "in",
|
||||
);
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
// Unless we implement an "equals approximately..." or "same order of magnitude"
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '>':
|
||||
return $this->GetSQLExpr()." > $sQValue";
|
||||
break;
|
||||
case '>=':
|
||||
return $this->GetSQLExpr()." >= $sQValue";
|
||||
break;
|
||||
case '<':
|
||||
return $this->GetSQLExpr()." < $sQValue";
|
||||
break;
|
||||
case '<=':
|
||||
return $this->GetSQLExpr()." <= $sQValue";
|
||||
break;
|
||||
case 'in':
|
||||
if (!is_array($value)) {
|
||||
throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')");
|
||||
}
|
||||
|
||||
return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')";
|
||||
break;
|
||||
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = \"$value\"";
|
||||
}
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return is_null($proposedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return utils::IsNotNullOrEmptyString($proposedValue);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return null;
|
||||
}
|
||||
if ($proposedValue === '') {
|
||||
return null;
|
||||
} // 0 is transformed into '' !
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
assert(is_numeric($value) || is_null($value));
|
||||
|
||||
return $value; // supposed to be an int
|
||||
}
|
||||
}
|
||||
940
sources/Core/AttributeDefinition/AttributeLinkedSet.php
Normal file
940
sources/Core/AttributeDefinition/AttributeLinkedSet.php
Normal file
@@ -0,0 +1,940 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
|
||||
use Combodo\iTop\Form\Field\LinkedSetField;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
use CoreException;
|
||||
use CSVParser;
|
||||
use DBObject;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use ExceptionLog;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use ormLinkSet;
|
||||
use ValueSetObjects;
|
||||
|
||||
/**
|
||||
* Set of objects directly linked to an object, and being part of its definition
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeLinkedSet extends AttributeDefinition
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-set';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(),
|
||||
array("allowed_values", "depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "LinkedSet";
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function IsBulkModifyCompatible(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsLinkSet()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsIndirect()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oValSetDef = $this->Get("allowed_values");
|
||||
if (!$oValSetDef) {
|
||||
// Let's propose every existing value
|
||||
$oValSetDef = new ValueSetObjects('SELECT '.LinkSetModel::GetTargetClass($this));
|
||||
}
|
||||
|
||||
return $oValSetDef;
|
||||
}
|
||||
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
/** @var ormLinkSet $value * */
|
||||
if ($value->Count() === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** Return linked objects key as string **/
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return implode(' ', $aValues);
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
return $this->Get("depends_on");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject|null $oHostObject
|
||||
*
|
||||
* @return \ormLinkSet
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws CoreException
|
||||
* @throws CoreWarning
|
||||
*/
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
if ($oHostObject === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sLinkClass = $this->GetLinkedClass();
|
||||
$sExtKeyToMe = $this->GetExtKeyToMe();
|
||||
|
||||
// The class to target is not the current class, because if this is a derived class,
|
||||
// it may differ from the target class, then things start to become confusing
|
||||
/** @var AttributeExternalKey $oRemoteExtKeyAtt */
|
||||
$oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe);
|
||||
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
|
||||
|
||||
$oMyselfSearch = new DBObjectSearch($sMyClass);
|
||||
if ($oHostObject !== null) {
|
||||
$oMyselfSearch->AddCondition('id', $oHostObject->GetKey(), '=');
|
||||
}
|
||||
|
||||
$oLinkSearch = new DBObjectSearch($sLinkClass);
|
||||
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
|
||||
if ($this->IsIndirect()) {
|
||||
// Join the remote class so that the archive flag will be taken into account
|
||||
/** @var AttributeLinkedSetIndirect $this */
|
||||
$sExtKeyToRemote = $this->GetExtKeyToRemote();
|
||||
/** @var AttributeExternalKey $oExtKeyToRemote */
|
||||
$oExtKeyToRemote = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToRemote);
|
||||
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
|
||||
if (MetaModel::IsArchivable($sRemoteClass)) {
|
||||
$oRemoteSearch = new DBObjectSearch($sRemoteClass);
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oLinkSearch->AddCondition_PointingTo($oRemoteSearch, $this->GetExtKeyToRemote());
|
||||
}
|
||||
}
|
||||
$oLinks = new DBObjectSet($oLinkSearch);
|
||||
$oLinkSet = new ormLinkSet($this->GetHostClass(), $this->GetCode(), $oLinks);
|
||||
|
||||
return $oLinkSet;
|
||||
}
|
||||
|
||||
public function GetTrackingLevel()
|
||||
{
|
||||
return $this->GetOptional('tracking_level', MetaModel::GetConfig()->Get('tracking_level_linked_set_default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_EDITMODE_* constants
|
||||
*/
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int see LINKSET_EDITWHEN_* constants
|
||||
* @since 3.1.1 3.2.0 N°6385
|
||||
*/
|
||||
public function GetEditWhen(): int
|
||||
{
|
||||
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_DISPLAY_STYLE_* constants
|
||||
* @since 3.1.0 N°3190
|
||||
*/
|
||||
public function GetDisplayStyle()
|
||||
{
|
||||
$sDisplayStyle = $this->GetOptional('display_style', LINKSET_DISPLAY_STYLE_TAB);
|
||||
if ($sDisplayStyle === '') {
|
||||
$sDisplayStyle = LINKSET_DISPLAY_STYLE_TAB;
|
||||
}
|
||||
|
||||
return $sDisplayStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the current Attribute has constraints (php constraints or datamodel constraints)
|
||||
*
|
||||
* @return bool true if Attribute has constraints
|
||||
* @since 3.1.0 N°6228
|
||||
*/
|
||||
public function HasPHPConstraint(): bool
|
||||
{
|
||||
return $this->GetOptional('with_php_constraint', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true if Attribute has computation (DB_LINKS_CHANGED event propagation, `with_php_computation` attribute xml property), false otherwise
|
||||
* @since 3.1.1 3.2.0 N°6228
|
||||
*/
|
||||
public function HasPHPComputation(): bool
|
||||
{
|
||||
return $this->GetOptional('with_php_computation', false);
|
||||
}
|
||||
|
||||
public function GetLinkedClass()
|
||||
{
|
||||
return $this->Get('linked_class');
|
||||
}
|
||||
|
||||
public function GetExtKeyToMe()
|
||||
{
|
||||
return $this->Get('ext_key_to_me');
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @inheritDoc * */
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB) {
|
||||
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
|
||||
} else {
|
||||
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
|
||||
$sValue->Rewind();
|
||||
$aItems = array();
|
||||
while ($oObj = $sValue->Fetch()) {
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach (MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef) {
|
||||
if ($sAttCode == $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField()) {
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsHTML($sAttCode);
|
||||
if (strlen($sAttValue) > 0) {
|
||||
$aAttributes[] = $sAttValue;
|
||||
}
|
||||
}
|
||||
$sAttributes = implode(', ', $aAttributes);
|
||||
$aItems[] = $sAttributes;
|
||||
}
|
||||
|
||||
return implode('<br/>', $aItems);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
{
|
||||
try {
|
||||
|
||||
/** @var ormLinkSet $sValue */
|
||||
if (is_null($sValue) || $sValue->Count() === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$oLinkSetBlock = new BlockLinkSetDisplayAsProperty($this->GetCode(), $this, $sValue);
|
||||
|
||||
return ConsoleBlockRenderer::RenderBlockTemplates($oLinkSetBlock);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sMessage = "Error while displaying attribute {$this->GetCode()}";
|
||||
IssueLog::Error($sMessage, IssueLog::CHANNEL_DEFAULT, [
|
||||
'host_object_class' => $this->GetHostClass(),
|
||||
'host_object_key' => $oHostObject->GetKey(),
|
||||
'attribute' => $this->GetCode(),
|
||||
]);
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
|
||||
$sValue->Rewind();
|
||||
$sRes = "<Set>\n";
|
||||
while ($oObj = $sValue->Fetch()) {
|
||||
$sObjClass = get_class($oObj);
|
||||
$sRes .= "<$sObjClass id=\"".$oObj->GetKey()."\">\n";
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
|
||||
if ($sAttCode == 'finalclass') {
|
||||
if ($sObjClass == $this->GetLinkedClass()) {
|
||||
// Simplify the output if the exact class could be determined implicitely
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($sAttCode == $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField()) {
|
||||
/** @var \AttributeExternalField $oAttDef */
|
||||
if ($oAttDef->GetKeyAttCode() == $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
/** @var AttributeExternalField $oAttDef */
|
||||
if ($oAttDef->IsFriendlyName()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($oAttDef instanceof AttributeFriendlyName) {
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsScalar()) {
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsXML($sAttCode, $bLocalize);
|
||||
$sRes .= "<$sAttCode>$sAttValue</$sAttCode>\n";
|
||||
}
|
||||
$sRes .= "</$sObjClass>\n";
|
||||
}
|
||||
$sRes .= "</Set>\n";
|
||||
} else {
|
||||
$sRes = '';
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sValue
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return mixed|string
|
||||
* @throws CoreException
|
||||
*/
|
||||
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');
|
||||
$sSepValue = MetaModel::GetConfig()->Get('link_set_value_separator');
|
||||
$sAttributeQualifier = MetaModel::GetConfig()->Get('link_set_attribute_qualifier');
|
||||
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
|
||||
$sValue->Rewind();
|
||||
$aItems = array();
|
||||
while ($oObj = $sValue->Fetch()) {
|
||||
$sObjClass = get_class($oObj);
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
|
||||
if ($sAttCode == 'finalclass') {
|
||||
if ($sObjClass == $this->GetLinkedClass()) {
|
||||
// Simplify the output if the exact class could be determined implicitely
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($sAttCode == $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField()) {
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsBasedOnDBColumns()) {
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsScalar()) {
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsCSV($sAttCode, $sSepValue, '', $bLocalize);
|
||||
if (strlen($sAttValue) > 0) {
|
||||
$sAttributeData = str_replace($sAttributeQualifier, $sAttributeQualifier.$sAttributeQualifier,
|
||||
$sAttCode.$sSepValue.$sAttValue);
|
||||
$aAttributes[] = $sAttributeQualifier.$sAttributeData.$sAttributeQualifier;
|
||||
}
|
||||
}
|
||||
$sAttributes = implode($sSepAttribute, $aAttributes);
|
||||
$aItems[] = $sAttributes;
|
||||
}
|
||||
$sRes = implode($sSepItem, $aItems);
|
||||
} else {
|
||||
$sRes = '';
|
||||
}
|
||||
$sRes = str_replace($sTextQualifier, $sTextQualifier.$sTextQualifier, $sRes);
|
||||
$sRes = $sTextQualifier.$sRes.$sTextQualifier;
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available verbs for 'GetForTemplate'
|
||||
*/
|
||||
public function EnumTemplateVerbs()
|
||||
{
|
||||
return array(
|
||||
'' => 'Plain text (unlocalized) representation',
|
||||
'html' => 'HTML representation (unordered list)',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param mixed $value The current value of the field
|
||||
* @param string $sVerb The verb specifying the representation of the value
|
||||
* @param DBObject $oHostObject The object
|
||||
* @param bool $bLocalize Whether or not to localize the value
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sRemoteName = $this->IsIndirect() ?
|
||||
/** @var AttributeLinkedSetIndirect $this */
|
||||
$this->GetExtKeyToRemote().'_friendlyname' : 'friendlyname';
|
||||
|
||||
$oLinkSet = clone $value; // Workaround/Safety net for Trac #887
|
||||
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
|
||||
$iCount = 0;
|
||||
$aNames = array();
|
||||
foreach ($oLinkSet as $oItem) {
|
||||
if (($iLimit > 0) && ($iCount == $iLimit)) {
|
||||
$iTotal = $oLinkSet->Count();
|
||||
$aNames[] = '... '.Dict::Format('UI:TruncatedResults', $iCount, $iTotal);
|
||||
break;
|
||||
}
|
||||
$aNames[] = $oItem->Get($sRemoteName);
|
||||
$iCount++;
|
||||
}
|
||||
|
||||
switch ($sVerb) {
|
||||
case '':
|
||||
return implode("\n", $aNames);
|
||||
|
||||
case 'html':
|
||||
return '<ul><li>'.implode("</li><li>", $aNames).'</li></ul>';
|
||||
|
||||
default:
|
||||
throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
|
||||
}
|
||||
}
|
||||
|
||||
public function DuplicatesAllowed()
|
||||
{
|
||||
return false;
|
||||
} // No duplicates for 1:n links, never
|
||||
|
||||
public function GetImportColumns()
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'MEDIUMTEXT'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sProposedValue
|
||||
* @param bool $bLocalizedValue
|
||||
* @param string $sSepItem
|
||||
* @param string $sSepAttribute
|
||||
* @param string $sSepValue
|
||||
* @param string $sAttributeQualifier
|
||||
*
|
||||
* @return DBObjectSet
|
||||
* @throws CSVParserException
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws MissingQueryArgument
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeValueFromString(
|
||||
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
|
||||
$sAttributeQualifier = null
|
||||
)
|
||||
{
|
||||
if (is_null($sSepItem) || empty($sSepItem)) {
|
||||
$sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
|
||||
}
|
||||
if (is_null($sSepAttribute) || empty($sSepAttribute)) {
|
||||
$sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
|
||||
}
|
||||
if (is_null($sSepValue) || empty($sSepValue)) {
|
||||
$sSepValue = MetaModel::GetConfig()->Get('link_set_value_separator');
|
||||
}
|
||||
if (is_null($sAttributeQualifier) || empty($sAttributeQualifier)) {
|
||||
$sAttributeQualifier = MetaModel::GetConfig()->Get('link_set_attribute_qualifier');
|
||||
}
|
||||
|
||||
$sTargetClass = $this->Get('linked_class');
|
||||
|
||||
$sInput = str_replace($sSepItem, "\n", $sProposedValue);
|
||||
$oCSVParser = new CSVParser($sInput, $sSepAttribute, $sAttributeQualifier);
|
||||
|
||||
$aInput = $oCSVParser->ToArray(0 /* do not skip lines */);
|
||||
|
||||
$aLinks = array();
|
||||
foreach ($aInput as $aRow) {
|
||||
// 1st - get the values, split the extkey->searchkey specs, and eventually get the finalclass value
|
||||
$aExtKeys = array();
|
||||
$aValues = array();
|
||||
foreach ($aRow as $sCell) {
|
||||
$iSepPos = strpos($sCell, $sSepValue);
|
||||
if ($iSepPos === false) {
|
||||
// Houston...
|
||||
throw new CoreException('Wrong format for link attribute specification', array('value' => $sCell));
|
||||
}
|
||||
|
||||
$sAttCode = trim(substr($sCell, 0, $iSepPos));
|
||||
$sValue = substr($sCell, $iSepPos + strlen($sSepValue));
|
||||
|
||||
if (preg_match('/^(.+)->(.+)$/', $sAttCode, $aMatches)) {
|
||||
$sKeyAttCode = $aMatches[1];
|
||||
$sRemoteAttCode = $aMatches[2];
|
||||
$aExtKeys[$sKeyAttCode][$sRemoteAttCode] = $sValue;
|
||||
if (!MetaModel::IsValidAttCode($sTargetClass, $sKeyAttCode)) {
|
||||
throw new CoreException('Wrong attribute code for link attribute specification',
|
||||
array('class' => $sTargetClass, 'attcode' => $sKeyAttCode));
|
||||
}
|
||||
/** @var \AttributeExternalKey $oKeyAttDef */
|
||||
$oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
|
||||
$sRemoteClass = $oKeyAttDef->GetTargetClass();
|
||||
if (!MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode)) {
|
||||
throw new CoreException('Wrong attribute code for link attribute specification',
|
||||
array('class' => $sRemoteClass, 'attcode' => $sRemoteAttCode));
|
||||
}
|
||||
} else {
|
||||
if (!MetaModel::IsValidAttCode($sTargetClass, $sAttCode)) {
|
||||
throw new CoreException('Wrong attribute code for link attribute specification',
|
||||
array('class' => $sTargetClass, 'attcode' => $sAttCode));
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sTargetClass, $sAttCode);
|
||||
$aValues[$sAttCode] = $oAttDef->MakeValueFromString($sValue, $bLocalizedValue, $sSepItem,
|
||||
$sSepAttribute, $sSepValue, $sAttributeQualifier);
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd - Instanciate the object and set the value
|
||||
if (isset($aValues['finalclass'])) {
|
||||
$sLinkClass = $aValues['finalclass'];
|
||||
if (!is_subclass_of($sLinkClass, $sTargetClass)) {
|
||||
throw new CoreException('Wrong class for link attribute specification',
|
||||
array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
|
||||
}
|
||||
} elseif (MetaModel::IsAbstract($sTargetClass)) {
|
||||
throw new CoreException('Missing finalclass for link attribute specification');
|
||||
} else {
|
||||
$sLinkClass = $sTargetClass;
|
||||
}
|
||||
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach ($aValues as $sAttCode => $sValue) {
|
||||
$oLink->Set($sAttCode, $sValue);
|
||||
}
|
||||
|
||||
// 3rd - Set external keys from search conditions
|
||||
foreach ($aExtKeys as $sKeyAttCode => $aReconciliation) {
|
||||
$oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
|
||||
$sKeyClass = $oKeyAttDef->GetTargetClass();
|
||||
$oExtKeyFilter = new DBObjectSearch($sKeyClass);
|
||||
$aReconciliationDesc = array();
|
||||
foreach ($aReconciliation as $sRemoteAttCode => $sValue) {
|
||||
$oExtKeyFilter->AddCondition($sRemoteAttCode, $sValue, '=');
|
||||
$aReconciliationDesc[] = "$sRemoteAttCode=$sValue";
|
||||
}
|
||||
$oExtKeySet = new DBObjectSet($oExtKeyFilter);
|
||||
switch ($oExtKeySet->Count()) {
|
||||
case 0:
|
||||
$sReconciliationDesc = implode(', ', $aReconciliationDesc);
|
||||
throw new CoreException("Found no match",
|
||||
array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
|
||||
break;
|
||||
case 1:
|
||||
$oRemoteObj = $oExtKeySet->Fetch();
|
||||
$oLink->Set($sKeyAttCode, $oRemoteObj->GetKey());
|
||||
break;
|
||||
default:
|
||||
$sReconciliationDesc = implode(', ', $aReconciliationDesc);
|
||||
throw new CoreException("Found several matches",
|
||||
array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
|
||||
// Found several matches, ambiguous
|
||||
}
|
||||
}
|
||||
|
||||
// Check (roughly) if such a link is valid
|
||||
$aErrors = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
|
||||
if ($oAttDef->IsExternalKey()) {
|
||||
/** @var \AttributeExternalKey $oAttDef */
|
||||
if (($oAttDef->GetTargetClass() == $this->GetHostClass()) || (is_subclass_of($this->GetHostClass(),
|
||||
$oAttDef->GetTargetClass()))) {
|
||||
continue; // Don't check the key to self
|
||||
}
|
||||
}
|
||||
|
||||
if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
|
||||
$aErrors[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
if (count($aErrors) > 0) {
|
||||
throw new CoreException("Missing value for mandatory attribute(s): ".implode(', ', $aErrors));
|
||||
}
|
||||
|
||||
$aLinks[] = $oLink;
|
||||
}
|
||||
$oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormLinkSet $value
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
$aRet = array();
|
||||
if (is_object($value) && ($value instanceof ormLinkSet)) {
|
||||
$value->Rewind();
|
||||
while ($oObj = $value->Fetch()) {
|
||||
$sObjClass = get_class($oObj);
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
|
||||
if ($sAttCode == 'finalclass') {
|
||||
if ($sObjClass == $this->GetLinkedClass()) {
|
||||
// Simplify the output if the exact class could be determined implicitely
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($sAttCode == $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField()) {
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsBasedOnDBColumns()) {
|
||||
continue;
|
||||
}
|
||||
if (!$oAttDef->IsScalar()) {
|
||||
continue;
|
||||
}
|
||||
$attValue = $oObj->Get($sAttCode);
|
||||
$aAttributes[$sAttCode] = $oAttDef->GetForJSON($attValue);
|
||||
}
|
||||
$aRet[] = $aAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return DBObjectSet
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
$sTargetClass = $this->Get('linked_class');
|
||||
|
||||
$aLinks = array();
|
||||
foreach ($json as $aValues) {
|
||||
if (isset($aValues['finalclass'])) {
|
||||
$sLinkClass = $aValues['finalclass'];
|
||||
if (!is_subclass_of($sLinkClass, $sTargetClass)) {
|
||||
throw new CoreException('Wrong class for link attribute specification',
|
||||
array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
|
||||
}
|
||||
} elseif (MetaModel::IsAbstract($sTargetClass)) {
|
||||
throw new CoreException('Missing finalclass for link attribute specification');
|
||||
} else {
|
||||
$sLinkClass = $sTargetClass;
|
||||
}
|
||||
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach ($aValues as $sAttCode => $sValue) {
|
||||
$oLink->Set($sAttCode, $sValue);
|
||||
}
|
||||
|
||||
// Check (roughly) if such a link is valid
|
||||
$aErrors = array();
|
||||
foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
|
||||
if ($oAttDef->IsExternalKey()) {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
if (($oAttDef->GetTargetClass() == $this->GetHostClass()) || (is_subclass_of($this->GetHostClass(),
|
||||
$oAttDef->GetTargetClass()))) {
|
||||
continue; // Don't check the key to self
|
||||
}
|
||||
}
|
||||
|
||||
if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
|
||||
$aErrors[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
if (count($aErrors) > 0) {
|
||||
throw new CoreException("Missing value for mandatory attribute(s): ".implode(', ', $aErrors));
|
||||
}
|
||||
|
||||
$aLinks[] = $oLink;
|
||||
}
|
||||
$oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $proposedValue
|
||||
* @param $oHostObj
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if ($proposedValue === null) {
|
||||
$sLinkedClass = $this->GetLinkedClass();
|
||||
$aLinkedObjectsArray = array();
|
||||
$oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
|
||||
|
||||
return new ormLinkSet(
|
||||
get_class($oHostObj),
|
||||
$this->GetCode(),
|
||||
$oSet
|
||||
);
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormLinkSet $val1
|
||||
* @param ormLinkSet $val2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
if ($val1 === $val2) {
|
||||
$bAreEquivalent = true;
|
||||
} else {
|
||||
$bAreEquivalent = ($val2->HasDelta() === false);
|
||||
}
|
||||
|
||||
return $bAreEquivalent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
*
|
||||
* @return null | AttributeDefinition
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
$oRemoteAtt = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToMe());
|
||||
|
||||
return $oRemoteAtt;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\LinkedSetField';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param LinkedSetField $oFormField
|
||||
*
|
||||
* @return LinkedSetField
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
// Setting target class
|
||||
if (!$this->IsIndirect()) {
|
||||
$sTargetClass = $this->GetLinkedClass();
|
||||
} else {
|
||||
/** @var \AttributeExternalKey $oRemoteAttDef */
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
|
||||
$sTargetClass = $oRemoteAttDef->GetTargetClass();
|
||||
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
|
||||
}
|
||||
$oFormField->SetTargetClass($sTargetClass);
|
||||
$oFormField->SetLinkedClass($this->GetLinkedClass());
|
||||
$oFormField->SetIndirect($this->IsIndirect());
|
||||
// Setting attcodes to display
|
||||
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
|
||||
// - Adding friendlyname attribute to the list is not already in it
|
||||
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
|
||||
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
|
||||
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
|
||||
}
|
||||
// - Adding attribute properties
|
||||
$aAttributesToDisplay = array();
|
||||
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
|
||||
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
|
||||
$aAttributesToDisplay[$sAttCodeToDisplay] = [
|
||||
'att_code' => $sAttCodeToDisplay,
|
||||
'label' => $oAttDefToDisplay->GetLabel(),
|
||||
];
|
||||
}
|
||||
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
|
||||
|
||||
// Append lnk attributes (filtered from zlist)
|
||||
if ($this->IsIndirect()) {
|
||||
$aLnkAttDefToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->m_sHostClass, $this->m_sCode);
|
||||
$aLnkAttributesToDisplay = array();
|
||||
foreach ($aLnkAttDefToDisplay as $oLnkAttDefToDisplay) {
|
||||
$aLnkAttributesToDisplay[$oLnkAttDefToDisplay->GetCode()] = [
|
||||
'att_code' => $oLnkAttDefToDisplay->GetCode(),
|
||||
'label' => $oLnkAttDefToDisplay->GetLabel(),
|
||||
'mandatory' => !$oLnkAttDefToDisplay->IsNullAllowed(),
|
||||
];
|
||||
}
|
||||
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
|
||||
}
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormLinkSet $proposedValue
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Protection against wrong value type
|
||||
if (false === ($proposedValue instanceof ormLinkSet)) {
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
// We test if there is at least 1 item in the linkset (new or existing), not if an item is being added to it.
|
||||
return $proposedValue->Count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* SearchSpecificLabel.
|
||||
*
|
||||
* @param string $sDictEntrySuffix
|
||||
* @param string $sDefault
|
||||
* @param bool $bUserLanguageOnly
|
||||
* @param ...$aArgs
|
||||
*
|
||||
* @return string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public function SearchSpecificLabel(string $sDictEntrySuffix, string $sDefault, bool $bUserLanguageOnly, ...$aArgs): string
|
||||
{
|
||||
try {
|
||||
$sNextClass = $this->m_sHostClass;
|
||||
|
||||
do {
|
||||
$sKey = "Class:{$sNextClass}/Attribute:{$this->m_sCode}/{$sDictEntrySuffix}";
|
||||
if (Dict::S($sKey, null, $bUserLanguageOnly) !== $sKey) {
|
||||
return Dict::Format($sKey, ...$aArgs);
|
||||
}
|
||||
$sNextClass = MetaModel::GetParentClass($sNextClass);
|
||||
} while ($sNextClass !== null);
|
||||
|
||||
if (Dict::S($sDictEntrySuffix, null, $bUserLanguageOnly) !== $sKey) {
|
||||
return Dict::Format($sDictEntrySuffix, ...$aArgs);
|
||||
} else {
|
||||
return $sDefault;
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
ExceptionLog::LogException($e);
|
||||
|
||||
return $sDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php
Normal file
106
sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CoreException;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* Set of objects linked to an object (n-n), and being part of its definition
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote"));
|
||||
}
|
||||
|
||||
public function IsIndirect()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetExtKeyToRemote()
|
||||
{
|
||||
return $this->Get('ext_key_to_remote');
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "LinkedSet";
|
||||
}
|
||||
|
||||
public function DuplicatesAllowed()
|
||||
{
|
||||
return $this->GetOptional("duplicates", false);
|
||||
} // The same object may be linked several times... or not...
|
||||
|
||||
public function GetTrackingLevel()
|
||||
{
|
||||
return $this->GetOptional('tracking_level',
|
||||
MetaModel::GetConfig()->Get('tracking_level_linked_set_indirect_default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the corresponding "link" attribute on the target class, if any
|
||||
*
|
||||
* @return null | AttributeDefinition
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetMirrorLinkAttribute()
|
||||
{
|
||||
$oRet = null;
|
||||
/** @var \AttributeExternalKey $oExtKeyToRemote */
|
||||
$oExtKeyToRemote = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
|
||||
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
|
||||
foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
|
||||
if (!$oRemoteAttDef instanceof AttributeLinkedSetIndirect) {
|
||||
continue;
|
||||
}
|
||||
if ($oRemoteAttDef->GetLinkedClass() != $this->GetLinkedClass()) {
|
||||
continue;
|
||||
}
|
||||
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetExtKeyToRemote()) {
|
||||
continue;
|
||||
}
|
||||
if ($oRemoteAttDef->GetExtKeyToRemote() != $this->GetExtKeyToMe()) {
|
||||
continue;
|
||||
}
|
||||
$oRet = $oRemoteAttDef;
|
||||
break;
|
||||
}
|
||||
|
||||
return $oRet;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function IsBulkModifyCompatible(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
56
sources/Core/AttributeDefinition/AttributeLongText.php
Normal file
56
sources/Core/AttributeDefinition/AttributeLongText.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOpSetAttributeHTML;
|
||||
use CMDBChangeOpSetAttributeLongText;
|
||||
use CMDBSource;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Map a log to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeLongText extends AttributeText
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "LONGTEXT".CMDBSource::GetSqlStringColumnDefinition();
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
// Is there a way to know the current limitation for mysql?
|
||||
// See mysql_field_len()
|
||||
return 65535 * 1024; // Limited... still 64 MB!
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return ($this->GetFormat() === 'html')
|
||||
? CMDBChangeOpSetAttributeHTML::class
|
||||
: CMDBChangeOpSetAttributeLongText::class;
|
||||
}
|
||||
}
|
||||
146
sources/Core/AttributeDefinition/AttributeMetaEnum.php
Normal file
146
sources/Core/AttributeDefinition/AttributeMetaEnum.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* A meta enum is an aggregation of enum from subclasses into an enum of a base class
|
||||
* It has been designed is to cope with the fact that statuses must be defined in leaf classes, while it makes sense to
|
||||
* have a superstatus available on the root classe(s)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeMetaEnum extends AttributeEnum
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array('allowed_values', 'sql', 'default_value', 'mapping');
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return false; // Well... this actually depends on the mapping
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
if (is_null($sClass)) {
|
||||
$sClass = $this->GetHostClass();
|
||||
}
|
||||
$aMappingData = $this->GetMapRule($sClass);
|
||||
if ($aMappingData == null) {
|
||||
$aRet = array();
|
||||
} else {
|
||||
$aRet = array($aMappingData['attcode']);
|
||||
}
|
||||
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload the standard so as to leave the data unsorted
|
||||
*
|
||||
* @param array $aArgs
|
||||
* @param string $sContains
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
if (!$oValSetDef) {
|
||||
return null;
|
||||
}
|
||||
$aRawValues = $oValSetDef->GetValueList();
|
||||
|
||||
if (is_null($aRawValues)) {
|
||||
return null;
|
||||
}
|
||||
$aLocalizedValues = array();
|
||||
foreach ($aRawValues as $sKey => $sValue) {
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the meta value for the given object.
|
||||
* See also MetaModel::RebuildMetaEnums() that must be maintained when MapValue changes
|
||||
*
|
||||
* @param $oObject
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MapValue($oObject)
|
||||
{
|
||||
$aMappingData = $this->GetMapRule(get_class($oObject));
|
||||
if ($aMappingData == null) {
|
||||
$sRet = $this->GetDefaultValue();
|
||||
} else {
|
||||
$sAttCode = $aMappingData['attcode'];
|
||||
$value = $oObject->Get($sAttCode);
|
||||
if (array_key_exists($value, $aMappingData['values'])) {
|
||||
$sRet = $aMappingData['values'][$value];
|
||||
} elseif ($this->GetDefaultValue() != '') {
|
||||
$sRet = $this->GetDefaultValue();
|
||||
} else {
|
||||
throw new Exception('AttributeMetaEnum::MapValue(): mapping not found for value "'.$value.'" in '.get_class($oObject).', on attribute '.MetaModel::GetAttributeOrigin($this->GetHostClass(),
|
||||
$this->GetCode()).'::'.$this->GetCode());
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetMapRule($sClass)
|
||||
{
|
||||
$aMappings = $this->Get('mapping');
|
||||
if (array_key_exists($sClass, $aMappings)) {
|
||||
$aMappingData = $aMappings[$sClass];
|
||||
} else {
|
||||
$sParent = MetaModel::GetParentClass($sClass);
|
||||
if (is_null($sParent)) {
|
||||
$aMappingData = null;
|
||||
} else {
|
||||
$aMappingData = $this->GetMapRule($sParent);
|
||||
}
|
||||
}
|
||||
|
||||
return $aMappingData;
|
||||
}
|
||||
}
|
||||
41
sources/Core/AttributeDefinition/AttributeOQL.php
Normal file
41
sources/Core/AttributeDefinition/AttributeOQL.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Specialization of a string: OQL expression
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeOQL extends AttributeText
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "OQLExpression";
|
||||
}
|
||||
}
|
||||
117
sources/Core/AttributeDefinition/AttributeObjectKey.php
Normal file
117
sources/Core/AttributeDefinition/AttributeObjectKey.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* An external key for which the class is defined as the value of another attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('class_attcode', 'is_null_allowed'));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "INT(11)".($bFullSpec ? " DEFAULT 0" : "");
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return $this->Get("is_null_allowed");
|
||||
}
|
||||
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return parent::GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return ($proposedValue == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return ((int)$proposedValue) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return 0;
|
||||
}
|
||||
if ($proposedValue === '') {
|
||||
return 0;
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue)) {
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Dict;
|
||||
use Exception;
|
||||
|
||||
class AttributeObsolescenceDate extends AttributeDate
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label', $sDefault);
|
||||
|
||||
return parent::GetLabel($sDefault);
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label+', $sDefault);
|
||||
|
||||
return parent::GetDescription($sDefault);
|
||||
}
|
||||
}
|
||||
134
sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php
Normal file
134
sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
|
||||
class AttributeObsolescenceFlag extends AttributeBoolean
|
||||
{
|
||||
public function __construct($sCode)
|
||||
{
|
||||
parent::__construct($sCode, array(
|
||||
"allowed_values" => null,
|
||||
"sql" => $sCode,
|
||||
"default_value" => "",
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
));
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsMagic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute value is built after other attributes by the mean of an expression (obtained via
|
||||
* GetOQLExpression)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsBasedOnOQLExpression()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetOQLExpression()
|
||||
{
|
||||
return MetaModel::GetObsolescenceExpression($this->GetHostClass());
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
return array();
|
||||
} // returns column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation)
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
return array();
|
||||
} // returns column/value pairs (1 in most of the cases), for WRITING (Insert, Update)
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
// Code duplicated with AttributeFriendlyName
|
||||
$aAttributes = $this->GetOptional("depends_on", array());
|
||||
$oExpression = $this->GetOQLExpression();
|
||||
foreach ($oExpression->ListRequiredFields() as $sClass => $sAttCode) {
|
||||
if (!in_array($sAttCode, $aAttributes)) {
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
public function IsDirectField()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSQLExpr()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->MakeRealValue(false, $oHostObject);
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeObsolescenceFlag/Label', $sDefault);
|
||||
|
||||
return parent::GetLabel($sDefault);
|
||||
}
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
$sDefault = Dict::S('Core:AttributeObsolescenceFlag/Label+', $sDefault);
|
||||
|
||||
return parent::GetDescription($sDefault);
|
||||
}
|
||||
}
|
||||
247
sources/Core/AttributeDefinition/AttributeOneWayPassword.php
Normal file
247
sources/Core/AttributeDefinition/AttributeOneWayPassword.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeOneWayPassword;
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use ormPassword;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* One way encrypted (hashed) password
|
||||
*/
|
||||
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("depends_on"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "One Way Password";
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return $this->GetOptional("is_null_allowed", false);
|
||||
}
|
||||
|
||||
// Facilitate things: allow the user to Set the value from a string or from an ormPassword (already encrypted)
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$oPassword = $proposedValue;
|
||||
if (is_object($oPassword)) {
|
||||
$oPassword = clone $proposedValue;
|
||||
} else {
|
||||
$oPassword = new ormPassword('', '');
|
||||
$oPassword->SetPassword($proposedValue);
|
||||
}
|
||||
|
||||
return $oPassword;
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '') {
|
||||
$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
|
||||
$aColumns[''] = $sPrefix.'_hash';
|
||||
$aColumns['_salt'] = $sPrefix.'_salt';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
if (!array_key_exists($sPrefix, $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
|
||||
}
|
||||
$hashed = isset($aCols[$sPrefix]) ? $aCols[$sPrefix] : '';
|
||||
|
||||
if (!array_key_exists($sPrefix.'_salt', $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '".$sPrefix."_salt' from {$sAvailable}");
|
||||
}
|
||||
$sSalt = isset($aCols[$sPrefix.'_salt']) ? $aCols[$sPrefix.'_salt'] : '';
|
||||
|
||||
$value = new ormPassword($hashed, $sSalt);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
// #@# Optimization: do not load blobs anytime
|
||||
// As per mySQL doc, selecting blob columns will prevent mySQL from
|
||||
// using memory in case a temporary table has to be created
|
||||
// (temporary tables created on disk)
|
||||
// We will have to remove the blobs from the list of attributes when doing the select
|
||||
// then the use of Get() should finalize the load
|
||||
if ($value instanceof ormPassword) {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode().'_hash'] = $value->GetHash();
|
||||
$aValues[$this->GetCode().'_salt'] = $value->GetSalt();
|
||||
} else {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode().'_hash'] = '';
|
||||
$aValues[$this->GetCode().'_salt'] = '';
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode().'_hash'] = 'TINYBLOB';
|
||||
$aColumns[$this->GetCode().'_salt'] = 'TINYBLOB';
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetImportColumns()
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'TINYTEXT'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function FromImportToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
if (!isset($aCols[$sPrefix])) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
|
||||
}
|
||||
$sClearPwd = $aCols[$sPrefix];
|
||||
|
||||
$oPassword = new ormPassword('', '');
|
||||
$oPassword->SetPassword($sClearPwd);
|
||||
|
||||
return $oPassword;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return 'true';
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
return $value->GetAsHTML();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
return ''; // Not exportable in CSV
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return ''; // Not exportable in XML
|
||||
}
|
||||
|
||||
public function GetValueLabel($sValue, $oHostObj = null)
|
||||
{
|
||||
// Don't display anything in "group by" reports
|
||||
return '*****';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Protection against wrong value type
|
||||
if (false === ($proposedValue instanceof ormPassword)) {
|
||||
// On object creation, the attribute value is "" instead of an ormPassword...
|
||||
if (is_string($proposedValue)) {
|
||||
return utils::IsNotNullOrEmptyString($proposedValue);
|
||||
}
|
||||
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
return $proposedValue->IsEmpty() === false;
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
if (is_null($original)) {
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prev_pwd", $original);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeOneWayPassword::class;
|
||||
}
|
||||
}
|
||||
75
sources/Core/AttributeDefinition/AttributePassword.php
Normal file
75
sources/Core/AttributeDefinition/AttributePassword.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use Exception;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map a varchar column (size < ?) to an attribute that must never be shown to the user
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Password";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "VARCHAR(64)"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (utils::IsNullOrEmptyString($sValue)) {
|
||||
return '';
|
||||
} else {
|
||||
return '******';
|
||||
}
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
return false;
|
||||
} // Cannot reliably compare two encrypted passwords since the same password will be encrypted in diffferent manners depending on the random 'salt'
|
||||
}
|
||||
61
sources/Core/AttributeDefinition/AttributePercentage.php
Normal file
61
sources/Core/AttributeDefinition/AttributePercentage.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Display an integer between 0 and 100 as a percentage / horizontal bar graph
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributePercentage extends AttributeInteger
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$iWidth = 5; // Total width of the percentage bar graph, in em...
|
||||
$iValue = (int)$sValue;
|
||||
if ($iValue > 100) {
|
||||
$iValue = 100;
|
||||
} else {
|
||||
if ($iValue < 0) {
|
||||
$iValue = 0;
|
||||
}
|
||||
}
|
||||
if ($iValue > 90) {
|
||||
$sColor = "#cc3300";
|
||||
} else {
|
||||
if ($iValue > 50) {
|
||||
$sColor = "#cccc00";
|
||||
} else {
|
||||
$sColor = "#33cc00";
|
||||
}
|
||||
}
|
||||
$iPercentWidth = ($iWidth * $iValue) / 100;
|
||||
|
||||
return "<div style=\"width:{$iWidth}em;-moz-border-radius: 3px;-webkit-border-radius: 3px;border-radius: 3px;display:inline-block;border: 1px #ccc solid;\"><div style=\"width:{$iPercentWidth}em; display:inline-block;background-color:$sColor;\"> </div></div> $sValue %";
|
||||
}
|
||||
}
|
||||
60
sources/Core/AttributeDefinition/AttributePhoneNumber.php
Normal file
60
sources/Core/AttributeDefinition/AttributePhoneNumber.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Specialization of a string: phone number
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributePhoneNumber extends AttributeString
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return $this->GetOptional('validation_pattern',
|
||||
'^'.utils::GetConfig()->Get('phone_number_validation_pattern').'$');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\PhoneField';
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$sUrlDecorationClass = utils::GetConfig()->Get('phone_number_decoration_class');
|
||||
$sUrlPattern = utils::GetConfig()->Get('phone_number_url_pattern');
|
||||
$sUrl = sprintf($sUrlPattern, $sValue);
|
||||
|
||||
return '<a class="tel" href="'.$sUrl.'"><span class="text_decoration '.$sUrlDecorationClass.'"></span>'.parent::GetAsHTML($sValue).'</a>';
|
||||
}
|
||||
|
||||
}
|
||||
118
sources/Core/AttributeDefinition/AttributePropertySet.php
Normal file
118
sources/Core/AttributeDefinition/AttributePropertySet.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CoreException;
|
||||
use Exception;
|
||||
use Str;
|
||||
|
||||
class AttributePropertySet extends AttributeTable
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "PropertySet";
|
||||
}
|
||||
|
||||
// Facilitate things: allow the user to Set the value from a string
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (!is_array($proposedValue)) {
|
||||
return array('?' => (string)$proposedValue);
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
throw new CoreException('Expecting an array', array('found' => get_class($value)));
|
||||
}
|
||||
if (count($value) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$sRes = "<TABLE class=\"listResults\">";
|
||||
$sRes .= "<TBODY>";
|
||||
foreach ($value as $sProperty => $sValue) {
|
||||
if ($sProperty == 'auth_pwd') {
|
||||
$sValue = '*****';
|
||||
}
|
||||
$sRes .= "<TR>";
|
||||
$sCell = str_replace("\n", "<br>\n", Str::pure2html(@(string)$sValue));
|
||||
$sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>";
|
||||
$sRes .= "</TR>";
|
||||
}
|
||||
$sRes .= "</TBODY>";
|
||||
$sRes .= "</TABLE>";
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
if (!is_array($value) || count($value) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$aRes = array();
|
||||
foreach ($value as $sProperty => $sValue) {
|
||||
if ($sProperty == 'auth_pwd') {
|
||||
$sValue = '*****';
|
||||
}
|
||||
$sFrom = array(',', '=');
|
||||
$sTo = array('\,', '\=');
|
||||
$aRes[] = $sProperty.'='.str_replace($sFrom, $sTo, (string)$sValue);
|
||||
}
|
||||
$sRaw = implode(',', $aRes);
|
||||
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, $sRaw);
|
||||
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value) || count($value) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$sRes = "";
|
||||
foreach ($value as $sProperty => $sValue) {
|
||||
if ($sProperty == 'auth_pwd') {
|
||||
$sValue = '*****';
|
||||
}
|
||||
$sRes .= "<property id=\"$sProperty\">";
|
||||
$sRes .= Str::pure2xml((string)$sValue);
|
||||
$sRes .= "</property>";
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
}
|
||||
191
sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php
Normal file
191
sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use CoreException;
|
||||
use CoreUnexpectedValue;
|
||||
use DBSearch;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use OQLException;
|
||||
use ormSet;
|
||||
use utils;
|
||||
|
||||
class AttributeQueryAttCodeSet extends AttributeSet
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
public function __construct($sCode, array $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-query-attcode-set';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('query_field'));
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "TEXT".CMDBSource::GetSqlStringColumnDefinition();
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 65535;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a class array indexed by alias
|
||||
*
|
||||
* @param $oHostObj
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function GetClassList($oHostObj)
|
||||
{
|
||||
try {
|
||||
$sQueryField = $this->Get('query_field');
|
||||
$sQuery = $oHostObj->Get($sQueryField);
|
||||
if (empty($sQuery)) {
|
||||
return array();
|
||||
}
|
||||
$oFilter = DBSearch::FromOQL($sQuery);
|
||||
|
||||
return $oFilter->GetSelectedClasses();
|
||||
|
||||
}
|
||||
catch (OQLException $e) {
|
||||
IssueLog::Warning($e->getMessage());
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
if (isset($aArgs['this'])) {
|
||||
$oHostObj = $aArgs['this'];
|
||||
$aClasses = $this->GetClassList($oHostObj);
|
||||
|
||||
$aAllowedAttributes = array();
|
||||
$aAllAttributes = array();
|
||||
|
||||
if ((count($aClasses) == 1) && (array_keys($aClasses)[0] == array_values($aClasses)[0])) {
|
||||
$sClass = reset($aClasses);
|
||||
$aAttributes = MetaModel::GetAttributesList($sClass);
|
||||
foreach ($aAttributes as $sAttCode) {
|
||||
$aAllowedAttributes[$sAttCode] = "$sAttCode (".MetaModel::GetLabel($sClass, $sAttCode).')';
|
||||
}
|
||||
} else {
|
||||
if (!empty($aClasses)) {
|
||||
ksort($aClasses);
|
||||
foreach ($aClasses as $sAlias => $sClass) {
|
||||
$aAttributes = MetaModel::GetAttributesList($sClass);
|
||||
foreach ($aAttributes as $sAttCode) {
|
||||
$aAllAttributes[] = array('alias' => $sAlias, 'class' => $sClass, 'att_code' => $sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($aAllAttributes as $aFullAttCode) {
|
||||
$sAttCode = $aFullAttCode['alias'].'.'.$aFullAttCode['att_code'];
|
||||
$sClass = $aFullAttCode['class'];
|
||||
$sLabel = "$sAttCode (".MetaModel::GetLabel($sClass, $aFullAttCode['att_code']).')';
|
||||
$aAllowedAttributes[$sAttCode] = $sLabel;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAllowedAttributes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
|
||||
*
|
||||
* @param $proposedValue
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @param bool $bIgnoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws OQLException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
|
||||
{
|
||||
$oSet = new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
$aArgs = array();
|
||||
if (!empty($oHostObj)) {
|
||||
$aArgs['this'] = $oHostObj;
|
||||
}
|
||||
$aAllowedAttributes = $this->GetAllowedValues($aArgs);
|
||||
$aInvalidAttCodes = array();
|
||||
if (is_string($proposedValue) && !empty($proposedValue)) {
|
||||
$proposedValue = trim($proposedValue);
|
||||
$aProposedValues = $this->FromStringToArray($proposedValue);
|
||||
$aValues = array();
|
||||
foreach ($aProposedValues as $sValue) {
|
||||
$sAttCode = trim($sValue);
|
||||
if (empty($aAllowedAttributes) || isset($aAllowedAttributes[$sAttCode])) {
|
||||
$aValues[$sAttCode] = $sAttCode;
|
||||
} else {
|
||||
$aInvalidAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
$oSet->SetValues($aValues);
|
||||
} elseif ($proposedValue instanceof ormSet) {
|
||||
$oSet = $proposedValue;
|
||||
}
|
||||
if (!empty($aInvalidAttCodes) && !$bIgnoreErrors) {
|
||||
throw new CoreUnexpectedValue("The attribute(s) ".implode(', ', $aInvalidAttCodes)." are invalid");
|
||||
}
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
|
||||
if ($value instanceof ormSet) {
|
||||
$value = $value->GetValues();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
if (!empty($oHostObject) && $bLocalize) {
|
||||
$aArgs['this'] = $oHostObject;
|
||||
$aAllowedAttributes = $this->GetAllowedValues($aArgs);
|
||||
|
||||
$aLocalizedValues = array();
|
||||
foreach ($value as $sAttCode) {
|
||||
if (isset($aAllowedAttributes[$sAttCode])) {
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities($aAllowedAttributes[$sAttCode]);
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
}
|
||||
}
|
||||
$value = $aLocalizedValues;
|
||||
}
|
||||
$value = implode('', $value);
|
||||
}
|
||||
|
||||
return '<span class="'.implode(' ', $this->aCSSClasses).'">'.$value.'</span>';
|
||||
}
|
||||
}
|
||||
460
sources/Core/AttributeDefinition/AttributeRedundancySettings.php
Normal file
460
sources/Core/AttributeDefinition/AttributeRedundancySettings.php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use DictExceptionMissingString;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Holds the setting for the redundancy on a specific relation
|
||||
* Its value is a string, containing either:
|
||||
* - 'disabled'
|
||||
* - 'n', where n is a positive integer value giving the minimum count of items upstream
|
||||
* - 'n%', where n is a positive integer value, giving the minimum as a percentage of the total count of items upstream
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeRedundancySettings extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array(
|
||||
'sql',
|
||||
'relation_code',
|
||||
'from_class',
|
||||
'neighbour_id',
|
||||
'enabled',
|
||||
'enabled_mode',
|
||||
'min_up',
|
||||
'min_up_type',
|
||||
'min_up_mode',
|
||||
);
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "RedundancySetting";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "VARCHAR(20)"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return "^[0-9]{1,3}|[0-9]{1,2}%|disabled$";
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$sRet = 'disabled';
|
||||
if ($this->Get('enabled')) {
|
||||
if ($this->Get('min_up_type') == 'count') {
|
||||
$sRet = (string)$this->Get('min_up');
|
||||
} else // percent
|
||||
{
|
||||
$sRet = $this->Get('min_up').'%';
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return ($proposedValue == '');
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string)$proposedValue;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new CoreException('Expected the attribute value to be a string', array(
|
||||
'found_type' => gettype($value),
|
||||
'value' => $value,
|
||||
'class' => $this->GetHostClass(),
|
||||
'attribute' => $this->GetCode(),
|
||||
));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetRelationQueryData()
|
||||
{
|
||||
foreach (MetaModel::EnumRelationQueries($this->GetHostClass(), $this->Get('relation_code'),
|
||||
false) as $sDummy => $aQueryInfo) {
|
||||
if ($aQueryInfo['sFromClass'] == $this->Get('from_class')) {
|
||||
if ($aQueryInfo['sNeighbour'] == $this->Get('neighbour_id')) {
|
||||
return $aQueryInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user option label
|
||||
*
|
||||
* @param string $sUserOption possible values : disabled|cout|percent
|
||||
* @param string $sDefault
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetUserOptionFormat($sUserOption, $sDefault = null)
|
||||
{
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/'.$sUserOption, null, true /*user lang*/);
|
||||
if (is_null($sLabel)) {
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault)) {
|
||||
$sDefault = str_replace('_', ' ', $this->m_sCode.':'.$sUserOption.'(%1$s)');
|
||||
}
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/'.$sUserOption, $sDefault, false);
|
||||
}
|
||||
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to display the value in the GUI
|
||||
*
|
||||
* @param string $sValue
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sCurrentOption = $this->GetCurrentOption($sValue);
|
||||
$sClass = $oHostObject ? get_class($oHostObject) : $this->m_sHostClass;
|
||||
|
||||
return sprintf($this->GetUserOptionFormat($sCurrentOption), $this->GetMinUpValue($sValue),
|
||||
MetaModel::GetName($sClass));
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
|
||||
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to interpret the value, given the current settings and string representation of the attribute
|
||||
*/
|
||||
public function IsEnabled($sValue)
|
||||
{
|
||||
if ($this->get('enabled_mode') == 'fixed') {
|
||||
$bRet = $this->get('enabled');
|
||||
} else {
|
||||
$bRet = ($sValue != 'disabled');
|
||||
}
|
||||
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to interpret the value, given the current settings and string representation of the attribute
|
||||
*/
|
||||
public function GetMinUpType($sValue)
|
||||
{
|
||||
if ($this->get('min_up_mode') == 'fixed') {
|
||||
$sRet = $this->get('min_up_type');
|
||||
} else {
|
||||
$sRet = 'count';
|
||||
if (substr(trim($sValue), -1, 1) == '%') {
|
||||
$sRet = 'percent';
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to interpret the value, given the current settings and string representation of the attribute
|
||||
*/
|
||||
public function GetMinUpValue($sValue)
|
||||
{
|
||||
if ($this->get('min_up_mode') == 'fixed') {
|
||||
$iRet = (int)$this->Get('min_up');
|
||||
} else {
|
||||
$sRefValue = $sValue;
|
||||
if (substr(trim($sValue), -1, 1) == '%') {
|
||||
$sRefValue = substr(trim($sValue), 0, -1);
|
||||
}
|
||||
$iRet = (int)trim($sRefValue);
|
||||
}
|
||||
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to determine if the redundancy can be viewed/edited by the end-user
|
||||
*/
|
||||
public function IsVisible()
|
||||
{
|
||||
$bRet = false;
|
||||
if ($this->Get('enabled_mode') == 'fixed') {
|
||||
$bRet = $this->Get('enabled');
|
||||
} elseif ($this->Get('enabled_mode') == 'user') {
|
||||
$bRet = true;
|
||||
}
|
||||
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
if (($this->Get('enabled_mode') == 'fixed') && ($this->Get('min_up_mode') == 'fixed')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML form that can be read by ReadValueFromPostedForm
|
||||
*/
|
||||
public function GetDisplayForm($sCurrentValue, $oPage, $bEditMode = false, $sFormPrefix = '')
|
||||
{
|
||||
$sRet = '';
|
||||
$aUserOptions = $this->GetUserOptions($sCurrentValue);
|
||||
if (count($aUserOptions) < 2) {
|
||||
$bEditOption = false;
|
||||
} else {
|
||||
$bEditOption = $bEditMode;
|
||||
}
|
||||
$sCurrentOption = $this->GetCurrentOption($sCurrentValue);
|
||||
foreach ($aUserOptions as $sUserOption) {
|
||||
$bSelected = ($sUserOption == $sCurrentOption);
|
||||
$sRet .= '<div>';
|
||||
$sRet .= $this->GetDisplayOption($sCurrentValue, $oPage, $sFormPrefix, $bEditOption, $sUserOption,
|
||||
$bSelected);
|
||||
$sRet .= '</div>';
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
const USER_OPTION_DISABLED = 'disabled';
|
||||
const USER_OPTION_ENABLED_COUNT = 'count';
|
||||
const USER_OPTION_ENABLED_PERCENT = 'percent';
|
||||
|
||||
/**
|
||||
* Depending on the xxx_mode parameters, build the list of options that are allowed to the end-user
|
||||
*/
|
||||
protected function GetUserOptions($sValue)
|
||||
{
|
||||
$aRet = array();
|
||||
if ($this->Get('enabled_mode') == 'user') {
|
||||
$aRet[] = self::USER_OPTION_DISABLED;
|
||||
}
|
||||
|
||||
if ($this->Get('min_up_mode') == 'user') {
|
||||
$aRet[] = self::USER_OPTION_ENABLED_COUNT;
|
||||
$aRet[] = self::USER_OPTION_ENABLED_PERCENT;
|
||||
} else {
|
||||
if ($this->GetMinUpType($sValue) == 'count') {
|
||||
$aRet[] = self::USER_OPTION_ENABLED_COUNT;
|
||||
} else {
|
||||
$aRet[] = self::USER_OPTION_ENABLED_PERCENT;
|
||||
}
|
||||
}
|
||||
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the string representation into one of the existing options
|
||||
*/
|
||||
protected function GetCurrentOption($sValue)
|
||||
{
|
||||
$sRet = self::USER_OPTION_DISABLED;
|
||||
if ($this->IsEnabled($sValue)) {
|
||||
if ($this->GetMinUpType($sValue) == 'count') {
|
||||
$sRet = self::USER_OPTION_ENABLED_COUNT;
|
||||
} else {
|
||||
$sRet = self::USER_OPTION_ENABLED_PERCENT;
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an option (form, or current value)
|
||||
*
|
||||
* @param string $sCurrentValue
|
||||
* @param WebPage $oPage
|
||||
* @param string $sFormPrefix
|
||||
* @param bool $bEditMode
|
||||
* @param string $sUserOption
|
||||
* @param bool $bSelected
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function GetDisplayOption(
|
||||
$sCurrentValue, $oPage, $sFormPrefix, $bEditMode, $sUserOption, $bSelected = true
|
||||
)
|
||||
{
|
||||
$sRet = '';
|
||||
|
||||
$iCurrentValue = $this->GetMinUpValue($sCurrentValue);
|
||||
if ($bEditMode) {
|
||||
$sValue = null;
|
||||
$sHtmlNamesPrefix = 'rddcy_'.$this->Get('relation_code').'_'.$this->Get('from_class').'_'.$this->Get('neighbour_id');
|
||||
switch ($sUserOption) {
|
||||
case self::USER_OPTION_DISABLED:
|
||||
$sValue = ''; // Empty placeholder
|
||||
break;
|
||||
|
||||
case self::USER_OPTION_ENABLED_COUNT:
|
||||
if ($bEditMode) {
|
||||
$sName = $sHtmlNamesPrefix.'_min_up_count';
|
||||
$sEditValue = $bSelected ? $iCurrentValue : '';
|
||||
$sValue = '<input class="redundancy-min-up-count" type="string" size="3" name="'.$sName.'" value="'.$sEditValue.'">';
|
||||
// To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).trigger('focus');}, 100);});");
|
||||
} else {
|
||||
$sValue = $iCurrentValue;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::USER_OPTION_ENABLED_PERCENT:
|
||||
if ($bEditMode) {
|
||||
$sName = $sHtmlNamesPrefix.'_min_up_percent';
|
||||
$sEditValue = $bSelected ? $iCurrentValue : '';
|
||||
$sValue = '<input class="redundancy-min-up-percent" type="string" size="3" name="'.$sName.'" value="'.$sEditValue.'">';
|
||||
// To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).trigger('focus');}, 100);});");
|
||||
} else {
|
||||
$sValue = $iCurrentValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$sLabel = sprintf($this->GetUserOptionFormat($sUserOption), $sValue,
|
||||
MetaModel::GetName($this->GetHostClass()));
|
||||
|
||||
$sOptionName = $sHtmlNamesPrefix.'_user_option';
|
||||
$sOptionId = $sOptionName.'_'.$sUserOption;
|
||||
$sChecked = $bSelected ? 'checked' : '';
|
||||
$sRet = '<input type="radio" name="'.$sOptionName.'" id="'.$sOptionId.'" value="'.$sUserOption.'" '.$sChecked.'> <label for="'.$sOptionId.'">'.$sLabel.'</label>';
|
||||
} else {
|
||||
// Read-only: display only the currently selected option
|
||||
if ($bSelected) {
|
||||
$sRet = sprintf($this->GetUserOptionFormat($sUserOption), $iCurrentValue,
|
||||
MetaModel::GetName($this->GetHostClass()));
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the string representation out of the values given by the form defined in GetDisplayForm
|
||||
*/
|
||||
public function ReadValueFromPostedForm($sFormPrefix)
|
||||
{
|
||||
$sHtmlNamesPrefix = 'rddcy_'.$this->Get('relation_code').'_'.$this->Get('from_class').'_'.$this->Get('neighbour_id');
|
||||
|
||||
$iMinUpCount = (int)utils::ReadPostedParam($sHtmlNamesPrefix.'_min_up_count', null, 'raw_data');
|
||||
$iMinUpPercent = (int)utils::ReadPostedParam($sHtmlNamesPrefix.'_min_up_percent', null, 'raw_data');
|
||||
$sSelectedOption = utils::ReadPostedParam($sHtmlNamesPrefix.'_user_option', null, 'raw_data');
|
||||
switch ($sSelectedOption) {
|
||||
case self::USER_OPTION_ENABLED_COUNT:
|
||||
$sRet = $iMinUpCount;
|
||||
break;
|
||||
|
||||
case self::USER_OPTION_ENABLED_PERCENT:
|
||||
$sRet = $iMinUpPercent.'%';
|
||||
break;
|
||||
|
||||
case self::USER_OPTION_DISABLED:
|
||||
default:
|
||||
$sRet = 'disabled';
|
||||
break;
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
479
sources/Core/AttributeDefinition/AttributeSet.php
Normal file
479
sources/Core/AttributeDefinition/AttributeSet.php
Normal file
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use ApplicationContext;
|
||||
use cmdbAbstractObject;
|
||||
use CMDBChangeOpSetAttributeTagSet;
|
||||
use CMDBSource;
|
||||
use CoreException;
|
||||
use CoreUnexpectedValue;
|
||||
use DBObject;
|
||||
use DBSearch;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use ormSet;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* An unordered multi values attribute
|
||||
* Allowed values are mandatory for this attribute to be modified
|
||||
*
|
||||
* Class AttributeSet
|
||||
*/
|
||||
abstract class AttributeSet extends AttributeDBFieldVoid
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
const EDITABLE_INPUT_ID_SUFFIX = '-setwidget-values'; // used client side, see js/jquery.itop-set-widget.js
|
||||
protected $bDisplayLink; // Display search link in readonly mode
|
||||
|
||||
public function __construct($sCode, array $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-set';
|
||||
$this->bDisplayLink = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bDisplayLink
|
||||
*/
|
||||
public function setDisplayLink($bDisplayLink)
|
||||
{
|
||||
$this->bDisplayLink = $bDisplayLink;
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('is_null_allowed', 'max_items'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed different values for the set values are mandatory for this attribute to be modified
|
||||
*
|
||||
* @param array $aArgs
|
||||
* @param string $sContains
|
||||
*
|
||||
* @return array|null
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function GetPossibleValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
return $this->GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ormSet $oValue
|
||||
*
|
||||
* @param $aArgs
|
||||
*
|
||||
* @return string JSON to be used in the itop.set_widget JQuery widget
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function GetJsonForWidget($oValue, $aArgs = array())
|
||||
{
|
||||
$aJson = array();
|
||||
|
||||
// possible_values
|
||||
$aAllowedValues = $this->GetPossibleValues($aArgs);
|
||||
$aSetKeyValData = array();
|
||||
foreach ($aAllowedValues as $sCode => $sLabel) {
|
||||
$aSetKeyValData[] = [
|
||||
'code' => $sCode,
|
||||
'label' => $sLabel,
|
||||
];
|
||||
}
|
||||
$aJson['possible_values'] = $aSetKeyValData;
|
||||
$aRemoved = array();
|
||||
if (is_null($oValue)) {
|
||||
$aJson['partial_values'] = array();
|
||||
$aJson['orig_value'] = array();
|
||||
} else {
|
||||
$aPartialValues = $oValue->GetModified();
|
||||
foreach ($aPartialValues as $key => $value) {
|
||||
if (!isset($aAllowedValues[$value])) {
|
||||
unset($aPartialValues[$key]);
|
||||
}
|
||||
}
|
||||
$aJson['partial_values'] = array_values($aPartialValues);
|
||||
$aOrigValues = array_merge($oValue->GetValues(), $oValue->GetModified());
|
||||
foreach ($aOrigValues as $key => $value) {
|
||||
if (!isset($aAllowedValues[$value])) {
|
||||
// Remove unwanted values
|
||||
$aRemoved[] = $value;
|
||||
unset($aOrigValues[$key]);
|
||||
}
|
||||
}
|
||||
$aJson['orig_value'] = array_values($aOrigValues);
|
||||
}
|
||||
$aJson['added'] = array();
|
||||
$aJson['removed'] = $aRemoved;
|
||||
|
||||
$iMaxTags = $this->GetMaxItems();
|
||||
$aJson['max_items_allowed'] = $iMaxTags;
|
||||
|
||||
return json_encode($aJson);
|
||||
}
|
||||
|
||||
public function RequiresIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function RequiresFullTextIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
return $this->Get("is_null_allowed");
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Set";
|
||||
}
|
||||
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
if ($value instanceof ormSet) {
|
||||
$value = $value->GetValues();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
return implode(', ', $value);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
$iLen = $this->GetMaxSize();
|
||||
|
||||
return "VARCHAR($iLen)"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
|
||||
{
|
||||
$aValues = array();
|
||||
if (!empty($proposedValue)) {
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
// convert also , separated strings
|
||||
if ($sSepItem !== $sDefaultSepItem) {
|
||||
$proposedValue = str_replace($sDefaultSepItem, $sSepItem, $proposedValue);
|
||||
}
|
||||
foreach (explode($sSepItem, $proposedValue) as $sCode) {
|
||||
$sValue = trim($sCode);
|
||||
if ($sValue !== '') {
|
||||
$aValues[] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aCols
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$sValue = $aCols["$sPrefix"];
|
||||
|
||||
return $this->MakeRealValue($sValue, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
|
||||
*
|
||||
* @param $proposedValue
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @param bool $bIgnoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
|
||||
{
|
||||
$oSet = new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
$aAllowedValues = $this->GetPossibleValues();
|
||||
if (is_string($proposedValue) && !empty($proposedValue)) {
|
||||
$proposedValue = trim("$proposedValue");
|
||||
$aValues = $this->FromStringToArray($proposedValue);
|
||||
foreach ($aValues as $i => $sValue) {
|
||||
if (!isset($aAllowedValues[$sValue])) {
|
||||
unset($aValues[$i]);
|
||||
}
|
||||
}
|
||||
$oSet->SetValues($aValues);
|
||||
} elseif ($proposedValue instanceof ormSet) {
|
||||
$oSet = $proposedValue;
|
||||
}
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value from a given string (plain text, CSV import)
|
||||
*
|
||||
* @param string $sProposedValue
|
||||
* @param bool $bLocalizedValue
|
||||
* @param string $sSepItem
|
||||
* @param string $sSepAttribute
|
||||
* @param string $sSepValue
|
||||
* @param string $sAttributeQualifier
|
||||
*
|
||||
* @return mixed null if no match could be found
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
return $this->MakeRealValue($sProposedValue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|ormSet
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetNullValue()
|
||||
{
|
||||
return new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
if (empty($proposedValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var ormSet $proposedValue */
|
||||
return $proposedValue->Count() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
if (false === ($proposedValue instanceof ormSet)) {
|
||||
return parent::HasAValue($proposedValue);
|
||||
}
|
||||
|
||||
return $proposedValue->Count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overloaded for localized enums
|
||||
*
|
||||
* @param $sValue
|
||||
*
|
||||
* @return string label corresponding to the given value (in plain text)
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
if ($sValue instanceof ormSet) {
|
||||
$sValue = $sValue->GetValues();
|
||||
}
|
||||
if (is_array($sValue)) {
|
||||
return implode(', ', $sValue);
|
||||
}
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param null $oHostObj
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetAsPlainText($sValue, $oHostObj = null)
|
||||
{
|
||||
return $this->GetValueLabel($sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
if ($value instanceof ormSet) {
|
||||
$value = $value->GetValues();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
$sRes = implode($sSepItem, $value);
|
||||
if (!empty($sRes)) {
|
||||
$value = "{$sSepItem}{$sRes}{$sSepItem}";
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceof ormSet) {
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return $this->GenerateViewHtmlForValues($aValues);
|
||||
}
|
||||
if (is_array($value)) {
|
||||
return implode(', ', $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML representation of a list of values (read-only)
|
||||
* accept a list of strings
|
||||
*
|
||||
* @param array $aValues
|
||||
* @param string $sCssClass
|
||||
* @param bool $bWithLink if true will generate a link, otherwise just a "a" tag without href
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function GenerateViewHtmlForValues($aValues, $sCssClass = '', $bWithLink = true)
|
||||
{
|
||||
if (empty($aValues)) {
|
||||
return '';
|
||||
}
|
||||
$sHtml = '<span class="'.$sCssClass.' '.implode(' ', $this->aCSSClasses).'">';
|
||||
foreach ($aValues as $sValue) {
|
||||
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode());
|
||||
$sAttCode = $this->GetCode();
|
||||
$sLabel = utils::EscapeHtml($this->GetValueLabel($sValue));
|
||||
$sDescription = utils::EscapeHtml($this->GetValueDescription($sValue));
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sValue'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
// Prepare tooltip
|
||||
if (empty($sDescription)) {
|
||||
$sTooltipContent = $sLabel;
|
||||
$sTooltipHtmlEnabled = 'false';
|
||||
} else {
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sLabel</h4>
|
||||
<div>$sDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
}
|
||||
$sTooltipContent = utils::EscapeHtml($sTooltipContent);
|
||||
|
||||
$sHtml .= '<a'.$sLink.' class="attribute-set-item attribute-set-item-'.$sValue.'" data-code="'.$sValue.'" data-label="'.$sLabel.'" data-description="'.$sDescription.'" data-tooltip-content="'.$sTooltipContent.'" data-tooltip-html-enabled="'.$sTooltipHtmlEnabled.'">'.$sLabel.'</a>';
|
||||
}
|
||||
$sHtml .= '</span>';
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
|
||||
{
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
if (is_object($value) && ($value instanceof ormSet)) {
|
||||
if ($bLocalize) {
|
||||
$aValues = $value->GetLabels();
|
||||
} else {
|
||||
$aValues = $value->GetValues();
|
||||
}
|
||||
$sRes = implode($sSepItem, $aValues);
|
||||
} else {
|
||||
$sRes = '';
|
||||
}
|
||||
|
||||
return "{$sTextQualifier}{$sRes}{$sTextQualifier}";
|
||||
}
|
||||
|
||||
public function GetMaxItems()
|
||||
{
|
||||
return $this->Get('max_items');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\SetField';
|
||||
}
|
||||
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
/** @var \ormSet $original */
|
||||
/** @var \ormSet $value */
|
||||
parent::RecordAttChange($oObject,
|
||||
implode(' ', $original->GetValues()),
|
||||
implode(' ', $value->GetValues())
|
||||
);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeTagSet::class;
|
||||
}
|
||||
}
|
||||
824
sources/Core/AttributeDefinition/AttributeStopWatch.php
Normal file
824
sources/Core/AttributeDefinition/AttributeStopWatch.php
Normal file
@@ -0,0 +1,824 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOpSetAttributeScalar;
|
||||
use CoreException;
|
||||
use DateTime;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use ormStopWatch;
|
||||
use Str;
|
||||
|
||||
/**
|
||||
* A stop watch is an ormStopWatch object, it is stored as several columns in the database
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeStopWatch extends AttributeDefinition
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
// The list of thresholds must be an array of iPercent => array of 'option' => value
|
||||
return array_merge(parent::ListExpectedParams(),
|
||||
array("states", "goal_computing", "working_time_computing", "thresholds"));
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "StopWatch";
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return $this->NewStopWatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormStopWatch $value
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
|
||||
public function GetStates()
|
||||
{
|
||||
return $this->Get('states');
|
||||
}
|
||||
|
||||
public function AlwaysLoadInTables()
|
||||
{
|
||||
// Each and every stop watch is accessed for computing the highlight code (DBObject::GetHighlightCode())
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a brand new (but configured) stop watch
|
||||
*/
|
||||
public function NewStopWatch()
|
||||
{
|
||||
$oSW = new ormStopWatch();
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$oSW->DefineThreshold($iThreshold);
|
||||
}
|
||||
|
||||
return $oSW;
|
||||
}
|
||||
|
||||
// Facilitate things: allow the user to Set the value from a string
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (!$proposedValue instanceof ormStopWatch) {
|
||||
return $this->NewStopWatch();
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
if ($sPrefix == '') {
|
||||
$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
|
||||
$aColumns[''] = $sPrefix.'_timespent';
|
||||
$aColumns['_started'] = $sPrefix.'_started';
|
||||
$aColumns['_laststart'] = $sPrefix.'_laststart';
|
||||
$aColumns['_stopped'] = $sPrefix.'_stopped';
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = '_'.$iThreshold;
|
||||
$aColumns[$sThPrefix.'_deadline'] = $sPrefix.$sThPrefix.'_deadline';
|
||||
$aColumns[$sThPrefix.'_passed'] = $sPrefix.$sThPrefix.'_passed';
|
||||
$aColumns[$sThPrefix.'_triggered'] = $sPrefix.$sThPrefix.'_triggered';
|
||||
$aColumns[$sThPrefix.'_overrun'] = $sPrefix.$sThPrefix.'_overrun';
|
||||
}
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public static function DateToSeconds($sDate)
|
||||
{
|
||||
if (is_null($sDate)) {
|
||||
return null;
|
||||
}
|
||||
$oDateTime = new DateTime($sDate);
|
||||
$iSeconds = $oDateTime->format('U');
|
||||
|
||||
return $iSeconds;
|
||||
}
|
||||
|
||||
public static function SecondsToDate($iSeconds)
|
||||
{
|
||||
if (is_null($iSeconds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return date("Y-m-d H:i:s", $iSeconds);
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$aExpectedCols = array($sPrefix, $sPrefix.'_started', $sPrefix.'_laststart', $sPrefix.'_stopped');
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = '_'.$iThreshold;
|
||||
$aExpectedCols[] = $sPrefix.$sThPrefix.'_deadline';
|
||||
$aExpectedCols[] = $sPrefix.$sThPrefix.'_passed';
|
||||
$aExpectedCols[] = $sPrefix.$sThPrefix.'_triggered';
|
||||
$aExpectedCols[] = $sPrefix.$sThPrefix.'_overrun';
|
||||
}
|
||||
foreach ($aExpectedCols as $sExpectedCol) {
|
||||
if (!array_key_exists($sExpectedCol, $aCols)) {
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sExpectedCol' from {$sAvailable}");
|
||||
}
|
||||
}
|
||||
|
||||
$value = new ormStopWatch(
|
||||
$aCols[$sPrefix],
|
||||
self::DateToSeconds($aCols[$sPrefix.'_started']),
|
||||
self::DateToSeconds($aCols[$sPrefix.'_laststart']),
|
||||
self::DateToSeconds($aCols[$sPrefix.'_stopped'])
|
||||
);
|
||||
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aDefinition) {
|
||||
$sThPrefix = '_'.$iThreshold;
|
||||
$value->DefineThreshold(
|
||||
$iThreshold,
|
||||
self::DateToSeconds($aCols[$sPrefix.$sThPrefix.'_deadline']),
|
||||
(bool)($aCols[$sPrefix.$sThPrefix.'_passed'] == 1),
|
||||
(bool)($aCols[$sPrefix.$sThPrefix.'_triggered'] == 1),
|
||||
$aCols[$sPrefix.$sThPrefix.'_overrun'],
|
||||
array_key_exists('highlight', $aDefinition) ? $aDefinition['highlight'] : null
|
||||
);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
if ($value instanceof ormStopWatch) {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode().'_timespent'] = $value->GetTimeSpent();
|
||||
$aValues[$this->GetCode().'_started'] = self::SecondsToDate($value->GetStartDate());
|
||||
$aValues[$this->GetCode().'_laststart'] = self::SecondsToDate($value->GetLastStartDate());
|
||||
$aValues[$this->GetCode().'_stopped'] = self::SecondsToDate($value->GetStopDate());
|
||||
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sPrefix = $this->GetCode().'_'.$iThreshold;
|
||||
$aValues[$sPrefix.'_deadline'] = self::SecondsToDate($value->GetThresholdDate($iThreshold));
|
||||
$aValues[$sPrefix.'_passed'] = $value->IsThresholdPassed($iThreshold) ? '1' : '0';
|
||||
$aValues[$sPrefix.'_triggered'] = $value->IsThresholdTriggered($iThreshold) ? '1' : '0';
|
||||
$aValues[$sPrefix.'_overrun'] = $value->GetOverrun($iThreshold);
|
||||
}
|
||||
} else {
|
||||
$aValues = array();
|
||||
$aValues[$this->GetCode().'_timespent'] = '';
|
||||
$aValues[$this->GetCode().'_started'] = '';
|
||||
$aValues[$this->GetCode().'_laststart'] = '';
|
||||
$aValues[$this->GetCode().'_stopped'] = '';
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode().'_timespent'] = 'INT(11) UNSIGNED';
|
||||
$aColumns[$this->GetCode().'_started'] = 'DATETIME';
|
||||
$aColumns[$this->GetCode().'_laststart'] = 'DATETIME';
|
||||
$aColumns[$this->GetCode().'_stopped'] = 'DATETIME';
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sPrefix = $this->GetCode().'_'.$iThreshold;
|
||||
$aColumns[$sPrefix.'_deadline'] = 'DATETIME';
|
||||
$aColumns[$sPrefix.'_passed'] = 'TINYINT(1) UNSIGNED';
|
||||
$aColumns[$sPrefix.'_triggered'] = 'TINYINT(1)';
|
||||
$aColumns[$sPrefix.'_overrun'] = 'INT(11) UNSIGNED';
|
||||
}
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetMagicFields()
|
||||
{
|
||||
$aRes = [
|
||||
$this->GetCode().'_started',
|
||||
$this->GetCode().'_laststart',
|
||||
$this->GetCode().'_stopped',
|
||||
];
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sPrefix = $this->GetCode().'_'.$iThreshold;
|
||||
$aRes[] = $sPrefix.'_deadline';
|
||||
$aRes[] = $sPrefix.'_passed';
|
||||
$aRes[] = $sPrefix.'_triggered';
|
||||
$aRes[] = $sPrefix.'_overrun';
|
||||
}
|
||||
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return '=';
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormStopWatch $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
return $value->GetAsHTML($this, $oHostObject);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormStopWatch $value
|
||||
* @param string $sSeparator
|
||||
* @param string $sTextQualifier
|
||||
* @param null $oHostObject
|
||||
* @param bool $bLocalize
|
||||
* @param bool $bConvertToPlainText
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsCSV(
|
||||
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormStopWatch $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $value->GetTimeSpent();
|
||||
}
|
||||
|
||||
public function ListThresholds()
|
||||
{
|
||||
return $this->Get('thresholds');
|
||||
}
|
||||
|
||||
public function Fingerprint($value)
|
||||
{
|
||||
$sFingerprint = '';
|
||||
if (is_object($value)) {
|
||||
$sFingerprint = $value->GetAsHTML($this);
|
||||
}
|
||||
|
||||
return $sFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* To expose internal values: Declare an attribute AttributeSubItem
|
||||
* and implement the GetSubItemXXXX verbs
|
||||
*
|
||||
* @param string $sItemCode
|
||||
*
|
||||
* @return array
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetSubItemSQLExpression($sItemCode)
|
||||
{
|
||||
$sPrefix = $this->GetCode();
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
return array('' => $sPrefix.'_timespent');
|
||||
case 'started':
|
||||
return array('' => $sPrefix.'_started');
|
||||
case 'laststart':
|
||||
return array('' => $sPrefix.'_laststart');
|
||||
case 'stopped':
|
||||
return array('' => $sPrefix.'_stopped');
|
||||
}
|
||||
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
return array('' => $sPrefix.'_'.$iThreshold.'_deadline');
|
||||
case 'passed':
|
||||
return array('' => $sPrefix.'_'.$iThreshold.'_passed');
|
||||
case 'triggered':
|
||||
return array('' => $sPrefix.'_'.$iThreshold.'_triggered');
|
||||
case 'overrun':
|
||||
return array('' => $sPrefix.'_'.$iThreshold.'_overrun');
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sItemCode
|
||||
* @param ormStopWatch $value
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetSubItemValue($sItemCode, $value, $oHostObject = null)
|
||||
{
|
||||
$oStopWatch = $value;
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
return $oStopWatch->GetTimeSpent();
|
||||
case 'started':
|
||||
return $oStopWatch->GetStartDate();
|
||||
case 'laststart':
|
||||
return $oStopWatch->GetLastStartDate();
|
||||
case 'stopped':
|
||||
return $oStopWatch->GetStopDate();
|
||||
}
|
||||
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
return $oStopWatch->GetThresholdDate($iThreshold);
|
||||
case 'passed':
|
||||
return $oStopWatch->IsThresholdPassed($iThreshold);
|
||||
case 'triggered':
|
||||
return $oStopWatch->IsThresholdTriggered($iThreshold);
|
||||
case 'overrun':
|
||||
return $oStopWatch->GetOverrun($iThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
|
||||
}
|
||||
|
||||
|
||||
public function GetSubItemSearchType($sItemCode)
|
||||
{
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
|
||||
}
|
||||
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
return static::SEARCH_WIDGET_TYPE_ENUM; //booleans, used in conjuction with GetSubItemAllowedValues and IsSubItemNullAllowed
|
||||
case 'overrun':
|
||||
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static::SEARCH_WIDGET_TYPE_RAW;
|
||||
}
|
||||
|
||||
public function GetSubItemAllowedValues($sItemCode, $aArgs = array(), $sContains = '')
|
||||
{
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
return array(
|
||||
0 => $this->GetBooleanLabel(0),
|
||||
1 => $this->GetBooleanLabel(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function IsSubItemNullAllowed($sItemCode, $bDefaultValue)
|
||||
{
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bDefaultValue;
|
||||
}
|
||||
|
||||
protected function GetBooleanLabel($bValue)
|
||||
{
|
||||
$sDictKey = $bValue ? 'yes' : 'no';
|
||||
|
||||
return Dict::S('BooleanLabel:'.$sDictKey, 'def:'.$sDictKey);
|
||||
}
|
||||
|
||||
public function GetSubItemAsHTMLForHistory($sItemCode, $sValue)
|
||||
{
|
||||
$sHtml = null;
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
$sHtml = (int)$sValue ? Str::pure2html(AttributeDuration::FormatDuration($sValue)) : null;
|
||||
break;
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
$sHtml = (int)$sValue ? date((string)AttributeDateTime::GetFormat(), (int)$sValue) : null;
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
$sHtml = (int)$sValue ? date((string)AttributeDateTime::GetFormat(),
|
||||
(int)$sValue) : null;
|
||||
break;
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
$sHtml = $this->GetBooleanLabel((int)$sValue);
|
||||
break;
|
||||
case 'overrun':
|
||||
$sHtml = (int)$sValue > 0 ? Str::pure2html(AttributeDuration::FormatDuration((int)$sValue)) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function GetSubItemAsPlainText($sItemCode, $value)
|
||||
{
|
||||
$sRet = $value;
|
||||
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
$sRet = AttributeDuration::FormatDuration($value);
|
||||
break;
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
if (is_null($value)) {
|
||||
$sRet = ''; // Undefined
|
||||
} else {
|
||||
$oDateTime = new DateTime();
|
||||
$oDateTime->setTimestamp($value);
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$sRet = $oDateTimeFormat->Format($oDateTime);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
if ($value) {
|
||||
if (is_int($value)) {
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
|
||||
$sRet = AttributeDeadline::FormatDeadline($sDate);
|
||||
} else {
|
||||
$sRet = $value;
|
||||
}
|
||||
} else {
|
||||
$sRet = '';
|
||||
}
|
||||
break;
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
$sRet = $this->GetBooleanLabel($value);
|
||||
break;
|
||||
case 'overrun':
|
||||
$sRet = AttributeDuration::FormatDuration($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetSubItemAsHTML($sItemCode, $value)
|
||||
{
|
||||
$sHtml = $value;
|
||||
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
$sHtml = Str::pure2html(AttributeDuration::FormatDuration($value));
|
||||
break;
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
if (is_null($value)) {
|
||||
$sHtml = ''; // Undefined
|
||||
} else {
|
||||
$oDateTime = new DateTime();
|
||||
$oDateTime->setTimestamp($value);
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$sHtml = Str::pure2html($oDateTimeFormat->Format($oDateTime));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
if ($value) {
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
|
||||
$sHtml = Str::pure2html(AttributeDeadline::FormatDeadline($sDate));
|
||||
} else {
|
||||
$sHtml = '';
|
||||
}
|
||||
break;
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
$sHtml = $this->GetBooleanLabel($value);
|
||||
break;
|
||||
case 'overrun':
|
||||
$sHtml = Str::pure2html(AttributeDuration::FormatDuration($value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function GetSubItemAsCSV(
|
||||
$sItemCode, $value, $sSeparator = ',', $sTextQualifier = '"', $bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$value);
|
||||
$sRet = $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
$sRet = $sTextQualifier.AttributeDuration::FormatDuration($value).$sTextQualifier;
|
||||
break;
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
if ($value !== null) {
|
||||
$oDateTime = new DateTime();
|
||||
$oDateTime->setTimestamp($value);
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$sRet = $sTextQualifier.$oDateTimeFormat->Format($oDateTime).$sTextQualifier;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
if ($value != '') {
|
||||
$oDateTime = new DateTime();
|
||||
$oDateTime->setTimestamp($value);
|
||||
$oDateTimeFormat = AttributeDateTime::GetFormat();
|
||||
$sRet = $sTextQualifier.$oDateTimeFormat->Format($oDateTime).$sTextQualifier;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
$sRet = $sTextQualifier.$this->GetBooleanLabel($value).$sTextQualifier;
|
||||
break;
|
||||
|
||||
case 'overrun':
|
||||
$sRet = $sTextQualifier.AttributeDuration::FormatDuration($value).$sTextQualifier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetSubItemAsXML($sItemCode, $value)
|
||||
{
|
||||
$sRet = Str::pure2xml((string)$value);
|
||||
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
case 'overrun':
|
||||
break;
|
||||
|
||||
case 'triggered':
|
||||
case 'passed':
|
||||
$sRet = $this->GetBooleanLabel($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented for the HTML spreadsheet format!
|
||||
*
|
||||
* @param string $sItemCode
|
||||
* @param ormStopWatch $value
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function GetSubItemAsEditValue($sItemCode, $value)
|
||||
{
|
||||
$sRet = $value;
|
||||
|
||||
switch ($sItemCode) {
|
||||
case 'timespent':
|
||||
break;
|
||||
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
if (is_null($value)) {
|
||||
$sRet = ''; // Undefined
|
||||
} else {
|
||||
$sRet = date((string)AttributeDateTime::GetFormat(), $value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sThPrefix = $iThreshold.'_';
|
||||
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix) {
|
||||
// The current threshold is concerned
|
||||
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
|
||||
switch ($sThresholdCode) {
|
||||
case 'deadline':
|
||||
if ($value) {
|
||||
$sRet = date((string)AttributeDateTime::GetFormat(), $value);
|
||||
} else {
|
||||
$sRet = '';
|
||||
}
|
||||
break;
|
||||
case 'passed':
|
||||
case 'triggered':
|
||||
$sRet = $this->GetBooleanLabel($value);
|
||||
break;
|
||||
case 'overrun':
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// A stopwatch always has a value
|
||||
return true;
|
||||
}
|
||||
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
foreach ($this->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef) {
|
||||
$item_value = $this->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $oObject);
|
||||
$item_original = $this->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $oObject);
|
||||
|
||||
if ($item_value != $item_original) {
|
||||
$oMyChangeOp = MetaModel::NewObject(CMDBChangeOpSetAttributeScalar::class);
|
||||
$oMyChangeOp->Set("objclass", get_class($oObject));
|
||||
$oMyChangeOp->Set("objkey", $oObject->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
|
||||
$oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
199
sources/Core/AttributeDefinition/AttributeString.php
Normal file
199
sources/Core/AttributeDefinition/AttributeString.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use CoreWarning;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map a varchar column (size < ?) to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeString extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return 'VARCHAR(255)'
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
$sPattern = $this->GetOptional('validation_pattern', '');
|
||||
if (empty($sPattern)) {
|
||||
return parent::GetValidationPattern();
|
||||
} else {
|
||||
return $sPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public function CheckFormat($value)
|
||||
{
|
||||
$sRegExp = $this->GetValidationPattern();
|
||||
if (empty($sRegExp)) {
|
||||
return true;
|
||||
} else {
|
||||
$sRegExp = str_replace('/', '\\/', $sRegExp);
|
||||
|
||||
return preg_match("/$sRegExp/", $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"=" => "equals",
|
||||
"!=" => "differs from",
|
||||
"Like" => "equals (no case)",
|
||||
"NotLike" => "differs from (no case)",
|
||||
"Contains" => "contains",
|
||||
"Begins with" => "begins with",
|
||||
"Finishes with" => "finishes with",
|
||||
);
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "Contains";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '=':
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." $sOpCode $sQValue";
|
||||
case 'Begins with':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("$value%");
|
||||
case 'Finishes with':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value");
|
||||
case 'Contains':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value%");
|
||||
case 'NotLike':
|
||||
return $this->GetSQLExpr()." NOT LIKE $sQValue";
|
||||
case 'Like':
|
||||
default:
|
||||
return $this->GetSQLExpr()." LIKE $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return ($proposedValue == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return utils::IsNotNullOrEmptyString($proposedValue);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string)$proposedValue;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (!is_string($value) && !is_null($value)) {
|
||||
throw new CoreWarning('Expected the attribute value to be a string', array(
|
||||
'found_type' => gettype($value),
|
||||
'value' => $value,
|
||||
'class' => $this->GetHostClass(),
|
||||
'attribute' => $this->GetCode(),
|
||||
));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
$sFrom = array("\r\n", $sTextQualifier);
|
||||
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
|
||||
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
|
||||
|
||||
return $sTextQualifier.$sEscaped.$sTextQualifier;
|
||||
}
|
||||
|
||||
public function GetDisplayStyle()
|
||||
{
|
||||
return $this->GetOptional('display_style', 'select');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\StringField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
}
|
||||
273
sources/Core/AttributeDefinition/AttributeSubItem.php
Normal file
273
sources/Core/AttributeDefinition/AttributeSubItem.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* View of a subvalue of another attribute
|
||||
* If an attribute implements the verbs GetSubItem.... then it can expose
|
||||
* internal values, each of them being an attribute and therefore they
|
||||
* can be displayed at different times in the object lifecycle, and used for
|
||||
* reporting (as a condition in OQL, or as an additional column in an export)
|
||||
* Known usages: Stop Watches can expose threshold statuses
|
||||
*/
|
||||
class AttributeSubItem extends AttributeDefinition
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search widget type corresponding to this attribute
|
||||
* the computation is made by AttributeStopWatch::GetSubItemSearchType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetSearchType()
|
||||
{
|
||||
/** @var AttributeStopWatch $oParent */
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
|
||||
return $oParent->GetSubItemSearchType($this->Get('item_code'));
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
/** @var AttributeStopWatch $oParent */
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
|
||||
return $oParent->GetSubItemAllowedValues($this->Get('item_code'), $aArgs, $sContains);
|
||||
}
|
||||
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
/** @var AttributeStopWatch $oParent */
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
|
||||
$bDefaultValue = parent::IsNullAllowed();
|
||||
|
||||
return $oParent->IsSubItemNullAllowed($this->Get('item_code'), $bDefaultValue);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('target_attcode', 'item_code'));
|
||||
}
|
||||
|
||||
public function GetParentAttCode()
|
||||
{
|
||||
return $this->Get("target_attcode");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper : get the attribute definition to which the execution will be forwarded
|
||||
*/
|
||||
public function GetTargetAttDef()
|
||||
{
|
||||
$sClass = $this->GetHostClass();
|
||||
$oParentAttDef = MetaModel::GetAttributeDef($sClass, $this->Get('target_attcode'));
|
||||
|
||||
return $oParentAttDef;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function IsBasedOnDBColumns()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsScalar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// public function IsNullAllowed() {return false;}
|
||||
|
||||
public static function LoadInObject()
|
||||
{
|
||||
return false;
|
||||
} // if this verb returns false, then GetValues must be implemented
|
||||
|
||||
/**
|
||||
* Used by DBOBject::Get()
|
||||
*
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
* @return AttributeSubItem
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetValue($oHostObject)
|
||||
{
|
||||
/** @var AttributeStopWatch $oParent */
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$parentValue = $oHostObject->GetStrict($oParent->GetCode());
|
||||
$res = $oParent->GetSubItemValue($this->Get('item_code'), $parentValue, $oHostObject);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
//
|
||||
// protected function ScalarToSQL($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode) {
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSQLExpressions($sPrefix = '')
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemSQLExpression($this->Get('item_code'));
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsPlainText($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsPlainText($this->Get('item_code'), $value);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsHTML($this->Get('item_code'), $value);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForHistory($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsHTMLForHistory($this->Get('item_code'), $value);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
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,
|
||||
$bConvertToPlainText);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsXML($this->Get('item_code'), $value);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* As of now, this function must be implemented to have the value in spreadsheet format
|
||||
*/
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsEditValue($this->Get('item_code'), $value);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\LabelField';
|
||||
}
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
// Note : As of today, this attribute is -by nature- only supported in readonly mode, not edition
|
||||
$sAttCode = $this->GetCode();
|
||||
$oFormField->SetCurrentValue(html_entity_decode($oObject->GetAsHTML($sAttCode), ENT_QUOTES, 'UTF-8'));
|
||||
$oFormField->SetReadOnly(true);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
}
|
||||
176
sources/Core/AttributeDefinition/AttributeTable.php
Normal file
176
sources/Core/AttributeDefinition/AttributeTable.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBSource;
|
||||
use CoreException;
|
||||
use Exception;
|
||||
|
||||
class AttributeTable extends AttributeDBField
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "Table";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "LONGTEXT".CMDBSource::GetSqlStringColumnDefinition();
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return (count($proposedValue) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
return count($proposedValue) > 0;
|
||||
}
|
||||
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
// Facilitate things: allow the user to Set the value from a string
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return array();
|
||||
} else {
|
||||
if (!is_array($proposedValue)) {
|
||||
return array(0 => array(0 => $proposedValue));
|
||||
}
|
||||
}
|
||||
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
try {
|
||||
$value = @unserialize($aCols[$sPrefix.'']);
|
||||
if ($value === false) {
|
||||
$value = @json_decode($aCols[$sPrefix.''], true);
|
||||
if (is_null($value)) {
|
||||
$value = false;
|
||||
}
|
||||
}
|
||||
if ($value === false) {
|
||||
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$value = $this->MakeRealValue($aCols[$sPrefix.''], null);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$aValues = array();
|
||||
try {
|
||||
$sSerializedValue = serialize($value);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sSerializedValue = json_encode($value);
|
||||
}
|
||||
$aValues[$this->Get("sql")] = $sSerializedValue;
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
throw new CoreException('Expecting an array', array('found' => get_class($value)));
|
||||
}
|
||||
if (count($value) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$sRes = "<TABLE class=\"listResults\">";
|
||||
$sRes .= "<TBODY>";
|
||||
foreach ($value as $iRow => $aRawData) {
|
||||
$sRes .= "<TR>";
|
||||
foreach ($aRawData as $iCol => $cell) {
|
||||
// Note: avoid the warning in case the cell is made of an array
|
||||
$sCell = @Str::pure2html((string)$cell);
|
||||
$sCell = str_replace("\n", "<br>\n", $sCell);
|
||||
$sRes .= "<TD>$sCell</TD>";
|
||||
}
|
||||
$sRes .= "</TR>";
|
||||
}
|
||||
$sRes .= "</TBODY>";
|
||||
$sRes .= "</TABLE>";
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
public function GetAsCSV(
|
||||
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
|
||||
$bConvertToPlainText = false
|
||||
)
|
||||
{
|
||||
// Not implemented
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (!is_array($value) || count($value) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$sRes = "";
|
||||
foreach ($value as $iRow => $aRawData) {
|
||||
$sRes .= "<row>";
|
||||
foreach ($aRawData as $iCol => $cell) {
|
||||
$sCell = Str::pure2xml((string)$cell);
|
||||
$sRes .= "<cell icol=\"$iCol\">$sCell</cell>";
|
||||
}
|
||||
$sRes .= "</row>";
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
}
|
||||
662
sources/Core/AttributeDefinition/AttributeTagSet.php
Normal file
662
sources/Core/AttributeDefinition/AttributeTagSet.php
Normal file
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CoreException;
|
||||
use CoreUnexpectedValue;
|
||||
use CoreWarning;
|
||||
use DBObject;
|
||||
use DBSearch;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use OQLException;
|
||||
use ormTagSet;
|
||||
use TagSetFieldData;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Multi value list of tags
|
||||
*
|
||||
* @see TagSetFieldData
|
||||
* @since 2.6.0 N°931 tag fields
|
||||
*/
|
||||
class AttributeTagSet extends AttributeSet
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_TAG_SET;
|
||||
|
||||
public function __construct($sCode, array $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
$this->aCSSClasses[] = 'attribute-tag-set';
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return 'TagSet';
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array('tag_code_max_len'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ormTagSet $oValue
|
||||
*
|
||||
* @param $aArgs
|
||||
*
|
||||
* @return string JSON to be used in the itop.tagset_widget JQuery widget
|
||||
*/
|
||||
public function GetJsonForWidget($oValue, $aArgs = array())
|
||||
{
|
||||
$aJson = array();
|
||||
|
||||
// possible_values
|
||||
$aTagSetObjectData = $this->GetAllowedValues($aArgs);
|
||||
$aTagSetKeyValData = array();
|
||||
foreach ($aTagSetObjectData as $sTagCode => $sTagLabel) {
|
||||
$aTagSetKeyValData[] = [
|
||||
'code' => $sTagCode,
|
||||
'label' => $sTagLabel,
|
||||
];
|
||||
}
|
||||
$aJson['possible_values'] = $aTagSetKeyValData;
|
||||
|
||||
if (is_null($oValue)) {
|
||||
$aJson['partial_values'] = array();
|
||||
$aJson['orig_value'] = array();
|
||||
$aJson['added'] = array();
|
||||
$aJson['removed'] = array();
|
||||
} else {
|
||||
$aJson['orig_value'] = array_merge($oValue->GetValues(), $oValue->GetModified());
|
||||
$aJson['added'] = $oValue->GetAdded();
|
||||
$aJson['removed'] = $oValue->GetRemoved();
|
||||
|
||||
if ($oValue->DisplayPartial()) {
|
||||
// For bulk updates
|
||||
$aJson['partial_values'] = $oValue->GetModified();
|
||||
} else {
|
||||
// For simple updates
|
||||
$aJson['partial_values'] = array();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$iMaxTags = $this->GetMaxItems();
|
||||
$aJson['max_items_allowed'] = $iMaxTags;
|
||||
|
||||
return json_encode($aJson);
|
||||
}
|
||||
|
||||
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
|
||||
{
|
||||
$aValues = array();
|
||||
if (!empty($proposedValue)) {
|
||||
foreach (explode(' ', $proposedValue) as $sCode) {
|
||||
$sValue = trim($sCode);
|
||||
$aValues[] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all existing tags from a string and ignore bad tags
|
||||
*
|
||||
* @param $sValue
|
||||
* @param bool $bNoLimit : don't apply the maximum tag limit
|
||||
*
|
||||
* @return ormTagSet
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*/
|
||||
public function GetExistingTagsFromString($sValue, $bNoLimit = false)
|
||||
{
|
||||
$aTagCodes = $this->FromStringToArray("$sValue");
|
||||
$sAttCode = $this->GetCode();
|
||||
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $sAttCode);
|
||||
if ($bNoLimit) {
|
||||
$oTagSet = new ormTagSet($sClass, $sAttCode, 0);
|
||||
} else {
|
||||
$oTagSet = new ormTagSet($sClass, $sAttCode, $this->GetMaxItems());
|
||||
}
|
||||
$aGoodTags = array();
|
||||
foreach ($aTagCodes as $sTagCode) {
|
||||
if ($sTagCode === '') {
|
||||
continue;
|
||||
}
|
||||
if ($oTagSet->IsValidTag($sTagCode)) {
|
||||
$aGoodTags[] = $sTagCode;
|
||||
if (!$bNoLimit && (count($aGoodTags) === $this->GetMaxItems())) {
|
||||
// extra and bad tags are ignored
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$oTagSet->SetValues($aGoodTags);
|
||||
|
||||
return $oTagSet;
|
||||
}
|
||||
|
||||
public function GetTagCodeMaxLength()
|
||||
{
|
||||
return $this->Get('tag_code_max_len');
|
||||
}
|
||||
|
||||
public function GetEditValue($value, $oHostObj = null)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
if ($value instanceof ormTagSet) {
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return implode(' ', $aValues);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return max(255, ($this->GetMaxItems() * $this->GetTagCodeMaxLength()) + 1);
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
if (($val1 instanceof ormTagSet) && ($val2 instanceof ormTagSet)) {
|
||||
return $val1->Equals($val2);
|
||||
}
|
||||
|
||||
return ($val1 == $val2);
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$sAttCode = $this->GetCode();
|
||||
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $sAttCode);
|
||||
$aAllowedTags = TagSetFieldData::GetAllowedValues($sClass, $sAttCode);
|
||||
$aAllowedValues = array();
|
||||
foreach ($aAllowedTags as $oAllowedTag) {
|
||||
$aAllowedValues[$oAllowedTag->Get('code')] = $oAllowedTag->Get('label');
|
||||
}
|
||||
|
||||
return $aAllowedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aCols
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
$sValue = $aCols["$sPrefix"];
|
||||
|
||||
return $this->GetExistingTagsFromString($sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
|
||||
*
|
||||
* @param $proposedValue
|
||||
* @param $oHostObj
|
||||
*
|
||||
* @param bool $bIgnoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
|
||||
{
|
||||
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
if (is_string($proposedValue) && !empty($proposedValue)) {
|
||||
$sJsonFromWidget = json_decode($proposedValue, true);
|
||||
if (is_null($sJsonFromWidget)) {
|
||||
$proposedValue = trim("$proposedValue");
|
||||
$aTagCodes = $this->FromStringToArray($proposedValue);
|
||||
$oTagSet->SetValues($aTagCodes);
|
||||
}
|
||||
} elseif ($proposedValue instanceof ormTagSet) {
|
||||
$oTagSet = $proposedValue;
|
||||
}
|
||||
|
||||
return $oTagSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value from a given string (plain text, CSV import)
|
||||
*
|
||||
* @param string $sProposedValue
|
||||
* @param bool $bLocalizedValue
|
||||
* @param string $sSepItem
|
||||
* @param string $sSepAttribute
|
||||
* @param string $sSepValue
|
||||
* @param string $sAttributeQualifier
|
||||
*
|
||||
* @return mixed null if no match could be found
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
|
||||
{
|
||||
if (is_null($sSepItem) || empty($sSepItem)) {
|
||||
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
|
||||
}
|
||||
if (!empty($sProposedValue)) {
|
||||
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()),
|
||||
$this->GetCode(), $this->GetMaxItems());
|
||||
$aLabels = explode($sSepItem, $sProposedValue);
|
||||
$aCodes = array();
|
||||
foreach ($aLabels as $sTagLabel) {
|
||||
if (!empty($sTagLabel)) {
|
||||
$aCodes[] = ($bLocalizedValue) ? $oTagSet->GetTagFromLabel($sTagLabel) : $sTagLabel;
|
||||
}
|
||||
}
|
||||
$sProposedValue = implode(' ', $aCodes);
|
||||
}
|
||||
|
||||
return $this->MakeRealValue($sProposedValue, null);
|
||||
}
|
||||
|
||||
public function GetNullValue()
|
||||
{
|
||||
return new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
}
|
||||
|
||||
public function GetDefaultValue(DBObject $oHostObject = null)
|
||||
{
|
||||
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
|
||||
$oTagSet->SetValues([]);
|
||||
|
||||
return $oTagSet;
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
if (is_null($proposedValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var ormTagSet $proposedValue */
|
||||
return count($proposedValue->GetValues()) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overloaded for localized enums
|
||||
*
|
||||
* @param $sValue
|
||||
*
|
||||
* @return string label corresponding to the given value (in plain text)
|
||||
* @throws CoreWarning
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetValueLabel($sValue)
|
||||
{
|
||||
if (empty($sValue)) {
|
||||
return '';
|
||||
}
|
||||
if (is_string($sValue)) {
|
||||
$sValue = $this->GetExistingTagsFromString($sValue);
|
||||
}
|
||||
if ($sValue instanceof ormTagSet) {
|
||||
$aValues = $sValue->GetLabels();
|
||||
|
||||
return implode(', ', $aValues);
|
||||
}
|
||||
throw new CoreWarning('Expected the attribute value to be a TagSet', array(
|
||||
'found_type' => gettype($sValue),
|
||||
'value' => $sValue,
|
||||
'class' => $this->GetHostClass(),
|
||||
'attribute' => $this->GetCode(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreWarning
|
||||
*/
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
if ($value instanceof ormTagSet) {
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return implode(' ', $aValues);
|
||||
}
|
||||
throw new CoreWarning('Expected the attribute value to be a TagSet', array(
|
||||
'found_type' => gettype($value),
|
||||
'value' => $value,
|
||||
'class' => $this->GetHostClass(),
|
||||
'attribute' => $this->GetCode(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if ($value instanceof ormTagSet) {
|
||||
if ($bLocalize) {
|
||||
$aValues = $value->GetTags();
|
||||
} else {
|
||||
$aValues = $value->GetValues();
|
||||
}
|
||||
if (empty($aValues)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->GenerateViewHtmlForValues($aValues);
|
||||
}
|
||||
if (is_string($value)) {
|
||||
try {
|
||||
$oValue = $this->MakeRealValue($value, $oHostObject);
|
||||
|
||||
return $this->GetAsHTML($oValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// unknown tags are present display the code instead
|
||||
}
|
||||
$aTagCodes = $this->FromStringToArray($value);
|
||||
$aValues = array();
|
||||
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()),
|
||||
$this->GetCode(), $this->GetMaxItems());
|
||||
foreach ($aTagCodes as $sTagCode) {
|
||||
try {
|
||||
$oTagSet->Add($sTagCode);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$aValues[] = $sTagCode;
|
||||
}
|
||||
}
|
||||
$sHTML = '';
|
||||
if (!empty($aValues)) {
|
||||
$sHTML .= $this->GenerateViewHtmlForValues($aValues, 'attribute-set-item-undefined');
|
||||
}
|
||||
$aValues = $oTagSet->GetTags();
|
||||
if (!empty($aValues)) {
|
||||
$sHTML .= $this->GenerateViewHtmlForValues($aValues);
|
||||
}
|
||||
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
return parent::GetAsHTML($value, $oHostObject, $bLocalize);
|
||||
}
|
||||
|
||||
// Do not display friendly names in the history of change
|
||||
public function DescribeChangeAsHTML($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_Changed', $this->GetLabel()).", ";
|
||||
|
||||
$aNewValues = $this->FromStringToArray($sNewValue);
|
||||
$aOldValues = $this->FromStringToArray($sOldValue);
|
||||
|
||||
$aDelta['removed'] = array_diff($aOldValues, $aNewValues);
|
||||
$aDelta['added'] = array_diff($aNewValues, $aOldValues);
|
||||
|
||||
$aAllowedTags = TagSetFieldData::GetAllowedValues(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode());
|
||||
|
||||
if (!empty($aDelta['removed'])) {
|
||||
$aRemoved = array();
|
||||
foreach ($aDelta['removed'] as $idx => $sTagCode) {
|
||||
if (empty($sTagCode)) {
|
||||
continue;
|
||||
}
|
||||
$sTagLabel = $sTagCode;
|
||||
foreach ($aAllowedTags as $oTag) {
|
||||
if ($sTagCode === $oTag->Get('code')) {
|
||||
$sTagLabel = $oTag->Get('label');
|
||||
}
|
||||
}
|
||||
$aRemoved[] = $sTagLabel;
|
||||
}
|
||||
|
||||
$sRemoved = $this->GenerateViewHtmlForValues($aRemoved, 'history-removed');
|
||||
if (!empty($sRemoved)) {
|
||||
$sResult .= Dict::Format('Change:LinkSet:Removed', $sRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($aDelta['added'])) {
|
||||
if (!empty($sRemoved)) {
|
||||
$sResult .= ', ';
|
||||
}
|
||||
|
||||
$aAdded = array();
|
||||
foreach ($aDelta['added'] as $idx => $sTagCode) {
|
||||
if (empty($sTagCode)) {
|
||||
continue;
|
||||
}
|
||||
$sTagLabel = $sTagCode;
|
||||
foreach ($aAllowedTags as $oTag) {
|
||||
if ($sTagCode === $oTag->Get('code')) {
|
||||
$sTagLabel = $oTag->Get('label');
|
||||
}
|
||||
}
|
||||
$aAdded[] = $sTagLabel;
|
||||
}
|
||||
|
||||
$sAdded = $this->GenerateViewHtmlForValues($aAdded, 'history-added');
|
||||
if (!empty($sAdded)) {
|
||||
$sResult .= Dict::Format('Change:LinkSet:Added', $sAdded);
|
||||
}
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML representation of a list of tags (read-only)
|
||||
* accept a list of strings or a list of TagSetFieldData
|
||||
*
|
||||
* @param array $aValues
|
||||
* @param string $sCssClass
|
||||
* @param bool $bWithLink if true will generate a link, otherwise just a "a" tag without href
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function GenerateViewHtmlForValues($aValues, $sCssClass = '', $bWithLink = true)
|
||||
{
|
||||
if (empty($aValues)) {
|
||||
return '';
|
||||
}
|
||||
$sHtml = '<span class="'.$sCssClass.' '.implode(' ', $this->aCSSClasses).'">';
|
||||
foreach ($aValues as $oTag) {
|
||||
if ($oTag instanceof TagSetFieldData) {
|
||||
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode());
|
||||
$sAttCode = $this->GetCode();
|
||||
$sTagCode = $oTag->Get('code');
|
||||
$sTagLabel = $oTag->Get('label');
|
||||
$sTagDescription = $oTag->Get('description');
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
$sLabelForHtml = utils::EscapeHtml($sTagLabel);
|
||||
$sDescriptionForHtml = utils::EscapeHtml($sTagDescription);
|
||||
if (empty($sTagDescription)) {
|
||||
$sTooltipContent = $sTagLabel;
|
||||
$sTooltipHtmlEnabled = 'false';
|
||||
} else {
|
||||
$sTagLabelEscaped = utils::EscapeHtml($sTagLabel);
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sTagLabelEscaped</h4>
|
||||
<div>$sTagDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
}
|
||||
$sTooltipContent = utils::HtmlEntities($sTooltipContent);
|
||||
|
||||
$sHtml .= '<a'.$sLink.' class="attribute-set-item attribute-set-item-'.$sTagCode.'" data-code="'.$sTagCode.'" data-label="'.$sLabelForHtml.'" data-description="'.$sDescriptionForHtml.'" data-tooltip-content="'.$sTooltipContent.'" data-tooltip-html-enabled="'.$sTooltipHtmlEnabled.'">'.$sLabelForHtml.'</a>';
|
||||
} else {
|
||||
$sHtml .= '<span class="attribute-set-item">'.utils::EscapeHtml($oTag).'</span>';
|
||||
}
|
||||
}
|
||||
$sHtml .= '</span>';
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param DBObject $oHostObject
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value) && ($value instanceof ormTagSet)) {
|
||||
$sRes = "<Set>\n";
|
||||
if ($bLocalize) {
|
||||
$aValues = $value->GetLabels();
|
||||
} else {
|
||||
$aValues = $value->GetValues();
|
||||
}
|
||||
if (!empty($aValues)) {
|
||||
$sRes .= '<Tag>'.implode('</Tag><Tag>', $aValues).'</Tag>';
|
||||
}
|
||||
$sRes .= "</Set>\n";
|
||||
} else {
|
||||
$sRes = '';
|
||||
}
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available verbs for 'GetForTemplate'
|
||||
*/
|
||||
public function EnumTemplateVerbs()
|
||||
{
|
||||
return array(
|
||||
'' => 'Plain text representation',
|
||||
'html' => 'HTML representation (unordered list)',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
|
||||
*
|
||||
* @param mixed $value The current value of the field
|
||||
* @param string $sVerb The verb specifying the representation of the value
|
||||
* @param DBObject $oHostObject The object
|
||||
* @param bool $bLocalize Whether or not to localize the value
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($value) && ($value instanceof ormTagSet)) {
|
||||
if ($bLocalize) {
|
||||
$aValues = $value->GetLabels();
|
||||
$sSep = ', ';
|
||||
} else {
|
||||
$aValues = $value->GetValues();
|
||||
$sSep = ' ';
|
||||
}
|
||||
|
||||
switch ($sVerb) {
|
||||
case '':
|
||||
return implode($sSep, $aValues);
|
||||
|
||||
case 'html':
|
||||
return '<ul><li>'.implode("</li><li>", $aValues).'</li></ul>';
|
||||
|
||||
default:
|
||||
throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
|
||||
}
|
||||
}
|
||||
throw new CoreUnexpectedValue("Bad value '$value' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param ormTagSet $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
$aRet = array();
|
||||
if (is_object($value) && ($value instanceof ormTagSet)) {
|
||||
$aRet = $value->GetValues();
|
||||
}
|
||||
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return ormTagSet
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws Exception
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
$oSet = new ormTagSet($this->GetHostClass(), $this->GetCode(), $this->GetMaxItems());
|
||||
$oSet->SetValues($json);
|
||||
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the current attribute in the object's signature, for the supplied value
|
||||
*
|
||||
* @param mixed $value The value of this attribute for the object
|
||||
*
|
||||
* @return string The "signature" for this field/attribute
|
||||
*/
|
||||
public function Fingerprint($value)
|
||||
{
|
||||
if ($value instanceof ormTagSet) {
|
||||
$aValues = $value->GetValues();
|
||||
|
||||
return implode(' ', $aValues);
|
||||
}
|
||||
|
||||
return parent::Fingerprint($value);
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\TagSetField';
|
||||
}
|
||||
}
|
||||
61
sources/Core/AttributeDefinition/AttributeTemplateHTML.php
Normal file
61
sources/Core/AttributeDefinition/AttributeTemplateHTML.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Specialization of a HTML: template (contains iTop placeholders like $current_contact_id$ or $this->name$)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeTemplateHTML extends AttributeText
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetSQLColumns($bFullSpec = false)
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->Get('sql')] = $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
|
||||
}
|
||||
}
|
||||
36
sources/Core/AttributeDefinition/AttributeTemplateString.php
Normal file
36
sources/Core/AttributeDefinition/AttributeTemplateString.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Specialization of a string: template (contains iTop placeholders like $current_contact_id$ or $this->name$)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeTemplateString extends AttributeString
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
}
|
||||
36
sources/Core/AttributeDefinition/AttributeTemplateText.php
Normal file
36
sources/Core/AttributeDefinition/AttributeTemplateText.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Specialization of a text: template (contains iTop placeholders like $current_contact_id$ or $this->name$)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeTemplateText extends AttributeText
|
||||
{
|
||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
}
|
||||
408
sources/Core/AttributeDefinition/AttributeText.php
Normal file
408
sources/Core/AttributeDefinition/AttributeText.php
Normal file
@@ -0,0 +1,408 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOp;
|
||||
use CMDBChangeOpSetAttributeHTML;
|
||||
use CMDBChangeOpSetAttributeText;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use HTMLSanitizer;
|
||||
use InlineImage;
|
||||
use MetaModel;
|
||||
use ormCaseLog;
|
||||
use Str;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map a text column (size > ?) to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeText extends AttributeString
|
||||
{
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return ($this->GetFormat() == 'text') ? 'Text' : "HTML";
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "TEXT".CMDBSource::GetSqlStringColumnDefinition();
|
||||
}
|
||||
|
||||
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')".CMDBSource::GetSqlStringColumnDefinition();
|
||||
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?
|
||||
// See mysql_field_len()
|
||||
return 65535;
|
||||
}
|
||||
|
||||
public static function RenderWikiHtml($sText, $bWikiOnly = false)
|
||||
{
|
||||
if (!$bWikiOnly) {
|
||||
$sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i';
|
||||
if (preg_match_all($sPattern, $sText, $aAllMatches,
|
||||
PREG_SET_ORDER /* important !*/ | PREG_OFFSET_CAPTURE /* important ! */)) {
|
||||
$i = count($aAllMatches);
|
||||
// Replace the URLs by an actual hyperlink <a href="...">...</a>
|
||||
// Let's do it backwards so that the initial positions are not modified by the replacement
|
||||
// This works if the matches are captured: in the order they occur in the string AND
|
||||
// with their offset (i.e. position) inside the string
|
||||
while ($i > 0) {
|
||||
$i--;
|
||||
$sUrl = $aAllMatches[$i][0][0]; // String corresponding to the main pattern
|
||||
$iPos = $aAllMatches[$i][0][1]; // Position of the main pattern
|
||||
$sText = substr_replace($sText, "<a href=\"$sUrl\">$sUrl</a>", $iPos, strlen($sUrl));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sText, $aAllMatches, PREG_SET_ORDER)) {
|
||||
foreach ($aAllMatches as $iPos => $aMatches) {
|
||||
$sClass = trim($aMatches[1]);
|
||||
$sName = trim($aMatches[2]);
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
|
||||
if (MetaModel::IsValidClass($sClass)) {
|
||||
$bFound = false;
|
||||
|
||||
// Try to find by name, then by id
|
||||
if (is_object($oObj = MetaModel::GetObjectByName($sClass, $sName, false /* MustBeFound */))) {
|
||||
$bFound = true;
|
||||
} elseif (is_object($oObj = MetaModel::GetObject($sClass, (int)$sName, false /* MustBeFound */, true))) {
|
||||
$bFound = true;
|
||||
}
|
||||
|
||||
if ($bFound === true) {
|
||||
// Propose a std link to the object
|
||||
$sHyperlinkLabel = (empty($sLabel)) ? $oObj->GetName() : $sLabel;
|
||||
$sText = str_replace($aMatches[0], $oObj->GetHyperlink(null, true, $sHyperlinkLabel), $sText);
|
||||
} else {
|
||||
// Propose a std link to the object
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sToolTipForHtml = utils::EscapeHtml(Dict::Format('Core:UnknownObjectLabel', $sClass, $sName));
|
||||
$sReplacement = "<span class=\"wiki_broken_link ibo-is-broken-hyperlink\" data-tooltip-content=\"$sToolTipForHtml\">$sClassLabel:$sName".(!empty($sLabel) ? " ($sLabel)" : "")."</span>";
|
||||
$sText = str_replace($aMatches[0], $sReplacement, $sText);
|
||||
// Later: propose a link to create a new object
|
||||
// Anyhow... there is no easy way to suggest default values based on the given FRIENDLY name
|
||||
//$sText = preg_replace('/\[\[(.+):(.+)\]\]/', '<a href="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.'&default[att1]=xxx&default[att2]=yyy">'.$sName.'</a>', $sText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sText;
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$aStyles = array();
|
||||
if ($this->GetWidth() != '') {
|
||||
$aStyles[] = 'width:'.$this->GetWidth();
|
||||
}
|
||||
if ($this->GetHeight() != '') {
|
||||
$aStyles[] = 'height:'.$this->GetHeight();
|
||||
}
|
||||
$sStyle = '';
|
||||
if (count($aStyles) > 0) {
|
||||
$sStyle = 'style="'.implode(';', $aStyles).'"';
|
||||
}
|
||||
|
||||
if ($this->GetFormat() == 'text') {
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
$sValue = nl2br($sValue);
|
||||
|
||||
return "<div $sStyle>$sValue</div>";
|
||||
} else {
|
||||
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
|
||||
|
||||
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
|
||||
if ($sValue == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->GetFormat() == 'text') {
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
|
||||
foreach ($aAllMatches as $iPos => $aMatches) {
|
||||
$sClass = trim($aMatches[1]);
|
||||
$sName = trim($aMatches[2]);
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
|
||||
if (MetaModel::IsValidClass($sClass)) {
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
|
||||
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* For fields containing a potential markup, return the value without this markup
|
||||
*
|
||||
* @param string $sValue
|
||||
* @param DBObject $oHostObj
|
||||
*
|
||||
* @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;
|
||||
|
||||
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
|
||||
if ($sValue == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($this->GetFormat()) {
|
||||
case 'html':
|
||||
if (($sValue !== null) && ($sValue !== '')) {
|
||||
$sValue = HTMLSanitizer::Sanitize($sValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
default:
|
||||
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
|
||||
foreach ($aAllMatches as $iPos => $aMatches) {
|
||||
$sClassLabel = trim($aMatches[1]);
|
||||
$sName = trim($aMatches[2]);
|
||||
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
|
||||
|
||||
if (!MetaModel::IsValidClass($sClassLabel)) {
|
||||
$sClass = MetaModel::GetClassFromLabel($sClassLabel);
|
||||
if ($sClass) {
|
||||
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
|
||||
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetWidth()
|
||||
{
|
||||
return $this->GetOptional('width', '');
|
||||
}
|
||||
|
||||
public function GetHeight()
|
||||
{
|
||||
return $this->GetOptional('height', '');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\TextAreaField';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param TextAreaField $oFormField
|
||||
*
|
||||
* @return TextAreaField
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
/** @var TextAreaField $oFormField */
|
||||
$oFormField = new $sFormFieldClass($this->GetCode(), null, $oObject);
|
||||
$oFormField->SetFormat($this->GetFormat());
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @param array $aCols
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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 = InlineImage::FixUrls((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);
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
/** @noinspection PhpConditionCheckedByNextConditionInspection */
|
||||
if (false === is_null($original) && ($original instanceof ormCaseLog)) {
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return ($this->GetFormat() === 'html')
|
||||
? CMDBChangeOpSetAttributeHTML::class
|
||||
: CMDBChangeOpSetAttributeText::class;
|
||||
}
|
||||
}
|
||||
125
sources/Core/AttributeDefinition/AttributeURL.php
Normal file
125
sources/Core/AttributeDefinition/AttributeURL.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use CMDBChangeOpSetAttributeURL;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Form\Field\UrlField;
|
||||
use CoreException;
|
||||
use DBObject;
|
||||
use Exception;
|
||||
use Str;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Map a varchar column to an URL (formats the ouput in HMTL)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class AttributeURL extends AttributeString
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* SCHEME....... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH......................... GET............................................ ANCHOR..........................
|
||||
* Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
|
||||
* @link http://www.php.net/manual/fr/function.preg-match.php#93824 regexp source
|
||||
* @since 3.0.1 N°4515 handle Alfresco and Sharepoint URLs
|
||||
* @since 3.0.3 moved from Config to AttributeURL constant
|
||||
*/
|
||||
public const DEFAULT_VALIDATION_PATTERN = /** @lang RegExp */
|
||||
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%@+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
|
||||
|
||||
/**
|
||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||
*
|
||||
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
|
||||
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
|
||||
*
|
||||
* @param string $sCode
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws Exception
|
||||
* @noinspection SenselessProxyMethodInspection
|
||||
*/
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
|
||||
public static function ListExpectedParams()
|
||||
{
|
||||
//return parent::ListExpectedParams();
|
||||
return array_merge(parent::ListExpectedParams(), array("target"));
|
||||
}
|
||||
|
||||
protected function GetSQLCol($bFullSpec = false)
|
||||
{
|
||||
return "VARCHAR(2048)"
|
||||
.CMDBSource::GetSqlStringColumnDefinition()
|
||||
.($bFullSpec ? $this->GetSQLColSpec() : '');
|
||||
}
|
||||
|
||||
public function GetMaxSize()
|
||||
{
|
||||
return 2048;
|
||||
}
|
||||
|
||||
public function GetEditClass()
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$sTarget = $this->Get("target");
|
||||
if (empty($sTarget)) {
|
||||
$sTarget = "_blank";
|
||||
}
|
||||
$sLabel = Str::pure2html($sValue);
|
||||
if (strlen($sLabel) > 128) {
|
||||
// Truncate the length to 128 characters, by removing the middle
|
||||
$sLabel = substr($sLabel, 0, 100).'.....'.substr($sLabel, -20);
|
||||
}
|
||||
|
||||
return "<a target=\"$sTarget\" href=\"$sValue\">$sLabel</a>";
|
||||
}
|
||||
|
||||
public function GetValidationPattern()
|
||||
{
|
||||
return $this->GetOptional('validation_pattern', '^'.utils::GetConfig()->Get('url_validation_pattern').'$');
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
{
|
||||
return '\\Combodo\\iTop\\Form\\Field\\UrlField';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param UrlField $oFormField
|
||||
*
|
||||
* @return null
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
if ($oFormField === null) {
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
$oFormField->SetTarget($this->Get('target'));
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
protected function GetChangeRecordClassName(): string
|
||||
{
|
||||
return CMDBChangeOpSetAttributeURL::class;
|
||||
}
|
||||
}
|
||||
18
sources/Core/AttributeDefinition/MissingColumnException.php
Normal file
18
sources/Core/AttributeDefinition/MissingColumnException.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* MissingColumnException - sent if an attribute is being created but the column is missing in the row
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class MissingColumnException extends Exception
|
||||
{
|
||||
}
|
||||
17
sources/Core/AttributeDefinition/iAttributeNoGroupBy.php
Normal file
17
sources/Core/AttributeDefinition/iAttributeNoGroupBy.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Core\AttributeDefinition;
|
||||
|
||||
/**
|
||||
* Attributes implementing this interface won't be accepted as `group by` field
|
||||
*
|
||||
* @since 2.7.4 N°3473
|
||||
*/
|
||||
interface iAttributeNoGroupBy
|
||||
{
|
||||
//no method, just a contract on implement
|
||||
}
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace Combodo\iTop\PhpParser\Evaluation;
|
||||
|
||||
use ModuleFileParser;
|
||||
use ModuleFileReaderException;
|
||||
use \Combodo\iTop\Setup\ModuleDiscovery\ModuleFileParser;
|
||||
use \Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
use PhpParser\ConstExprEvaluator;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
/**
|
||||
* Used at runtime/setup time
|
||||
*/
|
||||
@@ -31,7 +30,7 @@ class PhpExpressionEvaluator {
|
||||
* @param string $sBooleanExpr
|
||||
*
|
||||
* @return bool
|
||||
* @throws \ModuleFileReaderException
|
||||
* @throws ModuleFileReaderException
|
||||
*/
|
||||
public function ParseAndEvaluateBooleanExpression(string $sBooleanExpr) : bool
|
||||
{
|
||||
|
||||
@@ -31,4 +31,66 @@ class_alias(\Combodo\iTop\Application\WebPage\PDFPage::class, 'PDFPage');
|
||||
class_alias(\Combodo\iTop\Application\WebPage\TabManager::class, 'TabManager');
|
||||
class_alias(\Combodo\iTop\Application\WebPage\UnauthenticatedWebPage::class, 'UnauthenticatedWebPage');
|
||||
class_alias(\Combodo\iTop\Application\WebPage\WebPage::class, 'WebPage');
|
||||
class_alias(\Combodo\iTop\Application\WebPage\XMLPage::class, 'XMLPage');
|
||||
class_alias(\Combodo\iTop\Application\WebPage\XMLPage::class, 'XMLPage');
|
||||
|
||||
// attribute definitions
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeApplicationLanguage::class, 'AttributeApplicationLanguage');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeArchiveDate::class, 'AttributeArchiveDate');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeArchiveFlag::class, 'AttributeArchiveFlag');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeBlob::class, 'AttributeBlob');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeBoolean::class, 'AttributeBoolean');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeCaseLog::class, 'AttributeCaseLog');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeClass::class, 'AttributeClass');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeClassAttCodeSet::class, 'AttributeClassAttCodeSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeClassState::class, 'AttributeClassState');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeCustomFields::class, 'AttributeCustomFields');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDashboard::class, 'AttributeDashboard');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDate::class, 'AttributeDate');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDateTime::class, 'AttributeDateTime');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDBField::class, 'AttributeDBField');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDBFieldVoid::class, 'AttributeDBFieldVoid');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDeadline::class, 'AttributeDeadline');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDecimal::class, 'AttributeDecimal');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDefinition::class, 'AttributeDefinition');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeDuration::class, 'AttributeDuration');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeEmailAddress::class, 'AttributeEmailAddress');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeEncryptedString::class, 'AttributeEncryptedString');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeEnum::class, 'AttributeEnum');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeEnumSet::class, 'AttributeEnumSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeExternalField::class, 'AttributeExternalField');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeExternalKey::class, 'AttributeExternalKey');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeFinalClass::class, 'AttributeFinalClass');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeFriendlyName::class, 'AttributeFriendlyName');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeHierarchicalKey::class, 'AttributeHierarchicalKey');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeHTML::class, 'AttributeHTML');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeImage::class, 'AttributeImage');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeInteger::class, 'AttributeInteger');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeIPAddress::class, 'AttributeIPAddress');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeLinkedSet::class, 'AttributeLinkedSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeLinkedSetIndirect::class, 'AttributeLinkedSetIndirect');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeLongText::class, 'AttributeLongText');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeMetaEnum::class, 'AttributeMetaEnum');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeObjectKey::class, 'AttributeObjectKey');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeObsolescenceDate::class, 'AttributeObsolescenceDate');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeObsolescenceFlag::class, 'AttributeObsolescenceFlag');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeOneWayPassword::class, 'AttributeOneWayPassword');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeOQL::class, 'AttributeOQL');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributePassword::class, 'AttributePassword');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributePercentage::class, 'AttributePercentage');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributePhoneNumber::class, 'AttributePhoneNumber');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributePropertySet::class, 'AttributePropertySet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeQueryAttCodeSet::class, 'AttributeQueryAttCodeSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeRedundancySettings::class, 'AttributeRedundancySettings');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeSet::class, 'AttributeSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeStopWatch::class, 'AttributeStopWatch');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeString::class, 'AttributeString');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeSubItem::class, 'AttributeSubItem');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeTable::class, 'AttributeTable');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeTagSet::class, 'AttributeTagSet');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeTemplateHTML::class, 'AttributeTemplateHTML');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeTemplateString::class, 'AttributeTemplateString');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeTemplateText::class, 'AttributeTemplateText');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeText::class, 'AttributeText');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeURL::class, 'AttributeURL');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\iAttributeNoGroupBy::class, 'iAttributeNoGroupBy');
|
||||
class_alias(\Combodo\iTop\Core\AttributeDefinition\MissingColumnException::class, 'MissingColumnException');
|
||||
@@ -3284,6 +3284,15 @@ span.search-button, span.refresh-button {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.qtip-content {
|
||||
font-size: 12px;
|
||||
}
|
||||
.qtip-content p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.qtip-content p:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.synchro-source {
|
||||
}
|
||||
.synchro-source-title {
|
||||
|
||||
@@ -130,16 +130,16 @@ class DBBackupTest extends ItopTestCase
|
||||
'Localhost with port' => ['localhost', $iTestPort, $iTestPort, ' --protocol=tcp'],
|
||||
|
||||
// we want both port and protocol for 127.0.0.1, because it is an ip address so using tcp/ip stack !
|
||||
'127.0.0.1 no port' => ['127.0.0.1', null, $iDefaultPort, ''],
|
||||
'127.0.0.1 no port' => ['127.0.0.1', null, null, ''],
|
||||
'127.0.0.1 with port' => ['127.0.0.1', $iTestPort, $iTestPort, ''],
|
||||
|
||||
'IP no port' => ['192.168.1.15', null, $iDefaultPort, ''],
|
||||
'IP no port' => ['192.168.1.15', null, null, ''],
|
||||
'IP with port' => ['192.168.1.15', $iTestPort, $iTestPort, ''],
|
||||
|
||||
'DNS no port' => ['dbserver.mycompany.com', null, $iDefaultPort, ''],
|
||||
'DNS no port' => ['dbserver.mycompany.com', null, null, ''],
|
||||
'DNS with port' => ['dbserver.mycompany.com', $iTestPort, $iTestPort, ''],
|
||||
|
||||
'Windows name no port' => ['dbserver', null, $iDefaultPort, ''],
|
||||
'Windows name no port' => ['dbserver', null, null, ''],
|
||||
'Windows name with port' => ['dbserver', $iTestPort, $iTestPort, ''],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery;
|
||||
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ModuleFileReader;
|
||||
|
||||
class ModuleFileReaderTest extends ItopDataTestCase
|
||||
{
|
||||
@@ -59,7 +60,7 @@ class ModuleFileReaderTest extends ItopDataTestCase
|
||||
{
|
||||
$sModuleFilePath = __DIR__.'/resources/module.__MODULE__.php';
|
||||
|
||||
$this->expectException(\ModuleFileReaderException::class);
|
||||
$this->expectException(ModuleFileReaderException::class);
|
||||
$this->expectExceptionMessage("Syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting ',' or ']' or ')' on line 31");
|
||||
|
||||
ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user