Compare commits

...

50 Commits

Author SHA1 Message Date
Romain Quetiez
1e6437a615 Created branch 1.2.0 (now the branch 1.2 means 1.2.1)
SVN:1.2.0[1722]
2011-12-15 16:17:30 +00:00
Denis Flaven
78e173d5fb Bug fix: paginated lists were broken in the Impact Analysis "List" tab
SVN:1.2[1708]
2011-12-07 10:41:13 +00:00
Romain Quetiez
6b8abce03a - commited merge information (done earlier) -
SVN:1.2[1705]
2011-12-06 13:52:44 +00:00
Denis Flaven
33a7005069 - Bug ! Incorrectly appending a parameter ?version= to linked scripts already containing a parameter in their URL !
- changed the parameter name to itopversion to avoid collisions

SVN:1.2[1702]
2011-12-01 17:18:47 +00:00
Denis Flaven
006a6037d1 Increased Suhosin minimum value for get_max_value to 2048 due to a bug seen on some installations
SVN:1.2[1700]
2011-11-30 13:23:32 +00:00
Denis Flaven
e2f8be1745 Use the default language when creating a new user from CAS
SVN:1.2[1698]
2011-11-29 15:56:24 +00:00
Denis Flaven
ebae45f6a5 Support patterns for casMemberof
SVN:1.2[1696]
2011-11-29 15:36:58 +00:00
Denis Flaven
0685835d49 Allow to log entries on behalf of another user
SVN:1.2[1694]
2011-11-24 17:22:19 +00:00
Romain Quetiez
1b1e88f9a4 In french, organiZation takes an S
SVN:1.2[1691]
2011-11-23 15:30:11 +00:00
Denis Flaven
b90f443e75 - Don't create warnings for empty ext keys (i.e. empty string)
- Properly record multiple warnings
- Don't record warnings in case of creation error (error has precedence)

SVN:1.2[1689]
2011-11-23 09:55:44 +00:00
Romain Quetiez
4da64a64b1 Cosmetic on the API of the dictionnary (internal)
SVN:1.2[1686]
2011-11-21 17:03:36 +00:00
Denis Flaven
6673e171dc Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:1.2[1685]
2011-11-21 13:08:10 +00:00
Denis Flaven
afee7297cc Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:1.2[1683]
2011-11-21 10:57:50 +00:00
Denis Flaven
3cc8b5b88a Enhancement: when an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a warning are then processed everytime in case the ext key changes
Also improved the search/display of replicas

SVN:1.2[1678]
2011-11-18 11:42:29 +00:00
Romain Quetiez
b2e6981b24 Fixed bug in change tracking: it was impossible to factorize code creating objects in the background either in the context of an object update or in the context of the application of a stimulus
SVN:1.2[1674]
2011-11-18 10:46:42 +00:00
Denis Flaven
c0a79fa573 Prevent warnings when checking the available stimuli in the menu...
SVN:1.2[1672]
2011-11-16 17:26:27 +00:00
Denis Flaven
02ad6d19fe Prevent Javascript errors in case a name contains a quote.
SVN:1.2[1670]
2011-11-15 13:12:22 +00:00
Denis Flaven
d16308ab62 - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:1.2[1665]
2011-11-14 10:44:31 +00:00
Denis Flaven
4598959bc2 - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:1.2[1664]
2011-11-14 10:43:06 +00:00
Denis Flaven
65a3755f81 - Regression from previous fix: don't Reload an object before saving it !
SVN:1.2[1662]
2011-11-10 16:37:18 +00:00
Denis Flaven
dc46c65499 - Don't activate triggers if the transition fails
- Reload the object, in case some custom action changed an external object

SVN:1.2[1659]
2011-11-10 10:12:43 +00:00
Denis Flaven
9d691c8e56 Removed obsolete code which caused a warning in bulk_stimulus
SVN:1.2[1658]
2011-11-10 10:07:28 +00:00
Denis Flaven
99f897bff7 Better error handling in case of OQL error
SVN:1.2[1657]
2011-11-09 17:07:25 +00:00
Denis Flaven
8d83447222 Added the capability for plug-ins to "listen" to add/remove attachment events.
SVN:1.2[1652]
2011-10-28 12:46:22 +00:00
Romain Quetiez
dcc8ad08a4 New helper class: TemplateString - to allow extended syntaxes such as $this->location_id->org_id->parent_id->name$... to be progressively introduced and replace the heavy ToArgs()
SVN:1.2[1646]
2011-10-24 13:45:49 +00:00
Denis Flaven
c1b0b73b51 Bug fix: Trac #494. It seems that PHPSoap does not understand the <wsdl:documentation> tag.
SVN:1.2[1644]
2011-10-21 08:42:13 +00:00
Romain Quetiez
81173decca Fixed issue: nobody in the list of persons to notify for portal users (security takes precedence)
SVN:1.2[1641]
2011-10-21 08:05:07 +00:00
Denis Flaven
9aca062bf5 Enhancement to provide a forward compatible API for some external plugins: support adding attachments to an email in a "clean" way.
SVN:1.2[1640]
2011-10-20 16:33:46 +00:00
Denis Flaven
09aba95d0a Fixed Trac #493: incorrect display of Users' Grant Matrix
SVN:1.2[1635]
2011-10-17 09:27:23 +00:00
Denis Flaven
1683ca2dd6 Merged some enhancements fro the trunk to better keep track of sent emails
SVN:1.2[1634]
2011-10-13 15:42:59 +00:00
Denis Flaven
9e732d6045 Fixed Trac #487: resizable text areas disappeared when located on the second tab !
SVN:1.2[1628]
2011-10-04 10:52:03 +00:00
Denis Flaven
69df343bd2 Automatic synchro of CAS/LDAP users
SVN:1.2[1627]
2011-10-03 14:07:29 +00:00
Denis Flaven
eb8f49ebfe Initializes the admin contact's phone number, in case it is a mandatory field in the data model...
SVN:1.2[1623]
2011-09-30 08:03:45 +00:00
Denis Flaven
398e294604 Prevent crash when trying to load the favicon during the setup !
SVN:1.2[1621]
2011-09-29 15:27:25 +00:00
Denis Flaven
d04c6bccd5 Added a link to a favicon (icon in the browser's bar and tab)
SVN:1.2[1619]
2011-09-29 14:54:03 +00:00
Denis Flaven
f00c7c6bc2 GetValueLabel is used in some dashboards... make sure that it is available for any attribute
SVN:1.2[1617]
2011-09-29 09:46:58 +00:00
Denis Flaven
d30e8c359f Make sure that the organisation's drop-down list is not bigger than the left menu...
SVN:1.2[1616]
2011-09-29 09:44:38 +00:00
Romain Quetiez
2bd4a61c00 #485 Export.php improved for integration into Excel / web queries (bug with IIS/HTTPS, limitation on the size of the OQL)
SVN:1.2[1613]
2011-09-29 08:12:28 +00:00
Romain Quetiez
e35c8323df Merge most recent (few) bug fixes from trunk
SVN:1.2[1611]
2011-09-28 14:38:37 +00:00
Romain Quetiez
e4e814281d #484 Fixed issue with IIS ("Wrong password" at first prompt)
SVN:1.2[1610]
2011-09-28 12:59:40 +00:00
Denis Flaven
635cb424a2 Fixed Trac #482: OpenSearch broken.
SVN:1.2[1608]
2011-09-28 10:50:58 +00:00
Romain Quetiez
e95aa6cc69 Merged latest changes in module Attachments (bug fix on install and cosmetic improvements)
SVN:1.2[1604]
2011-09-23 13:49:26 +00:00
Romain Quetiez
c58fd17fc9 Fixed regression on attribute labels; introduced in [1582]
SVN:1.2[1603]
2011-09-23 13:36:57 +00:00
Romain Quetiez
efdec7a343 #478 Fixed issue in the audit: the results are wrong whenever an organization is selected
SVN:1.2[1599]
2011-09-22 12:08:11 +00:00
Romain Quetiez
2352c05d36 Fixed security issue: the attachments were visible by anybody (by forming URLs manually), whatever the allowed organizations. The change requires the execution of the setup/migration procedure.
SVN:1.2[1597]
2011-09-22 11:58:31 +00:00
Romain Quetiez
23634964a5 #477 Could not specify more than one reconciliation key (regression) + took the opportunity to enhance protection against XSS injection (using column names in the data)
SVN:1.2[1589]
2011-09-21 12:39:22 +00:00
Romain Quetiez
619252db99 Merge fix on previous change (fixed #473, but getting a warning)
SVN:1.2[1585]
2011-09-19 11:49:43 +00:00
Romain Quetiez
ca4dbd833c (record merge info)
SVN:1.2[1583]
2011-09-19 10:58:40 +00:00
Romain Quetiez
f62a3b22a3 #473 Could not load NW interfaces (reconciliation issue) - merged from trunk
SVN:1.2[1582]
2011-09-19 10:55:24 +00:00
Romain Quetiez
88416bca5d Created branch 1.2
SVN:1.2[1580]
2011-09-19 10:30:29 +00:00
37 changed files with 1689 additions and 800 deletions

View File

@@ -557,6 +557,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
$oContact->Set('org_id', $iOrgId);
}
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org');
$iContactId = $oContact->DBInsertTrackedNoReload($oChange, true /* skip security */);
}

View File

