User Portal:

- Portal power users can see ALL the tickets of the current customer
- Reopen and Close are done in a separate dialog box where the user will input requested data
- On closed tickets, the user satisfaction and comments are displayed

SVN:trunk[2697]
This commit is contained in:
Romain Quetiez
2013-04-18 15:23:43 +00:00
parent 4ae69372d4
commit f2738a79a0
6 changed files with 200 additions and 78 deletions

View File

@@ -571,6 +571,11 @@
</group>
</groups>
</profile>
<profile id="12" _delta="define">
<name>Portal power user</name>
<description>Users having this profile will have the rights to see all the tickets for a customer in the portal. This profile must be used in conjunction with other profiles (e.g. Portal User or Administrator).</description>
<groups/>
</profile>
</profiles>
</user_rights>
</itop_design>

View File

@@ -16,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
define('PORTAL_POWER_USER_PROFILE', 'Portal power user');
define('PORTAL_SERVICECATEGORY_QUERY', 'SELECT Service AS s JOIN SLA AS sla ON sla.service_id=s.id JOIN lnkContractToSLA AS ln ON ln.sla_id=sla.id JOIN CustomerContract AS cc ON ln.contract_id=cc.id WHERE cc.org_id = :org_id');
define('PORTAL_SERVICE_SUBCATEGORY_QUERY', 'SELECT ServiceSubcategory WHERE service_id = :svc_id');

View File

@@ -599,6 +599,11 @@
</group>
</groups>
</profile>
<profile id="12" _delta="define">
<name>Portal power user</name>
<description>Users having this profile will have the rights to see all the tickets for a customer in the portal. Must be used in conjunction with other profiles (e.g. Portal User).</description>
<groups/>
</profile>
</profiles>
</user_rights>
</itop_design>

View File

@@ -16,6 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
define('PORTAL_POWER_USER_PROFILE', 'Portal power user');
define('PORTAL_SERVICECATEGORY_QUERY', 'SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id');
define('PORTAL_SERVICE_SUBCATEGORY_QUERY', 'SELECT ServiceSubcategory WHERE service_id = :svc_id AND request_type="service_request"');

View File

@@ -16,6 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
define('PORTAL_POWER_USER_PROFILE', 'Portal power user');
define('PORTAL_SERVICECATEGORY_QUERY', 'SELECT Service AS s JOIN lnkCustomerContractToService AS l1 ON l1.service_id=s.id JOIN CustomerContract AS cc ON l1.customercontract_id=cc.id WHERE cc.org_id = :org_id');
define('PORTAL_SERVICE_SUBCATEGORY_QUERY', 'SELECT ServiceSubcategory WHERE service_id = :svc_id');

View File

