diff --git a/bootstrap.php b/bootstrap.php index 3d7b6a4e9a5ad9c70b3512c63d67fc218e69969c..e70c79a6bb9801b06c49e4aaa5349a93e84b6de6 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -69,11 +69,15 @@ class Bootstrap { define('FILE_CHUNK_TABLE',$prefix.'file_chunk'); define('ATTACHMENT_TABLE',$prefix.'attachment'); + define('USER_TABLE',$prefix.'user'); + define('USER_CDATA_TABLE', $prefix.'user__cdata'); define('USER_EMAIL_TABLE',$prefix.'user_email'); define('USER_ACCOUNT_TABLE',$prefix.'user_account'); define('ORGANIZATION_TABLE', $prefix.'organization'); + define('ORGANIZATION_CDATA_TABLE', $prefix.'organization__cdata'); + define('NOTE_TABLE', $prefix.'note'); define('STAFF_TABLE',$prefix.'staff'); diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 2b258688612e668c1c968da89d2a64fddaf49312..2b112078d8f92d037cbdef4107395b9ff5f05cb8 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -198,7 +198,7 @@ class DynamicForm extends VerySimpleModel { return $this->save(); } - function getExportableFields($exclude=array()) { + function getExportableFields($exclude=array(), $prefix='__') { $fields = array(); foreach ($this->getFields() as $f) { // Ignore core fields @@ -209,7 +209,8 @@ class DynamicForm extends VerySimpleModel { elseif (!$f->hasData() || $f->isPresentationOnly()) continue; - $fields['__field_'.$f->get('id')] = $f; + $name = $f->get('name') ?: ('field_'.$f->get('id')); + $fields[$prefix.$name] = $f; } return $fields; } @@ -342,6 +343,10 @@ class DynamicForm extends VerySimpleModel { return TicketForm::updateDynamicDataView($answer, $data); case 'A': return TaskForm::updateDynamicDataView($answer, $data); + case 'U': + return UserForm::updateDynamicDataView($answer, $data); + case 'O': + return OrganizationForm::updateDynamicDataView($answer, $data); } } @@ -355,6 +360,10 @@ class DynamicForm extends VerySimpleModel { return TicketForm::dropDynamicDataView(TicketForm::$cdata['table']); case 'A': return TaskForm::dropDynamicDataView(TaskForm::$cdata['table']); + case 'U': + return UserForm::dropDynamicDataView(UserForm::$cdata['table']); + case 'O': + return OrganizationForm::dropDynamicDataView(OrganizationForm::$cdata['table']); } } @@ -407,6 +416,12 @@ class UserForm extends DynamicForm { static $instance; static $form; + static $cdata = array( + 'table' => USER_CDATA_TABLE, + 'object_id' => 'user_id', + 'object_type' => ObjectModel::OBJECT_TYPE_USER, + ); + static function objects() { $os = parent::objects(); return $os->filter(array('type'=>'U')); diff --git a/include/class.export.php b/include/class.export.php index 72b66967adde73f9d2e912017292d0b85dd6d151..ee2c6a8ec8e2b3029c08602136aa3ee452442dcc 100644 --- a/include/class.export.php +++ b/include/class.export.php @@ -178,32 +178,21 @@ class Export { $exclude = array('name', 'email'); $form = UserForm::getUserForm(); - $fields = $form->getExportableFields($exclude); - - // Field selection callback - $fname = function ($f) { - return 'cdata.`'.$f->getSelectName().'` AS __field_'.$f->get('id'); - }; - - $sql = substr_replace($sql, - ','.implode(',', array_map($fname, $fields)).' ', - strpos($sql, 'FROM '), 0); - - $sql = substr_replace($sql, - 'LEFT JOIN ('.$form->getCrossTabQuery($form->type, 'user_id', $exclude).') cdata - ON (cdata.user_id = user.id) ', - strpos($sql, 'WHERE '), 0); + $fields = $form->getExportableFields($exclude, 'cdata.'); $cdata = array_combine(array_keys($fields), array_values(array_map( - function ($f) { return $f->get('label'); }, $fields))); + function ($f) { return $f->getLocal('label'); }, $fields))); + + $users = $sql->models() + ->select_related('org', 'cdata'); ob_start(); - echo self::dumpQuery($sql, + echo self::dumpQuery($users, array( 'name' => __('Name'), - 'organization' => __('Organization'), - 'email' => __('Email'), + 'org' => __('Organization'), + '::getEmail' => __('Email'), ) + $cdata, $how, array('modify' => function(&$record, $keys) use ($fields) { @@ -228,30 +217,19 @@ class Export { $exclude = array('name'); $form = OrganizationForm::getDefaultForm(); - $fields = $form->getExportableFields($exclude); - - // Field selection callback - $fname = function ($f) { - return 'cdata.`'.$f->getSelectName().'` AS __field_'.$f->get('id'); - }; - - $sql = substr_replace($sql, - ','.implode(',', array_map($fname, $fields)).' ', - strpos($sql, 'FROM '), 0); - - $sql = substr_replace($sql, - 'LEFT JOIN ('.$form->getCrossTabQuery($form->type, '_org_id', $exclude).') cdata - ON (cdata._org_id = org.id) ', - strpos($sql, 'WHERE '), 0); - + $fields = $form->getExportableFields($exclude, 'cdata.'); $cdata = array_combine(array_keys($fields), array_values(array_map( - function ($f) { return $f->get('label'); }, $fields))); + function ($f) { return $f->getLocal('label'); }, $fields))); - $cdata += array('account_manager' => 'Account Manager', 'users' => 'Users'); + $cdata += array( + '::getNumUsers' => 'Users', + '::getAccountManager' => 'Account Manager', + ); + $orgs = $sql->models(); ob_start(); - echo self::dumpQuery($sql, + echo self::dumpQuery($orgs, array( 'name' => 'Name', ) + $cdata, diff --git a/include/class.organization.php b/include/class.organization.php index e870667b1f10e2132b9bdc51a2fa7c18e65f02aa..aa3690beea32fc41ef237d23e6e26ef1895d9dda 100644 --- a/include/class.organization.php +++ b/include/class.organization.php @@ -70,6 +70,10 @@ class OrganizationModel extends VerySimpleModel { return $this->name; } + function getNumUsers() { + return $this->users->count(); + } + function getAccountManager() { if (!isset($this->_manager)) { if ($this->manager[0] == 't') @@ -145,19 +149,16 @@ RolePermission::register(/* @trans */ 'Organizations', class OrganizationCdata extends VerySimpleModel { static $meta = array( - 'table' => 'org__cdata', - 'view' => true, + 'table' => ORGANIZATION_CDATA_TABLE, 'pk' => array('org_id'), + 'joins' => array( + 'org' => array( + 'constraint' => array('ord_id' => 'OrganizationModel.id'), + ), + ), ); - - function getQuery($compiler) { - $form = OrganizationForm::getDefaultForm(); - $exclude = array('name'); - return '('.$form->getCrossTabQuery($form->type, 'org_id', $exclude).')'; - } } - class Organization extends OrganizationModel implements TemplateVariable { var $_entries; @@ -507,6 +508,12 @@ class OrganizationForm extends DynamicForm { static $instance; static $form; + static $cdata = array( + 'table' => ORGANIZATION_CDATA_TABLE, + 'object_id' => 'org_id', + 'object_type' => ObjectModel::OBJECT_TYPE_ORG, + ); + static function objects() { $os = parent::objects(); return $os->filter(array('type'=>'O')); diff --git a/include/class.ticket.php b/include/class.ticket.php index 8e0871df0160a787dbe1c8d26550d25d9c199f5e..6d68aae893ee823da44fbedf9638f4d75cf0d394 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -219,6 +219,7 @@ RolePermission::register(/* @trans */ 'Tickets', TicketModel::getPermissions(), class TicketCData extends VerySimpleModel { static $meta = array( + 'table' => TICKET_CDATA_TABLE, 'pk' => array('ticket_id'), 'joins' => array( 'ticket' => array( @@ -231,7 +232,6 @@ class TicketCData extends VerySimpleModel { ), ); } -TicketCData::$meta['table'] = TABLE_PREFIX . 'ticket__cdata'; class Ticket extends TicketModel implements RestrictedAccess, Threadable { diff --git a/include/class.user.php b/include/class.user.php index cc64e736298c436b6398ece1700380728fb469d1..b4feed8283c3a9bdf50d21861c209ee88714926d 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -180,20 +180,14 @@ RolePermission::register(/* @trans */ 'Users', UserModel::getPermissions()); class UserCdata extends VerySimpleModel { static $meta = array( - 'table' => 'user__cdata', - 'view' => true, + 'table' => USER_CDATA_TABLE, 'pk' => array('user_id'), + 'joins' => array( + 'user' => array( + 'constraint' => array('user_id' => 'UserModel.id'), + ), + ), ); - - static function getQuery($compiler) { - $form = UserForm::getUserForm(); - $exclude = array('name', 'email'); - return '('.$form->getCrossTabQuery($form->type, 'user_id', $exclude).')'; - } - - static function getSqlAddParams($compiler) { - return static::getQuery($compiler); - } } class User extends UserModel diff --git a/include/staff/orgs.inc.php b/include/staff/orgs.inc.php index aba6ed30869ee4c56f2fe3b4399324c406135ae3..feb53acb399b5e934cd49bf9c1eedb5420f3e856 100644 --- a/include/staff/orgs.inc.php +++ b/include/staff/orgs.inc.php @@ -1,75 +1,61 @@ <?php if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied'); -$qs = array(); - -$select = 'SELECT org.* - ,COALESCE(team.name, - IF(staff.staff_id, CONCAT_WS(" ", staff.firstname, staff.lastname), NULL) - ) as account_manager '; -$from = 'FROM '.ORGANIZATION_TABLE.' org ' - .'LEFT JOIN '.STAFF_TABLE.' staff ON ( - LEFT(org.manager, 1) = "s" AND staff.staff_id = SUBSTR(org.manager, 2)) ' - .'LEFT JOIN '.TEAM_TABLE.' team ON ( - LEFT(org.manager, 1) = "t" AND team.team_id = SUBSTR(org.manager, 2)) '; +OrganizationForm::ensureDynamicDataView(); -$where = ' WHERE 1 '; +$qs = array(); +$orgs = Organization::objects() + ->annotate(array('user_count'=>SqlAggregate::COUNT('users'))); if ($_REQUEST['query']) { - - $from .=' LEFT JOIN '.FORM_ENTRY_TABLE.' entry - ON (entry.object_type=\'O\' AND entry.object_id = org.id) - LEFT JOIN '.FORM_ANSWER_TABLE.' value - ON (value.entry_id=entry.id) '; - - $search = db_input(strtolower($_REQUEST['query']), false); - $where .= ' AND ( - org.name LIKE \'%'.$search.'%\' OR value.value LIKE \'%'.$search.'%\' - )'; - + $search = $_REQUEST['query']; + $orgs->filter(Q::any(array( + 'name__contains' => $search, + // TODO: Add search for cdata + ))); $qs += array('query' => $_REQUEST['query']); } -$sortOptions = array('name' => 'org.name', - 'users' => 'users', - 'create' => 'org.created', - 'update' => 'org.updated'); -$orderWays = array('DESC'=>'DESC','ASC'=>'ASC'); +$sortOptions = array( + 'name' => 'name', + 'users' => 'users', + 'create' => 'created', + 'update' => 'updated' + ); + +$orderWays = array('DESC' => '-', 'ASC' => ''); $sort= ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; //Sorting options... if ($sort && $sortOptions[$sort]) - $order_column =$sortOptions[$sort]; + $order_column = $sortOptions[$sort]; -$order_column = $order_column ?: 'org.name'; +$order_column = $order_column ?: 'name'; if ($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) $order = $orderWays[strtoupper($_REQUEST['order'])]; -$order=$order ?: 'ASC'; if ($order_column && strpos($order_column,',')) $order_column = str_replace(','," $order,",$order_column); $x=$sort.'_sort'; -$$x=' class="'.strtolower($order).'" '; +$$x=' class="'.($order == '' ? 'asc' : 'desc').'" '; $order_by="$order_column $order "; -$total=db_count('SELECT count(DISTINCT org.id) '.$from.' '.$where); -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total,$page,PAGE_LIMIT); +$total = $orgs->count(); +$page=($_GET['p'] && is_numeric($_GET['p']))? $_GET['p'] : 1; +$pageNav=new Pagenate($total, $page, PAGE_LIMIT); +$pageNav->paginate($orgs); + $qstr = '&'. Http::build_query($qs); $qs += array('sort' => $_REQUEST['sort'], 'order' => $_REQUEST['order']); $pageNav->setURL('orgs.php', $qs); -$qstr.='&order='.($order=='DESC' ? 'ASC' : 'DESC'); - -$select .= ', count(DISTINCT user.id) as users '; - -$from .= ' LEFT JOIN '.USER_TABLE.' user ON (user.org_id = org.id) '; - +$qstr.='&order='.($order=='-' ? 'ASC' : 'DESC'); -$query="$select $from $where GROUP BY org.id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); //echo $query; -$qhash = md5($query); -$_SESSION['orgs_qs_'.$qhash] = $query; +$_SESSION[':Q:orgs'] = $orgs; + +$orgs->values('id', 'name', 'created', 'updated'); +$orgs->order_by($order . $order_column); ?> <div id="basic_search"> <div style="min-height:25px;"> @@ -120,11 +106,11 @@ $_SESSION['orgs_qs_'.$qhash] = $query; <div class="clear"></div> <?php $showing = $search ? __('Search Results').': ' : ''; -$res = db_query($query); -if($res && ($num=db_num_rows($res))) +if ($orgs->exists(true)) $showing .= $pageNav->showing(); else $showing .= __('No organizations found!'); + ?> <form id="orgs-list" action="orgs.php" method="POST" name="staff" > <?php csrf_token(); ?> @@ -143,31 +129,31 @@ else </thead> <tbody> <?php - if($res && db_num_rows($res)): - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - while ($row = db_fetch_array($res)) { - - $sel=false; - if($ids && in_array($row['id'], $ids)) - $sel=true; - ?> - <tr id="<?php echo $row['id']; ?>"> - <td nowrap align="center"> - <input type="checkbox" value="<?php echo $row['id']; ?>" class="ckb mass nowarn"/> - </td> - <td> <a href="orgs.php?id=<?php echo $row['id']; ?>"><?php echo $row['name']; ?></a> </td> - <td> <?php echo $row['users']; ?></td> - <td><?php echo Format::date($row['created']); ?></td> - <td><?php echo Format::datetime($row['updated']); ?> </td> - </tr> - <?php - } //end of while. - endif; ?> + $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; + foreach ($orgs as $org) { + + $sel=false; + if($ids && in_array($org['id'], $ids)) + $sel=true; + ?> + <tr id="<?php echo $org['id']; ?>"> + <td nowrap align="center"> + <input type="checkbox" value="<?php echo $org['id']; ?>" class="ckb mass nowarn"/> + </td> + <td> <a href="orgs.php?id=<?php echo $org['id']; ?>"><?php + echo $org['name']; ?></a> </td> + <td> <?php echo $org['user_count']; ?></td> + <td><?php echo Format::date($org['created']); ?></td> + <td><?php echo Format::datetime($org['updated']); ?> </td> + </tr> + <?php + } + ?> </tbody> <tfoot> <tr> <td colspan="7"> - <?php if ($res && $num) { ?> + <?php if ($total) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -182,12 +168,11 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($total): //Show options.. echo sprintf('<div> %s: %s <a class="no-pjax" - href="orgs.php?a=export&qh=%s">%s</a></div>', + href="orgs.php?a=export">%s</a></div>', __('Page'), $pageNav->getPageLinks(), - $qhash, __('Export')); endif; ?> diff --git a/include/staff/users.inc.php b/include/staff/users.inc.php index f948114d0fca412bae344b9410df3a235a92088c..afb54e57b51daeb501d6340d22e5052a3ff193f7 100644 --- a/include/staff/users.inc.php +++ b/include/staff/users.inc.php @@ -1,8 +1,10 @@ <?php if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied'); -$qs = array(); +// Ensure cdata +UserForm::ensureDynamicDataView(); +$qs = array(); $users = User::objects() ->annotate(array('ticket_count'=>SqlAggregate::COUNT('tickets'))); @@ -50,7 +52,7 @@ $pageNav->setURL('users.php', $qs); $qstr.='&order='.($order=='-' ? 'ASC' : 'DESC'); //echo $query; -$_SESSION[':Q:users'] = clone $users; +$_SESSION[':Q:users'] = $users; $users->values('id', 'name', 'default_email__address', 'account__id', 'account__status', 'created', 'updated'); @@ -205,7 +207,7 @@ else <tfoot> <tr> <td colspan="7"> - <?php if ($res && $num) { ?> + <?php if ($total) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> diff --git a/scp/orgs.php b/scp/orgs.php index ad9624b9492a1ece928c29e43cc8032a47c21deb..bfd2210abf39db4d913398669c7c0418fc7fb02e 100644 --- a/scp/orgs.php +++ b/scp/orgs.php @@ -98,9 +98,7 @@ if ($_POST) { } elseif (!$org && $_REQUEST['a'] == 'export') { require_once(INCLUDE_DIR.'class.export.php'); $ts = strftime('%Y%m%d'); - if (!($token=$_REQUEST['qh'])) - $errors['err'] = __('Query token required'); - elseif (!($query=$_SESSION['orgs_qs_'.$token])) + if (!($query=$_SESSION[':Q:orgs'])) $errors['err'] = __('Query token not found'); elseif (!Export::saveOrganizations($query, __('organizations')."-$ts.csv", 'csv')) $errors['err'] = __('Internal error: Unable to export results'); diff --git a/scp/users.php b/scp/users.php index edc6b52fb7a1289670e6f782280f1fb6d91a0ba8..f665050384c88854bd93b6ae310ef91f11eb9cbb 100644 --- a/scp/users.php +++ b/scp/users.php @@ -162,9 +162,7 @@ if ($_POST) { } elseif(!$user && $_REQUEST['a'] == 'export') { require_once(INCLUDE_DIR.'class.export.php'); $ts = strftime('%Y%m%d'); - if (!($token=$_REQUEST['qh'])) - $errors['err'] = __('Query token required'); - elseif (!($query=$_SESSION['users_qs_'.$token])) + if (!($query=$_SESSION[':Q:users'])) $errors['err'] = __('Query token not found'); elseif (!Export::saveUsers($query, __("users")."-$ts.csv", 'csv')) $errors['err'] = __('Internal error: Unable to dump query results');