@@ -1565,8 +1565,9 @@ EOF
case 'LinkedSet':
$aEventsList[] ='validate';
$aEventsList[] ='change';
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed());
$sHTMLValue = $oWidget->Display($oPage, $value);
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed(), $aArgs);
$oObj = isset($aArgs['this']) ? $aArgs['this'] : null;
$sHTMLValue = $oWidget->Display($oPage, $value, array(), $sFormPrefix, $oObj);
break;
case 'Document':
@@ -1733,7 +1734,7 @@ EOF
if ($iKey > 0)
{
// The object already exists in the database, it's a modification
$sButtons = "<input type=\"hidden\" name=\"id\" value=\"$iKey\">\n";
$sButtons = "<input id=\"{$sPrefix}_id\" type=\"hidden\" name=\"id\" value=\"$iKey\">\n";
$sButtons .= "<input type=\"hidden\" name=\"operation\" value=\"{$sOperation}\">\n";
$sButtons .= "<button type=\"button\" class=\"action cancel\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n";
$sButtons .= "<button type=\"submit\" class=\"action\"><span>{$sApplyButton}</span></button>\n";
@@ -1819,6 +1820,10 @@ EOF
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
$aFieldsMap = $this->DisplayBareProperties($oPage, true, $sPrefix, $aExtraParams);
if ($iKey > 0)
{
$aFieldsMap['id'] = $sPrefix.'_id';
}
// Now display the relations, one tab per relation
if (!isset($aExtraParams['noRelations']))
{

View File

@@ -1107,7 +1107,7 @@ class MenuBlock extends DisplayBlock
if (count($aTransitions))
{
$this->AddMenuSeparator($aActions);
$aStimuli = Metamodel::EnumStimuli($sClass);
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
foreach($aTransitions as $sStimulusCode => $aTransitionDef)
{
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;

View File

@@ -146,7 +146,12 @@ class iTopWebPage extends NiceWebPage
// that the tabs aren't changed on click, and any custom event name can be
// specified. Note that if you define a callback for the 'select' event, it
// will be executed for the selected tab whenever the hash changes.
tabs.tabs({ event: 'change'});
tabs.tabs({ event: 'change', 'show': function(event, ui) {
$('.resizable', ui.panel).resizable(); // Make resizable everything that claims to be resizable !
}
});
$('.resizable').filter(':visible').resizable();
}
catch(err)
{
@@ -198,7 +203,6 @@ EOF
}
});
$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
// Adjust initial size
$('.v-resizable').each( function()
{
@@ -589,13 +593,13 @@ EOF
{
// Make sure that the URL to the script contains the application's version number
// so that the new script do NOT get reloaded from the cache when the application is upgraded
if (strpos('?', $s_script) === false)
if (strpos($s_script, '?') === false)
{
$s_script .= "?version=".ITOP_VERSION;
$s_script .= "?itopversion=".ITOP_VERSION;
}
else
{
$s_script .= "&version=".ITOP_VERSION;
$s_script .= "&itopversion=".ITOP_VERSION;
}
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
@@ -624,6 +628,8 @@ EOF
echo "</style>\n";
}
echo "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/opensearch.xml.php\" />\n";
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
echo "</head>\n";
echo "<body>\n";

View File

@@ -271,23 +271,52 @@ EOF
$aFilteredGroupNames = array();
foreach($aMemberOf as $sGroupName)
{
phpCAS::log("Info: user if a member of the group: ".$sGroupName);
$sGroupName = trim(iconv('UTF-8', 'ASCII//TRANSLIT', $sGroupName)); // Remove accents and spaces as well
$aFilteredGroupNames[] = $sGroupName;
if (in_array($sGroupName, $aCASMemberships))
$bIsMember = false;
foreach($aCASMemberships as $sCASPattern)
{
if (self::IsPattern($sCASPattern))
{
if (preg_match($sCASPattern, $sGroupName))
{
$bIsMember = true;
break;
}
}
else if ($sPattern == $sGroupName)
{
$bIsMember = true;
break;
}
}
if ($bIsMember)
{
$bCASUserSynchro = MetaModel::GetConfig()->Get('cas_user_synchro');
if ($bCASUserSynchro)
{
// If needed create a new user for this email/profile
phpCAS::log('Info: cas_user_synchro is ON');
self::CreateCASUser(phpCAS::getUser(), $aMemberOf);
}
else
{
phpCAS::log('Info: cas_user_synchro is OFF');
}
$bFound = true;
break;
}
}
if(!$bFound)
{
phpCAS :: log("User ".phpCAS::getUser().", none of his/her groups (".implode('; ', $aFilteredGroupNames).") match any of the required groups: ".implode('; ', $aCASMemberships));
phpCAS::log("User ".phpCAS::getUser().", none of his/her groups (".implode('; ', $aFilteredGroupNames).") match any of the required groups: ".implode('; ', $aCASMemberships));
}
}
else
{
// Too bad, the user is not part of any of the group => not allowed
phpCAS :: log("No 'memberOf' attribute found for user ".phpCAS::getUser().". Are you using the SAML protocol (S1) ?");
phpCAS::log("No 'memberOf' attribute found for user ".phpCAS::getUser().". Are you using the SAML protocol (S1) ?");
}
}
else
@@ -498,6 +527,123 @@ EOF
}
return $sMessage;
}
protected static function CreateCASUser($sEmail, $aGroups)
{
if (!MetaModel::IsValidClass('URP_Profiles'))
{
phpCAS::log("URP_Profiles is not a valid class. Automatic creation of Users is not supported in this context, sorry.");
return;
}
// read all the existing profiles
$oProfilesSearch = new DBObjectSearch('URP_Profiles');
$oProfilesSet = new DBObjectSet($oProfilesSearch);
$aAllProfiles = array();
while($oProfile = $oProfilesSet->Fetch())
{
$aAllProfiles[strtolower($oProfile->GetName())] = $oProfile->GetKey();
}
// Translate the CAS/LDAP group names into iTop profile names
$aProfiles = array();
$sPattern = MetaModel::GetConfig()->Get('cas_profile_pattern');
foreach($aGroups as $sGroupName)
{
if (preg_match($sPattern, $sGroupName, $aMatches))
{
if (array_key_exists(strtolower($aMatches[1]), $aAllProfiles))
{
$aProfiles[] = $aAllProfiles[strtolower($aMatches[1])];
}
else
{
phpCAS::log("Warning: {$aMatches[1]} is not a valid iTop profile (extracted from group name: '$sGroupName'). Ignored.");
}
}
}
if (count($aProfiles) == 0)
{
phpCAS::log("Error: no group name matches the pattern: '$sPattern'. The user '$sEmail' has no profiles in iTop, and therefore cannot be created.");
return;
}
$oUser = MetaModel::GetObjectByName('UserExternal', $sEmail, false);
if ($oUser == null)
{
// Create the user, link it to a contact
phpCAS::log("Info: the user '$sEmail' does not exist. A new UserExternal will be created.");
$oSearch = new DBObjectSearch('Person');
$oSearch->AddCondition('email', $sEmail);
$oSet = new DBObjectSet($oSearch);
$iContactId = 0;
switch($oSet->Count())
{
case 0:
phpCAS::log("Error: found no contact with the email: '$sEmail'. Cannot create the user in iTop.");
return;
case 1:
$oContact = $oSet->Fetch();
$iContactId = $oContact->GetKey();
phpCAS::log("Info: Found 1 contact '".$oContact->GetName()."' (id=$iContactId) corresponding to the email '$sEmail'.");
break;
default:
phpCAS::log("Error: ".$oSet->Count()." contacts have the same email: '$sEmail'. Cannot create a user for this email.");
return;
}
$oUser = new UserExternal();
$oUser->Set('login', $sEmail);
$oUser->Set('contactid', $iContactId);
$oUser->Set('language', MetaModel::GetConfig()->GetDefaultLanguage());
}
else
{
phpCAS::log("Info: the user '$sEmail' already exists (id=".$oUser->GetKey().").");
}
// Now synchronize the profiles
$oProfilesSet = DBObjectSet::FromScratch('URP_UserProfile');
foreach($aProfiles as $iProfileId)
{
$oLink = new URP_UserProfile();
$oLink->Set('profileid', $iProfileId);
$oLink->Set('reason', 'CAS/LDAP Synchro');
$oProfilesSet->AddObject($oLink);
}
$oUser->Set('profile_list', $oProfilesSet);
phpCAS::log("Info: the user $sEmail (id=".$oUser->GetKey().") now has the following profiles: '".implode("', '", $aProfiles)."'.");
if ($oUser->IsNew() || $oUser->IsModified())
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", 'CAS/LDAP Synchro');
$oMyChange->DBInsert();
if ($oUser->IsNew())
{
$oUser->DBInsertTracked($oMyChange);
}
else
{
$oUser->DBUpdateTracked($oMyChange);
}
}
}
protected static function IsPattern($sCASPattern)
{
if ((substr($sCASPattern, 0, 1) == '/') && (substr($sCASPattern, -1) == '/'))
{
// the string is enclosed by slashes, let's assume it's a pattern
return true;
}
else
{
return false;
}
}
} // End of class
?>

View File

@@ -34,6 +34,7 @@ class UILinksWidget
protected $m_iInputId;
protected $m_aAttributes;
protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
protected $m_sLinkedClass;
protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed;
@@ -50,6 +51,7 @@ class UILinksWidget
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
@@ -97,7 +99,7 @@ class UILinksWidget
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment of the one-row form
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array() )
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
{
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
@@ -107,6 +109,7 @@ class UILinksWidget
$sPrefix .= "[$key][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['this'] = $linkObjOrId;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode)
@@ -121,8 +124,13 @@ class UILinksWidget
{
// form for creating a new record
$sPrefix .= "[$linkObjOrId][";
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object alsoo fills the related external fields
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['this'] = $oNewLinkObj;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
foreach($this->m_aEditableFields as $sFieldCode)
@@ -207,9 +215,11 @@ class UILinksWidget
* @param WebPage $oP The web page used for all the output
* @param DBObjectSet The initial value of the linked set
* @param Hash $aArgs Extra context arguments
* @param string $sFormPrefix prefix of the fields in the current form
* @param DBObject $oCurrentObj the current object to which the linkset is related
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array())
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$sHtmlValue = '';
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
@@ -223,19 +233,20 @@ class UILinksWidget
if ($oCurrentLink->IsNew())
{
$key = -$oLinkedObj->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs, $oCurrentObj);
}
else
{
$key = $oCurrentLink->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
}
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates);
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper);
oWidget{$this->m_iInputId}.Init();
EOF
);
@@ -344,7 +355,7 @@ EOF
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
}
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
@@ -353,7 +364,7 @@ EOF
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
if (is_object($oLinkedObj))
{
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
}
else

View File

@@ -372,13 +372,13 @@ class WebPage
{
// Make sure that the URL to the script contains the application's version number
// so that the new script do NOT get reloaded from the cache when the application is upgraded
if (strpos('?', $s_script) === false)
if (strpos($s_script, '?') === false)
{
$s_script .= "?version=".ITOP_VERSION;
$s_script .= "?itopversion=".ITOP_VERSION;
}
else
{
$s_script .= "&version=".ITOP_VERSION;
$s_script .= "&itopversion=".ITOP_VERSION;
}
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
@@ -413,6 +413,10 @@ class WebPage
}
echo "</style>\n";
}
if (class_exists('MetaModel') && MetaModel::GetConfig())
{
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
}
echo "</head>\n";
echo "<body>\n";
echo self::FilterXSS($this->s_content);