@@ -409,7 +409,7 @@ function ListOpenRequests(WebPage $oP)
$sOQL = 'SELECT UserRequest WHERE org_id = :org_id AND status NOT IN ("closed", "resolved")';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$iUser = UserRights::GetContactId();
if ($iUser > 0)
if ($iUser > 0 && !IsPowerUser())
{
$oSearch->AddCondition('caller_id', $iUser);
}
@@ -430,7 +430,7 @@ function ListResolvedRequests(WebPage $oP)
$sOQL = 'SELECT UserRequest WHERE org_id = :org_id AND status = "resolved"';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$iUser = UserRights::GetContactId();
if ($iUser > 0)
if ($iUser > 0 && !IsPowerUser())
{
$oSearch->AddCondition('caller_id', $iUser);
}
@@ -462,7 +462,7 @@ function ListClosedTickets(WebPage $oP)
$oSearch->AddCondition('org_id', $oUserOrg->GetKey());
$oSearch->AddCondition('status', 'closed');
$iUser = UserRights::GetContactId();
if ($iUser > 0)
if ($iUser > 0 && !IsPowerUser())
{
$oSearch->AddCondition('caller_id', $iUser);
}
@@ -505,9 +505,10 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
$sClass = get_class($oObj);
$bIsEscalateButton = false;
$bIsReopenButton = false;
$bIsCloseButton = false;
$bEditAttachments = false;
$aEditAtt = array();
$aEditAtt = array(); // List of attributes editable in the main form
if (!MetaModel::DBIsReadOnly())
{
switch($oObj->GetState())
@@ -531,12 +532,14 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
break;
case 'resolved':
$aEditAtt = array(
// non, read-only dans cet etat - 'ticket_log' => '????',
'user_satisfaction' => '????',
PORTAL_ATTCODE_COMMENT => '????',
);
$aEditAtt = array();
if (array_key_exists('ev_reopen', MetaModel::EnumStimuli($sClass)))
{
$bIsReopenButton = true;
MakeStimulusForm($oP, $oObj, 'ev_reopen', array(PORTAL_ATTCODE_LOG));
}
$bIsCloseButton = true;
MakeStimulusForm($oP, $oObj, 'ev_close', array('user_satisfaction', PORTAL_ATTCODE_COMMENT));
break;
case 'closed':
@@ -551,16 +554,18 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
switch($sClass)
{
case 'UserIssue':
$aAttList = array('col:0'=> array('ref','caller_id','impact','perimeter','servicesubcategory_id','title','description'),'col:1'=> array('status','priority','start_date','resolution_date','last_update','agent_id'));
break;
case 'UserRequest':
$aAttList = array('col:0'=> array('ref','caller_id','servicesubcategory_id','title','description'),'col:1'=> array('status','priority','start_date','resolution_date','last_update','agent_id'));
$aAttList = array('col:left'=> array('ref','caller_id','servicesubcategory_id','title','description'),'col:right'=> array('status','priority','start_date','resolution_date','last_update','agent_id'));
switch($oObj->GetState())
{
case 'closed':
$aAttList['centered'][] = 'user_satisfaction';
$aAttList['centered'][] = PORTAL_ATTCODE_COMMENT;
}
break;
default:
array('col:0'=> array('ref','service_id','servicesubcategory_id','title','description'),'col:1'=> array('status','start_date'));
array('col:left'=> array('ref','service_id','servicesubcategory_id','title','description'),'col:right'=> array('status','start_date'));
break;
}
@@ -588,12 +593,20 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
$oP->add('<tr>');
$oP->add('<td style="vertical-align:top;">');
$oP->DisplayObjectDetails($oObj, $aAttList['col:0']);
$oP->DisplayObjectDetails($oObj, $aAttList['col:left']);
$oP->add('</td>');
$oP->add('<td style="vertical-align:top;">');
$oP->DisplayObjectDetails($oObj, $aAttList['col:1']);
$oP->DisplayObjectDetails($oObj, $aAttList['col:right']);
$oP->add('</td>');
$oP->add('</tr>');
if (array_key_exists('centered', $aAttList))
{
$oP->add('<tr>');
$oP->add('<td style="vertical-align:top;" colspan="2">');
$oP->DisplayObjectDetails($oObj, $aAttList['centered']);
$oP->add('</td>');
$oP->add('</tr>');
}
// REFACTORISER
$oP->add('<tr>');
@@ -611,73 +624,78 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
$oP->add('</td>');
$oP->add('</tr>');
if (count($aEditAtt) > 0)
{
$oP->add('<tr>');
$oP->add('<td colspan="2" style="vertical-align:top;">');
$oP->add('<tr>');
$oP->add('<td colspan="2" style="vertical-align:top;">');
//$oP->add("<form action=\"../portal/index.php\" id=\"request_form\" method=\"post\">\n");
//$oP->add('<table id=""><tr><td style="vertical-align:top;">');
//$oP->add("<h1 id=\"title_request_details\">".Dict::Format('Portal:CommentsFor_Request', $oObj->GetName())."</h1>\n");
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">");
$oP->add("<input type=\"hidden\" name=\"id\" value=\"".$oObj->GetKey()."\">");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"update_request\">");
$oP->add("<input type=\"hidden\" id=\"stimulus_to_apply\" name=\"apply_stimulus\" value=\"\">\n");
$oP->add_script(
//$oP->add("<form action=\"../portal/index.php\" id=\"request_form\" method=\"post\">\n");
//$oP->add('<table id=""><tr><td style="vertical-align:top;">');
//$oP->add("<h1 id=\"title_request_details\">".Dict::Format('Portal:CommentsFor_Request', $oObj->GetName())."</h1>\n");
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">");
$oP->add("<input type=\"hidden\" name=\"id\" value=\"".$oObj->GetKey()."\">");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"update_request\">");
$oP->add("<input type=\"hidden\" id=\"stimulus_to_apply\" name=\"apply_stimulus\" value=\"\">\n");
$oP->add_script(
<<<EOF
function SetStimulusToApply(sStimulusCode)
{
$('#stimulus_to_apply').val(sStimulusCode);
}
function SetStimulusToApply(sStimulusCode)
{
$('#stimulus_to_apply').val(sStimulusCode);
}
EOF
);
$aEditFields = array(); // Intermediate array to avoid code duplication while splitting btw ticket_log and the rest
foreach($aEditAtt as $sAttCode => $foo)
{
$sValue = $oObj->Get($sAttCode);
$sDisplayValue = $oObj->GetEditValue($sAttCode);
$aArgs = array('this' => $oObj, 'formPrefix' => '');
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sInputId = 'input_'.$sAttCode;
$sHTMLValue = "<span id=\"field_{$sInputId}\">".cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', 0 /*$iFlags*/, $aArgs).'</span>';
$aEditFields = array(); // Intermediate array to avoid code duplication while splitting btw ticket_log and the rest
foreach($aEditAtt as $sAttCode => $foo)
{
$sValue = $oObj->Get($sAttCode);
$sDisplayValue = $oObj->GetEditValue($sAttCode);
$aArgs = array('this' => $oObj, 'formPrefix' => '');
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sInputId = 'input_'.$sAttCode;
$sHTMLValue = "<span id=\"field_{$sInputId}\">".cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', 0 /*$iFlags*/, $aArgs).'</span>';
$aEditFields[$sAttCode] = array(
'label' => MetaModel::GetLabel($sClass, $sAttCode),
'value' => $sHTMLValue
);
}
foreach($aEditFields as $sAttCode => $aFieldSpec)
{
if ($sAttCode == PORTAL_ATTCODE_LOG)
{
// Skip, the public log will be displayed below the buttons
continue;
}
$oP->add("<div class=\"edit_item\">");
$oP->add('<h1>'.$aFieldSpec['label'].'</h1>');
$oP->add($aFieldSpec['value']);
$oP->add('</div>');
}
// $oP->p('<textarea id="user_request_commment" name="commment"></textarea>');
if($bIsCloseButton)
{
$sStimulusCode = 'ev_close';
$oP->p('<input type="submit" onClick="SetStimulusToApply(\''.$sStimulusCode.'\');" value="'.Dict::S('Portal:Button:CloseTicket').'">');
}
else
{
$oP->p('<input type="submit" value="'.Dict::S('Portal:Button:UpdateRequest').'">');
}
if ($bIsEscalateButton)
{
$sStimulusCode = 'ev_timeout';
$oP->p('<input type="submit" onClick="SetStimulusToApply(\''.$sStimulusCode.'\');" value="'.Dict::S('Portal:ButtonEscalate').'">');
}
$oP->add('</td>');
$oP->add('</tr>');
$aEditFields[$sAttCode] = array(
'label' => MetaModel::GetLabel($sClass, $sAttCode),
'value' => $sHTMLValue
);
}
foreach($aEditFields as $sAttCode => $aFieldSpec)
{
if ($sAttCode == PORTAL_ATTCODE_LOG)
{
// Skip, the public log will be displayed below the buttons
continue;
}
$oP->add("<div class=\"edit_item\">");
$oP->add('<h1>'.$aFieldSpec['label'].'</h1>');
$oP->add($aFieldSpec['value']);
$oP->add('</div>');
}
if($bIsReopenButton)
{
$sStimulusCode = 'ev_reopen';
$sTitle = addslashes(Dict::S('Portal:Button:ReopenTicket'));
$sOk = addslashes(Dict::S('UI:Button:Ok'));
$oP->p('<input type="button" onClick="RunStimulusDialog(\''.$sStimulusCode.'\', \''.$sTitle.'\', \''.$sOk.'\');" value="'.$sTitle.'...">');
}
if($bIsCloseButton)
{
$sStimulusCode = 'ev_close';
$sTitle = addslashes(Dict::S('Portal:Button:CloseTicket'));
$sOk = addslashes(Dict::S('UI:Button:Ok'));
$oP->p('<input type="button" onClick="RunStimulusDialog(\''.$sStimulusCode.'\', \''.$sTitle.'\', \''.$sOk.'\');" value="'.$sTitle.'...">');
}
elseif (count($aEditAtt) > 0)
{
$oP->p('<input type="submit" value="'.Dict::S('Portal:Button:UpdateRequest').'">');
}
if ($bIsEscalateButton)
{
$sStimulusCode = 'ev_timeout';
$oP->p('<input type="submit" onClick="SetStimulusToApply(\''.$sStimulusCode.'\');" value="'.Dict::S('Portal:ButtonEscalate').'">');
}
$oP->add('</td>');
$oP->add('</tr>');
$oP->add('<tr>');
$oP->add('<td colspan="2" style="vertical-align:top;">');
@@ -703,6 +721,76 @@ EOF
$oP->add('</div>');
}
/**
* Create form to apply a stimulus
* @param WebPage $oP The current web page
* @param Object $oObj The target object
* @param String $sStimulusCode Stimulus that will be applied
* @param Array $aEditAtt List of attributes to edit
* @return void
*/
function MakeStimulusForm(WebPage $oP, $oObj, $sStimulusCode, $aEditAtt)
{
static $bHasStimulusForm = false;
$sDialogId = $sStimulusCode."_dialog";
$sFormId = $sStimulusCode."_form";
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oP->add('<div id="'.$sDialogId.'" style="display: none;">');
$sClass = get_class($oObj);
$oP->add('<form id="'.$sFormId.'" method="post">');
$sTransactionId = utils::GetNewTransactionId();
$oP->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">");
$oP->add("<input type=\"hidden\" name=\"id\" value=\"".$oObj->GetKey()."\">");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"update_request\">");
$oP->add("<input type=\"hidden\" id=\"stimulus_to_apply\" name=\"apply_stimulus\" value=\"$sStimulusCode\">\n");
foreach($aEditAtt as $sAttCode)
{
$sValue = $oObj->Get($sAttCode);
$sDisplayValue = $oObj->GetEditValue($sAttCode);
$aArgs = array('this' => $oObj, 'formPrefix' => '');
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sInputId = 'input_'.$sAttCode;
$sHTMLValue = "<span id=\"field_{$sStimulusCode}_{$sInputId}\">".cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', 0 /*$iFlags*/, $aArgs).'</span>';
$oP->add('<h1>'.MetaModel::GetLabel($sClass, $sAttCode).'</h1>');
$oP->add($sHTMLValue);
}
$oP->add('</form>');
$oP->add('</div>');
if (!$bHasStimulusForm)
{
$bHasStimulusForm = true;
$oP->add_script(
<<<EOF
function RunStimulusDialog(sStimulusCode, sTitle, sOkButtonLabel)
{
$('#'+sStimulusCode+'_dialog').dialog({
height: 'auto',
width: 'auto',
modal: true,
title: sTitle,
buttons: [
{ text: sOkButtonLabel, click: function() {
$(this).find('#'+sStimulusCode+'_form').submit();
} },
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" );
} },
],
});
}
EOF
);
}
}
/**
* Get The organization of the current user (i.e. the organization of its contact)
* @param WebPage $oP The current page, for errors output
@@ -724,6 +812,25 @@ function GetUserOrg()
return $oOrg;
}
/**
* Determine if the current user can be considered as being a portal power user
*/
function IsPowerUSer()
{
$iUserID = UserRights::GetUserId();
$sOQLprofile = "SELECT URP_Profiles AS p JOIN URP_UserProfile AS up ON up.profileid=p.id WHERE up.userid = :user AND p.name = :profile";
$oProfileSet = new DBObjectSet(
DBObjectSearch::FromOQL($sOQLprofile),
array(),
array(
'user' => $iUserID,
'profile' => PORTAL_POWER_USER_PROFILE,
)
);
$bRes = ($oProfileSet->count() > 0);
return $bRes;
}
///////////////////////////////////////////////////////////////////////////////
//
// Main program