\n");
+ }
+}
+
+/**
+ * Displays the tickets which need approval by mysel
+ * @param WebPage $oP The current web page
+ * @return void
+ */
+// =========================== THEBEN ==================================
+function ShowTicketsToApprove(WebPage $oP)
+{
+ $oP->add("
\n");
+ $oP->add("
".Dict::S('Portal:RequestsToApprove')."
\n");
+ ListRequestsToApprove($oP);
+ $oP->add("
\n");
+
+}
+
+/**
+ * Displays the tickets which need approval by mysel
+ * @param WebPage $oP The current web page
+ * @return void
+ */
+// =========================== THEBEN ==================================
+function ShowTicketsToResolve(WebPage $oP)
+{
+ $oP->add("
\n");
+ $oP->add("
".Dict::S('Portal:RequestsToResolve')."
\n");
+ ListRequestsToResolve($oP);
+ $oP->add("
\n");
+
}
/**
@@ -199,134 +247,6 @@ function ShowClosedTickets(WebPage $oP)
$oP->add("\n");
}
-/**
- * Displays the form to select a Service Category Id (among the valid ones for the specified user Organization)
- * @param WebPage $oP Web page for the form output
- * @param Organization $oUserOrg The organization of the current user
- * @return void
- */
-function SelectServiceCategory($oP, $oUserOrg)
-{
- $aParameters = $oP->ReadAllParams(PORTAL_ALL_PARAMS.',template_id');
-
- $oSearch = DBObjectSearch::FromOQL(PORTAL_SERVICECATEGORY_QUERY);
- $oSearch->AllowAllData(); // In case the user has the rights on his org only
- $oSet = new CMDBObjectSet($oSearch, array(), array('org_id' => $oUserOrg->GetKey()));
- if ($oSet->Count() == 1)
- {
- $oService = $oSet->Fetch();
- $iSvcCategory = $oService->GetKey();
- // Only one Category, skip this step in the wizard
- SelectServiceSubCategory($oP, $oUserOrg, $iSvcCategory);
- }
- else
- {
- $oP->add("
\n");
-
- $oP->DumpHiddenParams($aParameters, array('service_id'));
- $oP->add("");
- $oP->WizardFormButtons(BUTTON_NEXT | BUTTON_CANCEL); // NO back button since it's the first step of the Wizard
- $oP->WizardFormEnd();
- $oP->WizardCheckSelectionOnSubmit(Dict::S('Portal:PleaseSelectOneService'));
- $oP->add("
\n");
- }
-}
-
-/**
- * Displays the form to select a Service Subcategory Id (among the valid ones for the specified user Organization)
- * and based on the page's parameter 'service_id'
- * @param WebPage $oP Web page for the form output
- * @param Organization $oUserOrg The organization of the current user
- * @param $iSvcId Id of the selected service in case of pass-through (when there is only one service)
- * @return void
- */
-function SelectServiceSubCategory($oP, $oUserOrg, $iSvcId = null)
-{
- $aParameters = $oP->ReadAllParams(PORTAL_ALL_PARAMS.',template_id');
- if ($iSvcId == null)
- {
- $iSvcId = $aParameters['service_id'];
- }
- else
- {
- $aParameters['service_id'] = $iSvcId;
- }
- $iDefaultSubSvcId = isset($aParameters['servicesubcategory_id']) ? $aParameters['servicesubcategory_id'] : 0;
-
- $iDefaultWizNext = 2;
-
- $oSearch = DBObjectSearch::FromOQL(PORTAL_SERVICE_SUBCATEGORY_QUERY);
- RestrictSubcategories($oSearch);
- $oSearch->AllowAllData(); // In case the user has the rights on his org only
- $oSet = new CMDBObjectSet($oSearch, array(), array('svc_id' => $iSvcId, 'org_id' => $oUserOrg->GetKey()));
- if ($oSet->Count() == 1)
- {
- // Only one sub service, skip this step of the wizard
- $oSubService = $oSet->Fetch();
- $iSubSvdId = $oSubService->GetKey();
- SelectRequestTemplate($oP, $oUserOrg, $iSvcId, $iSubSvdId);
- }
- else
- {
- $oServiceCategory = MetaModel::GetObject('Service', $iSvcId, false, true /* allow all data*/);
- if (is_object($oServiceCategory))
- {
- $oP->add("
\n");
- $oP->WizardFormStart('request_form', 4);
+ $oP->WizardFormStart('request_form', 1);
$oP->details($aDetails);
// Add hidden fields for known values, enabling dependant attributes to be computed correctly
//
foreach($oRequest->ListChanges() as $sAttCode => $value)
- {
+ {
if (!in_array($sAttCode, $aList))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
@@ -587,7 +508,7 @@ EOF
$oP->add("
\n");
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
-
+ // IssueLog::Info("sJsonFieldsMap=".$sJsonFieldsMap);
$oP->add_ready_script(
<<ReadAllParams(PORTAL_ALL_PARAMS.',template_id');
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
if (!utils::IsTransactionValid($sTransactionId))
@@ -713,8 +634,8 @@ function CreateRequest(WebPage $oP, Organization $oUserOrg)
{
switch($oP->GetWizardStep())
{
- case 0:
- default:
+// case 0:
+/* default:
SelectServiceCategory($oP, $oUserOrg);
break;
@@ -724,13 +645,13 @@ function CreateRequest(WebPage $oP, Organization $oUserOrg)
case 2:
SelectRequestTemplate($oP, $oUserOrg);
- break;
+ break; */
- case 3:
+ case 0:
RequestCreationForm($oP, $oUserOrg);
break;
- case 4:
+ case 1:
DoCreateRequest($oP, $oUserOrg);
break;
}
@@ -825,6 +746,54 @@ function ListResolvedRequests(WebPage $oP)
DisplayRequestLists($oP, $aClassToSet);
}
+/**
+ * Lists all the currently resolved (not yet closed) User Requests for the current user
+ * @param WebPage $oP The current web page
+ * @return void
+ */
+// ========================= THEBEN ===================================
+function ListRequestsToApprove(WebPage $oP)
+{
+ $oUserOrg = GetUserOrg();
+
+ $aClassToSet = array();
+ foreach (GetTicketClasses() as $sClass)
+ {
+ $sOQL = "SELECT $sClass WHERE org_id = :org_id AND status = 'waiting_for_approval'";
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $iUser = UserRights::GetContactId();
+
+ $oSearch->AddCondition('approver_id', $iUser);
+
+ $aClassToSet[$sClass] = new CMDBObjectSet($oSearch, array(), array('org_id' => $oUserOrg->GetKey()));
+ }
+ DisplayRequestLists($oP, $aClassToSet);
+}
+
+/**
+ * Lists all the currently resolved (not yet closed) User Requests for the current user
+ * @param WebPage $oP The current web page
+ * @return void
+ */
+// ========================= THEBEN ===================================
+function ListRequestsToResolve(WebPage $oP)
+{
+ $oUserOrg = GetUserOrg();
+
+ $aClassToSet = array();
+ foreach (GetTicketClasses() as $sClass)
+ {
+ $sOQL = "SELECT $sClass WHERE org_id = :org_id AND status = 'assigned'";
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $iUser = UserRights::GetContactId();
+
+ $oSearch->AddCondition('agent_id', $iUser);
+
+ $aClassToSet[$sClass] = new CMDBObjectSet($oSearch, array(), array('org_id' => $oUserOrg->GetKey()));
+ }
+ DisplayRequestLists($oP, $aClassToSet);
+}
+
/**
* Lists all the currently closed tickets
* @param WebPage $oP The current web page
@@ -896,6 +865,8 @@ function DisplayObject($oP, $oObj, $oUserOrg)
* @param Object $oObj The target object
* @return void
*/
+//============= THEBEN ============= ApproveButton === ResolveButton ===============
+
function ShowDetailsRequest(WebPage $oP, $oObj)
{
$sClass = get_class($oObj);
@@ -905,6 +876,10 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
$bIsReopenButton = false;
$bIsCloseButton = false;
$bIsEscalateButton = false;
+ $bIsApproveButton = false;
+ $bIsRejectButton = false;
+ $bIsResolveButton = false;
+
$bEditAttachments = false;
$aEditAtt = array(); // List of attributes editable in the main form
if (!MetaModel::DBIsReadOnly())
@@ -923,16 +898,66 @@ function ShowDetailsRequest(WebPage $oP, $oObj)
}
// Add the "Close" button if this is valid action
if (array_key_exists('ev_close', $aTransitions) && UserRights::IsStimulusAllowed($sClass, 'ev_close', $oSet))
- {
- $bIsCloseButton = true;
- MakeStimulusForm($oP, $oObj, 'ev_close', array('user_satisfaction', $sUserCommentAttCode));
+ { if (($oObj->Get('caller_id') == UserRights::GetContactId())
+ || IsPowerUSer()) {
+ // I can only close a ticket if I'm the caller or a power user
+ $bIsCloseButton = true;
+ MakeStimulusForm($oP, $oObj, 'ev_close', array('user_satisfaction', $sUserCommentAttCode));
+ }
+ IssueLog::Info('caller_id='.$oObj->Get('caller_id')." user-id=".UserRights::GetContactId());
}
break;
+
+ case 'waiting_for_approval':
+ $aEditAtt = array();
+ $aTransitions = $oObj->EnumTransitions();
+ $oSet = DBObjectSet::FromObject($oObj);
+ // Add the "Approve" button if this is valid action
+ if (array_key_exists('ev_approve', $aTransitions)) //TODO check if current user is approver && UserRights::IsStimulusAllowed($sClass, 'ev_reopen', $oSet))
+ {
+ $bIsApproveButton = true;
+ MakeStimulusForm($oP, $oObj, 'ev_approve', array($sLogAttCode));
+ }
+ // Add the "Reject" button if this is valid action
+ if (array_key_exists('ev_reject', $aTransitions)) // TODO check if current user is approve && UserRights::IsStimulusAllowed($sClass, 'ev_close', $oSet))
+ {
+ $bIsRejectButton = true;
+ MakeStimulusForm($oP, $oObj, 'ev_reject', array($sLogAttCode) ); //array('user_satisfaction', $sUserCommentAttCode));
+ }
+ break;
case 'closed':
// By convention 'closed' is the final state of a ticket and nothing can be done in such a state
break;
+ case 'assigned':
+
+ $iFlags = $oObj->GetAttributeFlags($sLogAttCode);
+ $bReadOnly = (($iFlags & (OPT_ATT_READONLY | OPT_ATT_HIDDEN)) != 0);
+ if ($bReadOnly)
+ {
+ $aEditAtt = array();
+ $bEditAttachments = false;
+ }
+ else
+ {
+ $aEditAtt = array(
+ $sLogAttCode => '????'
+ );
+ $bEditAttachments = true;
+ }
+ if (isKeyUser()) {
+ $aTransitions = $oObj->EnumTransitions();
+ $oSet = DBObjectSet::FromObject($oObj);
+ // Add the "Resolver" button if this is valid action
+ if (array_key_exists('ev_resolve', $aTransitions)) //TODO check if current user is approver && UserRights::IsStimulusAllowed($sClass, 'ev_reopen', $oSet))
+ {
+ $bIsResolveButton = true;
+ MakeStimulusForm($oP, $oObj, 'ev_resolve', array($sLogAttCode));
+ }
+ }
+ break;
+
default:
// In all other states, the only possible action is to update the ticket (both the case log and the attachments)
// This update is possible only if the case log field is not read-only or hidden in the current state
@@ -1073,6 +1098,32 @@ EOF
$sOk = addslashes(Dict::S('UI:Button:Ok'));
$oP->p('');
}
+
+ if($bIsApproveButton)
+ {
+ $sStimulusCode = 'ev_approve';
+ $sTitle = addslashes(Dict::S('Portal:Button:ApproveTicket'));
+ $sOk = addslashes(Dict::S('UI:Button:Ok'));
+ $oP->p('');
+ }
+
+ if($bIsRejectButton)
+ {
+ $sStimulusCode = 'ev_reject';
+ $sTitle = addslashes(Dict::S('Portal:Button:RejectTicket'));
+ $sOk = addslashes(Dict::S('UI:Button:Ok'));
+ $oP->p('');
+ }
+
+ if($bIsResolveButton)
+ {
+ $sStimulusCode = 'ev_resolve';
+ $sTitle = addslashes(Dict::S('Portal:Button:ResolveTicket'));
+ $sOk = addslashes(Dict::S('UI:Button:Ok'));
+ $oP->p('');
+ }
+
+
if($bIsCloseButton)
{
$sStimulusCode = 'ev_close';
@@ -1257,6 +1308,26 @@ function IsPowerUSer()
return $bRes;
}
+/**
+ * Determine if the current user can be considered as being a portal key user
+ * (can update tickets where he is agent and can resolve them)
+ */
+function IsKeyUSer()
+{
+ $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_KEY_USER_PROFILE',
+ )
+ );
+ $bRes = ($oProfileSet->count() > 0);
+ return $bRes;
+}
+
///////////////////////////////////////////////////////////////////////////////
//
// Main program
@@ -1273,7 +1344,7 @@ try
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
- ApplicationContext::SetUrlMakerClass('MyPortalURLMaker');
+ ApplicationContext::SetUrlMakerClass('MyPortalURLMaker');
$aClasses = explode(',', MetaModel::GetConfig()->Get('portal_tickets'));
$sMainClass = trim(reset($aClasses));
@@ -1288,9 +1359,11 @@ try
$sCode = $oUserOrg->Get('code');
$sAlternateStylesheet = '';
+ //IssueLog::Info("org code of user=".$sCode);
if (@file_exists("./$sCode/portal.css"))
{
$sAlternateStylesheet = "$sCode";
+ IssueLog::Info("using Alt Stylesheet: ".$sAlternateStylesheet);
}
$oP = new PortalWebPage(Dict::S('Portal:Title'), $sAlternateStylesheet);
@@ -1349,12 +1422,27 @@ try
DisplayObject($oP, $oObj, $oUserOrg);
}
break;
-
+
+ // =================== THEBEN ===========================
+ case 'show_toapprove':
+ $oP->set_title(Dict::S('Portal:ShowToApprove'));
+ DisplayMainMenu($oP);
+ ShowTicketsToApprove($oP);
+ break;
+
+ // =================== THEBEN ===========================
+ case 'show_toresolve':
+ $oP->set_title(Dict::S('Portal:ShowToResolve'));
+ DisplayMainMenu($oP);
+ ShowTicketsToResolve($oP);
+ break;
+
case 'show_ongoing':
default:
$oP->set_title(Dict::S('Portal:ShowOngoing'));
DisplayMainMenu($oP);
ShowOngoingTickets($oP);
+ break;
}
}
}
diff --git a/setup/licenses/community-licences.xml b/setup/licenses/community-licences.xml
index f6fb0bf77..b21a457a8 100644
--- a/setup/licenses/community-licences.xml
+++ b/setup/licenses/community-licences.xml
@@ -803,5 +803,30 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]>
+
+ PHP XLSXWriter
+ Mark Jones
+ MIT
+ Copyright (c) 2013 Mark Jones
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]]>
+
diff --git a/webservices/export.php b/webservices/export.php
index 4a6ff8b47..6a1098094 100644
--- a/webservices/export.php
+++ b/webservices/export.php
@@ -28,9 +28,11 @@ if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/nicewebpage.class.inc.php');
+require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'/application/csvpage.class.inc.php');
require_once(APPROOT.'/application/xmlpage.class.inc.php');
require_once(APPROOT.'/application/clipage.class.inc.php');
+require_once(APPROOT.'/application/excelexporter.class.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
@@ -264,6 +266,32 @@ if (!empty($sExpression))
cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, array('localize_values' => $bLocalize));
break;
+ case 'xlsx':
+ $oP = new ajax_page('');
+ $oExporter = new ExcelExporter();
+ $oExporter->SetObjectList($oFilter);
+
+ // Run the export by chunk of 1000 objects to limit memory usage
+ $oExporter->SetChunkSize(1000);
+ do
+ {
+ $aStatus = $oExporter->Run(); // process one chunk
+ }
+ while( ($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'done')
+ {
+ $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
+ $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
+ $oExporter->Cleanup();
+ }
+ else
+ {
+ $oP->add('Error, xlsx export failed: '.$aStatus['message']);
+ }
+ break;
+
default:
$oP = new WebPage("iTop - Export");
$oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
@@ -301,7 +329,7 @@ if (!$oP)
$oP->p(" * expression: an OQL expression (URL encoded if needed)");
$oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
$oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
- $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv' or 'xml'");
+ $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv', 'xlsx' or 'xml'");
$oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a coma");
$oP->p(" * fields_advanced: (optional, no effect on XML/HTML formats ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
$oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");