View File

@@ -183,6 +183,7 @@ class ActionEmail extends ActionNotification
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSearch->AllowAllData();
}
catch (OQLException $e)
{
@@ -275,6 +276,7 @@ class ActionEmail extends ActionNotification
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
$aHeaders = array();
try
{
$this->m_iRecipients = 0;
@@ -294,7 +296,9 @@ class ActionEmail extends ActionNotification
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sReference = '<iTop/'.get_class($oObj).'/'.$oObj->GetKey().'>';
$sMessageId = sprintf('<iTop_%s_%d_%f@%s.openitop.org>', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetConfig()->Get('session_name'));
$sReference = $sMessageId;
$aHeaders['Message-ID'] = $sMessageId;
}
catch(Exception $e)
{
@@ -315,7 +319,7 @@ class ActionEmail extends ActionNotification
if (isset($sBody)) $oLog->Set('body', $sBody);
}
$oEmail = new EMail();
$oEmail = new EMail('', '', '', $aHeaders);
if ($this->IsBeingTested())
{

View File

@@ -206,6 +206,15 @@ abstract class AttributeDefinition
}
return $sLabel;
}
/**
* Get the label corresponding to the given value
* To be overloaded for localized enums
*/
public function GetValueLabel($sValue)
{
return GetAsHTML($sValue);
}
public function GetLabel_Obsolete()
{
@@ -1333,7 +1342,12 @@ class AttributeFinalClass extends AttributeString
{
return '=';
}
public function GetValueLabel($sValue)
{
if (empty($sValue)) return '';
return MetaModel::GetName($sValue);
}
}

View File

@@ -76,6 +76,7 @@ require_once('cmdbchangeop.class.inc.php');
// Romain: temporary moved into application.inc.php (see explanations there)
//require_once('event.class.inc.php');
require_once('templatestring.class.inc.php');
require_once('csvparser.class.inc.php');
require_once('bulkchange.class.inc.php');
@@ -279,9 +280,10 @@ abstract class CMDBObject extends DBObject
{
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal();
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
return $ret;
}
@@ -289,9 +291,10 @@ abstract class CMDBObject extends DBObject
{
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal(true);
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
return $ret;
}
@@ -320,9 +323,10 @@ abstract class CMDBObject extends DBObject
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBCloneTracked_Internal($newKey);
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBCloneTracked_Internal($newKey = null)
@@ -347,9 +351,10 @@ abstract class CMDBObject extends DBObject
{
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBUpdateTracked_Internal();
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBUpdateTracked_Internal()
@@ -382,9 +387,10 @@ abstract class CMDBObject extends DBObject
{
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBDeleteTracked_Internal($oDeletionPlan);
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
@@ -406,9 +412,10 @@ abstract class CMDBObject extends DBObject
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
{
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->BulkDeleteTracked_Internal($oFilter);
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
}
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
@@ -445,9 +452,10 @@ abstract class CMDBObject extends DBObject
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
{
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
self::$m_oCurrChange = null;
self::$m_oCurrChange = $oPreviousChange;
}
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)

View File

@@ -383,6 +383,24 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_user_synchro' => array(
'type' => 'bool',
'description' => 'Whether or not to synchronize users with CAS/LDAP',
// examples... not used (nor 'description')
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_profile_pattern' => array(
'type' => 'string',
'description' => 'A regular expression pattern to extract the name of the iTop profile from the name of an LDAP/CAS group',
// examples... not used (nor 'description')
'default' => '/^cn=([^,]+),/',
'value' => '/^cn=([^,]+),/',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_debug' => array(
'type' => 'bool',
'description' => 'Activate the CAS debug',

View File

@@ -154,7 +154,7 @@ abstract class DBObject
return true;
}
protected function Reload()
public function Reload()
{
assert($this->m_bIsInDB);
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false/*, $this->m_bAllowAllData*/);
@@ -367,6 +367,34 @@ abstract class DBObject
}
public function Get($sAttCode)
{
if (($iPos = strpos($sAttCode, '->')) === false)
{
return $this->GetStrict($sAttCode);
}
else
{
$sExtKeyAttCode = substr($sAttCode, 0, $iPos);
$sRemoteAttCode = substr($sAttCode, $iPos + 2);
if (!MetaModel::IsValidAttCode(get_class($this), $sExtKeyAttCode))
{
throw new CoreException("Unknown external key '$sExtKeyAttCode' for the class ".get_class($this));
}
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
if (is_null($oRemoteObj))
{
return '';
}
else
{
return $oRemoteObj->Get($sRemoteAttCode);
}
}
}
public function GetStrict($sAttCode)
{
if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this))))
{
@@ -1580,19 +1608,22 @@ abstract class DBObject
if (!$bRet) $bSuccess = false;
}
// Change state triggers...
$sClass = get_class($this);
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sPreviousState'"));
while ($oTrigger = $oSet->Fetch())
if ($bSuccess)
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sNewState'"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($this->ToArgs('this'));
// Change state triggers...
$sClass = get_class($this);
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sPreviousState'"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sNewState'"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
}
return $bSuccess;

View File

@@ -97,7 +97,7 @@ class Dict
}
public static function GetCurrentLanguage()
public static function GetUserLanguage()
{
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
{
@@ -124,12 +124,12 @@ class Dict
{
// Attempt to find the string in the user language
//
if (!array_key_exists(self::GetCurrentLanguage(), self::$m_aData))
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
{
// It may happen, when something happens before the dictionnaries get loaded
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[self::GetCurrentLanguage()];
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
if (array_key_exists($sStringCode, $aCurrentDictionary))
{
return $aCurrentDictionary[$sStringCode];

View File

@@ -35,6 +35,7 @@ class EMail
protected $m_sSubject;
protected $m_sTo;
protected $m_aHeaders; // array of key=>value
protected $m_aAttachments;
public function __construct($sTo = '', $sSubject = '', $sBody = '', $aHeaders = array())
{
@@ -42,6 +43,7 @@ class EMail
$this->m_sSubject = $sSubject;
$this->m_sBody = $sBody;
$this->m_aHeaders = $aHeaders;
$this->m_aAttachments = array();
}
// Errors management : not that simple because we need that function to be
@@ -73,8 +75,14 @@ class EMail
{
$sHeaders = 'MIME-Version: 1.0' . "\r\n";
// ! the case is important for MS-Outlook
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
if (!array_key_exists('Content-Type', $this->m_aHeaders))
{
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
}
if (!array_key_exists('Content-Transfer-Encoding', $this->m_aHeaders))
{
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
}
foreach ($this->m_aHeaders as $sKey => $sValue)
{
$sHeaders .= "$sKey: $sValue\r\n";
@@ -110,6 +118,7 @@ class EMail
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{
$this->BuildMessage(); // assemble the attachments into the header/body structure
if ($bForceSynchronous)
{
return $this->SendSynchronous($aIssues, $oLog);
@@ -181,6 +190,36 @@ class EMail
$this->AddToHeader('Reply-To', $sAddress);
}
public function AddAttachment($data, $sFileName, $sMimeType)
{
$this->m_aAttachments[] = array('data' => $data, 'filename' => $sFileName, 'mimeType' => $sMimeType);
}
/**
* Takes care of the attachments (if any) to build the header/body of the message before storing or sending it
*/
protected function BuildMessage()
{
if (count($this->m_aAttachments) == 0) return; // Nothing to do if there are no attachments
$sDelimiter = '== iTopEmailPart---'.md5(date('r', time()))." ==";
$sContentType = isset($this->m_aHeaders['Content-Type']) ? $this->m_aHeaders['Content-Type'] : 'text/html';
$sContentHeader = "Content-Type: $sContentType\r\n";
$this->m_aHeaders['Content-Type'] = "multipart/mixed; boundary=\"{$sDelimiter}\"";
$aAttachments = array();
foreach($this->m_aAttachments as $aAttach)
{
$sAttachmentHeader = "Content-Type: {$aAttach['mimeType']};\r\n Name=\"{$aAttach['filename']}\"\r\n";
$sAttachmentHeader .= "Content-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"{$aAttach['filename']}\"\r\n";
$sAttachmentHeader .= "\r\n";
$sAttachment = chunk_split(base64_encode($aAttach['data']));
$aAttachments[] = $sAttachmentHeader.$sAttachment."\r\n";
}
$this->m_sBody = "This is a multi-part message in MIME format.\r\n--".$sDelimiter."\r\n".$sContentHeader."\r\n".$this->m_sBody."\r\n--".$sDelimiter."\r\n";
$this->m_sBody .= implode("--".$sDelimiter."\r\n", $aAttachments);
$this->m_sBody .= "--".$sDelimiter."--";
}
}
?>

View File

@@ -676,10 +676,38 @@ abstract class MetaModel
if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) return false;
return (self::$m_aAttribDefs[$sClass][$sAttCode]->IsExternalKey());
}
final static public function IsValidAttCode($sClass, $sAttCode)
final static public function IsValidAttCode($sClass, $sAttCode, $bExtended = false)
{
if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false;
return (array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]));
if ($bExtended)
{
if (($iPos = strpos($sAttCode, '->')) === false)
{
$bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]);
}
else
{
$sExtKeyAttCode = substr($sAttCode, 0, $iPos);
$sRemoteAttCode = substr($sAttCode, $iPos + 2);
if (MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode))
{
$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$bRes = MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode, true);
}
else
{
$bRes = false;
}
}
}
else
{
$bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]);
}
return $bRes;
}
final static public function IsAttributeOrigin($sClass, $sAttCode)
{

View File

@@ -153,16 +153,25 @@ class ormCaseLog {
* Add a new entry to the log and updates the internal index
* @param $sText string The text of the new entry
*/
public function AddLogEntry($sText)
public function AddLogEntry($sText, $sOnBehalfOf = '')
{
$sDate = date(Dict::S('UI:CaseLog:DateFormat'));
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, UserRights::GetUserFriendlyName(), UserRights::GetUserId());
if ($sOnBehalfOf == '')
{
$sOnBehalfOf = UserRights::GetUserFriendlyName();
$iUserId = UserRights::GetUserId();
}
else
{
$iUserId = null;
}
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
$iSepLength = strlen($sSeparator);
$iTextlength = strlen($sText);
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
$this->m_aIndex[] = array(
'user_name' => UserRights::GetUserFriendlyName(),
'user_id' => UserRights::GetUserId(),
'user_name' => $sOnBehalfOf,
'user_id' => $iUserId,
'date' => time(),
'text_length' => $iTextlength,
'separator_length' => $iSepLength,

View File

@@ -0,0 +1,177 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Simple helper class to interpret and transform a template string
*
* Usage:
* $oString = new TemplateString("Blah $this->friendlyname$ is in location $this->location_id->name$ ('$this->location_id->org_id->name$)");
* echo $oString->Render(array('this' => $oContact));
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
/**
* Helper class
*/
class TemplateStringPlaceholder
{
public $sToken;
public $sAttCode;
public $sFunction;
public $sParamName;
public $bIsValid;
public function __construct($sToken)
{
$this->sToken = $sToken;
$this->sAttcode = '';
$this->sFunction = '';
$this->sParamName = '';
$this->bIsValid = false; // Validity may be false in general, but it can work anyway (thanks to specialization) when rendering
}
}
/**
* Class TemplateString
*/
class TemplateString
{
protected $m_sRaw;
protected $m_aPlaceholders;
public function __construct($sRaw)
{
$this->m_sRaw = $sRaw;
$this->m_aPlaceholders = null;
}
/**
* Split the string into placholders
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
protected function Analyze($aParamTypes = array())
{
if (!is_null($this->m_aPlaceholders)) return;
$this->m_aPlaceholders = array();
if (preg_match_all('/\\$([a-z0-9_]+(->[a-z0-9_]+)*)\\$/', $this->m_sRaw, $aMatches))
{
foreach($aMatches[1] as $sPlaceholder)
{
$oPlaceholder = new TemplateStringPlaceholder($sPlaceholder);
$oPlaceholder->bIsValid = false;
foreach ($aParamTypes as $sParamName => $sClass)
{
$sParamPrefix = $sParamName.'->';
if (substr($sPlaceholder, 0, strlen($sParamPrefix)) == $sParamPrefix)
{
// Todo - detect functions (label...)
$oPlaceholder->sFunction = '';
$oPlaceholder->sParamName = $sParamName;
$sAttcode = substr($sPlaceholder, strlen($sParamPrefix));
$oPlaceholder->sAttcode = $sAttcode;
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttcode, true /* extended */);
}
}
$this->m_aPlaceholders[] = $oPlaceholder;
}
}
}
/**
* Return the placeholders (for reporting purposes)
* @return void
*/
public function GetPlaceholders()
{
return $this->m_aPlaceholders;
}
/**
* Check the format when possible
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
public function IsValid($aParamTypes = array())
{
$this->Analyze($aParamTypes);
foreach($this->m_aPlaceholders as $oPlaceholder)
{
if (!$oPlaceholder->bIsValid)
{
if (count($aParamTypes) == 0)
{
return false;
}
if (array_key_exists($oPlaceholder->sParamName, $aParamTypes))
{
return false;
}
}
}
return true;
}
/**
* Apply the given parameters to replace the placeholders
* @param Hash $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
* @return void
*/
public function Render($aParamValues = array())
{
$aParamTypes = array();
foreach($aParamValues as $sParamName => $value)
{
$aParamTypes[$sParamName] = get_class($value);
}
$this->Analyze($aParamTypes);
$aSearch = array();
$aReplace = array();
foreach($this->m_aPlaceholders as $oPlaceholder)
{
if (array_key_exists($oPlaceholder->sParamName, $aParamValues))
{
$oRef = $aParamValues[$oPlaceholder->sParamName];
try
{
$value = $oRef->Get($oPlaceholder->sAttcode);
$aSearch[] = '$'.$oPlaceholder->sToken.'$';
$aReplace[] = $value;
$oPlaceholder->bIsValid = true;
}
catch(Exception $e)
{
$oPlaceholder->bIsValid = false;
}
}
else
{
$oPlaceholder->bIsValid = false;
}
}
return str_replace($aSearch, $aReplace, $this->m_sRaw);
}
}
?>

