Пример #1
0
 /**
  * Generate SQL to enumerate the records of an entity, returning the resulting
  * rows and related pagination data.
  *
  * @param array $params Parameters that will be used to generate the resulting SQL.
  *                      These parameters are typically provided by the entity's model,
  *                      usually in a method called list_params().
  * @param boolean $set_vars Automatically set template variables: (data, totalrows, curpage, perpage)
  * @return array An array of: (data, total rows, current page, records per page)
  */
 function enumerate($params, $set_vars = false)
 {
     // move the list parameters into the local namespace
     //   (from,exprs,gexprs,select,where,group_by,having,order,limit)
     extract($params, EXTR_OVERWRITE);
     assert_type($exprs, 'array');
     assert_type($gexprs, 'array');
     foreach ($exprs as $k => $v) {
         $select .= ",{$v} \"{$k}\"";
     }
     foreach ($gexprs as $k => $v) {
         $select .= ",{$v} \"{$k}\"";
     }
     // if $params[select] was empty, then we have a leading comma...
     if (substr($select, 0, 1) == ',') {
         $select = substr($select, 1);
     }
     // Build WHERE/HAVING clauses from list params and
     // criteria sent from the browser
     list($w_sql, $w_args, $h_sql, $h_args) = $this->filter($exprs, $gexprs);
     // WHERE
     if (is_array($where)) {
         foreach ($where as $v) {
             $w_sql .= " AND ({$v})";
         }
     } else {
         if (!empty($where)) {
             $w_sql .= " AND ({$where})";
         }
     }
     // backwards compatibility
     if (!empty($where_args)) {
         $w_args = array_merge($w_args, $where_args);
     }
     // HAVING
     if (is_array($having)) {
         foreach ($having as $v) {
             $h_sql .= " AND ({$v})";
         }
     } else {
         if (!empty($having)) {
             $h_sql .= " AND ({$having})";
         }
     }
     // Merge all SQL args if necessary ($h_args could be empty)
     $args = empty($h_args) ? $w_args : array_merge($w_args, $h_args);
     // ORDER/LIMIT
     $sort_sql = $this->sort($order, $exprs);
     $page_sql = $this->paginate($limit);
     // Get data rows
     if ($this->db->type == 'mysql') {
         $select = "SQL_CALC_FOUND_ROWS {$select}";
     }
     $sql = $this->db->build_sql($select, $from, $w_sql, $group_by, $h_sql, $sort_sql, $page_sql);
     $data = $this->db->get_all($sql, $args);
     // Count all matching rows
     if ($this->db->type == 'mysql') {
         $ttlrows = $this->db->get_value("SELECT FOUND_ROWS()");
     } else {
         $ttlrows = $this->db->get_value($this->db->build_sql("COUNT(*)", $from, $w_sql, $group_by, $h_sql), $args);
     }
     if ($set_vars) {
         $this->page->template->set('data', $data);
         $this->page->template->set('totalrows', $ttlrows);
         $this->page->template->set('curpage', $this->page->param('p_p', 1));
         $this->page->template->set('perpage', $this->page->param('p_pp', $limit));
     }
     // data, total rows, current page, per page
     return array($data, $ttlrows, $this->page->param('p_p', 1), $this->page->param('p_pp', $limit));
 }
