?> : <?php echo $data['first_name'] . ' ' . $data['last_name']; ?> </h1> <?php } else { ?> <h1><?php _e('Create a New User'); ?> </h1> <?php } ?> <?php $f = array('action' => url(CURRENT_URL), 'submit' => array(__('Create User'), __('Update User')), 'data_id' => $data['id'], 'options' => array('numcols' => 2, 'toplabel' => false), 'layout' => array('col1' => array('colspan' => 1, 'label_width' => '40%'), 'col2' => array('colspan' => 1, 'label_width' => '40%')), 'elements' => array('col1' => array('access_keys' => array('prompt' => __('Access Level:'), 'type' => 'select', 'options' => array_hash($access_keys), 'help' => __('Access levels determine the amount of control a user will have.')), 'first_name' => array('prompt' => __('First Name:'), 'type' => 'text'), 'last_name' => array('prompt' => __('Last Name:'), 'type' => 'text'), 'email' => array('prompt' => __('Email Address:'), 'type' => 'text', 'help' => __('Email address must be unique, as it will be used for login purposes.'))), 'col2' => array('language' => array('prompt' => __('Language:'), 'type' => 'select', 'options' => $languages), 'status' => array('prompt' => __('Status:'), 'type' => 'select', 'options' => array_hash(array('active', 'pending', 'deleted'))), 'password' => array('prompt' => __('Login Password:'******'type' => 'password'), 'password2' => array('prompt' => __('Confirm Password:'******'type' => 'password')))); if ($data['openid']) { unset($f['elements']['col2']['password'], $f['elements']['col2']['password2']); $f['elements']['col2']['openid'] = array('prompt' => __('Open ID:'), 'type' => 'text', attribs => array('readonly' => 'readonly')); } // Statistics table $html->css_load('grid'); // load the 'grid' CSS $stats = $table->build_table(array('class' => 'grid', 'rows' => array(array(__('Created On:'), $data['created_on']), array(__('Last Login:'******'last_login']), array(__('Status:'), ucfirst($data['status']))))); // Put them together in a tabbed form echo $form->build_tabbed_form(array('action' => url(CURRENT_URL), 'data_id' => $data['id'], 'submit' => array(__('Create User'), __('Update User')), 'spinner' => 'spinner.gif'), array('tab1' => array('label' => __('User Info'), 'form' => $f), 'tab2' => array('label' => __('Statistics'), 'content' => $stats)), $data, $errors); ?>
/** * Generate a table of data with search filters/pagination/sorting/totals * * @param array $params Table parameters * * Parameters: * - class :: css class for the table tag * - url :: url to pass to for filter changes (CURRENT_URL) * - noresults_txt :: text to display if no results are found (No Matches) * - data_id :: name of column in dataset that uniquely identifies each record (id) * - rows :: total number of rows in result set * - perpage :: number of rows per page * - curpage :: the current page number * - rowclick :: action URL to go to if a table row is clicked * - perpage_opts :: array of rows-per-page options for pagination control * - options array :: override default table behavior ('noheaders','nosorting','nofilters','nofilterbutton','nototals','nopagination','forcepagination') * - totals array :: array of data row indices that we should tally/display * eg, 'totals' => array('amount' => array('format'=>'%.2f')) * - cb_vars array :: array of variables that get passed to callback functions (used for special display logic) * - rowclassfn :: function to call with row data - use this to pass back an additional tr class * - tr_attribs :: attributes array (or callback function) used for the table row * - columns array * - key = index into data array * - val = array * - label :: header text label * - align :: td alignment * - type :: filter input type (text/select/date/none) * - nosort :: set true to disable sorting on this column * - flength :: length of filter field (for text fields only) * - options :: options array for type==select * - options_nokeys :: same as above, but use values in <option> fields instead of keys * - filter_attribs :: attributes array (or callback function) passed to the form-field generator function for the filter field * - td_attribs :: attributes array (or callback function) used for the table cell (variable substitution allowed) * - format :: a sprintf string to pass the data through before displaying it * - date_format :: if field is a date, specify the format string to be passed to date() * - row_func :: a function to run on the entire row, handy for output that isn't necessarily bound to a specific column from the result set * - value_func * - value_map :: if the values of a column map directly to certain display values, assign the map array to 'value_map' * - expr :: if column key is not a real db column, specify real one here * - sort_dir :: set to (asc/desc) to make this the default sorted column * - data array */ function build_grid($params) { $guid = 'grid' . ++$this->guid; $class = $this->_getparam($params, 'class', 'grid'); $options = $this->_getparam($params, 'options', array()); $data_id = $this->_getparam($params, 'data_id', 'id'); $grid_url = $this->_getparam($params, 'url', url(CURRENT_URL)); $pp_opts = $this->_getparam($params, 'perpage_opts', array(50, 200, 500, 1000)); if (!isset($params['noresults_txt'])) { $params['noresults_txt'] = __('No Matches'); } // setup tooltips $this->depends->html->js_load('jq_tooltip', 'jq/jquery.tooltip'); $this->depends->html->js_run('jq_tooltip', '$(\'td.options img\').Tooltip({showURL:false,extraClass:\'action\'});'); $this->depends->html->css_load('tooltip', 'tooltip'); // grid assets $this->depends->html->css_load('grid'); $this->depends->html->js_load('pronto.grid'); // setup AJAX routines if necessary if ($this->_opt_isset($options, 'ajax')) { $this->depends->html->js_load('ajax'); $this->depends->html->js_run('grid', '$(\'.ajax_action\').click(grid_dispatch);'); } $cb_vars = array(); if (isset($params['cb_vars'])) { foreach ($params['cb_vars'] as $k => $v) { // (foreach can't use references in PHP4, so we do it this way) $cb_vars[$k] =& $params['cb_vars']["{$k}"]; } } $totals = array(); if (isset($params['totals'])) { foreach ($params['totals'] as $k => $v) { $totals[$k] = 0; } } // beginning of output $out = ''; // save grid configuration in the DOM so we can send it back to the // page controller alongside AJAX requests // XXX: disabled - this was used for tpGrid, which is defunct at this time. /*$p = $params; unset($p['data'], $p['cb_vars']); $out .= '<script type="text/javascript">'; $out .= 'if(typeof window.grid == "undefined") window.grid = {};'; //$out .= "window.grid.{$guid} = ".json_encode($p).";"; $out .= "window.grid.{$guid} = '".str_replace("'", "\\'", serialize($p))."';"; $out .= "</script>\n"; */ // <table> $out .= '<table id="' . $guid . '" cellspacing="0"'; if ($class) { $out .= ' class="' . $class . '"'; } $out .= ">\n"; // COLUMN HEADERS if (!$this->_opt_isset($options, 'noheaders')) { $out .= "<tr class=\"label\">\n"; $i = 0; foreach ($params['columns'] as $name => $column) { $style = array(); $class = array(); $out .= '<th'; if (++$i == count($params['columns'])) { $style[] = 'border-right:none'; } if (!preg_match('|^_OPTIONS_|', $name)) { if ($_GET['s_f'] == $name) { $class[] = 'hover'; } else { $out .= ' onMouseOver="$(this).addClass(\'hover\')"'; $out .= ' onMouseOut="$(this).removeClass(\'hover\')"'; } if (!$this->_opt_isset($options, 'nosorting') && !$column['nosort']) { $out .= ' onClick="return grid_sort(this);"'; } if (preg_match('|^_MULTI_|', $name)) { $style[] = 'text-align:center'; } } // if 'attribs' was present, look for class/style declarations and mix them in with ours if (is_array($column['attribs'])) { if ($column['attribs']['style']) { foreach (explode(';', $column['attribs']['style']) as $s) { $style[] = trim($s); } } if ($column['attribs']['class']) { foreach (explode(' ', $column['attribs']['class']) as $s) { $class[] = trim($s); } } unset($column['attribs']['style'], $column['attribs']['class']); // output the non style/class attribs normally foreach ($column['attribs'] as $k => $v) { $out .= " {$k}=\"{$v}\""; } } $out .= ' class="' . implode(' ', $class) . '"'; $out .= ' style="' . implode(';', $style) . '">'; if (preg_match('|^_OPTIONS_|', $name)) { if ($name == '_OPTIONS_' && !$this->_opt_isset($options, 'nofilters') && !$this->_opt_isset($options, 'nofilterbutton')) { // TODO: show a different filters.html based on language selected (i18n) $out .= $this->depends->html->link(__('Filter Help'), url('/static/filters.en.html'), false, '640x550', array('class' => 'help'), true); } else { $out .= mb_substr($name, 9); } } else { if (preg_match('|^_MULTI_|', $name)) { $label = mb_substr($name, 7); $name = '_m_' . strtolower($label); } else { $label = $column['label'] ? $column['label'] : ' '; } if (!$this->_opt_isset($options, 'nosorting') && !$column['nosort']) { // build new query string with sort parameters $GET = $_GET; $qs = array(); $sortdir = $GET['s_f'] == $name && $GET['s_d'] == 'asc' ? 'desc' : 'asc'; $GET['s_f'] = $name; $GET['s_d'] = $sortdir; if ($_GET['s_f'] == $name) { $arrowimg = $_GET['s_d'] == 'desc' ? 'arrow_black_down.gif' : 'arrow_black_up.gif'; $out .= ' ' . $this->depends->html->image('icons/' . $arrowimg, array('style' => 'float:right')); } else { if (!isset($_GET['s_f']) && !empty($params['sort_col']) && $params['sort_col'] == $name) { $arrowimg = $params['sort_dir'] == 'asc' ? 'arrow_black_up.gif' : 'arrow_black_down.gif'; $out .= ' ' . $this->depends->html->image('icons/' . $arrowimg, array('style' => 'float:right')); $GET['s_d'] = $params['sort_dir'] == 'asc' ? 'desc' : 'asc'; } } foreach ($GET as $k => $v) { $qs[] = "{$k}={$v}"; } $qs = implode('&', $qs); $label = '<a href="' . $grid_url . '?' . $qs . '">' . $label . '</a>'; } $out .= $label; } $out .= "</th>\n"; } $out .= "</tr>\n"; } // SEARCH FILTERS if (!$this->_opt_isset($options, 'nofilters')) { // for AJAX: $out .= '<form method="get" action="'.$grid_url.'" onSubmit="return grid_submit(this);">'; $out .= '<form method="get" action="' . $grid_url . '">'; // propagate GET vars foreach ($_GET as $k => $v) { // ignore pagination vars if ($k == 'p_p' || $k == 'p_pp') { continue; } $out .= $this->depends->form->hidden($k, $v, array('id' => "{$guid}_1_{$k}")); } $out .= "<tr class=\"filter\">\n"; $i = 0; foreach ($params['columns'] as $name => $column) { $style = array(); $class = array(); $out .= '<th'; if (++$i == count($params['columns'])) { $style[] = 'border-right:none'; } if ($_GET['s_f'] == $name) { $class[] = 'hover'; } if (preg_match('|^_MULTI_|', $name)) { $style[] = 'text-align:center'; } $out .= ' class="' . implode(' ', $class) . '"'; $out .= ' style="' . implode(';', $style) . '">'; if (preg_match('|^_OPTIONS_|', $name)) { if ($name == '_OPTIONS_' && !$this->_opt_isset($options, 'nofilters') && !$this->_opt_isset($options, 'nofilterbutton')) { $out .= $this->depends->form->submit('filter_submit', __('Filter'), array('style' => 'width:auto')) . "</th>\n"; } else { $out .= ' '; } continue; } if (preg_match('|^_MULTI_|', $name)) { $mname = strtolower(mb_substr($name, 7)); if ($mname) { $mname .= '_'; } $out .= $this->depends->form->checkbox("_{$mname}all", 'all', '', false, array('style' => 'width:auto;border:none', 'onClick' => "var c=this.checked; \$('#{$guid} input[@type=checkbox][@name^={$mname}ids]').attr('checked',c?'checked':'')")) . "</th>\n"; continue; } /* XXX: disabled, can't remember why this was here... if(isset($column['cb_fn'])) { // no filters for special display callback functions $out .= " </th>\n"; continue; } */ if ($column['type'] == 'none') { $elem = ''; } else { $t = mb_substr($column['type'], 0, 1); if ($t == '') { $t = 't'; } $expr = isset($column['expr']) ? 'f_' . $t . '_' . $column['expr'] : 'f_' . $t . '_' . $name; $opts = array('' => ''); if (is_array($column['options'])) { foreach ($column['options'] as $k => $v) { $opts[$k] = $v; } } if (is_array($column['options_nokeys'])) { foreach ($column['options_nokeys'] as $v) { $opts[$v] = $v; } } switch (true) { case isset($column['filter_attribs']): $attribs = $column['filter_attribs']; break; // backwards compatibility (deprecated) // backwards compatibility (deprecated) case isset($column['attribs']): $attribs = $column['attribs']; break; default: $attribs = array(); } switch ($column['type']) { case 'select': $elem = $this->depends->form->select($expr, $_GET[$expr], $opts, '', false, $attribs); break; case 'date': $elem = $this->depends->form->date($expr, $_GET[$expr], '%Y-%m-%d', $attribs); break; case 'text': default: $elem = $this->depends->form->text($expr, $_GET[$expr], isset($column['flength']) ? $column['flength'] : 10, 255, $attribs); } } $out .= "{$elem}</th>\n"; } $out .= "</tr>\n"; $out .= '</form>'; } $cb_vars = array(); if (isset($params['cb_vars'])) { foreach ($params['cb_vars'] as $k => $v) { // (foreach can't use references in PHP4, so we do it this way) $cb_vars[$k] =& $params['cb_vars']["{$k}"]; } } $totals = array(); if (isset($params['totals'])) { foreach ($params['totals'] as $k => $v) { $totals[$k] = 0; } } // TABLE DATA $rowct = 0; if (!count($params['data'])) { $out .= "<tr>\n"; $out .= "<td colspan=\"100%\"><p>{$params['noresults_txt']}</p></td>\n"; $out .= "</tr>\n"; } // start a new form for multiselect boxes, if needed $multi_form = false; foreach ($params['columns'] as $name => $column) { if (preg_match('|^_MULTI_|', $name)) { $multi_form = true; } } if ($multi_form) { $out .= '<form name="multi" id="multi" method="get" action="' . $grid_url . '">'; } foreach ($params['data'] as $row) { // <tr> $tr_dom_id = 'tr' . $this->guid++; $out .= '<tr id="' . $tr_dom_id . '"'; $class = array('grid-row'); if (++$rowct % 2 != 1) { $class[] = 'altrow'; } if (isset($params['rowclassfn'])) { $class[] = $params['rowclassfn']($row); } $out .= ' class="' . implode(' ', $class) . '"'; $attr = is_array($params['tr_attribs']) ? $params['tr_attribs'] : array(); if (!empty($params['tr_attribs']) && !is_array($params['tr_attribs'])) { $attr = $params['tr_attribs'](); } foreach ($attr as $k => $v) { $out .= " {$k}=\"{$v}\""; } if ($params['rowclick']) { $subs = array(); preg_match_all('|<([A-z0-9\\._-]+)>|U', $params['rowclick'], $subs); $out .= ' onClick="location.href=\'' . $params['rowclick'] . '\'.replace(\'_ID_\',\'' . $this->_getrowdata($row, $data_id) . '\')'; if (is_array($subs[1])) { foreach ($subs[1] as $s) { $out .= ".replace('<{$s}>','" . $this->_getrowdata($row, $s) . "')"; } } $out .= '"'; } $out .= '>'; // fancy highlight/hover stuff $js = "\$('#{$guid} tr').click(function(){ \$('#{$guid} tr').removeClass('selected');\$(this).addClass('selected'); });"; $js .= "\$('#{$guid} tr').mouseover(function(){ \$(this).addClass('highlight'); });"; $js .= "\$('#{$guid} tr').mouseout(function(){ \$(this).removeClass('highlight'); });"; $this->depends->html->js_run("tpTable:{$guid}:highlight", $js); // <td> foreach ($params['columns'] as $name => $column) { $out .= '<td'; if (isset($column['align'])) { $out .= ' align="' . $column['align'] . '"'; } $attr = is_array($column['td_attribs']) ? $column['td_attribs'] : array(); if (!empty($column['td_attribs']) && !is_array($column['td_attribs'])) { $attr = $column['td_attribs'](); } foreach ($attr as $k => $v) { $out .= $this->_do_subs(" {$k}=\"{$v}\"", $row, $data_id); } if (preg_match('|^_OPTIONS_|', $name)) { $out .= ' class="options">'; foreach ($column as $opt) { if (function_exists($opt)) { $out .= $opt($cb_vars, $row); } else { // perform substitutions in URL $out .= $this->_do_subs($opt, $row, $data_id); } } } else { if (preg_match('|^_MULTI_|', $name)) { $mname = strtolower(mb_substr($name, 7)); if ($mname) { $mname .= '_'; } $out .= ' class="multi">'; $out .= '<input type="checkbox" style="border:none" name="' . $mname . 'ids[]" value="' . $this->_getrowdata($row, $data_id) . '"'; if ($this->_getrowdata($row, "_m_{$mname}")) { $out .= ' checked="checked"'; } $out .= '></td>' . "\n"; } else { $out .= ' class="grid-data">'; $data = $this->_getrowdata($row, $name); if (isset($totals[$name])) { $totals[$name] += $data; } /* * Mangle row data if necessary */ if (isset($column['value_map'][$data]) || isset($column['display_map'][$data])) { // 'display_map' is deprecated // 'value_map' is preferred $data = isset($column['value_map'][$data]) ? $column['value_map'][$data] : $column['display_map'][$data]; } else { if (isset($column['cb_fn']) || isset($column['display_func']) || isset($column['row_func'])) { // 'cb_fn' and 'display_func' are deprecated // 'row_func' is preferred switch (true) { case isset($column['row_func']): $f = $column['row_func']; break; case isset($column['display_func']): $f = $column['display_func']; break; default: $f = $column['cb_fn']; break; } if (!is_callable($f)) { // It's not a function, but some inline PHP code passed through as a string. // In this case, we'll wrap it in a proper lambda function, then. // $g is an array of all global callback vars as defined in cb_vars // $d is the full data array for this row $f = create_function('$g,$d', $f); } $data = $f($cb_vars, $row); } else { if (isset($column['value_func'])) { $f = $column['value_func']; $data = $f($cb_vars, $data); } else { if (isset($column['format'])) { $data = sprintf($column['format'], $data); } else { if (isset($column['date_format'])) { if ($data == '0000-00-00') { $data = __('Never'); } else { $data = date($column['date_format'], strtotime($data)); } } } } } } $out .= $data; } } $out .= "</td>\n"; } $out .= "</tr>\n"; // this <tr> is used by grids that load subcontent via AJAX $out .= '<tr id="' . $tr_dom_id . '_form" class="ajaxcontent"'; $out .= "><td id=\"{$tr_dom_id}_form_td\" style=\"display:none;padding-left:13px\" colspan=\"100%\"></td></tr>\n"; } // TOTALS if (!$this->_opt_isset($options, 'nototals') && count($totals)) { $out .= "<tr class=\"totals\">\n"; $i = 0; foreach ($params['columns'] as $name => $column) { $out .= '<td'; if (isset($column['align'])) { $out .= ' align="' . $column['align'] . '"'; } $out .= '>'; if ($i == 0 && !isset($totals[$name])) { $out .= '<strong>' . __('Totals') . ':</strong>'; } if (isset($totals[$name])) { if (isset($params['totals'][$name]['format'])) { $totals[$name] = sprintf($params['totals'][$name]['format'], $totals[$name]); } $out .= '<strong>' . $totals[$name] . '</strong>'; } $out .= "</td>\n"; $i++; } $out .= "</tr>\n"; } // MULTI-SELECT ACTIONS if (!empty($params['data'])) { $has_multi = false; foreach ($params['columns'] as $name => $column) { if (preg_match('|^_MULTI_|', $name)) { $has_multi = true; } } if ($has_multi) { $out .= '<tr class="multi">' . "\n"; foreach ($params['columns'] as $name => $column) { $out .= '<td>'; if (preg_match('|^_MULTI_|', $name)) { foreach ($column as $action) { $out .= $action . "<br/>"; } } $out .= '</td>'; } $out .= "</tr>\n"; } } if ($multi_form) { // close multiselect form $out .= '</form>'; } // PAGINATION $numpages = 0; if (isset($params['rows'])) { $numpages = (int) ($params['rows'] / $params['perpage']); if ($params['rows'] % $params['perpage']) { $numpages++; } } if (!$this->_opt_isset($options, 'nopagination') && ($numpages > 1 || $this->_opt_isset($options, 'forcepagination'))) { $cp = $params['curpage']; $pp = $params['perpage']; $out .= "<tr class=\"pagination\">\n"; $out .= '<form name="changepp" method="get" action="' . $grid_url . '">'; // propagate everything but p_p and p_pp vars foreach ($_GET as $k => $v) { if ($k != 'p_p' && $k != 'p_pp') { $out .= $this->depends->form->hidden($k, $v, array('id' => "{$guid}_2_{$k}")); } } $out .= '<td colspan="100%">'; $out .= '<div style="float:right">'; $page = $this->_pagelink($cp, $cp, $pp, $grid_url); // left side if ($cp > 1) { $page = $this->_pagelink($cp - 1, $cp, $pp, $grid_url) . $page; $left = $cp - 2; if ($left > 0) { if ($left > 2) { $page = '... ' . $page; } if ($left > 1) { $page = $this->_pagelink(2, $cp, $pp, $grid_url) . $page; } $page = $this->_pagelink(1, $cp, $pp, $grid_url) . $page; } } // right side if ($cp < $numpages) { $page = $page . $this->_pagelink($cp + 1, $cp, $pp, $grid_url); $left = $numpages - $cp - 1; if ($left > 0) { if ($left > 2) { $page = $page . '... '; } if ($left > 1) { $page = $page . $this->_pagelink($numpages - 1, $cp, $pp, $grid_url); } $page = $page . $this->_pagelink($numpages, $cp, $pp, $grid_url); } } $out .= rtrim($page); $out .= '</div>'; $out .= __('Showing') . ' '; if (!isset($pp_opts[$pp])) { $pp_opts[] = $pp; sort($pp_opts); } $out .= $this->depends->form->select('p_pp', $pp, array_hash($pp_opts), '', false, array('onChange' => 'document.changepp.submit();')); $out .= " " . __('per page') . ' (' . __('total records') . ": {$params['rows']})\n"; $out .= "</td>\n"; $out .= "</form>\n"; $out .= "</tr>\n"; } $out .= "</table>\n"; return $out; }
?> <?php /** * This is a stub for testing the new Javascript version of the rich Grid. * It is still in development, and probably not ready for general use. * * But you can play with it if you want. :) <table id="grid1" class="grid"></table> <script type="text/javascript"> $(function(){ var grid = new Pronto.UI.Grid($('#grid1'), '', { options: {}, columns: { first_name: {label:'First Name'}, last_name: {label:'Last Name'}, email: {label:'Email'}, status: {label:'Status', type:'select', options:['active','pending','delete']}, access_keys: {label:'Access Keys', type:'select', options:['Admin','User']}, last_login: {label:'Last Login', type:'date'} } }); grid.build(); }); </script> <?php */ ?> <?php echo $table->build_grid(array('columns' => array('_OPTIONS_' => array('edit' => $html->link($html->image('icons/edit.gif', array('title' => __('Edit Item'), 'class' => 'ajax_action')), url('User_Admin', 'edit') . '?id=<id>'), 'delete' => $html->link($html->image('icons/delete.gif', array('title' => __('Delete Item'))), url('User_Admin', 'delete') . '?id=<id>', __('Are you sure?'))), 'first_name' => array('label' => __('First Name')), 'last_name' => array('label' => __('Last Name')), 'email' => array('label' => __('Email')), 'status' => array('label' => __('Status'), 'type' => 'select', 'options' => array_hash(array('active', 'pending', 'deleted'))), 'access_keys' => array('label' => __('Access Keys'), 'type' => 'select', 'options' => array_hash($access_keys)), 'last_login' => array('label' => __('Last Login'), 'type' => 'date', 'date_format' => 'Y-m-d'), '_MULTI_' => array('delete' => $html->button(__('Delete'), url('User', 'delete'), __('Are you sure?'), false, 'multi'))), 'data' => $data, 'perpage' => $perpage, 'curpage' => $curpage, 'rows' => $totalrows));