View File

@@ -625,10 +625,6 @@ class UserRights
if (MetaModel::HasCategory($sClass, 'bizmodel'))
{
// #@# Temporary?????
// The read access is controlled in MetaModel::MakeSelectQuery()
if ($iActionCode == UR_ACTION_READ) return true;
if (is_null($oUser))
{
$oUser = self::$m_oUser;

View File

@@ -176,6 +176,10 @@ legend {
-webkit-border-radius: 6px;
border-radius: 6px;
}
.ui-widget-content td legend a, .ui-widget-content td legend a:hover, .ui-widget-content td legend a:visited {
color: #fff;
}
.ui-widget-content td a, p a, p a:visited, td a, td a:visited {
text-decoration:none;
color: #1C94C4;
@@ -1104,4 +1108,7 @@ div.actions_button a, .actions_button a:hover, .actions_button a:visited {
height:17px;
line-height: 17px;
display: block;
}
select#org_id {
max-width: 90%;
}

View File

@@ -639,7 +639,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:SyncDataSourceObsolete' => 'The data source is marked as obsolete. Operation cancelled.',
'Core:SyncDataSourceAccessRestriction' => 'Only adminstrators or the user specified in the data source can execute this operation. Operation cancelled.',
'Core:SyncTooManyMissingReplicas' => 'All records have been untouched for some time (all of the objects could be deleted). Please check that the process that writes into the synchronization table is still running. Operation cancelled.',
'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s error(s), %3$s warning(s).',
'Core:SynchroReplica:TargetObject' => 'Synchronized Object: %1$s',
'Class:AsyncSendEmail' => 'Email (asynchronous)',
'Class:AsyncSendEmail/Attribute:to' => 'To',
'Class:AsyncSendEmail/Attribute:subject' => 'Subject',
@@ -717,7 +718,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source',
'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)',
'Class:SynchroReplica/Attribute:dest_class' => 'Destination type',
'Class:SynchroReplica/Attribute:status_last_seen' => 'Lat seen',
'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen',
'Class:SynchroReplica/Attribute:status' => 'Status',
'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified',
'Class:SynchroReplica/Attribute:status/Value:new' => 'New',
@@ -726,6 +727,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized',
'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?',
'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error',
'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings',
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date',
'Class:appUserPreferences' => 'User Preferences',

View File

@@ -532,7 +532,9 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Audit:HeaderNbObjects' => '# Objects',
'UI:Audit:HeaderNbErrors' => '# Errors',
'UI:Audit:PercentageOk' => '% Ok',
'UI:Audit:ErrorIn_Rule_Reason' => 'OQL Error in the Rule %1$s: %2$s.',
'UI:Audit:ErrorIn_Category_Reason' => 'OQL Error in the Category %1$s: %2$s.',
'UI:RunQuery:Title' => 'iTop - OQL Query Evaluation',
'UI:RunQuery:QueryExamples' => 'Query Examples',
'UI:RunQuery:HeaderPurpose' => 'Purpose',

View File

@@ -386,6 +386,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SynchroReplica/Attribute:status_dest_creator+' => '',
'Class:SynchroReplica/Attribute:status_last_error' => 'Dernière erreur',
'Class:SynchroReplica/Attribute:status_last_error+' => '',
'Class:SynchroReplica/Attribute:status_last_warning' => 'Avertissements',
'Class:SynchroReplica/Attribute:status_last_warning+' => '',
'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création',
'Class:SynchroReplica/Attribute:info_creation_date+' => '',
'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification',
@@ -605,6 +607,8 @@ Opérateurs :<br/>
'Core:SyncDataSourceObsolete' => 'Cette source de données est obsolète. Opération annulée.',
'Core:SyncDataSourceAccessRestriction' => 'Seuls les administrateurs et l\'utilisateur spécifié dans la source de données peuvent exécuter cette synchronisation. Opération annulée.',
'Core:SyncTooManyMissingReplicas' => 'Tous les réplicas sont absents de l\'import. L\'import a-t-il réellement tourné. Opération annulée.',
'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s erreur(s), %3$s avertissement(s).',
'Core:SynchroReplica:TargetObject' => 'Objet Synchronisé : %1$s',
'Core:Duration_Seconds' => '%1$ds',
'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds',
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',

View File

@@ -71,7 +71,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:URP_UserProfile/Attribute:reason' => 'Raison',
'Class:URP_UserProfile/Attribute:reason+' => 'Justifie le rôle affecté à cet utilisateur',
'Class:URP_UserOrg' => 'Utilisateur/Organisation',
'Class:URP_UserOrg+' => 'Organizations permises pour l\'utilisateur',
'Class:URP_UserOrg+' => 'Organisations permises pour l\'utilisateur',
'Class:URP_UserOrg/Attribute:userid' => 'Utilisateur',
'Class:URP_UserOrg/Attribute:userid+' => '',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login',
@@ -215,7 +215,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:WelcomeMenu:OpenIncidents' => 'Incidents en cours: %1$d',
'UI:WelcomeMenu:AllConfigItems' => 'Actifs: %1$d',
'UI:WelcomeMenu:MyIncidents' => 'Mes Incidents',
'UI:AllOrganizations' => ' Toutes les Organizations ',
'UI:AllOrganizations' => ' Toutes les Organisations ',
'UI:YourSearch' => 'Votre recherche',
'UI:LoggedAsMessage' => 'Connecté comme: %1$s',
'UI:LoggedAsMessage+Admin' => 'Connecté comme: %1$s (Administrateur)',
@@ -416,6 +416,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Audit:HeaderNbObjects' => 'Nb d\'Objets',
'UI:Audit:HeaderNbErrors' => 'Nb d\'Erreurs',
'UI:Audit:PercentageOk' => '% Ok',
'UI:Audit:ErrorIn_Rule_Reason' => 'Erreur OQL dans la règle %1$s: %2$s.',
'UI:Audit:ErrorIn_Category_Reason' => 'Erreur OQL dans la catégorie %1$s: %2$s.',
'UI:RunQuery:Title' => 'iTop - Evaluation de requêtes OQL',
'UI:RunQuery:QueryExamples' => 'Exemples de requêtes',
'UI:RunQuery:HeaderPurpose' => 'Objectif',

View File

@@ -1,5 +1,5 @@
// JavaScript Document
function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizHelper)
{
this.id = id;
this.iInputId = iInputId;
@@ -7,6 +7,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
this.sAttCode = sAttCode;
this.sSuffix = sSuffix;
this.bDuplicates = bDuplicates;
this.oWizardHelper = oWizHelper;
var me = this;
this.Init = function()
{
@@ -202,6 +203,16 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
// }
theMap['operation'] = 'doAddObjects';
if (me.oWizardHelper == null)
{
theMap['json'] = '';
}
else
{
// Not inside a "search form", updating a real object
me.oWizardHelper.UpdateWizard();
theMap['json'] = me.oWizardHelper.ToJSON();
}
$('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
// Run the query and display the results
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,

View File

@@ -389,6 +389,7 @@ EOF
{
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
$('#display_attachment_'+att_id).hide();
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
return false; // Do not submit the form !
}
function ajaxFileUpload()
@@ -433,6 +434,8 @@ EOF
{
$('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
}
$('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg]);
//alert(data.msg);
}
}
@@ -465,6 +468,7 @@ EOF
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
//$oPage->p('<input type="button" onClick="ajaxFileUpload();" value=" Upload !">');
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin"/>');
$oPage->add('</fieldset>');
if ($this->m_bDeleteEnabled)
{

View File

@@ -63,24 +63,4 @@ class ItopWelcome extends ModuleHandlerAPI
}
}
/**
* Direct end-users to the standard Portal application
*/
class MyPortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
switch($sClass)
{
case 'UserRequest':
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
default:
return '';
}
}
}
?>

View File

@@ -393,6 +393,7 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction, $oMyChange
}
$oObj->DBUpdateTracked($oMyChange);
}
$oObj->Reload();
$oObj->DisplayDetails($oP);
}
else
@@ -842,7 +843,7 @@ try
$currValue = $oObj->Get($sAttCode);
if ($oAttDef instanceof AttributeCaseLog)
{
$currValue = '';
$currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory...
}
if (is_object($currValue)) continue; // Skip non scalar values...
if(!array_key_exists($currValue, $aValues[$sAttCode]))
@@ -935,6 +936,7 @@ try
}
}
$sTip .= "</ul></p>";
$sTip = addslashes($sTip);
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
$oDummyObj->Set($sAttCode, null);
@@ -1578,12 +1580,7 @@ EOF
{
$sDisplayValue = empty($aVal['display']) ? '<i>'.Dict::S('Enum:Undefined').'</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']);
$sTip .= "<li>".Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count'])."</li>";
$index++;
if ($iMaxCount == $index)
{
$sTip .= "<li>".(count($aMultiValues) - $iMaxCount)." more different values...</li>";
break;
}
$index++;
if ($iMaxCount == $index)
{
$sTip .= "<li>".Dict::Format('UI:BulkModify:N_MoreValues', count($aValues[$sAttCode]) - $iMaxCount)."</li>";
@@ -1591,6 +1588,7 @@ EOF
}
}
$sTip .= "</ul></p>";
$sTip = addslashes($sTip);
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );\n";
$sComments .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.count($aValues[$sAttCode]).'</div>';
}
@@ -2011,6 +2009,7 @@ EOF
$oP->p(implode('</p><p>', $aErrors));
}
}
$oObj->Reload();
$oObj->DisplayDetails($oP);
}
else
@@ -2070,8 +2069,6 @@ EOF
$('#impacted_objects').empty();
$('#impacted_objects').append(data);
$('#impacted_objects').unblock();
$('#impacted_objects .listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
$('#impacted_objects table.listResults').tableHover(); // hover tables
}
);
}