Пример #2
0
    /**
     * Generate a multi-column form.
     *
     * @param array $params General form parameters
     * @param array $data Values to prefill form elements with
     * @param array $errors Errors to attach to elements containing invalid data
     *
     * Form Parameters:
     *   - form_name ()   :: form name
     *   - form_id ()     :: form id
     *   - class (form)   :: CSS class to use for outer <div>
     *   - action ()
     *   - method (POST)
     *   - enctype ()
     *   - data_id ()     :: PK of data element being edited
     *   - submit         :: label on submit button; can be a 2-element array: ('create','update')
     *   - submit_msg     :: change button message to this when clicked 
     *   - submit_pos     :: "left" or "right" (default is "right")
     *   - submit_html    :: if set, use this for the submit button code instead of generating it
     *   - options array
     *     - noopen           :: don't include opening div/form tags
     *     - noclose          :: don't include closing div/form tags or a Submit button
     *     - nosubmit         :: don't include a Submit button
     *     - nopk             :: don't include the name="id" hidden element
     *     - toplabel         :: put labels above elements instead of beside them
     *     - numcols          :: number of columns to use for form elements (1)
     *     - hide_misc_errors :: don't show errors that aren't bound to an element in the form definition
     *     - spinner          :: image to show beside submit button when clicked
     *   - layout array
     *     - <column name> :: array(colspan<int>, label_width<int|"auto">, width<int>)
     *   - elements array
     *     - <name> => array
     *       - prompt     :: label to display beside form element
     *       - type       :: element type (see the _show_element() method for types)
     *       - value      :: value of element
     *       - error      :: error message associated with element
     *       - help       :: context-specific help message (popup)
     *       - help_image :: icon image to use for help popups
     *       - after      :: content (HTML) to be added after the element
     *       - before     :: content (HTML) to be added before the element
     *       - attribs    :: array of additional html attributes
     *       - type-specific parameters (size, maxlength, rows, cols, etc)
     */
    function build_form($params, $data = array(), $errors = array())
    {
        $guid = ++$this->guid;
        $class = $this->_getparam($params, 'class', 'form');
        $method = $this->_getparam($params, 'method', 'post');
        $submit = $this->_getparam($params, 'submit', __('Save Changes'));
        $options = $this->_getparam($params, 'options', array());
        $data_id = $this->_getparam($params, 'data_id', 0);
        $layout = $this->_getparam($params, 'layout', array());
        $elements = $this->_getparam($params, 'elements', array());
        $form_id = $this->_getparam($params, 'form_id', 'form' . $guid);
        $form_name = $this->_getparam($params, 'form_name', 'form' . $guid);
        $action = $this->_getparam($params, 'action', '');
        // this is used to pass the data ID back to all form element generators; it
        // will be unset once build_form() is done so it's not accidentally used
        // for form element calls that originate outside of build_form()
        $this->data_id = $data_id;
        if (!$options['noopen']) {
            // <form>
            $form_attribs = array('id' => $form_id);
            if (isset($params['enctype'])) {
                $form_attribs['enctype'] = $params['enctype'];
            }
            $out .= $this->open_form($form_name, $action, $method, $errors, $form_attribs);
            // <div>
            $out .= '<div';
            if ($class) {
                $out .= ' class="' . $class . '"';
            }
            $out .= ">\n";
        }
        $numcols = $this->_getparam($options, 'numcols', 1);
        // if numcols is one, then we might have to massage the elements array into
        // the proper multi-column-friendly format
        $first_key = key($elements);
        $first_val = current($elements);
        if (isset($first_val['type']) && !is_array($first_val['type'])) {
            $elements = array('0' => $elements);
        }
        // if we're editing a record (as opposed to creating one), then load in
        // the PK of the data element
        if ($data_id && !$options['nopk']) {
            $elements[$first_key]['id'] = array('type' => 'hidden', 'value' => $data_id);
        }
        // check for a happy layout definition
        assert_type($layout, 'array');
        foreach ($elements as $col => $subform) {
            assert_type($layout[$col], 'array');
            if (!isset($layout[$col]['colspan'])) {
                $layout[$col]['colspan'] = 1;
            }
            if (!isset($layout[$col]['label_width'])) {
                $layout[$col]['label_width'] = 'auto';
            }
        }
        // set element layout
        $elem_layout = $options['toplabel'] ? 'toplabel' : 'leftlabel';
        // Prefill form values and errors
        foreach ($elements as $column => $elems) {
            foreach ($elems as $name => $elem) {
                if (isset($elem['value']) && $elem['type'] == 'checkbox') {
                    if ($data[$name] == $elem['value']) {
                        $elements[$column][$name]['checked'] = true;
                    }
                } else {
                    if (!isset($elem['value']) && isset($data[$name])) {
                        // don't prefill a date field if the value is '0000-00-00'
                        if (!($elements[$column][$name]['type'] == 'date' && $data[$name] == '0000-00-00')) {
                            $elements[$column][$name]['value'] = $data[$name];
                        }
                    }
                }
                unset($errors[$name]);
            }
        }
        // If there are any errors not bound to a form element, display them at
        // the top
        if (count($errors) && !$options['hide_misc_errors']) {
            $out .= $this->error_box($errors);
        }
        // form elements (hidden)
        foreach ($elements as $column => $subform) {
            foreach ($subform as $name => $elem) {
                if ($elem['type'] != 'hidden') {
                    continue;
                }
                $out .= $this->hidden($name, $elem['value']);
            }
        }
        $c_loc = 0;
        $clearnext = true;
        foreach ($elements as $col_id => $subform) {
            // the <br> fixes some float issues in IE
            if ($clearnext && $c_loc > 0) {
                $out .= '<br class="clearfix" />';
            }
            $c_loc += $layout[$col_id]['colspan'];
            if (isset($layout[$col_id]['width'])) {
                $width = $layout[$col_id]['width'];
                if (is_numeric($width)) {
                    $width .= 'px';
                }
            } else {
                $width = floor($layout[$col_id]['colspan'] / $numcols * 100) . '%';
            }
            $cls = $c_loc % $numcols == 0 ? ' dynsize' : '';
            if ($layout[$col_id]['colspan']) {
                $cls .= " {$layout[$col_id]['class']}";
            }
            $out .= '<div class="subform' . $cls . '" style="width:' . $width . ';';
            if ($clearnext) {
                $out .= 'clear:left;';
                $clearnext = false;
            } else {
                if ($c_loc % $numcols == 0) {
                    // IE6 workaround: use clear:both if $numcols==1
                    $out .= $numcols == 1 ? 'clear:both' : 'clear:right;';
                    $clearnext = true;
                }
            }
            $out .= '">';
            // calculate optimal width of the label divs
            if (!isset($layout[$col_id]['label_width'])) {
                $layout[$col_id]['label_width'] = 'auto';
            }
            $lblwidth = $layout[$col_id]['label_width'];
            if ($elem_layout == 'leftlabel' && $lblwidth == 'auto') {
                if ($layout[$col_id]['label_width'] == 'auto') {
                    // find the longest label to determine width of the label divs
                    $llength = 0;
                    foreach ($subform as $name => $elem) {
                        if ($elem['type'] == 'label') {
                            continue;
                        }
                        if (!isset($elem['prompt'])) {
                            continue;
                        }
                        $s = mb_strlen(strip_tags($elem['prompt']));
                        if ($s > $llength) {
                            $llength = $s;
                        }
                    }
                    $lblwidth = $llength . 'em';
                }
            }
            if (is_numeric(substr($lblwidth, -1, 1))) {
                $lblwidth .= 'px';
            }
            foreach ($subform as $name => $elem) {
                if ($elem['type'] == 'hidden') {
                    continue;
                }
                $subvars = array('help' => '', 'label' => '', 'element' => '', 'error' => '', 'lblwidth' => $lblwidth);
                $attribs = $this->_getparam($elem, 'attribs', array());
                // set the "error" class if validation failed
                if (isset($elem['error'])) {
                    $attribs['class'] = 'error';
                }
                // set the tabindex relative to the column we're in
                $attribs['tabindex'] = $c_loc;
                // tooltip
                if (isset($elem['help'])) {
                    $img = isset($elem['help_image']) ? $elem['help_image'] : '';
                    $subvars['help'] = $this->tooltip($elem['help'], $img);
                    if ($elem_layout == 'toplabel') {
                        $subvars['help'] .= ' ';
                    }
                } else {
                    if ($elem_layout == 'leftlabel') {
                        $subvars['help'] = '&nbsp;';
                    }
                }
                // label, element, error
                if (empty($elem['prompt'])) {
                    $elem['prompt'] = '&nbsp;';
                }
                $subvars['label'] = '<label for="' . $name . '">' . $elem['prompt'] . '</label>';
                $subvars['element'] = $this->_show_element($name, $elem, $attribs);
                if (!empty($elem['error'])) {
                    $subvars['error'] = '<br /><p class="error">' . $elem['error'] . '</p>';
                }
                if ($elem['type'] == 'label') {
                    $el = $this->element_layouts['labelonly'];
                } else {
                    if ($elem['nolabel'] == true) {
                        $el = $this->element_layouts['elemonly'];
                    } else {
                        $el = $this->element_layouts[$elem_layout];
                    }
                }
                // sub in vars and output this element
                $out .= str_replace(array('{{HELP}}', '{{LABEL}}', '{{ELEMENT}}', '{{ERROR}}', '{{LBLWIDTH}}'), $subvars, $el);
            }
            $out .= "</div>\n";
        }
        if (!$options['noclose'] && !$options['nosubmit']) {
            $c = array_shift(explode(' ', $class));
            if (empty($params['submit_pos']) || $params['submit_pos'] == 'right') {
                // TODO: This is ugly, fix it.
                $js = <<<EOT
var elem_width = 0;
try { var parent = \$('#{$form_id} div.{$c}').position(); } catch(err) { return; }
\$('#{$form_id} div.dynsize').find('div.form_element').each(function(){
\tvar pos = \$(this).position().left + \$(this).width() - parent.left;
\tif(pos > elem_width) elem_width = pos;
});
\$('#{$form_id} div.form_submit').css('width', elem_width+'px');
EOT;
                $this->depends->html->js_run('', $js);
            }
            $out .= '<div class="form_submit">';
            // If submit_html was used, output the contents directly.  Otherwise
            // build a submit button ourselves.
            if (isset($params['submit_html'])) {
                $out .= $params['submit_html'];
            } else {
                if (!empty($submit)) {
                    $opts = array();
                    if (isset($options['spinner'])) {
                        $opts['onClick'] .= "\$(this).prev('img.spinner').css('display','inline');";
                        $out .= $this->depends->html->image($options['spinner'], array('class' => 'spinner', 'align' => 'top', 'style' => 'display:none'));
                    }
                    if (isset($params['submit_msg'])) {
                        $opts['onClick'] .= "this.value='{$params['submit_msg']}';this.disabled=true;this.form.submit();return false;";
                    }
                    if (is_array($submit)) {
                        if ($data_id) {
                            $out .= $this->submit('submit_btn' . $guid, $submit[1], $opts);
                        } else {
                            $out .= $this->submit('submit_btn' . $guid, $submit[0], $opts);
                        }
                    } else {
                        $out .= $this->submit('submit_btn' . $guid, $submit, $opts);
                    }
                }
            }
            $out .= "</div>\n";
        }
        if (!$options['noclose']) {
            $out .= "</div>\n";
            $out .= $this->close_form();
        }
        $this->data_id = 0;
        return $out;
    }
