public function execute(array &$param_pool = null)
 {
     $result = new XMLElement($this->dsParamROOTELEMENT);
     $type_sql = $parent_sql = null;
     if (trim($this->dsParamFILTERS['type']) != '') {
         $type_sql = $this->__processNavigationTypeFilter($this->dsParamFILTERS['type'], Datasource::determineFilterType($this->dsParamFILTERS['type']));
     }
     if (trim($this->dsParamFILTERS['parent']) != '') {
         $parent_sql = $this->__processNavigationParentFilter($this->dsParamFILTERS['parent']);
     }
     // Build the Query appending the Parent and/or Type WHERE clauses
     $pages = Symphony::Database()->fetch(sprintf("SELECT DISTINCT p.id, p.title, p.handle, (SELECT COUNT(id) FROM `tbl_pages` WHERE parent = p.id) AS children\n            FROM `tbl_pages` AS p\n            LEFT JOIN `tbl_pages_types` AS pt ON (p.id = pt.page_id)\n            WHERE 1 = 1\n            %s\n            %s\n            ORDER BY p.`sortorder` ASC", !is_null($parent_sql) ? $parent_sql : " AND p.parent IS null ", !is_null($type_sql) ? $type_sql : ""));
     if (!is_array($pages) || empty($pages)) {
         if ($this->dsParamREDIRECTONEMPTY === 'yes') {
             throw new FrontendPageNotFoundException();
         }
         $result->appendChild($this->__noRecordsFound());
     } else {
         // Build an array of all the types so that the page's don't have to do
         // individual lookups.
         $page_types = PageManager::fetchAllPagesPageTypes();
         foreach ($pages as $page) {
             $result->appendChild($this->__buildPageXML($page, $page_types));
         }
     }
     return $result;
 }
 public function __viewIndex()
 {
     if (!($section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']))) {
         Administration::instance()->throwCustomError(__('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')), __('Unknown Section'), Page::HTTP_STATUS_NOT_FOUND);
     } else {
         if (!is_writable(CONFIG)) {
             $this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.', array('<code>/manifest/config.php</code>')), Alert::NOTICE);
         }
     }
     $section = SectionManager::fetch($section_id);
     $this->setPageType('table');
     $this->setTitle(__('%1$s &ndash; %2$s', array($section->get('name'), __('Symphony'))));
     $filters = array();
     $filter_querystring = $prepopulate_querystring = $where = $joins = null;
     $current_page = isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1;
     if (isset($_REQUEST['filter'])) {
         // legacy implementation, convert single filter to an array
         // split string in the form ?filter=handle:value
         if (!is_array($_REQUEST['filter'])) {
             list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2);
             $filters[$field_handle] = rawurldecode($filter_value);
         } else {
             $filters = $_REQUEST['filter'];
         }
         foreach ($filters as $handle => $value) {
             // Handle multiple values through filtering. RE: #2290
             if (is_array($value) && empty($value) || trim($value) == '') {
                 continue;
             }
             if (!is_array($value)) {
                 $filter_type = Datasource::determineFilterType($value);
                 $value = preg_split('/' . ($filter_type == Datasource::FILTER_AND ? '\\+' : '(?<!\\\\),') . '\\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
                 $value = array_map('trim', $value);
                 $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
             }
             // Handle date meta data #2003
             $handle = Symphony::Database()->cleanValue($handle);
             if (in_array($handle, array('system:creation-date', 'system:modification-date'))) {
                 $date_joins = '';
                 $date_where = '';
                 $date = new FieldDate();
                 $date->buildDSRetrievalSQL($value, $date_joins, $date_where, $filter_type == Datasource::FILTER_AND ? true : false);
                 // Replace the date field where with the `creation_date` or `modification_date`.
                 $date_where = preg_replace('/`t\\d+`.date/', $field_id !== 'system:modification-date' ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where);
                 $where .= $date_where;
             } else {
                 // Handle normal fields
                 $field_id = FieldManager::fetchFieldIDFromElementName($handle, $section->get('id'));
                 $field = FieldManager::fetch($field_id);
                 if ($field instanceof Field) {
                     $field->buildDSRetrievalSQL($value, $joins, $where, $filter_type == Datasource::FILTER_AND ? true : false);
                     $value = implode(',', $value);
                     $encoded_value = rawurlencode($value);
                     $filter_querystring .= sprintf("filter[%s]=%s&amp;", $handle, $encoded_value);
                     // Some fields require that prepopulation be done via ID. RE: #2331
                     if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) {
                         $encoded_value = $field->fetchIDfromValue($value);
                     }
                     $prepopulate_querystring .= sprintf("prepopulate[%d]=%s&amp;", $field_id, $encoded_value);
                 } else {
                     unset($filters[$handle]);
                 }
             }
         }
         $filter_querystring = preg_replace("/&amp;\$/", '', $filter_querystring);
         $prepopulate_querystring = preg_replace("/&amp;\$/", '', $prepopulate_querystring);
     }
     Sortable::initialize($this, $entries, $sort, $order, array('current-section' => $section, 'filters' => $filter_querystring ? "&amp;" . $filter_querystring : '', 'unsort' => isset($_REQUEST['unsort'])));
     $this->Form->setAttribute('action', Administration::instance()->getCurrentPageURL() . '?pg=' . $current_page . ($filter_querystring ? "&amp;" . $filter_querystring : ''));
     // Build filtering interface
     $this->createFilteringInterface();
     $subheading_buttons = array(Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL() . 'new/' . ($prepopulate_querystring ? '?' . $prepopulate_querystring : ''), __('Create a new entry'), 'create button', null, array('accesskey' => 'c')));
     // Only show the Edit Section button if the Author is a developer. #938 ^BA
     if (Symphony::Author()->isDeveloper()) {
         array_unshift($subheading_buttons, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
     }
     $this->appendSubheading($section->get('name'), $subheading_buttons);
     /**
      * Allows adjustments to be made to the SQL where and joins statements
      * before they are used to fetch the entries for the page
      *
      * @delegate AdjustPublishFiltering
      * @since Symphony 2.3.3
      * @param string $context
      * '/publish/'
      * @param integer $section_id
      * An array of the current columns, passed by reference
      * @param string $where
      * The current where statement, or null if not set
      * @param string $joins
      */
     Symphony::ExtensionManager()->notifyMembers('AdjustPublishFiltering', '/publish/', array('section-id' => $section_id, 'where' => &$where, 'joins' => &$joins));
     // Check that the filtered query fails that the filter is dropped and an
     // error is logged. #841 ^BA
     try {
         $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), $where, $joins, true);
     } catch (DatabaseException $ex) {
         $this->pageAlert(__('An error occurred while retrieving filtered entries. Showing all entries instead.'), Alert::ERROR);
         $filter_querystring = null;
         Symphony::Log()->pushToLog(sprintf('%s - %s%s%s', $section->get('name') . ' Publish Index', $ex->getMessage(), $ex->getFile() ? " in file " . $ex->getFile() : null, $ex->getLine() ? " on line " . $ex->getLine() : null), E_NOTICE, true);
         $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'));
     }
     // Flag filtering
     if (isset($_REQUEST['filter'])) {
         $filter_stats = new XMLElement('p', '<span>– ' . __('%d of %d entries (filtered)', array($entries['total-entries'], EntryManager::fetchCount($section_id))) . '</span>', array('class' => 'inactive'));
     } else {
         $filter_stats = new XMLElement('p', '<span>– ' . __('%d entries', array($entries['total-entries'])) . '</span>', array('class' => 'inactive'));
     }
     $this->Breadcrumbs->appendChild($filter_stats);
     // Build table
     $visible_columns = $section->fetchVisibleColumns();
     $columns = array();
     if (is_array($visible_columns) && !empty($visible_columns)) {
         foreach ($visible_columns as $column) {
             $columns[] = array('label' => $column->get('label'), 'sortable' => $column->isSortable(), 'handle' => $column->get('id'), 'attrs' => array('id' => 'field-' . $column->get('id'), 'class' => 'field-' . $column->get('type')));
         }
     } else {
         $columns[] = array('label' => __('ID'), 'sortable' => true, 'handle' => 'id');
     }
     $aTableHead = Sortable::buildTableHeaders($columns, $sort, $order, $filter_querystring ? "&amp;" . $filter_querystring : '');
     $child_sections = array();
     $associated_sections = $section->fetchChildAssociations(true);
     if (is_array($associated_sections) && !empty($associated_sections)) {
         foreach ($associated_sections as $key => $as) {
             $child_sections[$key] = SectionManager::fetch($as['child_section_id']);
             $aTableHead[] = array($child_sections[$key]->get('name'), 'col');
         }
     }
     /**
      * Allows the creation of custom table columns for each entry. Called
      * after all the Section Visible columns have been added as well
      * as the Section Associations
      *
      * @delegate AddCustomPublishColumn
      * @since Symphony 2.2
      * @param string $context
      * '/publish/'
      * @param array $tableHead
      * An array of the current columns, passed by reference
      * @param integer $section_id
      * The current Section ID
      */
     Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/', array('tableHead' => &$aTableHead, 'section_id' => $section->get('id')));
     // Table Body
     $aTableBody = array();
     if (!is_array($entries['records']) || empty($entries['records'])) {
         $aTableBody = array(Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd'));
     } else {
         $field_pool = array();
         if (is_array($visible_columns) && !empty($visible_columns)) {
             foreach ($visible_columns as $column) {
                 $field_pool[$column->get('id')] = $column;
             }
         }
         $link_column = array_reverse($visible_columns);
         $link_column = end($link_column);
         reset($visible_columns);
         foreach ($entries['records'] as $entry) {
             $tableData = array();
             // Setup each cell
             if (!is_array($visible_columns) || empty($visible_columns)) {
                 $tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'), Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'));
             } else {
                 $link = Widget::Anchor('', Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/' . ($filter_querystring ? '?' . $prepopulate_querystring : ''), $entry->get('id'), 'content');
                 foreach ($visible_columns as $position => $column) {
                     $data = $entry->getData($column->get('id'));
                     $field = $field_pool[$column->get('id')];
                     $value = $field->prepareTableValue($data, $column == $link_column ? $link : null, $entry->get('id'));
                     if (!is_object($value) && (strlen(trim($value)) == 0 || $value == __('None'))) {
                         $value = $position == 0 ? $link->generate() : __('None');
                     }
                     if ($value == __('None')) {
                         $tableData[] = Widget::TableData($value, 'inactive field-' . $column->get('type') . ' field-' . $column->get('id'));
                     } else {
                         $tableData[] = Widget::TableData($value, 'field-' . $column->get('type') . ' field-' . $column->get('id'));
                     }
                     unset($field);
                 }
             }
             if (is_array($child_sections) && !empty($child_sections)) {
                 foreach ($child_sections as $key => $as) {
                     $field = FieldManager::fetch((int) $associated_sections[$key]['child_section_field_id']);
                     $parent_section_field_id = (int) $associated_sections[$key]['parent_section_field_id'];
                     if (!is_null($parent_section_field_id)) {
                         $search_value = $field->fetchAssociatedEntrySearchValue($entry->getData($parent_section_field_id), $parent_section_field_id, $entry->get('id'));
                     } else {
                         $search_value = $entry->get('id');
                     }
                     if (!is_array($search_value)) {
                         $associated_entry_count = $field->fetchAssociatedEntryCount($search_value);
                         $tableData[] = Widget::TableData(Widget::Anchor(sprintf('%d &rarr;', max(0, intval($associated_entry_count))), sprintf('%s/publish/%s/?filter[%s]=%s', SYMPHONY_URL, $as->get('handle'), $field->get('element_name'), rawurlencode($search_value)), $entry->get('id'), 'content'));
                     }
                 }
             }
             /**
              * Allows Extensions to inject custom table data for each Entry
              * into the Publish Index
              *
              * @delegate AddCustomPublishColumnData
              * @since Symphony 2.2
              * @param string $context
              * '/publish/'
              * @param array $tableData
              *  An array of `Widget::TableData`, passed by reference
              * @param integer $section_id
              *  The current Section ID
              * @param Entry $entry_id
              *  The entry object, please note that this is by error and this will
              *  be removed in Symphony 2.4. The entry object is available in
              *  the 'entry' key as of Symphony 2.3.1.
              * @param Entry $entry
              *  The entry object for this row
              */
             Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array('tableData' => &$tableData, 'section_id' => $section->get('id'), 'entry_id' => $entry, 'entry' => $entry));
             $tableData[count($tableData) - 1]->appendChild(Widget::Label(__('Select Entry %d', array($entry->get('id'))), null, 'accessible', null, array('for' => 'entry-' . $entry->get('id'))));
             $tableData[count($tableData) - 1]->appendChild(Widget::Input('items[' . $entry->get('id') . ']', null, 'checkbox', array('id' => 'entry-' . $entry->get('id'))));
             // Add a row to the body array, assigning each cell to the row
             $aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id'));
         }
     }
     $table = Widget::Table(Widget::TableHead($aTableHead), null, Widget::TableBody($aTableBody), 'selectable', null, array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive'));
     $this->Form->appendChild($table);
     $tableActions = new XMLElement('div');
     $tableActions->setAttribute('class', 'actions');
     $options = array(array(null, false, __('With Selected...')), array('delete', false, __('Delete'), 'confirm', null, array('data-message' => __('Are you sure you want to delete the selected entries?'))));
     $toggable_fields = $section->fetchToggleableFields();
     if (is_array($toggable_fields) && !empty($toggable_fields)) {
         $index = 2;
         foreach ($toggable_fields as $field) {
             $toggle_states = $field->getToggleStates();
             if (is_array($toggle_states)) {
                 $options[$index] = array('label' => __('Set %s', array($field->get('label'))), 'options' => array());
                 foreach ($toggle_states as $value => $state) {
                     $options[$index]['options'][] = array('toggle-' . $field->get('id') . '-' . $value, false, $state);
                 }
             }
             $index++;
         }
     }
     /**
      * Allows an extension to modify the existing options for this page's
      * With Selected menu. If the `$options` parameter is an empty array,
      * the 'With Selected' menu will not be rendered.
      *
      * @delegate AddCustomActions
      * @since Symphony 2.3.2
      * @param string $context
      * '/publish/'
      * @param array $options
      *  An array of arrays, where each child array represents an option
      *  in the With Selected menu. Options should follow the same format
      *  expected by `Widget::__SelectBuildOption`. Passed by reference.
      */
     Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array('options' => &$options));
     if (!empty($options)) {
         $tableActions->appendChild(Widget::Apply($options));
         $this->Form->appendChild($tableActions);
     }
     if ($entries['total-pages'] > 1) {
         $ul = new XMLElement('ul');
         $ul->setAttribute('class', 'page');
         // First
         $li = new XMLElement('li');
         if ($current_page > 1) {
             $li->appendChild(Widget::Anchor(__('First'), Administration::instance()->getCurrentPageURL() . '?pg=1' . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
         } else {
             $li->setValue(__('First'));
         }
         $ul->appendChild($li);
         // Previous
         $li = new XMLElement('li');
         if ($current_page > 1) {
             $li->appendChild(Widget::Anchor(__('&larr; Previous'), Administration::instance()->getCurrentPageURL() . '?pg=' . ($current_page - 1) . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
         } else {
             $li->setValue(__('&larr; Previous'));
         }
         $ul->appendChild($li);
         // Summary
         $li = new XMLElement('li');
         $li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', array($entries['start'], $current_page != $entries['total-pages'] ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows', 'symphony') : $entries['total-entries'], $entries['total-entries'])));
         $pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform');
         $pgmax = max($current_page, $entries['total-pages']);
         $pgform->appendChild(Widget::Input('pg', null, 'text', array('data-active' => __('Go to page …'), 'data-inactive' => __('Page %1$s of %2$s', array((string) $current_page, $pgmax)), 'data-max' => $pgmax)));
         $li->appendChild($pgform);
         $ul->appendChild($li);
         // Next
         $li = new XMLElement('li');
         if ($current_page < $entries['total-pages']) {
             $li->appendChild(Widget::Anchor(__('Next &rarr;'), Administration::instance()->getCurrentPageURL() . '?pg=' . ($current_page + 1) . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
         } else {
             $li->setValue(__('Next &rarr;'));
         }
         $ul->appendChild($li);
         // Last
         $li = new XMLElement('li');
         if ($current_page < $entries['total-pages']) {
             $li->appendChild(Widget::Anchor(__('Last'), Administration::instance()->getCurrentPageURL() . '?pg=' . $entries['total-pages'] . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
         } else {
             $li->setValue(__('Last'));
         }
         $ul->appendChild($li);
         $this->Contents->appendChild($ul);
     }
 }
 /**
  * This function iterates over `dsParamFILTERS` and builds the relevant
  * `$where` and `$joins` parameters with SQL. This SQL is generated from
  * `Field->buildDSRetrievalSQL`. A third parameter, `$group` is populated
  * with boolean from `Field->requiresSQLGrouping()`
  *
  * @param string $where
  * @param string $joins
  * @param boolean $group
  * @throws Exception
  */
 public function processFilters(&$where, &$joins, &$group)
 {
     if (!is_array($this->dsParamFILTERS) || empty($this->dsParamFILTERS)) {
         return;
     }
     $pool = FieldManager::fetch(array_filter(array_keys($this->dsParamFILTERS), 'is_int'));
     self::$_fieldPool += $pool;
     if (!is_string($where)) {
         $where = '';
     }
     foreach ($this->dsParamFILTERS as $field_id => $filter) {
         if (is_array($filter) && empty($filter) || trim($filter) == '') {
             continue;
         }
         if (!is_array($filter)) {
             $filter_type = Datasource::determineFilterType($filter);
             $value = preg_split('/' . ($filter_type == Datasource::FILTER_AND ? '\\+' : '(?<!\\\\),') . '\\s*/', $filter, -1, PREG_SPLIT_NO_EMPTY);
             $value = array_map('trim', $value);
             $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
         } else {
             $value = $filter;
         }
         if (!in_array($field_id, self::$_system_parameters) && $field_id != 'id' && !self::$_fieldPool[$field_id] instanceof Field) {
             throw new Exception(__('Error creating field object with id %1$d, for filtering in data source %2$s. Check this field exists.', array($field_id, '<code>' . $this->dsParamROOTELEMENT . '</code>')));
         }
         // Support system:id as well as the old 'id'. #1691
         if ($field_id === 'system:id' || $field_id === 'id') {
             $c = 'IN';
             if (stripos($value[0], 'not:') === 0) {
                 $value[0] = preg_replace('/^not:\\s*/', null, $value[0]);
                 $c = 'NOT IN';
             }
             // Cast all ID's to integers. (RE: #2191)
             $value = array_map(function ($val) {
                 $val = General::intval($val);
                 // General::intval can return -1, so reset that to 0
                 // so there are no side effects for the following
                 // array_sum and array_filter calls. RE: #2475
                 if ($val === -1) {
                     $val = 0;
                 }
                 return $val;
             }, $value);
             $count = array_sum($value);
             $value = array_filter($value);
             // If the ID was cast to 0, then we need to filter on 'id' = 0,
             // which will of course return no results, but without it the
             // Datasource will return ALL results, which is not the
             // desired behaviour. RE: #1619
             if ($count === 0) {
                 $value[] = 0;
             }
             // If there are no ID's, no need to filter. RE: #1567
             if (!empty($value)) {
                 $where .= " AND `e`.id " . $c . " (" . implode(", ", $value) . ") ";
             }
         } elseif ($field_id === 'system:creation-date' || $field_id === 'system:modification-date' || $field_id === 'system:date') {
             $date_joins = '';
             $date_where = '';
             $date = new FieldDate();
             $date->buildDSRetrievalSQL($value, $date_joins, $date_where, $filter_type == Datasource::FILTER_AND ? true : false);
             // Replace the date field where with the `creation_date` or `modification_date`.
             $date_where = preg_replace('/`t\\d+`.date/', $field_id !== 'system:modification-date' ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where);
             $where .= $date_where;
         } else {
             if (!self::$_fieldPool[$field_id]->buildDSRetrievalSQL($value, $joins, $where, $filter_type == Datasource::FILTER_AND ? true : false)) {
                 $this->_force_empty_result = true;
                 return;
             }
             if (!$group) {
                 $group = self::$_fieldPool[$field_id]->requiresSQLGrouping();
             }
         }
     }
 }