View File

@@ -297,6 +297,9 @@ try
$sSuffix = utils::ReadParam('sSuffix', '');
$sRemoteClass = utils::ReadParam('sRemoteClass', $sClass, false, 'class');
$bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true;
$sJson = utils::ReadParam('json', '', false, 'raw_data');
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
$oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates);
if ($sFilter != '')
{
@@ -306,7 +309,7 @@ try
{
$oFullSetFilter = new DBObjectSearch($sRemoteClass);
}
$oWidget->DoAddObjects($oPage, $oFullSetFilter);
$oWidget->DoAddObjects($oPage, $oFullSetFilter, $oObj);
break;
case 'wizard_helper_preview':

View File

@@ -177,52 +177,77 @@ try
$oP->add("</tr>\n");
while($oAuditCategory = $oCategoriesSet->fetch())
{
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
FilterByContext($oDefinitionFilter, $oAppContext);
$aObjectsWithErrors = array();
if (!empty($currentOrganization))
try
{
if (MetaModel::IsValidFilterCode($oDefinitionFilter->GetClass(), 'org_id'))
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
FilterByContext($oDefinitionFilter, $oAppContext);
$aObjectsWithErrors = array();
if (!empty($currentOrganization))
{
$oDefinitionFilter->AddCondition('org_id', $currentOrganization, '=');
if (MetaModel::IsValidFilterCode($oDefinitionFilter->GetClass(), 'org_id'))
{
$oDefinitionFilter->AddCondition('org_id', $currentOrganization, '=');
}
}
$aResults = array();
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
$iCount = $oDefinitionSet->Count();
$oRulesFilter = new CMDBSearchFilter('AuditRule');
$oRulesFilter->AddCondition('category_id', $oAuditCategory->GetKey(), '=');
$oRulesSet = new DBObjectSet($oRulesFilter);
while($oAuditRule = $oRulesSet->fetch() )
{
$aRow = array();
$aRow['description'] = $oAuditRule->GetName();
if ($iCount == 0)
{
// nothing to check, really !
$aRow['nb_errors'] = "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>";
$aRow['percent_ok'] = '100.00';
$aRow['class'] = GetReportColor($iCount, 0);
}
else
{
try
{
$oRuleFilter = DBObjectSearch::FromOQL($oAuditRule->Get('query'));
$oErrorObjectSet = GetRuleResultSet($oAuditRule->GetKey(), $oDefinitionFilter, $oAppContext);
$iErrorsCount = $oErrorObjectSet->Count();
while($oObj = $oErrorObjectSet->Fetch())
{
$aObjectsWithErrors[$oObj->GetKey()] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
}
catch(Exception $e)
{
$aRow['nb_errors'] = "OQL Error";
$aRow['percent_ok'] = 'n/a';
$aRow['class'] = 'red';
$sMessage = Dict::Format('UI:Audit:ErrorIn_Rule_Reason', $oAuditRule->GetHyperlink(), $e->getMessage());
$oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/>&nbsp;".$sMessage);
}
}
$aResults[] = $aRow;
$iTotalErrors = count($aObjectsWithErrors);
$sOverallPercentOk = ($iCount == 0) ? '100.00' : sprintf('%.2f', 100.0 * (($iCount - $iTotalErrors) / $iCount));
$sClass = GetReportColor($iCount, $iTotalErrors);
}
}
$aResults = array();
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
$iCount = $oDefinitionSet->Count();
$oRulesFilter = new CMDBSearchFilter('AuditRule');
$oRulesFilter->AddCondition('category_id', $oAuditCategory->GetKey(), '=');
$oRulesSet = new DBObjectSet($oRulesFilter);
while($oAuditRule = $oRulesSet->fetch() )
catch(Exception $e)
{
$aRow = array();
$aRow['description'] = $oAuditRule->GetName();
if ($iCount == 0)
{
// nothing to check, really !
$aRow['nb_errors'] = "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>";
$aRow['percent_ok'] = '100.00';
$aRow['class'] = GetReportColor($iCount, 0);
}
else
{
$oRuleFilter = DBObjectSearch::FromOQL($oAuditRule->Get('query'));
$oErrorObjectSet = GetRuleResultSet($oAuditRule->GetKey(), $oDefinitionFilter, $oAppContext);
$iErrorsCount = $oErrorObjectSet->Count();
while($oObj = $oErrorObjectSet->Fetch())
{
$aObjectsWithErrors[$oObj->GetKey()] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
}
$aResults[] = $aRow;
$iTotalErrors = count($aObjectsWithErrors);
$sOverallPercentOk = ($iCount == 0) ? '100.00' : sprintf('%.2f', 100.0 * (($iCount - $iTotalErrors) / $iCount));
$sClass = GetReportColor($iCount, $iTotalErrors);
$aRow['description'] = "OQL error";
$aRow['nb_errors'] = "n/a";
$aRow['percent_ok'] = '';
$aRow['class'] = 'red';
$sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), $e->getMessage());
$oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/>&nbsp;".$sMessage);
$aResults[] = $aRow;
}
$oP->add("<tr>\n");
$oP->add("<th><img src=\"../images/minus.gif\"></th><th class=\"alignLeft\">".$oAuditCategory->GetName()."</th><th class=\"alignRight\">$iCount</th><th class=\"alignRight\">$iTotalErrors</th><th class=\"alignRight $sClass\">$sOverallPercentOk %</th>\n");

File diff suppressed because it is too large Load Diff

View File

@@ -7,98 +7,72 @@ html, body {
height: 100%;
}
#content {
margin: auto;
margin: 10px;
padding-left: 10px;
padding-right: 10px;
text-align: center;
overflow-y: auto;
no.max-width: 90%;
min-width: 960px;
position: relative;
display: block;
clear: both;
}
div#portal #welcome {
display: none;
background: url("./images/dockbar_bg.png") repeat-x scroll 0 0 #97A1AE;
border-bottom: 1px solid #636364;
font-size: 13px;
padding: 1px 5px;
position: relative;
z-index: 300;
text-align:right;
color: #2C2F34;
font-weight: bold;
text-shadow: 1px 1px #FFFFFF;
}
div#portal #banner {
background-color: #F6F6F1;
display: block;
height: 60px;
vertical-align: middle;
width: 100%;
}
div#portal #logo {
background: url("../images/itop-logo.png") no-repeat scroll 0 0 transparent;
border: 0 none;
display: inline-block;
height: 116px;
line-height: 48px;
margin-left: 20px;
margin-right: 20px;
padding-right: 50px;
text-align: center;
vertical-align: middle;
width: 240px;
height: 60px;
display: block;
vertical-align:middle;
background-color: #f6f6f1;
}
div#portal #logo {
width: 126px;
background: url(../images/itop-logo.png) 0 0 no-repeat;
margin-left:20px;
margin-right:20px;
height: 60px;
border: 0;
vertical-align: middle;
text-align: center;
display: inline-block;
line-height: 48px;
padding-right:50px;
}
div#menu {
display: block;
height: 48px;
left: 200px;
line-height: 48px;
position: absolute;
right: 0;
top: 0;
width: auto;
position: absolute;
top: 0;
left: 200px;
right: 0px;
line-height: 48px;
height: 48px;
}
#portal_menu {
height: 60px;
}
#change_pwd {
background: url("../images/password.png") no-repeat scroll 0 0 transparent;
display: block;
float: right;
}
#logoff {
background: url("../images/logoff.png") no-repeat scroll 0 0 transparent;
display: block;
float: right;
}
#logoff span {
}
div.button {
font-size: 1.1em;
font-weight: bold;
text-decoration: none;
}
a.button, a.button:visited {
color: #1C94C4;
margin-left:20px;
margin-right:20px;
height: 60px;
border: 0;
vertical-align: middle;
text-align: center;
display: inline-block;
height: 48px;
line-height: 48px;
}
a.button , a.button:visited {
color: #1C94C4;
text-decoration: none;
vertical-align: middle;
height: 48px;
line-height: 48px;
display: inline-block;
}
a.button span {
margin-left: 50px;
vertical-align:middle;
margin-right: 20px;
vertical-align: middle;
margin-left: 50px;
}
#close_form_table {
@@ -106,117 +80,66 @@ a.button span {
padding: 20px;
}
#request_details td {
text-align:left;
#logoff {
display: block;
float: right;
background: url(../images/logoff.png) right center no-repeat;
}
#request_details td fieldset{
xxxheight:100%;
#logoff span {
margin-right: 50px;
margin-left: 20px;
}
#cancel {
background: url(../images/stop-mid.png) 0 0 no-repeat;
}
#create {
background: url(../modules/itop-request-mgmt-1.0.0/images/user-request.png) 0 0 no-repeat;
}
#user_info {
background: url(../images/clean-mid.png) 0 0 no-repeat;
}
#change_pwd {
background: url(../images/password.png) 0 0 no-repeat;
}
#back {
background: url(../images/back.png) 0 0 no-repeat;
}
#back span {
margin-left: 54px;
}
#refresh {
background: url(../images/refresh.png) 0 0 no-repeat;
margin-right: 40px;
}
#refresh span {
margin-left: 54px;
margin-right: 20px;
}
#request_details {
display: inline-block;
width:800px;
text-align: left;
}
#form_close_request {
display: inline-block;
width:800px;
text-align: left;
}
#request_details_log {
width:774px;
}
#request_details table {
border: #f1f1f6 2px solid;
text-align: left;
}
#form_details {
display: inline-block;
}
.wizContainer table {
display: inline-block;
text-align: left;
}
#user_request_comment {
width: 30em;
height: 20em;
}
#buttons {
margin-top: 1em;
}
div#buttons #btn_cancel {
margin-right: 50px;
}
div#buttons #btn_back {
margin-left: 50px;
margin-right: 5px;
}
div#buttons #btn_next {
margin-left: 5px;
}
div#buttons #btn_finish {
margin-left: 5px;
}
table.listContainer {
clear: both;
width: 100%;
}
h1 {
font-weight: bold;
font-weight: bold;
padding: 5px;
margin-top: 5px;
}
div.DrawerHandle {
display:none;
}
div.HRDrawer {
background: transparent;
border: 0;
height: 0.5em;
}
.SearchDrawer {
background-color: #F9EDBF;
border: 0;
-moz-border-radius: 4px 4px 4px 4px;
}
.SearchDrawer label {
background: transparent;
}
#open_incidents, #open_requests, #open_changes, #request_details {
margin-bottom: 1em;
}
legend {
background: url("./images/header_bg.png") repeat-x scroll 0 0 #D4D4D4;
border-color: #C8C9CA #9E9E9E #9E9E9E #C8C9CA;
border-style: solid;
border-width: 1px;
font-size: 1.1em;
font-weight: bold;
color: #222222;
font-weight: bold;
text-shadow: 1px 1px #FFFFFF;
padding: 5px;
-moz-border-radius: 4px 4px 4px 4px;
margin-top:0;
}
table.details > tbody > tr > td {
padding-bottom: 5px;
padding-top: 3px;
padding-right: 5px;
border: 0;
}
.label {
font-weight: bold;
}
.caselog {
display:block;
width: 100%;
}
.caselog textarea {
resize: none;
}
.edit_item {
margin-bottom: 1em;
}
div.edit_item span div table {
width: 100%;
}
div.edit_item span div table tbody tr td textarea{
width: 99%;
}
div#ticket_shortcuts form {
display: inline-block;
}

