From d3fa2181302e376552d977a428de354db2581b36 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Tue, 7 Jul 2015 12:13:12 -0500 Subject: [PATCH] Add an patch for access control, fix install --- bootstrap.php | 1 - css/thread.css | 2 +- include/ajax.admin.php | 8 +- include/class.canned.php | 35 ++- include/class.client.php | 38 ++- include/class.dept.php | 13 +- include/class.faq.php | 7 +- include/class.http.php | 12 +- include/class.nav.php | 2 +- include/class.staff.php | 15 +- include/class.thread.php | 14 +- include/class.ticket.php | 10 +- include/class.variable.php | 5 +- include/client/faq.inc.php | 2 - include/i18n/en_US/team.yaml | 8 +- include/staff/department.inc.php | 11 + include/staff/staff.inc.php | 2 + include/staff/staffmembers.inc.php | 2 +- include/staff/team.inc.php | 27 ++- include/staff/ticket-edit.inc.php | 2 +- include/staff/ticket-view.inc.php | 39 ++-- include/staff/tickets.inc.php | 6 +- include/upgrader/aborted.inc.php | 2 +- include/upgrader/done.inc.php | 2 +- include/upgrader/rename.inc.php | 2 +- include/upgrader/streams/core.sig | 2 +- .../core/0d6099a6-98ad7d55.cleanup.sql | 39 ++++ .../streams/core/0d6099a6-98ad7d55.patch.sql | 49 ++++ .../streams/core/0d6099a6-98ad7d55.task.php | 30 +++ .../streams/core/15b30765-dd0022fb.task.php | 2 +- .../streams/core/1ee831c8-36f6b328.task.php | 48 +++- .../streams/core/b26f29a6-1ee831c8.patch.sql | 4 - .../streams/core/f5692e24-4323a6a8.patch.sql | 7 - scp/canned.php | 2 +- scp/css/dropdown.css | 9 +- scp/css/scp.css | 21 +- scp/tickets.php | 4 +- setup/inc/class.installer.php | 219 ++++++++++-------- setup/inc/streams/core/install-mysql.sql | 13 +- 39 files changed, 445 insertions(+), 271 deletions(-) create mode 100644 include/upgrader/streams/core/0d6099a6-98ad7d55.cleanup.sql create mode 100644 include/upgrader/streams/core/0d6099a6-98ad7d55.patch.sql create mode 100644 include/upgrader/streams/core/0d6099a6-98ad7d55.task.php diff --git a/bootstrap.php b/bootstrap.php index 6fa05aa50..6e245a142 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -80,7 +80,6 @@ class Bootstrap { define('TEAM_TABLE',$prefix.'team'); define('TEAM_MEMBER_TABLE',$prefix.'team_member'); define('DEPT_TABLE',$prefix.'department'); - define('GROUP_TABLE', $prefix.'group'); define('STAFF_DEPT_TABLE', $prefix.'staff_dept_access'); define('ROLE_TABLE', $prefix.'role'); diff --git a/css/thread.css b/css/thread.css index 41c327a08..bb02ba597 100644 --- a/css/thread.css +++ b/css/thread.css @@ -396,7 +396,7 @@ .thread-body blockquote, .thread-body pre { font-size: 14px; - line-height: 1.4rem; + line-height: 1.5rem; } /* Adjust plain/text messages posted as <pre> in the thread body to show in diff --git a/include/ajax.admin.php b/include/ajax.admin.php index 39743c1b6..e5139d8da 100644 --- a/include/ajax.admin.php +++ b/include/ajax.admin.php @@ -176,9 +176,13 @@ class AdminAjaxAPI extends AjaxController { 'name' => (string) $staff->getName(), ), 'application/json')); } - foreach ($errors as $name=>$desc) - if ($F = $form->getField($name)) + foreach ($errors as $name=>$desc) { + if ($F = $form->getField($name)) { $F->addError($desc); + unset($errors[$name]); + } + } + $errors['err'] = implode(", ", $errors); } $title = __("Add New Agent"); diff --git a/include/class.canned.php b/include/class.canned.php index 9ea3a66cb..affa59d9f 100644 --- a/include/class.canned.php +++ b/include/class.canned.php @@ -15,25 +15,6 @@ **********************************************************************/ include_once(INCLUDE_DIR.'class.file.php'); -class CannedModel { - - const PERM_MANAGE = 'canned.manage'; - - static protected $perms = array( - self::PERM_MANAGE => array( - 'title' => - /* @trans */ 'Premade', - 'desc' => - /* @trans */ 'Ability to add/update/disable/delete canned responses') - ); - - static function getPermissions() { - return self::$perms; - } -} - -RolePermission::register( /* @trans */ 'Knowledgebase', CannedModel::getPermissions()); - class Canned extends VerySimpleModel { static $meta = array( @@ -52,6 +33,20 @@ extends VerySimpleModel { ), ); + const PERM_MANAGE = 'canned.manage'; + + static protected $perms = array( + self::PERM_MANAGE => array( + 'title' => + /* @trans */ 'Premade', + 'desc' => + /* @trans */ 'Ability to add/update/disable/delete canned responses') + ); + + static function getPermissions() { + return self::$perms; + } + function getId(){ return $this->canned_id; } @@ -297,4 +292,6 @@ extends VerySimpleModel { return true; } } +RolePermission::register( /* @trans */ 'Knowledgebase', Canned::getPermissions()); + ?> diff --git a/include/class.client.php b/include/class.client.php index bbd6270c5..92b58b1ef 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -253,13 +253,13 @@ class EndUser extends BaseAuthenticatedUser { if (!($stats=$this->getTicketStats())) return 0; - return $stats['org-open']+$stats['org-closed']; + return $stats['org']['open']+$stats['org']['closed']; } function getNumOpenOrganizationTickets() { - return ($stats=$this->getTicketStats())?$stats['org-open']:0; + return ($stats=$this->getTicketStats())?$stats['org']['open']:0; } function getNumClosedOrganizationTickets() { - return ($stats=$this->getTicketStats())?$stats['org-closed']:0; + return ($stats=$this->getTicketStats())?$stats['org']['closed']:0; } function getAccount() { @@ -278,17 +278,31 @@ class EndUser extends BaseAuthenticatedUser { private function getStats() { $basic = Ticket::objects() ->annotate(array('count' => SqlAggregate::COUNT('ticket_id'))) - ->values('status__state', 'topic_id') - ->filter(Q::any(array( - 'user_id' => $this->getId(), - 'thread__collaborators__user_id' => $this->getId(), - ))); + ->values('status__state', 'topic_id', 'user__org_id'); + + $q = new Q(); $q->union(); + if ($this->getOrgId()) + $q->add(array('user__org_id' => $this->getOrgId())); + + // Share tickets among the organization for owners only + $owners = clone $basic; + $q->add(array('user_id' => $this->getId())); + $owners->filter($q); + + $collabs = clone $basic; + $collabs->filter(array('thread__collaborators__user_id' => $this->getId())); + + // TODO: Implement UNION ALL support in the ORM $stats = array('open' => 0, 'closed' => 0, 'topics' => array()); - foreach ($basic as $row) { - $stats[$row['status__state']] += $row['count']; - if ($row['topic_id']) - $stats['topics'][$row['topic_id']] += $row['count']; + foreach (array($owners, $collabs) as $rs) { + foreach ($rs as $row) { + $stats[$row['status__state']] += $row['count']; + if ($row['topic_id']) + $stats['topics'][$row['topic_id']] += $row['count']; + if ($row['user__org_id']) + $stats['org'][$row['status__state']] += $row['count']; + } } return $stats; } diff --git a/include/class.dept.php b/include/class.dept.php index d27e5923c..9723c8925 100644 --- a/include/class.dept.php +++ b/include/class.dept.php @@ -532,7 +532,7 @@ implements TemplateVariable { global $cfg; $id = $this->id; - if ($id != $vars['id']) + if ($id && $id != $vars['id']) $errors['err']=__('Missing or invalid Dept ID (internal error).'); if (!$vars['name']) { @@ -576,8 +576,15 @@ implements TemplateVariable { $this->flags = isset($vars['assign_members_only']) ? self::FLAG_ASSIGN_MEMBERS_ONLY : 0; $this->path = $this->getFullPath(); - if ($this->save()) - return $this->extended->saveAll(); + $wasnew = $this->__new__; + if ($this->save() && $this->extended->saveAll()) { + if ($wasnew) { + // The ID wasn't available until after the commit + $this->path = $this->getFullPath(); + $this->save(); + } + return true; + } if (isset($this->id)) $errors['err']=sprintf(__('Unable to update %s.'), __('this department')) diff --git a/include/class.faq.php b/include/class.faq.php index a551beefa..361518d28 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -155,11 +155,6 @@ class FAQ extends VerySimpleModel { return $this->save(); } - function logView() { - $this->views++; - $this->save(); - } - function printPdf() { global $thisstaff; require_once(INCLUDE_DIR.'class.pdf.php'); @@ -380,7 +375,7 @@ class FAQ extends VerySimpleModel { static function getFeatured() { return self::objects() ->filter(array('ispublished__in'=>array(1,2), 'category__ispublic'=>1)) - ->order_by('-ispublished','-views'); + ->order_by('-ispublished'); } static function findIdByQuestion($question) { diff --git a/include/class.http.php b/include/class.http.php index c358355a7..5e14ee932 100644 --- a/include/class.http.php +++ b/include/class.http.php @@ -61,18 +61,20 @@ class Http { exit; } - function cacheable($etag, $modified, $ttl=3600) { + function cacheable($etag, $modified=false, $ttl=3600) { // Thanks, http://stackoverflow.com/a/1583753/1025836 // Timezone doesn't matter here — but the time needs to be // consistent round trip to the browser and back. - $last_modified = strtotime($modified." GMT"); - header("Last-Modified: ".date('D, d M Y H:i:s', $last_modified)." GMT", false); + if ($modified) { + $last_modified = strtotime($modified." GMT"); + header("Last-Modified: ".date('D, d M Y H:i:s', $last_modified)." GMT", false); + } header('ETag: "'.$etag.'"'); header("Cache-Control: private, max-age=$ttl"); header('Expires: ' . gmdate('D, d M Y H:i:s', Misc::gmtime() + $ttl)." GMT"); header('Pragma: private'); - if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified || - @trim($_SERVER['HTTP_IF_NONE_MATCH'], '" ') == $etag) { + if (($modified && @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified) + || @trim($_SERVER['HTTP_IF_NONE_MATCH'], '" ') == $etag) { header("HTTP/1.1 304 Not Modified"); exit(); } diff --git a/include/class.nav.php b/include/class.nav.php index 8acff9052..e2dde7775 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -177,7 +177,7 @@ class StaffNav { if($staff) { if ($staff->hasPerm(FAQ::PERM_MANAGE)) $subnav[]=array('desc'=>__('Categories'),'href'=>'categories.php','iconclass'=>'faq-categories'); - if ($cfg->isCannedResponseEnabled() && $staff->getRole()->hasPerm(CannedModel::PERM_MANAGE)) + if ($cfg->isCannedResponseEnabled() && $staff->getRole()->hasPerm(Canned::PERM_MANAGE, false)) $subnav[]=array('desc'=>__('Canned Responses'),'href'=>'canned.php','iconclass'=>'canned'); } break; diff --git a/include/class.staff.php b/include/class.staff.php index cd8b4e264..d3cdff2a1 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -28,7 +28,6 @@ implements AuthenticatedUser, EmailContact, TemplateVariable { static $meta = array( 'table' => STAFF_TABLE, 'pk' => array('staff_id'), - 'select_related' => array('dept'), 'joins' => array( 'dept' => array( 'constraint' => array('dept_id' => 'Dept.id'), @@ -1240,12 +1239,6 @@ class ChangeDepartmentForm extends AbstractForm { function buildFields() { return array( - 'header' => new FreeTextField(array( - 'configuration' => array( - 'content' => __('Change the primary department and primary role of the selected agents'), - 'classes' => ' ', - ) - )), 'dept_id' => new ChoiceField(array( 'default' => 0, 'required' => true, @@ -1278,6 +1271,10 @@ extends AbstractForm { ); } + function getInstructions() { + return __('Change the primary department and primary role of the selected agents'); + } + function getClean() { $clean = parent::getClean(); $clean['eavesdrop'] = $clean['eavesdrop'] ? 1 : 0; @@ -1374,7 +1371,9 @@ extends AbstractForm { function getClean() { $clean = parent::getClean(); - list($clean['username'],) = preg_split('/[^\w-]/', $clean['email'], 2); + list($clean['username'],) = preg_split('/[^\w.-]/', $clean['email'], 2); + if (Staff::lookup($clean['username'])) + $clean['username'] = mb_strtolower($clean['firstname']); $clean['role_id'] = 1; return $clean; } diff --git a/include/class.thread.php b/include/class.thread.php index 4d45e5f95..20cb0f8f2 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -2361,7 +2361,12 @@ implements TemplateVariable { $vars['threadId'] = $this->getId(); $vars['staffId'] = 0; - return MessageThreadEntry::create($vars, $errors); + if (!($message = MessageThreadEntry::create($vars, $errors))) + return $message; + + $this->lastmessage = SqlFunction::NOW(); + $this->save(); + return $message; } function addResponse($vars, &$errors) { @@ -2369,7 +2374,12 @@ implements TemplateVariable { $vars['threadId'] = $this->getId(); $vars['userId'] = 0; - return ResponseThreadEntry::create($vars, $errors); + if (!($resp = ResponseThreadEntry::create($vars, $errors))) + return $resp; + + $this->lastresponse = SqlFunction::NOW(); + $this->save(); + return $resp; } function getVar($name) { diff --git a/include/class.ticket.php b/include/class.ticket.php index b19fc6e9f..11bcda3db 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -143,7 +143,7 @@ class TicketModel extends VerySimpleModel { function getEffectiveDate() { return Format::datetime(max( - strtotime($this->lastmessage), + strtotime($this->thread->lastmessage), strtotime($this->closed), strtotime($this->reopened), strtotime($this->created) @@ -683,7 +683,7 @@ implements RestrictedAccess, Threadable { } function getLastMessageDate() { - return $this->lastmessage; + return $this->thread->lastmessage; } function getLastMsgDate() { @@ -691,7 +691,7 @@ implements RestrictedAccess, Threadable { } function getLastResponseDate() { - return $this->lastresponse; + return $this->thread->lastresponse; } function getLastRespDate() { @@ -1283,7 +1283,6 @@ implements RestrictedAccess, Threadable { function onResponse($response, $options=array()) { $this->isanswered = 1; - $this->lastresponse = SqlFunction::NOW(); $this->save(); $vars = array_merge($options, @@ -2090,9 +2089,6 @@ implements RestrictedAccess, Threadable { } } - // Set the last message time here - $this->lastmessage = SqlFunction::NOW(); - if (!$alerts) return $message; //Our work is done... diff --git a/include/class.variable.php b/include/class.variable.php index 0efd70d71..8c10fc61c 100644 --- a/include/class.variable.php +++ b/include/class.variable.php @@ -97,7 +97,10 @@ class VariableReplacer { } // Recurse with $rv - return $this->getVar($rv, $remainder); + if (is_object($rv) || $remainder) + return $this->getVar($rv, $remainder); + + return $rv; } function replaceVars($input) { diff --git a/include/client/faq.inc.php b/include/client/faq.inc.php index 36c25d5cf..3ab1c273c 100644 --- a/include/client/faq.inc.php +++ b/include/client/faq.inc.php @@ -63,5 +63,3 @@ if ($faq->getHelpTopics()->count()) { ?> </div> </div> - -<?php $faq->logView(); ?> diff --git a/include/i18n/en_US/team.yaml b/include/i18n/en_US/team.yaml index 16ae12449..136de6505 100644 --- a/include/i18n/en_US/team.yaml +++ b/include/i18n/en_US/team.yaml @@ -2,15 +2,15 @@ # Initial teams defined for the system. # # Fields: -# isenabled - (bool:1|0) true or false if the team should be initially +# flags - (int) +# - isenabled - (0x01) true or false if the team should be initially # enabled -# noalerts - (bool:1|0) +# - noalerts - (0x02) # name - Descriptive name for the team # notes - Administrative notes (viewable internal only) # --- -- isenabled: 1 - noalerts: 0 +- flags: 0x01 name: Level I Support notes: | Tier 1 support, responsible for the initial iteraction with customers diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php index c5f474255..ba30fdb29 100644 --- a/include/staff/department.inc.php +++ b/include/staff/department.inc.php @@ -10,6 +10,8 @@ if($dept && $_REQUEST['a']!='add') { $info['id'] = $dept->getId(); $qs += array('id' => $dept->getId()); } else { + if (!$dept) + $dept = Dept::create(); $title=__('Add New Department'); $action='create'; $submit_text=__('Create Dept'); @@ -293,6 +295,14 @@ $info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); <div id="access" class="hidden tab_content"> <table class="two-column table" width="100%"> <tbody> + <tr class="header"> + <td colspan="2"> + <?php echo __('Department Members'); ?> + <div><small> + <?php echo __('Agents who are primary members of this department'); ?> + </small></div> + </td> + </tr> <?php $agents = Staff::getStaffMembers(); foreach ($dept->getMembers() as $member) { @@ -354,6 +364,7 @@ foreach ($dept->getMembers() as $member) { <script type="text/javascript"> var addAccess = function(staffid, name, role, alerts, primary, error) { + if (!staffid) return; var copy = $('#member_template').clone(); copy.find('td:first').append(document.createTextNode(name)); diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php index 3478dcefd..fdb94df4c 100644 --- a/include/staff/staff.inc.php +++ b/include/staff/staff.inc.php @@ -411,6 +411,7 @@ foreach ($staff->teams as $TM) { <script type="text/javascript"> var addAccess = function(daid, name, role, alerts, error) { + if (!daid) return; var copy = $('#extended_access_template').clone(); copy.find('[data-name=dept_access\\[\\]]') @@ -448,6 +449,7 @@ $(document).on('click', 'a.drop-access', function() { }); var joinTeam = function(teamid, name, alerts, error) { + if (!teamid) return; var copy = $('#team_member_template').clone(); copy.find('[data-name=teams\\[\\]]') diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php index 4df361656..a9b6a8719 100644 --- a/include/staff/staffmembers.inc.php +++ b/include/staff/staffmembers.inc.php @@ -82,7 +82,7 @@ $agents->limit($pageNav->getLimit())->offset($pageNav->getStart()); ?> <h2><?php echo __('Agents');?></h2> -<div class="pull-left" style="width:700px;"> +<div class="pull-left"> <form action="staff.php" method="GET" name="filter"> <input type="hidden" name="a" value="filter" > <select name="did" id="did"> diff --git a/include/staff/team.inc.php b/include/staff/team.inc.php index c055f8743..92ad9f13d 100644 --- a/include/staff/team.inc.php +++ b/include/staff/team.inc.php @@ -80,16 +80,9 @@ $info = $team->getInfo(); </td> <td> <span> - <select name="lead_id"> + <select id="team-lead-select" name="lead_id" data-quick-add="staff"> <option value="0">— <?php echo __('None');?> —</option> - <?php - if ($members) { - foreach($members as $k=>$staff){ - $selected=($team->lead_id && $staff->getId()==$team->lead_id)?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s</option>',$staff->getId(),$selected,$staff->getName()); - } - } - ?> + <option value="0" data-quick-add>— <?php echo __('Add New');?> —</option> </select> <span class="error"><?php echo $errors['lead_id']; ?></span> <i class="help-tip icon-question-sign" href="#lead"></i> @@ -184,6 +177,7 @@ foreach ($members as $m) <script type="text/javascript"> var addMember = function(staffid, name, alerts, error) { + if (!staffid) return; var copy = $('#member_template').clone(); copy.find('[data-name=members\\[\\]]') @@ -200,19 +194,28 @@ var addMember = function(staffid, name, alerts, error) { }; $('#add_member').find('button').on('click', function() { - var selected = $('#add_access').find(':selected'); + var selected = $('#add_access').find(':selected'), + id = selected.val(); addMember(selected.val(), selected.text(), true); + if ($('#team-lead-select option[value='+id+']').length === 0) { + $('#team-lead-select').find('option[data-quick-add]') + .before( + $('<option>').val(selected.val()).text(selected.text()) + ); + } selected.remove(); return false; }); $(document).on('click', 'a.drop-membership', function() { - var tr = $(this).closest('tr'); + var tr = $(this).closest('tr'), + id = tr.find('input[name^=members][type=hidden]').val(); $('#add_access').append( $('<option>') - .attr('value', tr.find('input[name^=members][type=hidden]').val()) + .attr('value', id) .text(tr.find('td:first').text()) ); + $('#team-lead-select option[value='+id+']').remove(); tr.fadeOut(function() { $(this).remove(); }); return false; }); diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index fe92935d1..7d9140b45 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -40,7 +40,7 @@ if ($_POST) <span id="client-name"><?php echo Format::htmlchars($user->getName()); ?></span> <<span id="client-email"><?php echo $user->getEmail(); ?></span>> </a> - <a class="action-button" style="overflow:inherit" href="#" + <a class="inline action-button" style="overflow:inherit" href="#" onclick="javascript: $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/change-user', function(user) { diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 8cdc0b454..695ac4d74 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -59,7 +59,7 @@ if($ticket->isOverdue()) <div class="content"> <div class="pull-right flush-right"> <?php - if ($role->hasPerm(Email::PERM_BANLIST) + if ($thisstaff->hasPerm(Email::PERM_BANLIST) || $role->hasPerm(TicketModel::PERM_EDIT) || ($dept && $dept->isManager($thisstaff))) { ?> <span class="action-button pull-right" data-dropdown="#action-dropdown-more"> @@ -145,7 +145,7 @@ if($ticket->isOverdue()) return false" ><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></a></li> -<?php if ($role->hasPerm(Email::PERM_BANLIST)) { +<?php if ($thisstaff->hasPerm(Email::PERM_BANLIST)) { if(!$emailBanned) {?> <li><a class="confirm-action" id="ticket-banemail" href="#banemail"><i class="icon-ban-circle"></i> <?php echo sprintf( @@ -238,9 +238,19 @@ if($ticket->isOverdue()) <?php } ?> </ul> </div> +<?php } # end if ($user) ?> + </td> + </tr> + <tr> + <th><?php echo __('Email'); ?>:</th> + <td> + <span id="user-<?php echo $ticket->getOwnerId(); ?>-email"><?php echo $ticket->getEmail(); ?></span> + </td> + </tr> <?php if ($user->getOrgId()) { ?> - <span style="display:inline-block"> - <i class="icon-building"></i> + <tr> + <th><?php echo __('Organization'); ?>:</th> + <td><i class="icon-building"></i> <?php echo Format::htmlchars($user->getOrganization()->getName()); ?> <a href="tickets.php?<?php echo Http::build_query(array( 'status'=>'open', 'a'=>'search', 'orgid'=> $user->getOrgId() @@ -248,7 +258,6 @@ if($ticket->isOverdue()) data-dropdown="#action-dropdown-org-stats"> (<b><?php echo $user->getNumOrganizationTickets(); ?></b>) </a> - </span> <div id="action-dropdown-org-stats" class="action-dropdown anchor-right"> <ul> <?php if ($open = $user->getNumOpenOrganizationTickets()) { ?> @@ -275,23 +284,9 @@ if($ticket->isOverdue()) <?php } ?> </ul> </div> -<?php } # end if (user->org) - } # end if ($user) - ?> - </td> - </tr> - <tr> - <th><?php echo __('Email'); ?>:</th> - <td> - <span id="user-<?php echo $ticket->getOwnerId(); ?>-email"><?php echo $ticket->getEmail(); ?></span> - </td> - </tr> - <tr> - <th><?php echo __('Phone'); ?>:</th> - <td> - <span id="user-<?php echo $ticket->getOwnerId(); ?>-phone"><?php echo $ticket->getPhoneNumber(); ?></span> - </td> - </tr> + </td> + </tr> +<?php } # end if (user->org) ?> <tr> <th><?php echo __('Source'); ?>:</th> <td><?php diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index e95674962..b7b1983d1 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -237,10 +237,10 @@ case 'closed': case 'answered': $date_header = __('Last Response'); - $date_col = 'lastresponse'; + $date_col = 'thread__lastresponse'; $date_fallback = '<em class="faded">'.__('unanswered').'</em>'; - $tickets->order_by('-lastresponse'); - $tickets->values('lastresponse'); + $tickets->order_by('-thread__lastresponse'); + $tickets->values('thread__lastresponse'); break; case 'hot': diff --git a/include/upgrader/aborted.inc.php b/include/upgrader/aborted.inc.php index 5e4b1e9f7..846d5cd72 100644 --- a/include/upgrader/aborted.inc.php +++ b/include/upgrader/aborted.inc.php @@ -27,7 +27,7 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D </div> <p><strong><?php echo __('Need Help?');?></strong> <?php echo sprintf(__('We provide %1$s professional upgrade services %2$s and commercial support.'), '<a target="_blank" href="http://osticket.com/support/professional_services.php"><u>','</u></a>'); echo sprintf(__('%1$s Contact us %2$s today for <u>expedited</u> help.'), '<a target="_blank" href="http://osticket.com/support/">','</a>');?></p> </div> - <div id="sidebar"> + <div class="sidebar"> <h3><?php echo __('What to do?');?></h3> <p><?php echo sprintf(__('Restore your previous version from backup and try again or %1$s seek help %2$s.'), '<a target="_blank" href="http://osticket.com/support/">','</a>');?></p> </div> diff --git a/include/upgrader/done.inc.php b/include/upgrader/done.inc.php index 097cbe5bd..58edda1cb 100644 --- a/include/upgrader/done.inc.php +++ b/include/upgrader/done.inc.php @@ -21,7 +21,7 @@ $_SESSION['ost_upgrader']=null; <br> <p><b><?php echo __('PS');?></b>: <?php echo __("Don't just make customers happy, make happy customers!");?></p> </div> - <div id="sidebar"> + <div class="sidebar"> <h3><?php echo __("What's Next?");?></h3> <p><b><?php echo __('Post-upgrade');?></b>: <?php echo sprintf(__('You can now go to %s to enable the system and explore the new features. For complete and up-to-date release notes see the %s'), diff --git a/include/upgrader/rename.inc.php b/include/upgrader/rename.inc.php index 408c2f75a..c553f173f 100644 --- a/include/upgrader/rename.inc.php +++ b/include/upgrader/rename.inc.php @@ -24,7 +24,7 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D </form> </div> </div> - <div id="sidebar"> + <div class="sidebar"> <h3><?php echo __('Need Help?');?></h3> <p> <?php echo __('If you are looking for a greater level of support, we provide <u>professional upgrade</u> and commercial support with guaranteed response times and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support">Learn More!</a>'); ?> diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index 20bbf7d78..f6aa807b2 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -0d6099a650cc7884eb59a040feab2ce8 +98ad7d550c26ac44340350912296e673 diff --git a/include/upgrader/streams/core/0d6099a6-98ad7d55.cleanup.sql b/include/upgrader/streams/core/0d6099a6-98ad7d55.cleanup.sql new file mode 100644 index 000000000..362a94005 --- /dev/null +++ b/include/upgrader/streams/core/0d6099a6-98ad7d55.cleanup.sql @@ -0,0 +1,39 @@ +/** + * @signature 4e9f2e2441e82ba393df94647a1ec9ea + * @version v1.10.0 + * @title Access Control 2.0 + * + */ + +DROP TABLE IF EXISTS `%TABLE_PREFIX%group_dept_access`; + +-- Drop `updated` if it exists (it stayed in the install script after it was +-- removed from the update path +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = '%TABLE_PREFIX%team_member' + AND table_schema = DATABASE() + AND column_name = 'updated' + ) > 0, + "SELECT 1", + "ALTER TABLE `%TABLE_PREFIX%team_member` DROP `updated`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- Drop `views` and `score` from 1ee831c8 as it cannot handle translations +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = '%TABLE_PREFIX%faq' + AND table_schema = DATABASE() + AND column_name = 'views' + ) > 0, + "SELECT 1", + "ALTER TABLE `%TABLE_PREFIX%faq` DROP `views`, DROP `score`;" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +ALTER TABLE `%TABLE_PREFIX%ticket` DROP `lastmessage`, DROP `lastresponse`; diff --git a/include/upgrader/streams/core/0d6099a6-98ad7d55.patch.sql b/include/upgrader/streams/core/0d6099a6-98ad7d55.patch.sql new file mode 100644 index 000000000..ab6a3f757 --- /dev/null +++ b/include/upgrader/streams/core/0d6099a6-98ad7d55.patch.sql @@ -0,0 +1,49 @@ +/** + * @signature 98ad7d550c26ac44340350912296e673 + * @version v1.10.0 + * @title Access Control 2.0 + * + */ + +DROP TABLE IF EXISTS `%TABLE_PREFIX%staff_dept_access`; +CREATE TABLE `%TABLE_PREFIX%staff_dept_access` ( + `staff_id` int(10) unsigned NOT NULL DEFAULT 0, + `dept_id` int(10) unsigned NOT NULL DEFAULT 0, + `role_id` int(10) unsigned NOT NULL DEFAULT 0, + `flags` int(10) unsigned NOT NULL DEFAULT 1, + PRIMARY KEY `staff_dept` (`staff_id`,`dept_id`), + KEY `dept_id` (`dept_id`) +) DEFAULT CHARSET=utf8; + +INSERT INTO `%TABLE_PREFIX%staff_dept_access` + (`staff_id`, `dept_id`, `role_id`) + SELECT A1.`staff_id`, A2.`dept_id`, A2.`role_id` + FROM `%TABLE_PREFIX%staff` A1 + JOIN `%TABLE_PREFIX%group_dept_access` A2 ON (A1.`group_id` = A2.`group_id`); + +ALTER TABLE `%TABLE_PREFIX%staff` + DROP `group_id`, + ADD `permissions` text AFTER `extra`; + +ALTER TABLE `%TABLE_PREFIX%team_member` + ADD `flags` int(10) unsigned NOT NULL DEFAULT 1 AFTER `staff_id`; + +ALTER TABLE `%TABLE_PREFIX%thread_collaborator` + ADD KEY `user_id` (`user_id`); + +ALTER TABLE `%TABLE_PREFIX%task` + ADD `closed` datetime DEFAULT NULL AFTER `duedate`; + +ALTER TABLE `%TABLE_PREFIX%thread` + ADD `lastresponse` datetime DEFAULT NULL AFTER `extra`, + ADD `lastmessage` datetime DEFAULT NULL AFTER `lastresponse`; + +UPDATE `%TABLE_PREFIX%thread` A1 + JOIN `%TABLE_PREFIX%ticket` A2 ON (A2.`ticket_id` = A1.`object_id` AND A1.`object_type` = 'T') + SET A1.`lastresponse` = A2.`lastresponse`, + A1.`lastmessage` = A2.`lastmessage`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = '98ad7d550c26ac44340350912296e673' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/0d6099a6-98ad7d55.task.php b/include/upgrader/streams/core/0d6099a6-98ad7d55.task.php new file mode 100644 index 000000000..3f336a9e1 --- /dev/null +++ b/include/upgrader/streams/core/0d6099a6-98ad7d55.task.php @@ -0,0 +1,30 @@ +<?php + +class StaffPermissions extends MigrationTask { + var $description = "Add staff permissions"; + + function run($time) { + foreach (Staff::objects() as $staff) { + $role = $staff->getRole()->getPermission(); + $perms = array( + User::PERM_CREATE, + User::PERM_EDIT, + User::PERM_DELETE, + User::PERM_MANAGE, + User::PERM_DIRECTORY, + Organization::PERM_CREATE, + Organization::PERM_EDIT, + Organization::PERM_DELETE, + ); + if ($role->has(FAQ::PERM_MANAGE)) + $perms[] = FAQ::PERM_MANAGE; + if ($role->has(Email::PERM_BANLIST)) + $perms[] = Email::PERM_BANLIST; + + $errors = array(); + $staff->updatePerms($perms, $errors); + $staff->save(); + } + } +} +return 'StaffPermissions'; diff --git a/include/upgrader/streams/core/15b30765-dd0022fb.task.php b/include/upgrader/streams/core/15b30765-dd0022fb.task.php index faf8c9967..f633c36c6 100644 --- a/include/upgrader/streams/core/15b30765-dd0022fb.task.php +++ b/include/upgrader/streams/core/15b30765-dd0022fb.task.php @@ -207,7 +207,7 @@ class AttachmentMigrater extends MigrationTask { $this->enqueue($info); } - return $this->queueAttachments($limit); + return $this->getQueueLength(); } function skip($attachId, $error) { diff --git a/include/upgrader/streams/core/1ee831c8-36f6b328.task.php b/include/upgrader/streams/core/1ee831c8-36f6b328.task.php index d50755335..ddf109820 100644 --- a/include/upgrader/streams/core/1ee831c8-36f6b328.task.php +++ b/include/upgrader/streams/core/1ee831c8-36f6b328.task.php @@ -1,19 +1,43 @@ <?php +define('GROUP_TABLE', TABLE_PREFIX.'group'); +class Group extends VerySimpleModel { + static $meta = array( + 'table' => GROUP_TABLE, + 'pk' => array('id'), + ); + const FLAG_ENABLED = 0x0001; + + function getName() { + return $this->name; + } +} + +Staff::getMeta()->addJoin('group', array( + 'constraint' => array('group_id' => 'Group.id'), +)); + class GroupRoles extends MigrationTask { var $description = "Migrate permissions from Group to Role"; static $pmap = array( - 'can_create_tickets' => 'ticket.create', - 'can_edit_tickets' => 'ticket.edit', - 'can_post_ticket_reply' => 'ticket.reply', - 'can_delete_tickets' => 'ticket.delete', - 'can_close_tickets' => 'ticket.close', - 'can_assign_tickets' => 'ticket.assign', - 'can_transfer_tickets' => 'ticket.transfer', - 'can_ban_emails' => 'emails.banlist', - 'can_manage_premade' => 'canned.manage', - 'can_manage_faq' => 'faq.manage', - 'can_view_staff_stats' => 'stats.agents', + 'ticket.create' => 'can_create_tickets', + 'ticket.edit' => 'can_edit_tickets', + 'ticket.reply' => 'can_post_ticket_reply', + 'ticket.delete' => 'can_delete_tickets', + 'ticket.close' => 'can_close_tickets', + 'ticket.assign' => 'can_assign_tickets', + 'ticket.transfer' => 'can_transfer_tickets', + 'task.create' => 'can_create_tickets', + 'task.edit' => 'can_edit_tickets', + 'task.reply' => 'can_post_ticket_reply', + 'task.delete' => 'can_delete_tickets', + 'task.close' => 'can_close_tickets', + 'task.assign' => 'can_assign_tickets', + 'task.transfer' => 'can_transfer_tickets', + 'emails.banlist' => 'can_ban_emails', + 'canned.manage' => 'can_manage_premade', + 'faq.manage' => 'can_manage_faq', + 'stats.agents' => 'can_view_staff_stats', ); function run($max_time) { @@ -30,7 +54,7 @@ class GroupRoles extends MigrationTask { 'notes' => $group->getName() ); $perms = array(); - foreach (self::$pmap as $k => $v) { + foreach (self::$pmap as $v => $k) { if ($group->{$k}) $perms[] = $v; } diff --git a/include/upgrader/streams/core/b26f29a6-1ee831c8.patch.sql b/include/upgrader/streams/core/b26f29a6-1ee831c8.patch.sql index e9920fd42..4eb98233d 100644 --- a/include/upgrader/streams/core/b26f29a6-1ee831c8.patch.sql +++ b/include/upgrader/streams/core/b26f29a6-1ee831c8.patch.sql @@ -14,10 +14,6 @@ ALTER TABLE `%TABLE_PREFIX%attachment` ADD `lang` varchar(16) AFTER `inline`; -ALTER TABLE `%TABLE_PREFIX%faq` - ADD `views` int(10) unsigned NOT NULL default '0' AFTER `notes`, - ADD `score` int(10) NOT NULL default '0' AFTER `views`; - ALTER TABLE `%TABLE_PREFIX%staff` ADD `lang` varchar(16) DEFAULT NULL AFTER `signature`, ADD `timezone` varchar(64) default NULL AFTER `lang`, diff --git a/include/upgrader/streams/core/f5692e24-4323a6a8.patch.sql b/include/upgrader/streams/core/f5692e24-4323a6a8.patch.sql index 67a21aa77..5db5be880 100644 --- a/include/upgrader/streams/core/f5692e24-4323a6a8.patch.sql +++ b/include/upgrader/streams/core/f5692e24-4323a6a8.patch.sql @@ -66,7 +66,6 @@ ALTER TABLE `%TABLE_PREFIX%email` ALTER TABLE `%TABLE_PREFIX%help_topic` ADD `sort` int(10) unsigned NOT NULL default '0' AFTER `form_id`; --- Add `content_id` to the content table to allow for translations RENAME TABLE `%TABLE_PREFIX%page` TO `%TABLE_PREFIX%content`; ALTER TABLE `%TABLE_PREFIX%content` CHANGE `type` `type` varchar(32) NOT NULL default 'other'; @@ -110,9 +109,6 @@ INSERT INTO `%TABLE_PREFIX%content` WHERE A3.`key` = 'default_template_id' and `namespace` = 'core') AND A1.`code_name` = 'user.accesslink'; -UPDATE `%TABLE_PREFIX%content` SET `content_id` = LAST_INSERT_ID() - WHERE `id` = LAST_INSERT_ID(); - -- Transfer staff password reset link INSERT INTO `%TABLE_PREFIX%content` (`name`, `body`, `type`, `isactive`, `created`, `updated`) @@ -122,9 +118,6 @@ INSERT INTO `%TABLE_PREFIX%content` WHERE A3.`key` = 'default_template_id' and `namespace` = 'core') AND A1.`code_name` = 'staff.pwreset'; -UPDATE `%TABLE_PREFIX%content` SET `content_id` = LAST_INSERT_ID() - WHERE `id` = LAST_INSERT_ID(); - -- No longer saved in the email_template table DELETE FROM `%TABLE_PREFIX%email_template` WHERE `code_name` IN ('staff.pwreset', 'user.accesslink'); diff --git a/scp/canned.php b/scp/canned.php index 85af1b2c4..8a5447375 100644 --- a/scp/canned.php +++ b/scp/canned.php @@ -18,7 +18,7 @@ include_once(INCLUDE_DIR.'class.canned.php'); /* check permission */ if(!$thisstaff - || !$thisstaff->getRole()->hasPerm(CannedModel::PERM_MANAGE) + || !$thisstaff->getRole()->hasPerm(Canned::PERM_MANAGE, false) || !$cfg->isCannedResponseEnabled()) { header('Location: kb.php'); exit; diff --git a/scp/css/dropdown.css b/scp/css/dropdown.css index 8a5b560d8..c0b90c83a 100644 --- a/scp/css/dropdown.css +++ b/scp/css/dropdown.css @@ -43,8 +43,9 @@ .action-dropdown ul li > a i { margin-right: 0.1em; } -.action-dropdown ul li > a:hover { - background-color: #08C !important; +.action-dropdown ul li > a:hover, +.action-dropdown ul li.active > a:hover { + background-color: #08C; color: #FFF !important; cursor: pointer; } @@ -98,10 +99,6 @@ left: auto; right: 10px; } -.action-button { - line-height: 22px; - height: 22px; -} .action-button span, .action-button a { color: inherit; diff --git a/scp/css/scp.css b/scp/css/scp.css index 8287b4ce2..a65fb8294 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -247,9 +247,7 @@ a time.relative { border-left:1px solid #ccc; border-right:1px solid #ccc; border-radius: 0 0 5px 5px; - display:block; - padding-left: 5px; -moz-box-shadow: 3px 3px 3px #ccc; -webkit-box-shadow: 3px 3px 3px #ccc; box-shadow: 3px 3px 3px #ccc; @@ -601,13 +599,6 @@ a.print { background-image:url(../images/icons/printer.gif); } -.btn { - padding:3px 10px; - background:url(../images/btn_bg.png) top left repeat-x #ccc; - border:1px solid #777; - color:#000; -} - .btn_sm { padding:2px 5px; font-size:0.9em; @@ -617,10 +608,6 @@ a.print { font-weight:bold; } -.btn:hover, .btn_sm:hover { - background-position: bottom left; -} - .search label { display:block; line-height:25px; @@ -1884,6 +1871,10 @@ input[type=button].small, .small.button, input[type=submit].small { border: none; } +.action-button.inline, .button.inline { + vertical-align: middle; +} + /* Dynamic forms in dialogs */ .dialog th, .tip_box th { text-align: left; @@ -1948,8 +1939,6 @@ ul.progress li.yes small {color:green; } ul.progress li.no small {color:red;} #bar { clear: both; padding-top: 10px; height: 24px; line-height: 24px; text-align: center; border-top: 1px solid #aaaaaa; } -#bar a, #bar .btn { display: inline-block; margin: 0; height: 24px; line-height: 24px; font-weight: bold; border: 1px solid #666666; text-decoration: none; padding: 0 10px; background: url('../images/grey_btn_bg.png?1312910883') top left repeat-x; color: #333; } -#bar a:hover, #bar .btn:hover, #bar .btnover { background-position: bottom left; } #bar a.unstyled, #bar a.unstyled:hover { font-weight: normal; background: none; border: none; text-decoration: underline; color: #2a67ac; } #bar.error { background: #ffd; text-align: center; color: #a00; font-weight: bold; } @@ -2649,7 +2638,7 @@ input[type=checkbox] { margin-top: 3px; } -input, select, textarea { +input, textarea { padding: 3px 5px; font-size: 0.95em; font-family: inherit; diff --git a/scp/tickets.php b/scp/tickets.php index 7027667a1..f3bbe8d7c 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -318,7 +318,7 @@ if($_POST && !$errors): } break; case 'banemail': - if (!$role->hasPerm(Email::PERM_BANLIST)) { + if (!$thisstaff->hasPerm(Email::PERM_BANLIST)) { $errors['err']=__('Permission Denied. You are not allowed to ban emails'); } elseif(BanList::includes($ticket->getEmail())) { $errors['err']=__('Email already in banlist'); @@ -329,7 +329,7 @@ if($_POST && !$errors): } break; case 'unbanemail': - if (!$role->hasPerm(Email::PERM_BANLIST)) { + if (!$thisstaff->hasPerm(Email::PERM_BANLIST)) { $errors['err'] = __('Permission Denied. You are not allowed to remove emails from banlist.'); } elseif(Banlist::remove($ticket->getEmail())) { $msg = __('Email removed from banlist'); diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index c10b1ed9e..3986191a9 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -53,8 +53,8 @@ class Installer extends SetupWizard { $f['lname'] = array('type'=>'string', 'required'=>1, 'error'=>__('Last name required')); $f['admin_email'] = array('type'=>'email', 'required'=>1, 'error'=>__('Valid email required')); $f['username'] = array('type'=>'username', 'required'=>1, 'error'=>__('Username required')); - $f['passwd'] = array('type'=>'password', 'required'=>1, 'error'=>__('Password required')); - $f['passwd2'] = array('type'=>'password', 'required'=>1, 'error'=>__('Confirm Password')); + $f['passwd'] = array('type'=>'string', 'required'=>1, 'error'=>__('Password required')); + $f['passwd2'] = array('type'=>'string', 'required'=>1, 'error'=>__('Confirm Password')); $f['prefix'] = array('type'=>'string', 'required'=>1, 'error'=>__('Table prefix required')); $f['dbhost'] = array('type'=>'string', 'required'=>1, 'error'=>__('Host name required')); $f['dbname'] = array('type'=>'string', 'required'=>1, 'error'=>__('Database name required')); @@ -73,6 +73,14 @@ class Installer extends SetupWizard { //Admin's pass confirmation. if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2'])) $this->errors['passwd2']=__('Password(s) do not match'); + try { + require_once INCLUDE_DIR.'class.auth.php'; + PasswordPolicy::checkPassword($vars['passwd'], null); + } + catch (BadPassword $e) { + $this->errors['passwd'] = $e->getMessage(); + } + //Check table prefix underscore required at the end! if($vars['prefix'] && substr($vars['prefix'], -1)!='_') $this->errors['prefix']=__('Bad prefix. Must have underscore (_) at the end. e.g \'ost_\''); @@ -110,8 +118,9 @@ class Installer extends SetupWizard { } } - //bailout on errors. - if($this->errors) return false; + // bailout on errors. + if ($this->errors) + return false; /*************** We're ready to install ************************/ define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. @@ -151,111 +160,125 @@ class Installer extends SetupWizard { } } - if(!$this->errors) { - - // TODO: Use language selected from install worksheet - $i18n = new Internationalization($vars['lang_id']); - $i18n->loadDefaultData(); - - Signal::send('system.install', $this); - - $sql='SELECT `id` FROM `'.TABLE_PREFIX.'sla` ORDER BY `id` LIMIT 1'; - $sla_id_1 = db_result(db_query($sql, false)); - - $sql='SELECT `id` FROM `'.TABLE_PREFIX.'department` ORDER BY `id` LIMIT 1'; - $dept_id_1 = db_result(db_query($sql, false)); - - $sql='SELECT `tpl_id` FROM `'.TABLE_PREFIX.'email_template_group` ORDER BY `tpl_id` LIMIT 1'; - $template_id_1 = db_result(db_query($sql, false)); - - $sql='SELECT `id` FROM `'.TABLE_PREFIX.'group` ORDER BY `id` LIMIT 1'; - $group_id_1 = db_result(db_query($sql, false)); - - $sql='SELECT `id` FROM `'.TABLE_PREFIX.'role` ORDER BY `id` LIMIT 1'; - $role_id_1 = db_result(db_query($sql, false)); + if ($this->errors) + return false; - //Create admin user. - $sql='INSERT INTO '.TABLE_PREFIX.'staff SET created=NOW() ' - .', isactive=1, isadmin=1, max_page_size=25 ' - .', group_id='.db_input($group_id_1) - .', dept_id='.db_input($dept_id_1) - .', role_id='.db_input($role_id_1) - .', email='.db_input($vars['admin_email']) - .', firstname='.db_input($vars['fname']) - .', lastname='.db_input($vars['lname']) - .', username='.db_input($vars['username']) - .', passwd='.db_input(Passwd::hash($vars['passwd'])); - if(!db_query($sql, false) || !($uid=db_insert_id())) - $this->errors['err']=__('Unable to create admin user (#6)'); + // TODO: Use language selected from install worksheet + $i18n = new Internationalization($vars['lang_id']); + $i18n->loadDefaultData(); + + Signal::send('system.install', $this); + + list($sla_id) = Sla::objects()->order_by('id')->values_flat('id')->first(); + list($dept_id) = Dept::objects()->order_by('id')->values_flat('id')->first(); + list($role_id) = Role::objects()->order_by('id')->values_flat('id')->first(); + + $sql='SELECT `tpl_id` FROM `'.TABLE_PREFIX.'email_template_group` ORDER BY `tpl_id` LIMIT 1'; + $template_id_1 = db_result(db_query($sql, false)); + + // Create admin user. + $staff = Staff::create(array( + 'isactive' => 1, + 'isadmin' => 1, + 'max_page_size' => 25, + 'dept_id' => $dept_id, + 'role_id' => $role_id, + 'email' => $vars['admin_email'], + 'firstname' => $vars['fname'], + 'lastname' => $vars['lname'], + 'username' => $vars['username'], + )); + $staff->updatePerms(array( + User::PERM_CREATE, + User::PERM_EDIT, + User::PERM_DELETE, + User::PERM_MANAGE, + User::PERM_DIRECTORY, + Organization::PERM_CREATE, + Organization::PERM_EDIT, + Organization::PERM_DELETE, + FAQ::PERM_MANAGE, + Email::PERM_BANLIST, + ), $errors); + $staff->setPassword($vars['passwd']); + if (!$staff->save()) { + $this->errors['err'] = __('Unable to create admin user (#6)'); + return false; } - if(!$this->errors) { - //Create default emails! - $email = $vars['email']; - list(,$domain)=explode('@',$vars['email']); - $sql='INSERT INTO '.TABLE_PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES ' - ." ('Support','$email',NOW(),NOW())" - .",('osTicket Alerts','alerts@$domain',NOW(),NOW())" - .",('','noreply@$domain',NOW(),NOW())"; - $support_email_id = db_query($sql, false) ? db_insert_id() : 0; - - - $sql='SELECT `email_id` FROM '.TABLE_PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1"; - $alert_email_id = db_result(db_query($sql, false)); - - //Create config settings---default settings! - $defaults = array( - 'default_email_id'=>$support_email_id, - 'alert_email_id'=>$alert_email_id, - 'default_dept_id'=>$dept_id_1, 'default_sla_id'=>$sla_id_1, - 'default_template_id'=>$template_id_1, - 'default_timezone' => $vars['timezone'] ?: date_default_timezone_get(), - 'admin_email'=>$vars['admin_email'], - 'schema_signature'=>$streams['core'], - 'helpdesk_url'=>URL, - 'helpdesk_title'=>$vars['name']); - $config = new Config('core'); - if (!$config->updateAll($defaults)) - $this->errors['err']=__('Unable to create config settings').' (#7)'; - - // Set company name - require_once(INCLUDE_DIR.'class.company.php'); - $company = new Company(); - $company->getForm()->setAnswer('name', $vars['name']); - $company->getForm()->save(); - - foreach ($streams as $stream=>$signature) { - if ($stream != 'core') { - $config = new Config($stream); - if (!$config->update('schema_signature', $signature)) - $this->errors['err']=__('Unable to create config settings').' (#8)'; - } - } + // Create default emails! + $email = $vars['email']; + list(,$domain) = explode('@', $vars['email']); + foreach (array( + "Support" => $email, + "osTicket Alerts" => "alerts@$domain", + '' => "noreply@$domain", + ) as $name => $mailbox) { + $mb = Email::create(array( + 'name' => $name, + 'email' => $mailbox, + 'dept_id' => $dept_id, + )); + $mb->save(); + if ($mailbox == $email) + $support_email_id = $mb->email_id; + if ($mailbox == "alerts@$domain") + $alert_email_id = $mb->email_id; } - if($this->errors) return false; //Abort on internal errors. + //Create config settings---default settings! + $defaults = array( + 'default_email_id'=>$support_email_id, + 'alert_email_id'=>$alert_email_id, + 'default_dept_id'=>$dept_id, 'default_sla_id'=>$sla_id, + 'default_template_id'=>$template_id_1, + 'default_timezone' => $vars['timezone'] ?: date_default_timezone_get(), + 'admin_email'=>$vars['admin_email'], + 'schema_signature'=>$streams['core'], + 'helpdesk_url'=>URL, + 'helpdesk_title'=>$vars['name'] + ); + + $config = new Config('core'); + if (!$config->updateAll($defaults)) + $this->errors['err']=__('Unable to create config settings').' (#7)'; + + // Set company name + require_once(INCLUDE_DIR.'class.company.php'); + $company = new Company(); + $company->getForm()->setAnswer('name', $vars['name']); + $company->getForm()->save(); + + foreach ($streams as $stream => $signature) { + if ($stream != 'core') { + $config = new Config($stream); + if (!$config->update('schema_signature', $signature)) + $this->errors['err'] = __('Unable to create config settings').' (#8)'; + } + } + + if ($this->errors) + return false; //Abort on internal errors. //Rewrite the config file - MUST be done last to allow for installer recovery. - $configFile= str_replace("define('OSTINSTALLED',FALSE);","define('OSTINSTALLED',TRUE);",$configFile); - $configFile= str_replace('%ADMIN-EMAIL',$vars['admin_email'],$configFile); - $configFile= str_replace('%CONFIG-DBHOST',$vars['dbhost'],$configFile); - $configFile= str_replace('%CONFIG-DBNAME',$vars['dbname'],$configFile); - $configFile= str_replace('%CONFIG-DBUSER',$vars['dbuser'],$configFile); - $configFile= str_replace('%CONFIG-DBPASS',$vars['dbpass'],$configFile); - $configFile= str_replace('%CONFIG-PREFIX',$vars['prefix'],$configFile); - $configFile= str_replace('%CONFIG-SIRI',Misc::randCode(32),$configFile); - if(!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) { + $configFile = strtr($configFile, array( + "define('OSTINSTALLED',FALSE);" => "define('OSTINSTALLED',TRUE);", + '%ADMIN-EMAIL' => $vars['admin_email'], + '%CONFIG-DBHOST' => $vars['dbhost'], + '%CONFIG-DBNAME' => $vars['dbname'], + '%CONFIG-DBUSER' => $vars['dbuser'], + '%CONFIG-DBPASS' => $vars['dbpass'], + '%CONFIG-PREFIX' => $vars['prefix'], + '%CONFIG-SIRI' => Misc::randCode(32), + )); + if (!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) { $this->errors['err']=__('Unable to write to config file. Permission denied! (#5)'); return false; } @fclose($fp); /************* Make the system happy ***********************/ - - $sql='UPDATE '.TABLE_PREFIX."email SET dept_id=$dept_id_1"; - db_query($sql, false); - global $cfg; $cfg = new OsticketConfig(); @@ -266,9 +289,9 @@ class Installer extends SetupWizard { $ticket = Ticket::create($ticket_vars, $errors, 'api', false, false); if ($ticket - && ($org = Organization::objects()->order_by('id')->one())) { - - $user=User::lookup($ticket->getOwnerId()); + && ($org = Organization::objects()->order_by('id')->one()) + ) { + $user = User::lookup($ticket->getOwnerId()); $user->setOrganization($org); } diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index 720cb2557..105d0420a 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -37,8 +37,6 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%faq` ( `answer` text NOT NULL, `keywords` tinytext, `notes` text, - `views` int(10) unsigned NOT NULL default '0', - `score` int(10) NOT NULL default '0', `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`faq_id`), @@ -559,8 +557,7 @@ CREATE TABLE `%TABLE_PREFIX%staff` ( PRIMARY KEY (`staff_id`), UNIQUE KEY `username` (`username`), KEY `dept_id` (`dept_id`), - KEY `issuperuser` (`isadmin`), - KEY `group_id` (`group_id`,`staff_id`) + KEY `issuperuser` (`isadmin`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%staff_dept_access`; @@ -615,6 +612,8 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%thread` ( `object_id` int(11) unsigned NOT NULL, `object_type` char(1) NOT NULL, `extra` text, + `lastresponse` datetime DEFAULT NULL, + `lastmessage` datetime DEFAULT NULL, `created` datetime NOT NULL, PRIMARY KEY (`id`), KEY `object_id` (`object_id`), @@ -682,8 +681,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` ( `est_duedate` datetime default NULL, `reopened` datetime default NULL, `closed` datetime default NULL, - `lastmessage` datetime default NULL, - `lastresponse` datetime default NULL, `lastupdate` datetime default NULL, `created` datetime NOT NULL, `updated` datetime NOT NULL, @@ -772,7 +769,8 @@ CREATE TABLE `%TABLE_PREFIX%thread_collaborator` ( `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `collab` (`thread_id`,`user_id`) + UNIQUE KEY `collab` (`thread_id`,`user_id`), + KEY `user_id` (`user_id`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%task`; @@ -787,6 +785,7 @@ CREATE TABLE `%TABLE_PREFIX%task` ( `lock_id` int(11) unsigned NOT NULL DEFAULT '0', `flags` int(10) unsigned NOT NULL DEFAULT '0', `duedate` datetime DEFAULT NULL, + `closed` datetime DEFAULT NULL, `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), -- GitLab