diff --git a/README.markdown b/README.markdown
index 605e0a1..c47ed05 100644
--- a/README.markdown
+++ b/README.markdown
@@ -10,8 +10,12 @@ For older versions of EE use JSON version [1.0.3](https://github.com/rsanchez/js
## Installation
+#### EE2
* Copy the /system/expressionengine/third_party/json/ folder to your /system/expressionengine/third_party/ folder
+#### EE3
+* Copy the /ee/user/addons/json/ folder to your /ee/user/addons/ folder
+
## Global Parameters
### `xhr="yes"`
diff --git a/system/expressionengine/third_party/json/libraries/Json_Template.php b/ee2/system/expressionengine/third_party/json/libraries/Json_Template.php
similarity index 100%
rename from system/expressionengine/third_party/json/libraries/Json_Template.php
rename to ee2/system/expressionengine/third_party/json/libraries/Json_Template.php
diff --git a/system/expressionengine/third_party/json/libraries/Jsonp.php b/ee2/system/expressionengine/third_party/json/libraries/Jsonp.php
similarity index 100%
rename from system/expressionengine/third_party/json/libraries/Jsonp.php
rename to ee2/system/expressionengine/third_party/json/libraries/Jsonp.php
diff --git a/system/expressionengine/third_party/json/pi.json.php b/ee2/system/expressionengine/third_party/json/pi.json.php
similarity index 100%
rename from system/expressionengine/third_party/json/pi.json.php
rename to ee2/system/expressionengine/third_party/json/pi.json.php
diff --git a/ee3/ee/user/addons/json/.DS_Store b/ee3/ee/user/addons/json/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/ee3/ee/user/addons/json/.DS_Store differ
diff --git a/ee3/ee/user/addons/json/addon.setup.php b/ee3/ee/user/addons/json/addon.setup.php
new file mode 100644
index 0000000..f183fe9
--- /dev/null
+++ b/ee3/ee/user/addons/json/addon.setup.php
@@ -0,0 +1,12 @@
+ 'rsanchez',
+ 'author_url' => 'https://github.com/rsanchez/json',
+ 'description' => 'Output ExpressionEngine data in JSON format.',
+ 'docs_url' => 'https://github.com/rsanchez/json',
+ 'name' => 'Json',
+ 'settings_exist' => false,
+ 'version' => '1.0.0',
+ 'namespace' => 'json/Json'
+);
\ No newline at end of file
diff --git a/ee3/ee/user/addons/json/libraries/Json_Template.php b/ee3/ee/user/addons/json/libraries/Json_Template.php
new file mode 100644
index 0000000..be9c123
--- /dev/null
+++ b/ee3/ee/user/addons/json/libraries/Json_Template.php
@@ -0,0 +1,45 @@
+TMPL =& ee()->TMPL;
+
+ // Override the "real" TMPL object
+ ee()->TMPL =& $this;
+ }
+
+ public function __destruct()
+ {
+ // Restore the "real" TMPL object
+ ee()->TMPL =& $this->TMPL;
+ }
+
+ public function parse_variables($tagdata, $variables, $enable_backspace = TRUE)
+ {
+ $output = parent::parse_variables($tagdata, $variables, $enable_backspace);
+ $this->variables = $variables;
+ return $output;
+ }
+
+ public function parse_variables_row($tagdata, $variables, $solo = TRUE)
+ {
+ $this->variables = $variables;
+ return parent::parse_variables_row($tagdata, $variables, $solo);
+ }
+}
\ No newline at end of file
diff --git a/ee3/ee/user/addons/json/libraries/Jsonp.php b/ee3/ee/user/addons/json/libraries/Jsonp.php
new file mode 100644
index 0000000..f7df27b
--- /dev/null
+++ b/ee3/ee/user/addons/json/libraries/Jsonp.php
@@ -0,0 +1,168 @@
+ false,
+ 'hello' => true,
+ 'alert()' => false,
+ 'test()' => false,
+ 'a-b' => false,
+ '23foo' => false,
+ 'foo23' => true,
+ '$210' => true,
+ '_bar' => true,
+ 'some_var' => true,
+ '$' => true,
+ 'somevar' => true,
+ 'function' => false,
+ ' somevar' => false,
+ '$.ajaxHandler' => true,
+ '$.23' => false,
+ 'array_of_functions[42]' => true,
+ 'array_of_functions[42][1]' => true,
+ '$.ajaxHandler[42][1].foo' => true,
+ 'array_of_functions[42]foo[1]' => false,
+ 'array_of_functions[]' => false,
+ 'array_of_functions["key"]' => true,
+ 'myFunction[123].false' => false,
+ 'myFunction .tester' => false,
+ '_function' => true,
+ 'petersCallback1412331422[12]' => true,
+ ':myFunction' => false
+ );
+
+ /**
+ * Is valid callback
+ *
+ * @param string $callback
+ *
+ * @return boolean
+ */
+ function isValidCallback($callback)
+ {
+ $reserved = array(
+ 'break',
+ 'do',
+ 'instanceof',
+ 'typeof',
+ 'case',
+ 'else',
+ 'new',
+ 'var',
+ 'catch',
+ 'finally',
+ 'return',
+ 'void',
+ 'continue',
+ 'for',
+ 'switch',
+ 'while',
+ 'debugger',
+ 'function',
+ 'this',
+ 'with',
+ 'default',
+ 'if',
+ 'throw',
+ 'delete',
+ 'in',
+ 'try',
+ 'class',
+ 'enum',
+ 'extends',
+ 'super',
+ 'const',
+ 'export',
+ 'import',
+ 'implements',
+ 'let',
+ 'private',
+ 'public',
+ 'yield',
+ 'interface',
+ 'package',
+ 'protected',
+ 'static',
+ 'null',
+ 'true',
+ 'false'
+ );
+
+ foreach(explode('.', $callback) as $identifier) {
+ if(!preg_match('/^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/', $identifier)) {
+ return false;
+ }
+ if(in_array($identifier, $reserved)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Test callback strings
+ *
+ * @param string $callback
+ *
+ * @return void
+ *
+ * @access private
+ */
+ private function _test($callback, $valid)
+ {
+ $vocal = $valid ? 'valid' : 'invalid';
+ if($this->isValidCallback($callback) === $valid) {
+ echo '"'.$callback.'" passed as '.$vocal.'.', "\n";
+ return true;
+ }
+ else {
+ echo '"'.$callback.'" failed as '.$vocal.'.', "\n";
+ return false;
+ }
+ }
+
+ /**
+ * Run all tests
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function runTests()
+ {
+ echo 'Testing ', count($this->_tests), ' callback methods:', "\n\n";
+ $passed = 0;
+ foreach($this->_tests as $callback => $valid) {
+ $passed = self::_test($callback, $valid) ? $passed+1 : $passed;
+ }
+ echo "\n", $passed, ' of ', count($this->_tests), ' tests passed.';
+ }
+}
\ No newline at end of file
diff --git a/ee3/ee/user/addons/json/pi.json.php b/ee3/ee/user/addons/json/pi.json.php
new file mode 100644
index 0000000..3c766ff
--- /dev/null
+++ b/ee3/ee/user/addons/json/pi.json.php
@@ -0,0 +1,1126 @@
+ 'JSON',
+ 'pi_version' => '1.1.7',
+ 'pi_author' => 'Rob Sanchez',
+ 'pi_author_url' => 'https://github.com/rsanchez',
+ 'pi_description' => 'Output ExpressionEngine data in JSON format.',
+ 'pi_usage' => '
+{exp:json:entries channel="news"}
+
+{exp:json:entries channel="products" search:product_size="10"}
+
+{exp:json:members member_id="1"}',
+);
+
+class Json
+{
+ /* settings */
+ protected $content_type = 'application/json';
+ protected $terminate = FALSE;
+ protected $xhr = FALSE;
+ protected $fields = array();
+ protected $date_format = FALSE;
+ protected $jsonp = FALSE;
+ protected $callback;
+
+ /* caches */
+ public $entries;
+ public $entries_entry_ids;
+ public $entries_custom_fields;
+ protected $entries_matrix_rows;
+ protected $entries_matrix_cols;
+ protected $entries_grid_rows;
+ protected $entries_grid_cols;
+ protected $entries_rel_data;
+ protected $entries_relationship_data;
+ protected $entries_playa_data;
+ protected $entries_channel_files_data;
+ protected $image_manipulations = array();
+
+ public function entries($entry_ids = null)
+ {
+ $this->initialize('entries');
+
+ //exit if ajax request is required and not found
+ if ($this->check_xhr_required())
+ {
+ return '';
+ }
+
+ //instantiate channel module object
+ if (empty($this->channel))
+ {
+ require_once PATH_MOD.'channel/mod.channel.php';
+
+ $this->channel = new Channel;
+ }
+
+ $this->channel->initialize();
+
+ $order_by_string = '';
+
+ if (is_array($entry_ids))
+ {
+ $this->entries_entry_ids = $entry_ids;
+ $order_by_string = 'FIELD(t.entry_id,'.implode(',', $entry_ids).')';
+ }
+ else
+ {
+ //run through the channel module process to grab the entries
+ $this->channel->uri = ($this->channel->query_string != '') ? $this->channel->query_string : 'index.php';
+
+ if ($this->channel->enable['custom_fields'] === TRUE)
+ {
+ $this->channel->fetch_custom_channel_fields();
+ }
+
+ $save_cache = FALSE;
+
+ if (ee()->config->item('enable_sql_caching') == 'y')
+ {
+ if (FALSE == ($this->channel->sql = $this->channel->fetch_cache()))
+ {
+ $save_cache = TRUE;
+ }
+ else
+ {
+ if (ee()->TMPL->fetch_param('dynamic') != 'no')
+ {
+ if (preg_match("#(^|\/)C(\d+)#", $this->channel->query_string, $match) OR in_array($this->channel->reserved_cat_segment, explode("/", $this->channel->query_string)))
+ {
+ $this->channel->cat_request = TRUE;
+ }
+ }
+ }
+ }
+
+ if ( ! $this->channel->sql)
+ {
+ $this->channel->build_sql_query();
+ }
+
+ if (preg_match('/t\.entry_id IN \(([\d,]+)\)/', $this->channel->sql, $match))
+ {
+ $this->entries_entry_ids = explode(',', $match[1]);
+ }
+
+ if (preg_match('/ORDER BY (.*)?/', $this->channel->sql, $match))
+ {
+ $order_by_string = $match[1];
+ }
+ }
+
+ if ($this->entries_entry_ids)
+ {
+ $this->entries_custom_fields = ee()->db->select('channel_fields.*, channels.channel_id')
+ ->from('channel_fields')
+ ->join('channels', 'channel_fields.group_id = channels.field_group')
+ ->where('channels.site_id', ee()->config->item('site_id'))
+ ->where_in('channels.channel_name', explode('|', ee()->TMPL->fetch_param('channel')))
+ ->get()
+ ->result_array();
+
+ $default_fields = array(
+ 't.title',
+ 't.url_title',
+ 't.entry_id',
+ 't.channel_id',
+ 't.author_id',
+ 't.status',
+ 't.entry_date',
+ 't.edit_date',
+ 't.expiration_date',
+ );
+
+ $select = array();
+
+ if ( ! empty($this->fields))
+ {
+ foreach ($default_fields as $field)
+ {
+ $key = substr($field, 2);
+
+ if (in_array($key, $this->fields))
+ {
+ $select[] = $field;
+ }
+ }
+ }
+ else
+ {
+ $select = $default_fields;
+ }
+
+ foreach ($this->entries_custom_fields as &$field)
+ {
+ if (empty($this->fields) || in_array($field['field_name'], $this->fields))
+ {
+ $select[] = 'wd.'.ee()->db->protect_identifiers('field_id_'.$field['field_id']).' AS '.ee()->db->protect_identifiers($field['field_name']);
+ }
+ }
+
+ //we need entry_id, always grab it
+ if ( ! in_array('t.entry_id', $select))
+ {
+ $select[] = 't.entry_id';
+ }
+
+ ee()->db->select(implode(', ', $select), FALSE)
+ ->from('channel_titles t')
+ ->join('channel_data wd', 't.entry_id = wd.entry_id')
+ ->where_in('t.entry_id', $this->entries_entry_ids);
+
+ if ($order_by_string)
+ {
+ if (strpos($order_by_string, 'w.') !== FALSE)
+ {
+ ee()->db->join('channels w', 't.channel_id = w.channel_id');
+ }
+
+ if (strpos($order_by_string, 'm.') !== FALSE)
+ {
+ ee()->db->join('members m', 'm.member_id = t.author_id');
+ }
+
+ if (strpos($order_by_string, 'md.') !== FALSE)
+ {
+ ee()->db->join('member_data md', 'm.member_id = md.member_id');
+ }
+
+ if (ee()->TMPL->fetch_param('display_by') === 'week' && strpos($order_by_string, 'yearweek') !== FALSE)
+ {
+ $yearweek = TRUE;
+
+ $offset = ee()->localize->zones[ee()->config->item('server_timezone')] * 3600;
+
+ $format = (ee()->TMPL->fetch_param('start_day') === 'Monday') ? '%x%v' : '%X%V';
+
+ ee()->db->select("DATE_FORMAT(FROM_UNIXTIME(entry_date + $offset), '$format') AS yearweek", FALSE);
+ }
+
+ ee()->db->order_by($order_by_string, '', FALSE);
+ }
+
+ $query = $this->channel->query = ee()->db->get();
+
+ $show_categories = ee()->TMPL->fetch_param('show_categories') === 'yes';
+
+ if ($show_categories)
+ {
+ $this->channel->fetch_categories();
+
+ if (ee()->TMPL->fetch_param('show_category_group'))
+ {
+ $show_category_group = explode('|', ee()->TMPL->fetch_param('show_category_group'));
+ }
+ }
+
+ $this->entries = $query->result_array();
+
+ $query->free_result();
+
+ foreach ($this->entries as &$entry)
+ {
+ if (isset($yearweek))
+ {
+ unset($entry['yearweek']);
+ }
+
+ //format dates as javascript unix time (in microseconds!)
+ if (isset($entry['entry_date']))
+ {
+ $entry['entry_date'] = $this->date_format($entry['entry_date']);
+ }
+
+ if (isset($entry['edit_date']))
+ {
+ $entry['edit_date'] = $this->date_format(strtotime($entry['edit_date']));
+ }
+
+ if (isset($entry['expiration_date']))
+ {
+ $entry['expiration_date'] = $this->date_format($entry['expiration_date']);
+ }
+
+ foreach ($this->entries_custom_fields as &$field)
+ {
+ //call our custom callback for this fieldtype if it exists
+ if (isset($entry[$field['field_name']]) && is_callable(array($this, 'entries_'.$field['field_type'])))
+ {
+ $entry[$field['field_name']] = call_user_func(array($this, 'entries_'.$field['field_type']), $entry['entry_id'], $field, $entry[$field['field_name']], $entry);
+ }
+ }
+
+ if ($show_categories)
+ {
+ $entry['categories'] = array();
+
+ if (isset($this->channel->categories[$entry['entry_id']]))
+ {
+ foreach ($this->channel->categories[$entry['entry_id']] as $raw_category)
+ {
+ if ( ! empty($show_category_group) && ! in_array($raw_category[5], $show_category_group))
+ {
+ continue;
+ }
+
+ $category = array(
+ 'category_id' => (int) $raw_category[0],
+ 'parent_id' => (int) $raw_category[1],
+ 'category_name' => $raw_category[2],
+ 'category_image' => $raw_category[3],
+ 'category_description' => $raw_category[4],
+ 'category_group' => $raw_category[5],
+ 'category_url_title' => $raw_category[6],
+ );
+
+ foreach ($this->channel->catfields as $cat_field)
+ {
+ $category[$cat_field['field_name']] = (isset($raw_category['field_id_'.$cat_field['field_id']])) ? $raw_category['field_id_'.$cat_field['field_id']] : '';
+ }
+
+ $entry['categories'][] = $category;
+ }
+ }
+ }
+
+ $entry['entry_id'] = (int) $entry['entry_id'];
+
+ if (isset($entry['channel_id']))
+ {
+ $entry['channel_id'] = (int) $entry['channel_id'];
+ }
+
+ if (isset($entry['author_id']))
+ {
+ $entry['author_id'] = (int) $entry['author_id'];
+ }
+ }
+ }
+
+ ee()->load->library('javascript');
+
+ ee()->load->library('typography');
+
+ return $this->respond($this->entries, array(ee()->typography, 'parse_file_paths'));
+ }
+
+ protected function entries_matrix($entry_id, $field, $field_data)
+ {
+ if (is_null($this->entries_matrix_rows))
+ {
+ $query = ee()->db->where_in('entry_id', $this->entries_entry_ids)
+ ->order_by('row_order')
+ ->get('matrix_data');
+
+ foreach ($query->result_array() as $row)
+ {
+ if ( ! isset($this->entries_matrix_rows[$row['entry_id']]))
+ {
+ $this->entries_matrix_rows[$row['entry_id']] = array();
+ }
+
+ if ( ! isset($this->entries_matrix_rows[$row['entry_id']][$row['field_id']]))
+ {
+ $this->entries_matrix_rows[$row['entry_id']][$row['field_id']] = array();
+ }
+
+ $this->entries_matrix_rows[$row['entry_id']][$row['field_id']][] = $row;
+ }
+
+ $query->free_result();
+ }
+
+ if (is_null($this->entries_matrix_cols))
+ {
+ $query = ee()->db->get('matrix_cols');
+
+ foreach ($query->result_array() as $row)
+ {
+ $this->entries_matrix_cols[$row['col_id']] = $row;
+ }
+
+ $query->free_result();
+ }
+
+ $data = array();
+
+ if (isset($this->entries_matrix_rows[$entry_id][$field['field_id']]))
+ {
+ $field_settings = unserialize(base64_decode($field['field_settings']));
+
+ foreach ($this->entries_matrix_rows[$entry_id][$field['field_id']] as $matrix_row)
+ {
+ $row = array('row_id' => (int) $matrix_row['row_id']);
+
+ foreach ($field_settings['col_ids'] as $col_id)
+ {
+ if (isset($this->entries_matrix_cols[$col_id]))
+ {
+ $row[$this->entries_matrix_cols[$col_id]['col_name']] = $matrix_row['col_id_'.$col_id];
+ }
+ }
+
+ $data[] = $row;
+ }
+ }
+
+ return $data;
+ }
+
+ protected function entries_grid($entry_id, $field, $field_data)
+ {
+ if ( ! isset($this->entries_grid_rows[$field['field_id']]))
+ {
+ $query = ee()->db->where_in('entry_id', $this->entries_entry_ids)
+ ->order_by('row_order')
+ ->get('channel_grid_field_'.$field['field_id']);
+
+ foreach ($query->result_array() as $row)
+ {
+ if ( ! isset($this->entries_grid_rows[$field['field_id']][$row['entry_id']]))
+ {
+ $this->entries_grid_rows[$field['field_id']][$row['entry_id']] = array();
+ }
+
+ $this->entries_grid_rows[$field['field_id']][$row['entry_id']][] = $row;
+ }
+
+ $query->free_result();
+ }
+
+ if (is_null($this->entries_grid_cols))
+ {
+ $query = ee()->db->order_by('col_order', 'ASC')
+ ->get('grid_columns');
+
+ foreach ($query->result_array() as $row)
+ {
+ if ( ! isset($this->entries_grid_cols[$row['field_id']]))
+ {
+ $this->entries_grid_cols[$row['field_id']] = array();
+ }
+
+ $this->entries_grid_cols[$row['field_id']][$row['col_id']] = $row;
+ }
+
+ $query->free_result();
+ }
+
+ $data = array();
+
+ if (isset($this->entries_grid_rows[$field['field_id']][$entry_id]) && isset($this->entries_grid_cols[$field['field_id']]))
+ {
+ foreach ($this->entries_grid_rows[$field['field_id']][$entry_id] as $grid_row)
+ {
+ $row = array('row_id' => (int) $grid_row['row_id']);
+
+ foreach ($this->entries_grid_cols[$field['field_id']] as $col_id => $col)
+ {
+ $row[$col['col_name']] = $grid_row['col_id_'.$col_id];
+ }
+
+ $data[] = $row;
+ }
+ }
+
+ return $data;
+ }
+
+ protected function entries_rel($entry_id, $field, $field_data)
+ {
+ if (is_null($this->entries_rel_data))
+ {
+ $query = ee()->db->select('rel_child_id, rel_id')
+ ->where('rel_parent_id', $entry_id)
+ ->get('relationships');
+
+ $this->entries_rel_data = array();
+
+ foreach ($query->result() as $row)
+ {
+ $this->entries_rel_data[$row->rel_id] = (int) $row->rel_child_id;
+ }
+
+ $query->free_result();
+ }
+
+ if ( ! isset($this->entries_rel_data[$field_data]))
+ {
+ return NULL;
+ }
+
+ return $this->entries_rel_data[$field_data];
+ }
+
+ protected function entries_relationship($entry_id, $field, $field_data)
+ {
+ if (is_null($this->entries_relationship_data))
+ {
+ $query = ee()->db->select('parent_id, child_id, field_id')
+ ->where_in('parent_id', $this->entries_entry_ids)
+ ->order_by('order', 'asc')
+ ->get('relationships');
+
+ foreach ($query->result_array() as $row)
+ {
+ if ( ! isset($this->entries_relationship_data[$row['parent_id']]))
+ {
+ $this->entries_relationship_data[$row['parent_id']] = array();
+ }
+
+ if ( ! isset($this->entries_relationship_data[$row['parent_id']][$row['field_id']]))
+ {
+ $this->entries_relationship_data[$row['parent_id']][$row['field_id']] = array();
+ }
+
+ $this->entries_relationship_data[$row['parent_id']][$row['field_id']][] = (int) $row['child_id'];
+ }
+
+ $query->free_result();
+ }
+
+ if (isset($this->entries_relationship_data[$entry_id][$field['field_id']]))
+ {
+ return $this->entries_relationship_data[$entry_id][$field['field_id']];
+ }
+
+ return array();
+ }
+
+ protected function entries_playa($entry_id, $field, $field_data)
+ {
+ if (is_null($this->entries_playa_data))
+ {
+ $query = ee()->db->select('parent_entry_id, child_entry_id, parent_field_id')
+ ->where_in('parent_entry_id', $this->entries_entry_ids)
+ ->order_by('rel_order', 'asc')
+ ->get('playa_relationships');
+
+ foreach ($query->result_array() as $row)
+ {
+ if ( ! isset($this->entries_playa_data[$row['parent_entry_id']]))
+ {
+ $this->entries_playa_data[$row['parent_entry_id']] = array();
+ }
+
+ if ( ! isset($this->entries_playa_data[$row['parent_entry_id']][$row['parent_field_id']]))
+ {
+ $this->entries_playa_data[$row['parent_entry_id']][$row['parent_field_id']] = array();
+ }
+
+ $this->entries_playa_data[$row['parent_entry_id']][$row['parent_field_id']][] = (int) $row['child_entry_id'];
+ }
+
+ $query->free_result();
+ }
+
+ if (isset($this->entries_playa_data[$entry_id][$field['field_id']]))
+ {
+ return $this->entries_playa_data[$entry_id][$field['field_id']];
+ }
+
+ return array();
+ }
+
+ protected function entries_channel_files($entry_id, $field, $field_data, $entry)
+ {
+ $this->entries_channel_files_data = array();
+
+ $field_settings = unserialize(base64_decode($field['field_settings']));
+ $field_settings = $field_settings['channel_files'];
+
+ $query = ee()->db->select()
+ ->where('entry_id', $entry_id)
+ ->where('field_id', $field['field_id'])
+ ->order_by('file_order', 'asc')
+ ->get('channel_files');
+
+ foreach ($query->result_array() as $row)
+ {
+ $field_data = array(
+ 'file_id' => (int) $row['file_id'],
+ 'url' => $row['filename'],
+ 'filename' => $row['filename'],
+ 'extension' => $row['extension'],
+ 'kind' => $row['mime'],
+ 'size' => $row['filesize'],
+ 'title' => $row['title'],
+ 'date' => $this->date_format($row['date']),
+ 'author' => (int)$row['member_id'],
+ 'desc' => $row['description'],
+ 'primary' => (bool)$row['file_primary'],
+ 'downloads' => (int)$row['downloads'],
+ 'custom1' => (isset($row['cffield1']) ? $row['cffield1'] : null),
+ 'custom2' => (isset($row['cffield2']) ? $row['cffield2'] : null),
+ 'custom3' => (isset($row['cffield3']) ? $row['cffield3'] : null),
+ 'custom4' => (isset($row['cffield4']) ? $row['cffield4'] : null),
+ 'custom5' => (isset($row['cffield5']) ? $row['cffield5'] : null)
+ );
+
+ $fieldtype_specific_settings = $field_settings['locations'][$row['upload_service']];
+
+ switch ($row['upload_service'])
+ {
+ case 'local':
+ // get upload folder details from EE
+ $query = ee()->db->select('url')
+ ->where('id', $fieldtype_specific_settings['location'])
+ ->get('exp_upload_prefs');
+
+ $result = $query->row_array();
+ $query->free_result();
+
+ $base_url = $result['url'] . ($field_settings['entry_id_folder'] == 'yes' ? $entry_id . '/' : '');
+ $field_data['url'] = $base_url . $field_data['url'];
+ break;
+ case 's3':
+ if ($fieldtype_specific_settings['cloudfront_domain'] != '')
+ {
+ $domain = rtrim($fieldtype_specific_settings['cloudfront_domain'], '/');
+ $domain = 'http://' . preg_replace('#https?://#', '', $domain);
+ }
+ else
+ {
+ $domain = "http://{$fieldtype_specific_settings['bucket']}.s3.amazonaws.com";
+ }
+
+
+ $dir = ($fieldtype_specific_settings['directory'] != '' ? rtrim($fieldtype_specific_settings['directory'], '/') . '/' : '');
+
+ $base_url = "{$domain}/{$dir}{$entry_id}/";
+ $field_data['url'] = $base_url . $field_data['url'];
+ break;
+ case 'cloudfiles':
+ case 'ftp':
+ case 'sftp':
+ require_once PATH_THIRD.'channel_files/locations/cfile_location.php';
+ require_once PATH_THIRD."channel_files/locations/{$row['upload_service']}/{$row['upload_service']}.php";
+
+ $class_name = "CF_Location_{$row['upload_service']}";
+ $cf = new $class_name($fieldtype_specific_settings);
+ $dir = $entry_id;
+ $entry_id_folder = (isset($fieldtype_specific_settings['entry_id_folder']) ? $fieldtype_specific_settings['entry_id_folder'] : null);;
+ if (isset($entry_id_folder) && $fieldtype_specific_settings['entry_id_folder'] == 'no')
+ {
+ $dir = FALSE;
+ }
+
+ $field_data['url'] = $cf->parse_file_url($dir, $field_data['url']);
+ break;
+ default:
+ break;
+ }
+
+ // make file size relevant
+ $units = array('B', 'KB', 'MB', 'GB');
+ $units_index = 0;
+ while ($field_data['size'] >= 1024)
+ {
+ $field_data['size'] /= 1024;
+ $units_index++;
+ }
+ $field_data['size'] = round($field_data['size']) . ' ' . $units[$units_index];
+
+ $this->entries_channel_files_data[$row['field_id']][] = $field_data;
+ }
+
+ $query->free_result();
+
+ if (isset($row['field_id'], $this->entries_channel_files_data[$row['field_id']]))
+ {
+ return $this->entries_channel_files_data[$row['field_id']];
+ }
+
+ return array();
+ }
+
+ protected function entries_date($entry_id, $field, $field_data)
+ {
+ return $this->date_format($field_data);
+ }
+
+ protected function entries_text($entry_id, $field, $field_data)
+ {
+ $field_settings = ee()->api_channel_fields->get_settings($field['field_id']);
+
+ if ($field_settings['field_content_type'] === 'numeric' || $field_settings['field_content_type'] === 'decimal')
+ {
+ return floatval($field_data);
+ }
+
+ if ($field_settings['field_content_type'] === 'integer')
+ {
+ return intval($field_data);
+ }
+
+ return $field_data;
+ }
+
+ protected function entries_custom_field($entry_id, $field, $field_data, $entry, $tagdata = ' ')
+ {
+ ee()->load->add_package_path(ee()->api_channel_fields->ft_paths[$field['field_type']], FALSE);
+
+ ee()->api_channel_fields->setup_handler($field['field_id']);
+
+ ee()->api_channel_fields->apply('_init', array(array(
+ 'row' => $entry,
+ 'content_id' => $entry['entry_id'],
+ 'content_type' => 'channel',
+ )));
+
+ $field_data = ee()->api_channel_fields->apply('pre_process', array($field_data));
+
+ if (ee()->api_channel_fields->check_method_exists('replace_tag'))
+ {
+ require_once PATH_THIRD.'json/libraries/Json_Template.php';
+
+ $template = new Json_Template();
+
+ $field_data = ee()->api_channel_fields->apply('replace_tag', array($field_data, array(), $tagdata));
+
+ if ($template->variables)
+ {
+ $field_data = $template->variables;
+ }
+
+ unset($template);
+ }
+
+ ee()->load->remove_package_path(ee()->api_channel_fields->ft_paths[$field['field_type']]);
+
+ return $field_data;
+ }
+
+ protected function entries_assets($entry_id, $field, $field_data, $entry)
+ {
+ $field_data = $this->entries_custom_field($entry_id, $field, $field_data, $entry);
+
+ if ( ! is_array($field_data))
+ {
+ $field_data = array();
+ }
+
+ if (isset($field_data['absolute_total_files']) && $field_data['absolute_total_files'] === 0)
+ {
+ return array();
+ }
+
+ $fields = array(
+ 'file_id',
+ 'url',
+ 'subfolder',
+ 'filename',
+ 'extension',
+ 'date_modified',
+ 'kind',
+ 'width',
+ 'height',
+ 'size',
+ 'title',
+ 'date',
+ 'alt_text',
+ 'caption',
+ 'author',
+ 'desc',
+ 'location',
+ );
+
+ foreach ($field_data as &$row)
+ {
+ $source_type = $row['source_type'];
+ $filedir_id = $row['filedir_id'];
+ //excise any other fields from this row
+ $row = array_intersect_key($row, array_flip($fields));
+ $row['file_id'] = (int) $row['file_id'];
+ $row['date'] = $this->date_format($row['date']);
+ $row['date_modified'] = $this->date_format($row['date_modified']);
+
+ $row['manipulations'] = array();
+
+ if ($source_type === 'ee')
+ {
+ if ( ! isset($this->image_manipulations[$filedir_id]))
+ {
+ ee()->load->model('file_model');
+
+ $query = ee()->file_model->get_dimensions_by_dir_id($filedir_id);
+
+ $this->image_manipulations[$filedir_id] = $query->result();
+
+ $query->free_result();
+ }
+
+ foreach ($this->image_manipulations[$filedir_id] as $manipulation)
+ {
+ $row['manipulations'][$manipulation->short_name] = pathinfo($row['url'], PATHINFO_DIRNAME).'/_'.$manipulation->short_name.'/'.basename($row['url']);
+ }
+ }
+ }
+
+ return $field_data;
+ }
+
+ public function search()
+ {
+ $search_id = ee()->TMPL->fetch_param('search_id');
+
+ if ( ! $search_id)
+ {
+ $search_id = end(ee()->uri->segment_array());
+ }
+
+ if ($search_id)
+ {
+ $query = ee()->db->where('search_id', $search_id)
+ ->limit(1)
+ ->get('exp_search');
+
+ if ($query->num_rows() > 0)
+ {
+ $search = $query->row_array();
+
+ $query->free_result();
+
+ if (preg_match('/IN \(([\d,]+)\)/', $query->row('query'), $match))
+ {
+ ee()->TMPL->tagparams['entry_id'] = (strpos($match[1], ',') !== FALSE) ? str_replace(',', '|', $match[1]) : $match[1];
+
+ return $this->entries();
+ }
+ }
+ }
+
+ $this->initialize();
+
+ return $this->respond(array());
+ }
+
+ /**
+ * Categories
+ *
+ * @TODO a work in progress, does not work yet
+ *
+ * @param array|null $params
+ *
+ * @return string
+ */
+ public function categories($params = NULL)
+ {
+ $this->initialize();
+
+ if (is_null($params))
+ {
+ $params = ee()->TMPL->tagparams;
+ }
+
+ ee()->load->helper('array');
+
+ $channel = element('channel', $params);
+ $group_id = element('group_id', $params, element('category_group', $params));
+ $cat_id = element('cat_id', $params, element('category_id', $params, element('show', $params)));
+ $status = element('status', $params);
+ $parent_only = element('parent_only', $params);
+ $show_empty = element('show_empty', $params, TRUE);
+ $joins = array();
+
+ if ($channel)
+ {
+ ee()->db->join('channel_titles', 'channel_titles.entry_id = category_posts.entry_id');
+ ee()->db->join('channels', 'channels.channel_id = channel_titles.channel_id');
+ ee()->db->where_in('channels.channel_name', explode('|', $channel));
+ $joins[] = 'channels';
+ $joins[] = 'channel_titles';
+ }
+
+ if ($group_id)
+ {
+ ee()->db->where_in('categories.group_id', explode('|', $group_id));
+ }
+
+ if ($cat_id)
+ {
+ ee()->db->where_in('categories.cat_id', explode('|', $cat_id));
+ }
+
+ if ($status)
+ {
+ if ( ! in_array('channel_titles', $joins))
+ {
+ ee()->db->join('channel_titles', 'channel_titles.entry_id = category_posts.entry_id');
+ }
+
+ ee()->db->where_in('channel_titles.status', explode('|', $status));
+ }
+
+ if ($parent_only)
+ {
+ ee()->db->where('categories.parent_id', 0);
+ }
+
+ if ($show_empty)
+ {
+ ee()->db->where('count >', 0);
+ }
+ }
+
+ /**
+ * Members
+ *
+ * @return string
+ */
+ public function members()
+ {
+ $this->initialize();
+
+ if ($this->check_xhr_required())
+ {
+ return '';
+ }
+
+ $default_fields = array(
+ 'm.member_id',
+ 'm.group_id',
+ 'm.username',
+ 'm.screen_name',
+ 'm.email',
+ 'm.signature',
+ 'm.avatar_filename',
+ 'm.avatar_width',
+ 'm.avatar_height',
+ 'm.photo_filename',
+ 'm.photo_width',
+ 'm.photo_height',
+ 'm.url',
+ 'm.location',
+ 'm.occupation',
+ 'm.interests',
+ 'm.bio',
+ 'm.join_date',
+ 'm.last_visit',
+ 'm.last_activity',
+ 'm.last_entry_date',
+ 'm.last_comment_date',
+ 'm.last_forum_post_date',
+ 'm.total_entries',
+ 'm.total_comments',
+ 'm.total_forum_topics',
+ 'm.total_forum_posts',
+ 'm.language',
+ 'm.timezone',
+ 'm.bday_d',
+ 'm.bday_m',
+ 'm.bday_y',
+ );
+
+ if (version_compare(APP_VER, '2.6', '<'))
+ {
+ $default_fields[] = 'm.daylight_savings';
+ }
+
+ $query = ee()->db->select('m_field_id, m_field_name')
+ ->get('member_fields');
+
+ $custom_fields = $query->result_array();
+
+ $query->free_result();
+
+ $select = array();
+
+ if ( ! empty($this->fields))
+ {
+ foreach ($default_fields as $field)
+ {
+ $key = substr($field, 2);
+
+ if (in_array($key, $this->fields))
+ {
+ $select[] = $field;
+ }
+ }
+ }
+ else
+ {
+ $select = $default_fields;
+ }
+
+ foreach ($custom_fields as &$field)
+ {
+ if (empty($this->fields) || in_array($field['m_field_name'], $this->fields))
+ {
+ $select[] = 'd.'.ee()->db->protect_identifiers('m_field_id_'.$field['m_field_id']).' AS '.ee()->db->protect_identifiers($field['m_field_name']);
+ }
+ }
+
+ ee()->db->select(implode(', ', $select), FALSE)
+ ->from('members m')
+ ->join('member_data d', 'm.member_id = d.member_id');
+
+ if ($member_ids = ee()->TMPL->fetch_param('member_id'))
+ {
+ if ($member_ids === 'CURRENT_USER')
+ {
+ $member_ids = ee()->session->userdata('member_id');
+ }
+
+ ee()->db->where_in('m.member_id', explode('|', $member_ids));
+ }
+ else if (ee()->TMPL->fetch_param('username'))
+ {
+ ee()->db->where_in('m.member_id', explode('|', ee()->TMPL->fetch_param('member_id')));
+ }
+
+ if (ee()->TMPL->fetch_param('group_id'))
+ {
+ ee()->db->where_in('m.group_id', explode('|', ee()->TMPL->fetch_param('group_id')));
+ }
+
+ if (ee()->TMPL->fetch_param('limit'))
+ {
+ ee()->db->limit(ee()->TMPL->fetch_param('limit'));
+ }
+
+ if (ee()->TMPL->fetch_param('offset'))
+ {
+ ee()->db->offset(ee()->TMPL->fetch_param('offset'));
+ }
+
+ $query = ee()->db->get();
+
+ $members = $query->result_array();
+
+ $query->free_result();
+
+ $date_fields = array(
+ 'join_date',
+ 'last_visit',
+ 'last_activity',
+ 'last_entry_date',
+ 'last_comment_date',
+ 'last_forum_post_date'
+ );
+
+ foreach ($members as &$member)
+ {
+ foreach ($date_fields as $field)
+ {
+ if (isset($member[$field]))
+ {
+ $member[$field] = $this->date_format($member[$field]);
+ }
+ }
+ }
+
+ return $this->respond($members);
+ }
+
+ protected function initialize($which = NULL)
+ {
+ switch($which)
+ {
+ case 'entries':
+ //initialize caches
+ $this->entries = array();
+ $this->entries_entry_ids = array();
+ $this->entries_custom_fields = array();
+ $this->entries_matrix_rows = NULL;
+ $this->entries_rel_data = NULL;
+ $this->entries_relationship_data = NULL;
+ $this->entries_playa_data = NULL;
+ $this->entries_channel_files_data = NULL;
+ break;
+ }
+
+ $this->xhr = ee()->TMPL->fetch_param('xhr') === 'yes';
+
+ $this->terminate = ee()->TMPL->fetch_param('terminate') === 'yes';
+
+ $this->fields = (ee()->TMPL->fetch_param('fields')) ? explode('|', ee()->TMPL->fetch_param('fields')) : array();
+
+ $this->date_format = ee()->TMPL->fetch_param('date_format');
+
+ // get rid of EE formatted dates
+ if ($this->date_format && strstr($this->date_format, '%'))
+ {
+ $this->date_format = str_replace('%', '', $this->date_format);
+ }
+
+ $this->jsonp = ee()->TMPL->fetch_param('jsonp') === 'yes';
+
+ ee()->load->library('jsonp');
+
+ $this->callback = (ee()->TMPL->fetch_param('callback') && ee()->jsonp->isValidCallback(ee()->TMPL->fetch_param('callback')))
+ ? ee()->TMPL->fetch_param('callback') : NULL;
+
+ $this->content_type = ee()->TMPL->fetch_param('content_type', ($this->jsonp && $this->callback) ? 'application/javascript' : 'application/json');
+ }
+
+ protected function check_xhr_required()
+ {
+ return $this->xhr && ! ee()->input->is_ajax_request();
+ }
+
+ protected function date_format($date)
+ {
+ if ( ! $date)
+ {
+ return NULL;
+ }
+
+ return ($this->date_format) ? date($this->date_format, $date) : $date * 1000;
+ }
+
+ protected function respond(array $response, $callback = NULL)
+ {
+ ee()->load->library('javascript');
+
+ if ($item_root_node = ee()->TMPL->fetch_param('item_root_node'))
+ {
+ $response_with_nodes = array();
+
+ foreach($response as $item)
+ {
+ $response_with_nodes[] = array($item_root_node => $item);
+ }
+
+ $response = $response_with_nodes;
+ }
+
+ if ($root_node = ee()->TMPL->fetch_param('root_node'))
+ {
+ $response = array($root_node => $response);
+ }
+
+ $response = function_exists('json_encode')
+ ? json_encode($response)
+ : ee()->javascript->generate_json($response, TRUE);
+
+ if ( ! is_null($callback))
+ {
+ $response = call_user_func($callback, $response);
+ }
+
+ if ($this->check_xhr_required())
+ {
+ $response = '';
+ }
+ else if ($this->jsonp && $this->callback)
+ {
+ $response = sprintf('%s(%s)', $this->callback, $response);
+ }
+
+ if ($this->terminate)
+ {
+ @header('Content-Type: '.$this->content_type);
+
+ exit($response);
+ }
+
+ return $response;
+ }
+}
+
+/* End of file pi.json.php */
+/* Location: ./system/expressionengine/third_party/json/pi.json.php */