View File

@@ -37,7 +37,7 @@ define('FINAL_CONFIG_FILE', APPROOT.'/config-itop.php');
define('PHP_MIN_VERSION', '5.2.0');
define('MYSQL_MIN_VERSION', '5.0.0');
define('MIN_MEMORY_LIMIT', 32*1024*1024);
define('SUHOSIN_GET_MAX_VALUE_LENGTH', 1024);
define('SUHOSIN_GET_MAX_VALUE_LENGTH', 2048);
$sOperation = Utils::ReadParam('operation', 'step0');
$oP = new SetupWebPage('iTop configuration wizard');

View File

@@ -53,6 +53,9 @@ try
{
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql'));
}
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oBlock1 = new DisplayBlock($oFilter, 'search', false, array('menu'=>false));
$oBlock1->Display($oP, 0);
$oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>');
$iSourceId = utils::ReadParam('datasource', null);
if ($iSourceId != null)
@@ -60,7 +63,6 @@ try
$oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId);
$oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>');
}
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false));
$oBlock->Display($oP, 1);
break;

View File

@@ -166,12 +166,12 @@ foreach(explode(',', $sDataSourcesList) as $iSDS)
$oP->p("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors'));
$oP->p("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted'));
$oP->p("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors'));
$oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created'));
$oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)");
$oP->p("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
$oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated'));
$oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)");
$oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
$oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated'));
$oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged'));
$oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->p("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
$oP->p("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
}

View File

@@ -633,12 +633,12 @@ try
$oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors'));
$oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted'));
$oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors'));
$oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created'));
$oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)");
$oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
$oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated'));
$oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)");
$oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
$oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated'));
$oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged'));
$oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
$oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
}

View File