Пример #3
0
 /**
  * Return multiple records by PK, optionally ignoring empty/false records.
  *
  * @param array $ids
  * @param boolean $ignore_empty Ignore empty records.
  * @return array
  */
 function load_all($ids = null, $ignore_empty = true)
 {
     if (is_null($ids)) {
         $ids = $this->find()->get($this->pk);
     }
     assert_type($ids, 'array');
     $data = array();
     foreach ($ids as $id) {
         $rec = $this->load($id);
         if ($rec || !$ignore_empty) {
             $data[] = $rec;
         }
     }
     return $data;
 }
Пример #4
0
 function _get_ids()
 {
     if (!empty($this->id_cache)) {
         return $this->id_cache;
     }
     $ids = $this->get('id');
     assert_type($ids, 'array');
     $this->id_cache = $ids;
     return $ids;
 }
Пример #5
0
 /**
  * Used by form handlers to redirect back to the calling form after a
  * failed validation or other errors.
  *
  * @param string $url    URL to redirect to (leave blank to use referrer)
  * @param array $data    Form data array
  * @param array $errors  Form errors array
  * @param array $name    The unique names used for the form data and errors.
  *                       These are used when passing the data back to the
  *                       template. Use this if you're handling multiple
  *                       forms on one page. The first element is the name
  *                       of the form data array, the second is the name
  *                       of the form errors array.
  */
 function return_to_form($url, $data, $errors = array(), $name = array('data', 'errors'))
 {
     if ($this->ajax) {
         $vars = array($name[1] => $errors);
         $this->ajax_render('', $vars);
     } else {
         assert_type($_SESSION['form_data'], 'array');
         assert_type($_SESSION['form_errors'], 'array');
         $_SESSION['form_data'][$name[0]] = $data;
         $_SESSION['form_errors'][$name[1]] = $errors;
         if ($url) {
             $this->redirect($url);
         } else {
             $this->redirect_to_referrer();
         }
     }
 }