@@ -251,6 +251,19 @@ class SynchroDataSource extends cmdbAbstractObject
$sEndDate = $oLastLog->Get('end_date');
$iLastLog = $oLastLog->GetKey();
$oPage->p('<h2>'.Dict::Format('Core:Synchro:SynchroEndedOn_Date', $sEndDate).'</h2>');
$sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid";
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
$iCountAllReplicas = $oSet->Count();
$sAllReplicas = "<a href=\"../synchro/replica.php?operation=oql&datasource=$iDSid&oql=$sOQL\">$iCountAllReplicas</a>";
$sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid AND status_last_error !=''";
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
$iCountAllErrors = $oSet->Count();
$sAllErrors = "<a href=\"../synchro/replica.php?operation=oql&datasource=$iDSid&oql=$sOQL\">$iCountAllErrors</a>";
$sOQL = "SELECT SynchroReplica WHERE sync_source_id=$iDSid AND status_last_warning !=''";
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
$iCountAllWarnings = $oSet->Count();
$sAllWarnings = "<a href=\"../synchro/replica.php?operation=oql&datasource=$iDSid&oql=$sOQL\">$iCountAllWarnings</a>";
$oPage->p('<h2>'.Dict::Format('Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings', $sAllReplicas, $sAllErrors, $sAllWarnings).'</h2>');
}
$oPage->add('<table class="synoptics"><tr><td style="color:#333;vertical-align:top">');
@@ -273,6 +286,18 @@ class SynchroDataSource extends cmdbAbstractObject
$sScript .= "};\n";
$sScript .= <<<EOF
var sLastLog = '$iLastLog';
function ToggleSynoptics(sId, bShow)
{
if (bShow)
{
$(sId).show();
}
else
{
$(sId).hide();
}
}
function UpdateSynoptics(id)
{
var aValues = aSynchroLog[id];
@@ -310,6 +335,12 @@ class SynchroDataSource extends cmdbAbstractObject
{
$('#disappeared_errors_link').hide();
}
ToggleSynoptics('#cw_obj_created_warnings', aValues['obj_created_warnings'] > 0);
ToggleSynoptics('#cw_obj_new_updated_warnings', aValues['obj_new_updated_warnings'] > 0);
ToggleSynoptics('#cw_obj_new_unchanged_warnings', aValues['obj_new_unchanged_warnings'] > 0);
ToggleSynoptics('#cw_obj_updated_warnings', aValues['obj_updated_warnings'] > 0);
ToggleSynoptics('#cw_obj_unchanged_warnings', aValues['obj_unchanged_warnings'] > 0);
}
EOF
;
@@ -375,6 +406,10 @@ EOF
$sCount = "<span id=\"c_{$sId}\">$iCount</span>";
$sLabel = Dict::Format('Core:Synchro:label_'.$sId, $sCount);
$sOpacity = ($iCount==0) ? "opacity:0.3;" : "";
if (isset($aData[$sId.'_warnings']))
{
$sLabel .= " <span id=\"cw_{$sId}_warnings\"><img src=\"../images/error.png\" style=\"vertical-align:middle\"/> (<span id=\"c_{$sId}_warnings\">".$aData[$sId.'_warnings']."</span>)</span>";
}
return "<td id=\"$sId\" style=\"background-color:$sColor;$sOpacity;\" {$sHTMLAttribs}>{$sLabel}{$sErrorLink}</td>";
}
@@ -387,11 +422,15 @@ EOF
'obj_disappeared_errors' => $oLastLog->Get('stats_nb_obj_obsoleted_errors') + $oLastLog->Get('stats_nb_obj_deleted_errors'),
'obj_disappeared_no_action' => $oLastLog->Get('stats_nb_replica_disappeared_no_action'),
'obj_updated' => $oLastLog->Get('stats_nb_obj_updated'),
'obj_updated_warnings' => $oLastLog->Get('stats_nb_obj_updated_warnings'),
'obj_updated_errors' => $oLastLog->Get('stats_nb_obj_updated_errors'),
'obj_new_updated' => $oLastLog->Get('stats_nb_obj_new_updated'),
'obj_new_updated_warnings' => $oLastLog->Get('stats_nb_obj_new_updated_warnings'),
'obj_new_unchanged' => $oLastLog->Get('stats_nb_obj_new_unchanged'),
'obj_created' => $oLastLog->Get('stats_nb_obj_created'),
'obj_created_warnings' => $oLastLog->Get('stats_nb_obj_created_warnings'),
'obj_created_errors' => $oLastLog->Get('stats_nb_obj_created_errors'),
'obj_unchanged_warnings' => $oLastLog->Get('stats_nb_obj_unchanged_warnings'),
);
$iReconciledErrors = $oLastLog->Get('stats_nb_replica_reconciled_errors');
$iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted'] + $aData['obj_disappeared_no_action'];
@@ -949,13 +988,18 @@ EOF
$oStatLog->Set('stats_nb_obj_obsoleted_errors', 0);
$oStatLog->Set('stats_nb_obj_created', 0);
$oStatLog->Set('stats_nb_obj_created_errors', 0);
$oStatLog->Set('stats_nb_obj_created_warnings', 0);
$oStatLog->Set('stats_nb_obj_updated', 0);
$oStatLog->Set('stats_nb_obj_updated_warnings', 0);
$oStatLog->Set('stats_nb_obj_updated_errors', 0);
// $oStatLog->Set('stats_nb_replica_reconciled', 0);
$oStatLog->Set('stats_nb_obj_unchanged_warnings', 0);
// $oStatLog->Set('stats_nb_replica_reconciled', 0);
$oStatLog->Set('stats_nb_replica_reconciled_errors', 0);
$oStatLog->Set('stats_nb_replica_disappeared_no_action', 0);
$oStatLog->Set('stats_nb_obj_new_updated', 0);
$oStatLog->Set('stats_nb_obj_new_updated_warnings', 0);
$oStatLog->Set('stats_nb_obj_new_unchanged',0);
$oStatLog->Set('stats_nb_obj_new_unchanged_warnings',0);
$sSelectTotal = "SELECT SynchroReplica WHERE sync_source_id = :source_id";
$oSetTotal = new DBObjectSet(DBObjectSearch::FromOQL($sSelectTotal), array() /* order by*/, array('source_id' => $this->GetKey()));
@@ -989,14 +1033,14 @@ EOF
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_deleted_errors').": ".$oStatLog->Get('stats_nb_obj_deleted_errors')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_obsoleted').": ".$oStatLog->Get('stats_nb_obj_obsoleted')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_obsoleted_errors').": ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_created').": ".$oStatLog->Get('stats_nb_obj_created')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_created').": ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_created_errors').": ".$oStatLog->Get('stats_nb_obj_created_errors')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_updated').": ".$oStatLog->Get('stats_nb_obj_updated')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_updated').": ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_updated_errors').": ".$oStatLog->Get('stats_nb_obj_updated_errors')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_replica_reconciled_errors').": ".$oStatLog->Get('stats_nb_replica_reconciled_errors')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_replica_disappeared_no_action').": ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_new_updated').": ".$oStatLog->Get('stats_nb_obj_new_updated')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_new_unchanged').": ".$oStatLog->Get('stats_nb_obj_new_unchanged')."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_new_updated').": ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"."</li>\n";
$sStatistics .= "<li>".$oStatLog->GetLabel('stats_nb_obj_new_unchanged').": ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_unchanged_warnings')." warnings)"."</li>\n";
$sStatistics .= "</ul>\n";
$this->SendNotification("errors ($iErrors)", "<p>The synchronization has been executed, $iErrors errors have been encountered. Click <a href=\"$sIssuesURL\">here</a> to see the records being currently in error.</p>".$sStatistics);
@@ -1180,10 +1224,10 @@ EOF
$oSetSeen = new DBObjectSet(DBObjectSearch::FromOQL($sSelectSeen), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sLimitDate));
$oStatLog->Set('stats_nb_replica_seen', $oSetSeen->Count());
// Get all the replicas that are 'new' or modified
// Get all the replicas that are 'new' or modified or synchronized with a warning
//
$sSelectToSync = "SELECT SynchroReplica WHERE (status = 'new' OR status = 'modified') AND sync_source_id = :source_id";
$oSetToSync = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, array('source_id' => $this->GetKey()) /* aArgs */, $aExtDataSpec, 0 /* limitCount */, 0 /* limitStart */);
$sSelectToSync = "SELECT SynchroReplica WHERE (status = 'new' OR status = 'modified' OR (status = 'synchronized' AND status_last_warning != '')) AND sync_source_id = :source_id AND status_last_seen >= :last_import";
$oSetToSync = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sLimitDate) /* aArgs */, $aExtDataSpec, 0 /* limitCount */, 0 /* limitStart */);
while($oReplica = $oSetToSync->Fetch())
{
@@ -1484,15 +1528,20 @@ class SynchroLog extends DBObject
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_obsoleted_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_obsoleted_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_unchanged_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled_errors", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_disappeared_no_action", array("allowed_values"=>null, "sql"=>"stats_nb_replica_disappeared_no_action", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("traces", array("allowed_values"=>null, "sql"=>"traces", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
@@ -1596,6 +1645,7 @@ class SynchroLog extends DBObject
class SynchroReplica extends DBObject implements iDisplay
{
static $aSearches = array(); // Cache of OQL queries used for reconciliation (per data source)
protected $aWarnings;
public static function Init()
{
@@ -1624,7 +1674,8 @@ class SynchroReplica extends DBObject implements iDisplay
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('new,synchronized,modified,orphan,obsolete'), "sql"=>"status", "default_value"=>"new", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("status_dest_creator", array("allowed_values"=>null, "sql"=>"status_dest_creator", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("status_last_error", array("allowed_values"=>null, "sql"=>"status_last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("status_last_warning", array("allowed_values"=>null, "sql"=>"status_last_warning", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("info_creation_date", array("allowed_values"=>null, "sql"=>"info_creation_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("info_last_modified", array("allowed_values"=>null, "sql"=>"info_last_modified", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
@@ -1632,17 +1683,65 @@ class SynchroReplica extends DBObject implements iDisplay
MetaModel::Init_SetZListItems('details', array('' .
'col:0'=> array(
'fieldset:SynchroDataSource:Definition' => array('sync_source_id','dest_id','dest_class'),
'fieldset:SynchroDataSource:Status' => array('status','status_last_seen','status_dest_creator','status_last_error'),
'fieldset:SynchroDataSource:Status' => array('status','status_last_seen','status_dest_creator','status_last_error','status_last_warning'),
'fieldset:SynchroDataSource:Information' => array('info_creation_date','info_last_modified'))
)
);
MetaModel::Init_SetZListItems('list', array('sync_source_id', 'dest_id', 'dest_class', 'status_last_seen', 'status', 'status_dest_creator', 'status_last_error')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('sync_source_id', 'dest_id', 'dest_class', 'status_last_seen', 'status', 'status_dest_creator', 'status_last_error', 'status_last_warning')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('sync_source_id', 'status_last_seen', 'status', 'status_dest_creator', 'dest_class', 'dest_id', 'status_last_error')); // Criteria of the std search form
MetaModel::Init_SetZListItems('standard_search', array('sync_source_id', 'status_last_seen', 'status', 'status_dest_creator', 'dest_class', 'dest_id', 'status_last_error', 'status_last_warning')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
{
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
$this->aWarnings = array();
}
public function DBInsert()
protected function AddWarning($sWarningMessage)
{
$this->aWarnings[] = $sWarningMessage;
}
protected function ResetWarnings()
{
$this->aWarnings = array();
}
protected function HasWarnings()
{
return (count($this->aWarnings) > 0);
}
protected function RecordWarnings()
{
$sWarningMessage = '';
$MAX_WARNING_LENGTH = 255;
switch(count($this->aWarnings))
{
case 0:
$sWarningMessage = '';
break;
case 1:
$sWarningMessage = $this->aWarnings[0];
break;
default:
$sWarningMessage = count($this->aWarnings)." warnings: ".implode(' ', $this->aWarnings);
break;
}
if (strlen($sWarningMessage) > $MAX_WARNING_LENGTH)
{
$sWarningMessage = substr($sWarningMessage, 0, $MAX_WARNING_LENGTH - 3).'...';
}
$this->Set('status_last_warning', $sWarningMessage);
}
public function DBInsert()
{
throw new CoreException('A synchronization replica must be created only by the mean of triggers');
}
@@ -1688,6 +1787,7 @@ class SynchroReplica extends DBObject implements iDisplay
public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog)
{
$this->ResetWarnings();
switch($this->Get('status'))
{
case 'new':
@@ -1714,6 +1814,7 @@ class SynchroReplica extends DBObject implements iDisplay
}
else
{
// TO DO: can we retry this ??
// Reconciliation could not be performed - log and EXIT
$oStatLog->AddTrace("Could not reconcile on null value for attribute '$sFilterCode'", $this);
$this->SetLastError("Could not reconcile on null value for attribute '$sFilterCode'");
@@ -1736,7 +1837,19 @@ class SynchroReplica extends DBObject implements iDisplay
$oStatLog->AddTrace("Nothing found on: $sConditionDesc", $this);
if ($oDataSource->Get('action_on_zero') == 'create')
{
$this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog);
$bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog);
if ($bCreated)
{
if ($this->HasWarnings())
{
$oStatLog->Inc('stats_nb_obj_created_warnings');
}
}
else
{
// Creation error has precedence over any warning
$this->ResetWarnings();
}
}
else // assumed to be 'error'
{
@@ -1751,9 +1864,20 @@ class SynchroReplica extends DBObject implements iDisplay
if ($oDataSource->Get('action_on_one') == 'update')
{
$oDestObj = $oDestSet->Fetch();
$this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
$bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
$this->Set('dest_id', $oDestObj->GetKey());
$this->Set('dest_class', get_class($oDestObj));
if ($this->HasWarnings())
{
if ($bModified)
{
$oStatLog->Inc('stats_nb_obj_new_updated_warnings');
}
else
{
$oStatLog->Inc('stats_nb_obj_new_unchanged_warnings');
}
}
}
else
{
@@ -1774,21 +1898,46 @@ class SynchroReplica extends DBObject implements iDisplay
}
elseif ($oDataSource->Get('action_on_multiple') == 'create')
{
$this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog);
$bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog);
if ($bCreated)
{
if ($this->HasWarnings())
{
$oStatLog->Inc('stats_nb_obj_created_warnings');
}
}
else
{
// Creation error has precedence over any warning
$this->ResetWarnings();
}
}
else
{
// assumed to be 'take_first'
$oDestObj = $oDestSet->Fetch();
$this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
$bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
$this->Set('dest_id', $oDestObj->GetKey());
$this->Set('dest_class', get_class($oDestObj));
if ($this->HasWarnings())
{
if ($bModified)
{
$oStatLog->Inc('stats_nb_obj_new_updated_warnings');
}
else
{
$oStatLog->Inc('stats_nb_obj_new_unchanged_warnings');
}
}
}
}
$this->RecordWarnings();
break;
case 'synchronized': // try to recover synchronized replicas with warnings
case 'modified':
$oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id'));
$oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id'));
if ($oDestObj == null)
{
$this->Set('status', 'orphan'); // The destination object has been deleted !
@@ -1797,8 +1946,20 @@ class SynchroReplica extends DBObject implements iDisplay
}
else
{
$this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', 'stats_nb_obj_updated_errors');
$bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', 'stats_nb_obj_updated_errors');
if ($this->HasWarnings())
{
if ($bModified)
{
$oStatLog->Inc('stats_nb_obj_updated_warnings');
}
else
{
$oStatLog->Inc('stats_nb_obj_unchanged_warnings');
}
}
}
$this->RecordWarnings();
break;
default: // Do nothing in all other cases
@@ -1811,6 +1972,7 @@ class SynchroReplica extends DBObject implements iDisplay
protected function UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, &$oStatLog, $sStatsCode, $sStatsCodeError)
{
$aValueTrace = array();
$bModified = false;
try
{
foreach($aAttributes as $sAttCode => $oSyncAtt)
@@ -1826,6 +1988,7 @@ class SynchroReplica extends DBObject implements iDisplay
if ($oDestObj->IsModified())
{
$oDestObj->DBUpdateTracked($oChange);
$bModified = true;
$oStatLog->AddTrace('Updated object - Values: {'.implode(', ', $aValueTrace).'}', $this);
if (($sStatsCode != '') &&(MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_updated')))
{
@@ -1851,13 +2014,16 @@ class SynchroReplica extends DBObject implements iDisplay
$this->SetLastError('Unable to update destination object: ', $e);
$oStatLog->Inc($sStatsCodeError);
}
return $bModified;
}
/**
* Creates the destination object populating it with the Extended data found in the synchro_data_XXXX table
* @return bool Whether or not the object was created
*/
protected function CreateObjectFromReplica($sClass, $aAttributes, $oChange, &$oStatLog)
{
$bCreated = false;
$oDestObj = MetaModel::NewObject($sClass);
try
{
@@ -1879,6 +2045,7 @@ class SynchroReplica extends DBObject implements iDisplay
$this->Set('status_last_error', '');
$this->Set('status', 'synchronized');
$this->Set('info_creation_date', date('Y-m-d H:i:s'));
$bCreated = true;
$oStatLog->AddTrace("Created (".implode(', ', $aValueTrace).")", $this);
$oStatLog->Inc('stats_nb_obj_created');
@@ -1889,6 +2056,7 @@ class SynchroReplica extends DBObject implements iDisplay
$this->SetLastError('Unable to create destination object: ', $e);
$oStatLog->Inc('stats_nb_obj_created_errors');
}
return $bCreated;
}
/**
@@ -1983,6 +2151,7 @@ class SynchroReplica extends DBObject implements iDisplay
// $sExtAttCode is a valid attribute code
//
$sClass = $this->Get('base_class');
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExtAttCode);
if (!is_null($oSyncAtt) && ($oSyncAtt instanceof SynchroAttExtKey))
@@ -2005,8 +2174,12 @@ class SynchroReplica extends DBObject implements iDisplay
}
else
{
// Note: differs from null (in which case the value would be left unchanged)
$oStatLog->AddTrace("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'", $this);
if ($rawValue != '')
{
// Note: differs from null (in which case the value would be left unchanged)
$oStatLog->AddTrace("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'", $this);
$this->AddWarning("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'");
}
$retValue = 0;
}
}
@@ -2108,6 +2281,14 @@ class SynchroReplica extends DBObject implements iDisplay
}
$oPage->Details($aDetails);
$oPage->add('</fieldset>');
$oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'), false);
if (is_object($oDestObj))
{
$oPage->add('<fieldset>');
$oPage->add('<legend>'.Dict::Format('Core:SynchroReplica:TargetObject', $oDestObj->GetHyperlink()).'</legend>');
$oDestObj->DisplayBareProperties($oPage, false, $aExtraParams);
$oPage->add('<fieldset>');
}
$oPage->add('</td><td>');
$oPage->add('<fieldset>');
$oPage->add('<legend>'.Dict::S('Core:SynchroReplica:PublicData').'</legend>');

View File

@@ -8,10 +8,10 @@
<!-- Added the following import tag to pass the Eclipse validation -->
<xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
<xsd:complexType name="SearchCondition">
<wsdl:documentation>
<!--wsdl:documentation>
A criteria to restrict a search (strict search is performed)
Example: name = 'myserver.combodo.fr'
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="attcode" type="xsd:string"/>
<xsd:element name="value" type="xsd:string"/> <!-- should be anyType but this one is not well supported by Eclipse -->
@@ -25,20 +25,20 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ExternalKeySearch">
<wsdl:documentation>
<!-- wsdl:documentation>
Specifies [how to find] a value for an external key.
the class of object to search for will depend on the usage that is being made, therefore the search conditions that may be used will vary depending on the parameter that is concerned.
If one criteria is not relevant, then the match will not be attempted and warning will be logged (or an error if the target external key is mandatory)
Example: match on customer = 'Demo' and type = 'Router'
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="conditions" type="typens:ArrayOfSearchCondition"/>
</xsd:all>
</xsd:complexType>
<xsd:complexType name="AttributeValue">
<wsdl:documentation>
<!-- wsdl:documentation>
Specifies a value to set
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="attcode" type="xsd:string"/>
<xsd:element name="value" type="xsd:string"/> <!-- should be anyType but this one is not well supported by Eclipse -->
@@ -52,9 +52,9 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="LinkCreationSpec">
<wsdl:documentation>
<!-- wsdl:documentation>
Specifies [how to match] one item to attach and what values should be set on the newly created link.
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="class" type="xsd:string"/>
<xsd:element name="conditions" type="typens:ArrayOfSearchCondition"/>
@@ -69,9 +69,9 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="LogMessage">
<wsdl:documentation>
<!-- wsdl:documentation>
An event that happened during the execution of the web service
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="text" type="xsd:string"/>
</xsd:all>
@@ -84,9 +84,9 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ResultLog">
<wsdl:documentation>
<!-- wsdl:documentation>
A Log of events of the same category
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="messages" type="typens:ArrayOfLogMessage"/>
</xsd:all>
@@ -105,10 +105,10 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ResultMessage">
<wsdl:documentation>
<!-- wsdl:documentation>
Output expected, depending on the operation invoked.
Example: CreateIncidentTicket will return 'created' => basic information on the created ticket
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="label" type="xsd:string"/>
<xsd:element name="values" type="typens:ArrayOfResultData"/>
@@ -122,12 +122,12 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Result">
<wsdl:documentation>
<!-- wsdl:documentation>
Standard result structure returned by all of the operations, excepted GetVersion (returning a string)
result holds returned data if the status is set to true
errors, warnings and infos will help in understanding what happened (unknown identifiers, object matching issues/results)
This resulting structure is being tracked into the application log as well.
</wsdl:documentation>
</wsdl:documentation -->
<xsd:all>
<xsd:element name="status" type="xsd:boolean"/>
<xsd:element name="result" type="typens:ArrayOfResultMessage"/>
@@ -189,31 +189,31 @@
</message>
<portType name="WebServicePortType">
<operation name="GetVersion">
<wsdl:documentation>
<!-- wsdl:documentation>
Get the current version of Itop
As this service is very simple, it is a test to get trained for more complex operations
</wsdl:documentation> -->
</wsdl:documentation --> -->
<input message="typens:GetVersion"/>
<output message="typens:GetVersionResponse"/>
</operation>
<operation name="CreateRequestTicket">
<wsdl:documentation>
<!-- wsdl:documentation>
Create a ticket, return information about reconciliation on external keys and the created ticket
</wsdl:documentation>
</wsdl:documentation -->
<input message="typens:CreateRequestTicket"/>
<output message="typens:CreateRequestTicketResponse"/>
</operation>
<operation name="CreateIncidentTicket">
<wsdl:documentation>
<!-- wsdl:documentation>
Create a ticket, return information about reconciliation on external keys and the created ticket
</wsdl:documentation>
</wsdl:documentation -->
<input message="typens:CreateIncidentTicket"/>
<output message="typens:CreateIncidentTicketResponse"/>
</operation>
<operation name="SearchObjects">
<wsdl:documentation>
<!-- wsdl:documentation>
Create a ticket, return information about reconciliation on external keys and the created ticket
</wsdl:documentation>
</wsdl:documentation -->
<input message="typens:SearchObjects"/>
<output message="typens:SearchObjectsResponse"/>
</operation>
@@ -258,9 +258,9 @@
</operation>
</binding>
<service name="ITopService">
<wsdl:documentation>
<!-- wsdl:documentation>
ITop is the central solution for managing your IT infrastructure
</wsdl:documentation>
</wsdl:documentation -->
<port name="WebServicePort" binding="typens:WebServiceBinding">
<soap:address location="___SOAP_SERVER_URI___"/>
</port>