diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..0d387f6
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,49 @@
+module.exports = function(grunt) {
+ // Load tasks
+ require('load-grunt-tasks')(grunt);
+ // Display task timing
+ require('time-grunt')(grunt);
+ // Project configuration.
+ grunt.initConfig({
+ // Metadata
+ pkg : grunt.file.readJSON('package.json'),
+ // Variables
+ paths : {
+ // Base dir assets dir
+ base : 'client',
+
+ // PHP assets
+ php : {
+ files_std : ['*.php', '**/*.php', '!node_modules/**/*.php'], // Standard file match
+ files : '<%= paths.php.files_std %>' // Dynamic file match
+ },
+
+ // JavaScript assets
+ js : {
+ base : 'js', // Base dir
+ src : '<%= paths.js.base %>/dev', // Development code
+ dest : '<%= paths.js.base %>/prod', // Production code
+ files_std : '**/<%= paths.js.src %>/**/*.js', // Standard file match
+ files : '<%= paths.js.files_std %>' // Dynamic file match
+ },
+
+ // Sass assets
+ sass : {
+ src : 'sass', // Source files dir
+ dest : 'css', // Compiled files dir
+ ext : '.css', // Compiled extension
+ target : '*.scss', // Only Sass files in CWD
+ exclude : '!_*.scss', // Do not process partials
+ base_src : '<%= paths.base %>/<%= paths.sass.src %>', // Base source dir
+ base_dest : '<%= paths.base %>/<%= paths.sass.dest %>', // Base compile dir
+ }
+ },
+ });
+
+ // Load task configurations
+ grunt.loadTasks('grunt');
+
+ // Default Tasks
+ grunt.registerTask('build', ['phplint', 'jshint:all', 'uglify', 'sass']);
+ grunt.registerTask('watch_all', ['watch:js', 'watch:sass']);
+};
\ No newline at end of file
diff --git a/model.php b/controller.php
similarity index 90%
rename from model.php
rename to controller.php
index 2e77b72..9d2a530 100644
--- a/model.php
+++ b/controller.php
@@ -1,248 +1,235 @@
- array (
- 'file' => 'js/lib.core.js',
- 'deps' => 'jquery'
- ),
- 'admin' => array (
- 'file' => 'js/lib.admin.js',
- 'deps' => array('jquery', '[core]'),
- 'context' => 'admin'
- ),
- 'inline_edit' => array (
- 'file' => 'js/lib.posts.inline_edit.js',
- 'deps' => array('inline-edit-post','jquery', '[posts]'),
- 'context' => 'admin_page_edit'
- )
- );
-
- /**
- * Style files
- * @var array
- * @see CNR_Base::files
- */
- var $styles = array (
- 'admin' => array (
- 'file' => 'css/admin.css',
- 'context' => 'admin'
- )
- );
-
- /* Featured Content variables */
-
- /**
- * Category slug value that denotes a "featured" post
- * @var string
- * @see posts_featured_cat()
- * @todo Remove need for this property
- */
- var $posts_featured_cat = "feature";
-
- /**
- * Featured posts container
- * @var CNR_Post_Query
- */
- var $posts_featured = null;
-
- /* Children Content Variables */
-
- /**
- * Children posts
- * @var CNR_Post_Query
- */
- var $post_children_collection = null;
-
- /* Instance Variables */
-
- /**
- * Structure instance
- * @var CNR_Structure
- */
- var $structure = null;
-
- /**
- * Media instance
- * @var CNR_Media
- */
- var $media = null;
-
- /**
- * Post class instance
- * @var CNR_Post
- */
- var $post = null;
-
- /**
- * Feeds instance
- * @var CNR_Feeds
- */
- var $feeds = null;
-
- /* Constructor */
-
- function Cornerstone() {
- $this->__construct();
- }
-
- function __construct() {
- //Parent Constructor
- parent::__construct();
-
- //Init
- $this->init();
-
- //Special Queries
- $this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
- $this->post_children_collection = new CNR_Post_Query();
-
+ array (
+ 'file' => 'js/lib.core.js',
+ 'deps' => 'jquery'
+ ),
+ 'admin' => array (
+ 'file' => 'js/lib.admin.js',
+ 'deps' => array('jquery', '[core]'),
+ 'context' => 'admin'
+ ),
+ 'inline_edit' => array (
+ 'file' => 'js/lib.posts.inline_edit.js',
+ 'deps' => array('inline-edit-post','jquery', '[posts]'),
+ 'context' => 'admin_page_edit'
+ )
+ );
+
+ /**
+ * Style files
+ * @var array
+ * @see CNR_Base::files
+ */
+ var $styles = array (
+ 'admin' => array (
+ 'file' => 'css/admin.css',
+ 'context' => 'admin'
+ )
+ );
+
+ /* Featured Content variables */
+
+ /**
+ * Category slug value that denotes a "featured" post
+ * @var string
+ * @see posts_featured_cat()
+ * @todo Remove need for this property
+ */
+ var $posts_featured_cat = "feature";
+
+ /**
+ * Featured posts container
+ * @var CNR_Post_Query
+ */
+ var $posts_featured = null;
+
+ /* Children Content Variables */
+
+ /**
+ * Children posts
+ * @var CNR_Post_Query
+ */
+ var $post_children_collection = null;
+
+ /* Instance Variables */
+
+ /**
+ * Structure instance
+ * @var CNR_Structure
+ */
+ var $structure = null;
+
+ /**
+ * Media instance
+ * @var CNR_Media
+ */
+ var $media = null;
+
+ /**
+ * Post class instance
+ * @var CNR_Post
+ */
+ var $post = null;
+
+ /**
+ * Feeds instance
+ * @var CNR_Feeds
+ */
+ var $feeds = null;
+
+ /* Constructor */
+
+ function __construct() {
+ //Parent Constructor
+ parent::__construct();
+
+ //Init
+ $this->init();
+
+ //Special Queries
+ $this->posts_featured = new CNR_Post_Query( array( 'category' => $this->posts_featured_get_cat_id(), 'numberposts' => 4 ) );
+ $this->post_children_collection = new CNR_Post_Query();
+
$this->post = new CNR_Post();
- $this->post->init();
-
-
- //Init class instances
- $this->structure = new CNR_Structure();
- $this->structure->init();
-
- $this->media = new CNR_Media();
- $this->media->init();
-
- $this->feeds = new CNR_Feeds();
- $this->feeds->init();
- }
-
- /* Init */
-
- /**
- * Initialize environment
- * Overrides parent method
- * @see parent::init_env
- * @return void
- */
- function init_env() {
- //Localization
- $ldir = 'l10n';
- $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
- $lpath_abs = $this->util->get_file_path($ldir);
- if ( is_dir($lpath_abs) ) {
- load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
- }
-
- //Context
- add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
- }
-
- /* Methods */
-
- /*-** Request **-*/
-
- /**
- * Output current context to client-side
- * @uses `wp_head` action hook
- * @uses `admin_head` action hook
- * @return void
- */
- function set_client_context() {
- $ctx = new stdClass();
- $ctx->context = $this->util->get_context();
- $this->util->extend_client_object($ctx, true);
- }
-
- /*-** Child Content **-*/
-
- /**
- * Gets children posts of specified page and stores them for later use
- * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
- * @return array $posts Posts array (required by 'the_posts' filter)
- * @param array $posts Array of Posts (@see WP_QUERY)
- */
- function post_children_get($posts) {
- //Global variables
- global $wp_query;
-
- //Reset post children collection
- $this->post_children_collection->init();
-
- //Stop here if post is not a page
- if ( ! is_page() || empty($posts) )
- return $posts;
-
- //Get children posts
- $post =& $posts[0];
- $this->post_children_collection =& CNR_Post::get_children($post);
-
- //Return posts (required by filter)
- return $posts;
- }
-
- /*-** Featured Content **-*/
-
- /**
- * Retrieves featured post category object
- * @return object Featured post category object
- * @todo integrate into CNR_Post_Query
- */
- function posts_featured_get_cat() {
- static $cat = null;
-
- //Only fetch category object if it hasn't already been retrieved
- if (is_null($cat) || !is_object($cat)) {
- //Retrieve category object
- if (is_int($this->posts_featured_cat)) {
- $cat = get_category((int)$this->posts_featured_cat);
- }
- elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
- $cat = get_category_by_slug($this->posts_featured_cat);
- }
- }
-
- return $cat;
- }
-
- /**
- * @todo integrate into CNR_Post_Query
- */
- function posts_featured_get_cat_id() {
- static $id = '';
- if ($id == '') {
- $cat = $this->posts_featured_get_cat();
- if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
- $id = $cat->cat_ID;
- }
- return $id;
- }
-
- /**
- * Checks if post has content to display
- * @param object $post (optional) Post object
- * @return bool TRUE if post has content, FALSE otherwise
- * @todo Review for deletion/relocation
- */
- function post_has_content($post = null) {
- if ( !$this->util->check_post($post) )
- return false;
- if ( isset($post->post_content) && trim($post->post_content) != '' )
- return true;
- return false;
- }
-}
-
-?>
+ $this->post->init();
+
+
+ //Init class instances
+ $this->structure = new CNR_Structure();
+ $this->structure->init();
+
+ $this->media = new CNR_Media();
+ $this->media->init();
+
+ $this->feeds = new CNR_Feeds();
+ $this->feeds->init();
+ }
+
+ /* Init */
+
+ /**
+ * Initialize environment
+ * Overrides parent method
+ * @see parent::init_env
+ * @return void
+ */
+ function init_env() {
+ //Localization
+ $ldir = 'l10n';
+ $lpath = $this->util->get_plugin_file_path($ldir, array(false, false));
+ $lpath_abs = $this->util->get_file_path($ldir);
+ if ( is_dir($lpath_abs) ) {
+ load_plugin_textdomain($this->util->get_plugin_textdomain(), false, $lpath);
+ }
+
+ //Context
+ add_action(( is_admin() ) ? 'admin_head' : 'wp_head', $this->m('set_client_context'));
+ }
+
+ /* Methods */
+
+ /*-** Request **-*/
+
+ /**
+ * Output current context to client-side
+ * @uses `wp_head` action hook
+ * @uses `admin_head` action hook
+ * @return void
+ */
+ function set_client_context() {
+ $ctx = new stdClass();
+ $ctx->context = $this->util->get_context();
+ $this->util->extend_client_object($ctx, true);
+ }
+
+ /*-** Child Content **-*/
+
+ /**
+ * Gets children posts of specified page and stores them for later use
+ * This method hooks into 'the_posts' filter to retrieve child posts for any single page retrieved by WP
+ * @return array $posts Posts array (required by 'the_posts' filter)
+ * @param array $posts Array of Posts (@see WP_QUERY)
+ */
+ function post_children_get($posts) {
+ //Global variables
+ global $wp_query;
+
+ //Reset post children collection
+ $this->post_children_collection->init();
+
+ //Stop here if post is not a page
+ if ( ! is_page() || empty($posts) )
+ return $posts;
+
+ //Get children posts
+ $post =& $posts[0];
+ $this->post_children_collection =& CNR_Post::get_children($post);
+
+ //Return posts (required by filter)
+ return $posts;
+ }
+
+ /*-** Featured Content **-*/
+
+ /**
+ * Retrieves featured post category object
+ * @return object Featured post category object
+ * @todo integrate into CNR_Post_Query
+ */
+ function posts_featured_get_cat() {
+ static $cat = null;
+
+ //Only fetch category object if it hasn't already been retrieved
+ if (is_null($cat) || !is_object($cat)) {
+ //Retrieve category object
+ if (is_int($this->posts_featured_cat)) {
+ $cat = get_category((int)$this->posts_featured_cat);
+ }
+ elseif (is_string($this->posts_featured_cat) && strlen($this->posts_featured_cat) > 0) {
+ $cat = get_category_by_slug($this->posts_featured_cat);
+ }
+ }
+
+ return $cat;
+ }
+
+ /**
+ * @todo integrate into CNR_Post_Query
+ */
+ function posts_featured_get_cat_id() {
+ static $id = '';
+ if ($id == '') {
+ $cat = $this->posts_featured_get_cat();
+ if (!is_null($cat) && is_object($cat) && $this->util->property_exists($cat, 'cat_ID'))
+ $id = $cat->cat_ID;
+ }
+ return $id;
+ }
+
+ /**
+ * Checks if post has content to display
+ * @param object $post (optional) Post object
+ * @return bool TRUE if post has content, FALSE otherwise
+ * @todo Review for deletion/relocation
+ */
+ function post_has_content($post = null) {
+ if ( !$this->util->check_post($post) )
+ return false;
+ if ( isset($post->post_content) && trim($post->post_content) != '' )
+ return true;
+ return false;
+ }
+}
diff --git a/functions.php b/functions.php
new file mode 100644
index 0000000..be22199
--- /dev/null
+++ b/functions.php
@@ -0,0 +1,256 @@
+feeds->the_links();
+}
+
+/*-** Child Content **-*/
+
+function cnr_is_section() {
+ return ( is_page() && cnr_have_children() ) ? true : false;
+}
+
+/**
+ * Checks if current post/page has children elements
+ * @return bool TRUE if post/page has children, FALSE otherwise
+ */
+function cnr_have_children() {
+ global $cnr;
+ return $cnr->post_children_collection->has();
+}
+
+/**
+ * Prepares next child post for output to page
+ *
+ * @return void
+ */
+function cnr_next_child() {
+ global $cnr;
+ $cnr->post_children_collection->next();
+}
+
+/**
+ * Returns number of children in current request
+ * May not return total number of existing children (e.g. if output is paged, etc.)
+ * @return int Number of children returned in current request
+ */
+function cnr_children_count() {
+ global $cnr;
+ return $cnr->post_children_collection->count();
+}
+
+/**
+ * Returns total number of existing children
+ * @return int Total number of children
+ */
+function cnr_children_found() {
+ global $cnr;
+ return $cnr->post_children_collection->found();
+}
+
+/**
+ * Returns total number of pages of children
+ * Based on 'posts_per_page' option
+ * @return int Maximum number of pages
+ */
+function cnr_children_max_num_pages() {
+ global $cnr;
+ return $cnr->post_children_collection->max_num_pages();
+}
+
+/**
+ * Checks if current child item is the first child item
+ * @return bool TRUE if current item is first, FALSE otherwise
+ */
+function cnr_is_first_child() {
+ global $cnr;
+ return $cnr->post_children_collection->is_first();
+}
+
+/**
+ * Checks if current child item is the last child item
+ * @return bool TRUE if current item is last, FALSE otherwise
+ */
+function cnr_is_last_child() {
+ global $cnr;
+ return $cnr->post_children_collection->is_last();
+}
+
+/*-** Featured Content **-*/
+
+/**
+ * Retrieves featured posts
+ * @return array Featured posts matching criteria
+ * @param int $limit (optional) Maximum number of featured posts to retrieve
+ * @param int|bool $parent (optional) Section to get featured posts of (Defaults to current section). FALSE if latest featured posts should be retrieved regardless of section
+ */
+function cnr_get_featured($limit = 0, $parent = null) {
+ global $cnr;
+ return $cnr->posts_featured->get($limit, $parent);
+}
+
+function cnr_in_featured($post_id = null) {
+ global $cnr;
+ return $cnr->posts_featured->contains($post_id);
+}
+
+function cnr_have_featured() {
+ global $cnr;
+ return $cnr->posts_featured->has();
+}
+
+function cnr_next_featured() {
+ global $cnr;
+ return $cnr->posts_featured->next();
+
+}
+
+function cnr_current_featured() {
+ global $cnr;
+ return $cnr->posts_featured->current();
+}
+
+function cnr_is_first_featured() {
+ global $cnr;
+ return $cnr->posts_featured->is_first();
+}
+
+function cnr_is_last_featured() {
+ global $cnr;
+ return $cnr->posts_featured->is_last();
+}
+
+function cnr_featured_count() {
+ global $cnr;
+ return $cnr->posts_featured->count();
+}
+
+/**
+ * Returns total number of found posts
+ * @return int Total number of posts
+ */
+function cnr_featured_found() {
+ global $cnr;
+ return $cnr->posts_featured->found();
+}
+
+/*-** Post-Specific **-*/
+
+/**
+ * Checks if post has content to display
+ * @param object $post (optional) Post object
+ * @return bool TRUE if post has content, FALSE otherwise
+ */
+function cnr_has_content($post = null) {
+ global $cnr;
+ return $cnr->post_has_content($post);
+}
+
+ /* Images */
+
+function cnr_get_attachments($post = null) {
+ $m = new CNR_Media();
+ return $m->post_get_attachments($post);
+}
+
+function cnr_get_filesize($post = null, $formatted = true) {
+ $m = new CNR_Media();
+ return $m->get_attachment_filesize($post, $formatted);
+}
+
+ /* Section */
+
+/**
+ * Retrieves the post's section data
+ * @uses CNR_Post::get_section()
+ * @param string $data (optional) Type of data to return (Default: ID)
+ * Possible values:
+ * NULL Full section post object
+ * Column name Post column data (if exists)
+ *
+ * @param int $id (optional) Post ID (Default: current post)
+ * @return mixed post's section (or column data if specified via $data parameter)
+ */
+function cnr_get_the_section($data = 'ID', $id = null) {
+ return CNR_Post::get_section($id, $data);
+}
+
+/**
+ * Prints the post's section data
+ * @uses CNR_Post::the_section()
+ * @param string $data (optional) Type of data to return (Default: ID)
+ */
+function cnr_the_section($data = 'ID') {
+ CNR_Post::the_section(null, $data);
+}
+
+/* Content Types */
+
+/**
+ * Register handler for a placeholder in a content type template
+ * Placeholders allow templates to be populated with dynamic content at runtime
+ * Multiple handlers can be registered for a placeholder,
+ * thus allowing custom handlers to override default processing, etc.
+ * @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
+ * @param string $placeholder Placeholder identifier
+ * @param callback $handler Callback function to use as handler for placeholder
+ * @param int $priority (optional) Priority of registered handler (Default: 10)
+ */
+function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
+ CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
+}
+
+/**
+ * Checks if data exists for specified field
+ * @global $cnr_content_utilities
+ * @param string $field_id ID of field to check for data
+ * @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
+ * @return bool TRUE if field data exists
+ */
+function cnr_has_data($field_id = null, $item = null) {
+ global $cnr_content_utilities;
+ return $cnr_content_utilities->has_item_data($item, $field_id);
+}
+
+/**
+ * Retrieve data from a field
+ * @global $cnr_content_utilities
+ * @see CNR_Content_Utilities::get_item_data() for more information
+ * @param string $field_id ID of field to retrieve
+ * @param string $layout (optional) Name of layout to use when returning data
+ * @param array $attr (optional) Additional attributes to pass to field
+ * @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
+ * @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
+ * @return mixed Specified field data
+ */
+function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
+ global $cnr_content_utilities;
+ return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
+}
+
+/**
+ * Prints an item's field data
+ * @see CNR_Content_Utilities::the_item_data() for more information
+ * @param string $field_id Name of field to retrieve
+ * @param string $layout(optional) Layout to use when returning field data (Default: display)
+ * @param array $attr Additional items to pass to field
+ * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
+ * @param mixed $default Default value to return in case of errors, etc.
+ */
+function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
+ global $cnr_content_utilities;
+ $cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
+}
\ No newline at end of file
diff --git a/grunt/jshint.js b/grunt/jshint.js
new file mode 100644
index 0000000..8adbfb7
--- /dev/null
+++ b/grunt/jshint.js
@@ -0,0 +1,38 @@
+module.exports = function(grunt) {
+
+grunt.config('jshint', {
+ options : {
+ reporter: require('jshint-stylish'),
+ curly : true,
+ eqeqeq : true,
+ immed : true,
+ latedef : true,
+ newcap : false,
+ noarg : true,
+ sub : true,
+ undef : true,
+ unused : true,
+ boss : true,
+ eqnull : true,
+ browser : true,
+ jquery : true,
+ globals : {}
+ },
+ grunt : {
+ options : {
+ node : true
+ },
+ src : ['Gruntfile.js', 'grunt/*.js']
+ },
+ all : {
+ options : {
+ globals : {
+ 'SLB' : true,
+ 'console' : true
+ }
+ },
+ src : ['<%= paths.js.files %>']
+ },
+});
+
+};
\ No newline at end of file
diff --git a/grunt/phplint.js b/grunt/phplint.js
new file mode 100644
index 0000000..d1f095a
--- /dev/null
+++ b/grunt/phplint.js
@@ -0,0 +1,14 @@
+module.exports = function(grunt) {
+
+grunt.config('phplint', {
+ options : {
+ phpArgs : {
+ '-lf': null
+ }
+ },
+ all : {
+ src : '<%= paths.php.files %>'
+ }
+});
+
+};
\ No newline at end of file
diff --git a/grunt/sass.js b/grunt/sass.js
new file mode 100644
index 0000000..2278eb7
--- /dev/null
+++ b/grunt/sass.js
@@ -0,0 +1,35 @@
+module.exports = function(grunt) {
+
+grunt.config('sass', {
+ options : {
+ outputStyle : 'compressed',
+ },
+ core : {
+ files : [{
+ expand : true,
+ cwd : '<%= paths.sass.base_src %>/',
+ dest : '<%= paths.sass.base_dest %>/',
+ src : ['<%= paths.sass.target %>', '<%= paths.sass.exclude %>'],
+ ext : '<%= paths.sass.ext %>'
+ }]
+ },
+ themes : {
+ options : {
+ //includePaths : require('node-bourbon').includePaths
+ },
+ files : [{
+ expand : true,
+ cwd : 'themes/',
+ src : ['*/**/*.scss', '<%= paths.sass.exclude %>'],
+ dest : '<%= paths.sass.dest %>/',
+ srcd : '<%= paths.sass.src %>/',
+ ext : '<%= paths.sass.ext %>',
+ rename : function(dest, matchedSrcPath, options) {
+ var path = [options.cwd, matchedSrcPath.replace(options.srcd, dest)].join('');
+ return path;
+ }
+ }]
+ }
+});
+
+};
\ No newline at end of file
diff --git a/grunt/uglify.js b/grunt/uglify.js
new file mode 100644
index 0000000..b6fb8dc
--- /dev/null
+++ b/grunt/uglify.js
@@ -0,0 +1,21 @@
+module.exports = function(grunt) {
+
+grunt.config('uglify', {
+ options : {
+ mangle: false,
+ report: 'min'
+ },
+ all : {
+ files : [{
+ expand : true,
+ cwd : '',
+ dest : '',
+ src : ['<%= paths.js.files %>'],
+ rename : function(dest, srcPath) {
+ return srcPath.replace('/' + grunt.config.get('paths.js.src') + '/', '/' + grunt.config.get('paths.js.dest') + '/');
+ }
+ }]
+ },
+});
+
+};
\ No newline at end of file
diff --git a/grunt/watch.js b/grunt/watch.js
new file mode 100644
index 0000000..653b1f7
--- /dev/null
+++ b/grunt/watch.js
@@ -0,0 +1,57 @@
+module.exports = function(grunt) {
+
+grunt.config('watch', {
+ phplint : {
+ files : '<%= paths.php.files_std %>',
+ tasks : ['phplint'],
+ options : {
+ spawn : false
+ }
+ },
+ sass_core : {
+ files : ['<%= paths.sass.base_src %>/**/*.scss'],
+ tasks : ['sass:core']
+ },
+ sass_themes : {
+ files : ['themes/**/<%= paths.sass.src %>/**/*.scss'],
+ tasks : ['sass:themes']
+ },
+ jshint : {
+ files : '<%= paths.js.files_std %>',
+ tasks : ['jshint:all'],
+ options : {
+ spawn : false
+ }
+ },
+ js : {
+ files : '<%= paths.js.files_std %>',
+ tasks : ['jshint:all', 'uglify:all'],
+ options : {
+ spawn : false
+ }
+ }
+});
+
+grunt.event.on('watch', function(action, filepath) {
+ // Determine task based on filepath
+ var get_ext = function(path) {
+ var ret = '';
+ var i = path.lastIndexOf('.');
+ if ( -1 !== i && i <= path.length ) {
+ ret = path.substr(i + 1);
+ }
+ return ret;
+ };
+ switch ( get_ext(filepath) ) {
+ // PHP
+ case 'php' :
+ grunt.config('paths.php.files', [filepath]);
+ break;
+ // JavaScript
+ case 'js' :
+ grunt.config('paths.js.files', [filepath]);
+ break;
+ }
+});
+
+};
\ No newline at end of file
diff --git a/includes/class.base.php b/includes/class.base.php
index 7092c91..93b2ce5 100644
--- a/includes/class.base.php
+++ b/includes/class.base.php
@@ -1,7 +1,5 @@
util->m($this->util, 'remove_prefix'), $args);
}
-}
-
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/includes/class.content-types.php b/includes/class.content-types.php
deleted file mode 100644
index a2ac672..0000000
--- a/includes/class.content-types.php
+++ /dev/null
@@ -1,3068 +0,0 @@
-init();
-
-/* Functions */
-
-/**
- * Register handler for a placeholder in a content type template
- * Placeholders allow templates to be populated with dynamic content at runtime
- * Multiple handlers can be registered for a placeholder,
- * thus allowing custom handlers to override default processing, etc.
- * @uses CNR_Field_Type::register_placeholder_handler() to register placeholder
- * @param string $placeholder Placeholder identifier
- * @param callback $handler Callback function to use as handler for placeholder
- * @param int $priority (optional) Priority of registered handler (Default: 10)
- */
-function cnr_register_placeholder_handler($placeholder, $handler, $priority = 10) {
- CNR_Field_Type::register_placeholder_handler($placeholder, $handler, $priority);
-}
-
-/**
- * Checks if data exists for specified field
- * @global $cnr_content_utilities
- * @param string $field_id ID of field to check for data
- * @param int|obj $item (optional) Post ID or object to check for field data (Default: global post)
- * @return bool TRUE if field data exists
- */
-function cnr_has_data($field_id = null, $item = null) {
- global $cnr_content_utilities;
- return $cnr_content_utilities->has_item_data($item, $field_id);
-}
-
-/**
- * Retrieve data from a field
- * @global $cnr_content_utilities
- * @see CNR_Content_Utilities::get_item_data() for more information
- * @param string $field_id ID of field to retrieve
- * @param string $layout (optional) Name of layout to use when returning data
- * @param array $attr (optional) Additional attributes to pass to field
- * @param int|object $item (optional) Post object to retrieve data from (Default: global post object)
- * @param mixed $default Default value to return in case of errors (invalid field, no data, etc.)
- * @return mixed Specified field data
- */
-function cnr_get_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
- global $cnr_content_utilities;
- return $cnr_content_utilities->get_item_data($item, $field_id, $layout, $default, $attr);
-}
-
-/**
- * Prints an item's field data
- * @see CNR_Content_Utilities::the_item_data() for more information
- * @param string $field_id Name of field to retrieve
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
- * @param array $attr Additional items to pass to field
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
- * @param mixed $default Default value to return in case of errors, etc.
- */
-function cnr_the_data($field_id = null, $layout = 'display', $attr = null, $item = null, $default = '') {
- global $cnr_content_utilities;
- $cnr_content_utilities->the_item_data($item, $field_id, $layout, $default, $attr);
-}
-
-/* Hooks */
-
-//Default placeholder handlers
-cnr_register_placeholder_handler('all', array('CNR_Field_Type', 'process_placeholder_default'), 11);
-cnr_register_placeholder_handler('field_id', array('CNR_Field_Type', 'process_placeholder_id'));
-cnr_register_placeholder_handler('field_name', array('CNR_Field_Type', 'process_placeholder_name'));
-cnr_register_placeholder_handler('data', array('CNR_Field_Type', 'process_placeholder_data'));
-cnr_register_placeholder_handler('loop', array('CNR_Field_Type', 'process_placeholder_loop'));
-cnr_register_placeholder_handler('data_ext', array('CNR_Field_Type', 'process_placeholder_data_ext'));
-cnr_register_placeholder_handler('rich_editor', array('CNR_Field_Type', 'process_placeholder_rich_editor'));
-
-/**
- * Content Types - Base Class
- * Core properties/methods for Content Type derivative classes
- * @package Cornerstone
- * @subpackage Content Types
- * @author Archetyped
- */
-class CNR_Content_Base extends CNR_Base {
-
- /**
- * Base class name
- * @var string
- */
- var $base_class = 'cnr_content_base';
-
- /**
- * @var string Unique name
- */
- var $id = '';
-
- /**
- * Reference to parent object that current instance inherits from
- * @var object
- */
- var $parent = null;
-
- /**
- * Title
- * @var string
- */
- var $title = '';
-
- /**
- * Plural Title
- * @var string
- */
- var $title_plural = '';
-
- /**
- * @var string Short description
- */
- var $description = '';
-
- /**
- * @var array Object Properties
- */
- var $properties = array();
-
- /**
- * Data for object
- * May also contain data for nested objects
- * @var mixed
- */
- var $data = null;
-
- /**
- * @var array Script resources to include for object
- */
- var $scripts = array();
-
- /**
- * @var array CSS style resources to include for object
- */
- var $styles = array();
-
- /**
- * Hooks (Filters/Actions) for object
- * @var array
- */
- var $hooks = array();
-
- /**
- * Legacy Constructor
- */
- function CNR_Content_Base($id = '', $parent = null) {
- $this->__construct($id, $parent);
- }
-
- /**
- * Constructor
- */
- function __construct($id = '', $parent = null) {
- parent::__construct();
- $id = trim($id);
- $this->id = $id;
- if ( is_bool($parent) && $parent )
- $parent = $id;
- $this->set_parent($parent);
- }
-
- /* Getters/Setters */
-
- /**
- * Checks if the specified path exists in the object
- * @param array $path Path to check for
- * @return bool TRUE if path exists in object, FALSE otherwise
- */
- function path_isset($path = '') {
- //Stop execution if no path is supplied
- if ( empty($path) )
- return false;
- $args = func_get_args();
- $path = $this->util->build_path($args);
- $item =& $this;
- //Iterate over path and check if each level exists before moving on to the next
- for ($x = 0; $x < count($path); $x++) {
- if ( $this->util->property_exists($item, $path[$x]) ) {
- //Set $item as reference to next level in path for next iteration
- $item =& $this->util->get_property($item, $path[$x]);
- //$item =& $item[ $path[$x] ];
- } else {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Retrieves a value from object using a specified path
- * Checks to make sure path exists in object before retrieving value
- * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
- * @return mixed Value at specified path
- */
- function &get_path_value($path = '') {
- $ret = '';
- $path = $this->util->build_path(func_get_args());
- if ( $this->path_isset($path) ) {
- $ret =& $this;
- for ($x = 0; $x < count($path); $x++) {
- if ( 0 == $x )
- $ret =& $ret->{ $path[$x] };
- else
- $ret =& $ret[ $path[$x] ];
- }
- }
- return $ret;
- }
-
- /**
- * Search for specified member value in field type ancestors
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
- * @param string $name Value to retrieve from member
- * @return mixed Member value if found (Default: empty string)
- */
- function get_parent_value($member, $name = '', $default = '') {
- $parent =& $this->get_parent();
- return $this->get_object_value($parent, $member, $name, $default, 'parent');
- }
-
- /**
- * Retrieves specified member value
- * Handles inherited values
- * Merging corresponding parents if value is an array (e.g. for property groups)
- * @param string|array $member Member to search. May also contain a path to the desired member
- * @param string $name Value to retrieve from member
- * @param mixed $default Default value if no value found (Default: empty string)
- * @param string $dir Direction to move through hierarchy to find value
- * Possible Values:
- * parent (default) - Search through field parents
- * current - Do not search through connected objects
- * container - Search through field containers
- * caller - Search through field callers
- * @return mixed Specified member value
- */
- function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
- //Check if path to member is supplied
- $path = array();
- if ( is_array($member) && isset($member['tag']) ) {
- if ( isset($member['attributes']['ref_base']) ) {
- if ( 'root' != $member['attributes']['ref_base'] )
- $path[] = $member['attributes']['ref_base'];
- } else {
- $path[] = 'properties';
- }
-
- $path[] = $member['tag'];
- } else {
- $path = $member;
- }
-
- $path = $this->util->build_path($path, $name);
- //Set defaults and prepare data
- $val = $default;
- $inherit = false;
- $inherit_tag = '{inherit}';
-
- /* Determine whether the value must be retrieved from a parent/container object
- * Conditions:
- * > Path does not exist in current field
- * > Path exists and is not an object, but at least one of the following is true:
- * > Value at path is an array (e.g. properties, elements, etc. array)
- * > Parent/container values should be merged with retrieved array
- * > Value at path is a string that inherits from another field
- * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
- */
-
- $deeper = false;
-
- if ( !$this->path_isset($path) )
- $deeper = true;
- else {
- $val = $this->get_path_value($path);
- if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
- $deeper = true;
- else
- $deeper = false;
- }
- if ( $deeper && 'current' != $dir ) {
- //Get Parent value (recursive)
- $ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
- //Handle inheritance
- if ( is_array($val) ) {
- //Combine Arrays
- if ( is_array($ex_val) )
- $val = array_merge($ex_val, $val);
- } elseif ( $inherit !== false ) {
- //Replace placeholder with inherited string
- $val = str_replace($inherit_tag, $ex_val, $val);
- } else {
- //Default: Set parent value as value
- $val = $ex_val;
- }
- }
-
- return $val;
- }
-
- /**
- * Search for specified member value in an object
- * @param object $object Reference to object to retrieve value from
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
- * @param string $name (optional) Value to retrieve from member
- * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
- * @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
- * @return mixed Member value if found (Default: $default)
- */
- function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
- $ret = $default;
- if ( is_object($object) && method_exists($object, 'get_member_value') )
- $ret = $object->get_member_value($member, $name, $default, $dir);
- return $ret;
- }
-
- /**
- * Retrieve value from data member
- * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
- * @return mixed Value at specified path
- */
- function get_data($top = true) {
- $top = !!$top;
- $obj = $this;
- $obj_path = array($this);
- $path = array();
- //Iterate through hiearchy to get top-most object
- while ( !empty($obj) ) {
- $new = null;
- //Try to get caller first
- if ( method_exists($obj, 'get_caller') ) {
- $checked = true;
- $new = $obj->get_caller();
- }
- //Try to get container if no caller found
- if ( empty($new) && method_exists($obj, 'get_container') ) {
- $checked = true;
- $new = $obj->get_container();
- }
-
- $obj = $new;
-
- //Stop iteration
- if ( !empty($obj) ) {
- //Add object to path if it is valid
- $obj_path[] = $obj;
- }
- }
-
- //Check each object (starting with top-most) for matching data for current field
-
- //Reverse array
- $obj_path = array_reverse($obj_path);
- //Build path for data location
- foreach ( $obj_path as $obj ) {
- if ( $this->util->property_exists($obj, 'id') )
- $path[] = $obj->id;
- }
-
- //Iterate through objects
- while ( !empty($obj_path) ) {
- //Get next object
- $obj = array_shift($obj_path);
- //Shorten path
- array_shift($path);
- //Check for value in object and stop iteration if matching data found
- if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
- break;
- }
- }
-
- return $val;
- }
-
- /**
- * Sets value in data member
- * Sets value to data member itself by default
- * @param mixed $value Value to set
- * @param string|array $name Name of value to set (Can also be path to value)
- */
- function set_data($value, $name = '') {
- $ref =& $this->get_path_value('data', $name);
- $ref = $value;
- }
-
- /**
- * Retrieve base_class property
- * @return string base_class property of current class/instance object
- */
- function get_base_class() {
- $ret = '';
- if ( isset($this) )
- $ret = $this->base_class;
- else {
- $ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
- }
-
- return $ret;
- }
-
- /**
- * Sets parent object of current instance
- * Parent objects must be the same object type as current instance
- * @param string|object $parent Parent ID or reference
- */
- function set_parent($parent) {
- if ( !empty($parent) ) {
- //Validate parent object
- if ( is_array($parent) )
- $parent =& $parent[0];
-
- //Retrieve reference object if ID was supplied
- if ( is_string($parent) ) {
- $parent = trim($parent);
- //Check for existence of parent
- $lookup = $this->base_class . 's';
- if ( isset($GLOBALS[$lookup][$parent]) ) {
- //Get reference to parent
- $parent =& $GLOBALS[$lookup][$parent];
- }
- }
-
- //Set reference to parent field type
- if ( is_a($parent, $this->base_class) ) {
- $this->parent =& $parent;
- }
- }
- }
-
- /**
- * Retrieve field type parent
- * @return CNR_Field_Type Reference to parent field
- */
- function &get_parent() {
- return $this->parent;
- }
-
- /**
- * Retrieves field ID
- * @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
- * @return string|bool Field ID, FALSE if $field is invalid
- */
- function get_id($field = null) {
- $ret = false;
- if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
- $field =& $this;
- }
-
- if ( is_a($field, CNR_Field_Type::get_base_class()) )
- $id = $field->id;
-
- if ( is_string($id) )
- $ret = trim($id);
-
- //Setup options
- $options_def = array('format' => null);
- //Get options array
- $num_args = func_num_args();
- $options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
- $options = wp_parse_args($options, $options_def);
- //Check if field should be formatted
- if ( is_string($ret) && !empty($options['format']) ) {
- //Clear format option if it is an invalid value
- if ( is_bool($options['format']) || is_int($options['format']) )
- $options['format'] = null;
- //Setup values
- $wrap = array('open' => '[', 'close' => ']');
- if ( isset($options['wrap']) && is_array($options['wrap']) )
- $wrap = wp_parse_args($options['wrap'], $wrap);
- $wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
- switch ( $options['format'] ) {
- case 'attr_id' :
- $wrap = (array('open' => '_', 'close' => '_'));
- $wrap_trailing = false;
- break;
- }
- $c = $field->get_caller();
- $field_id = array($ret);
- while ( !!$c ) {
- //Add ID of current field to array
- if ( isset($c->id) && is_a($c, $this->base_class) )
- $field_id[] = $c->id;
- $c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
- }
-
- //Add prefix to ID value
- $field_id[] = 'attributes';
-
- //Convert array to string
- return $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
- }
- return $ret;
- }
-
- /**
- * Set object title
- * @param string $title Title for object
- * @param string $plural Plural form of title
- */
- function set_title($title = '', $plural = '') {
- $this->title = strip_tags(trim($title));
- if ( isset($plural) )
- $this->title_plural = strip_tags(trim($plural));
- }
-
- /**
- * Retrieve object title
- * @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
- */
- function get_title($plural = false) {
- $dir = 'current';
- //Singular
- if ( !$plural )
- return $this->get_member_value('title', '','', $dir);
- //Plural
- $title = $this->get_member_value('title_plural', '', '', $dir);
- if ( empty($title) ) {
- //Use singular title for plural base
- $title = $this->get_member_value('title', '', '', $dir);
- //Determine technique for making title plural
- //Get last letter
- if ( !empty($title) ) {
- $tail = substr($title, -1);
- switch ( $tail ) {
- case 's' :
- $title .= 'es';
- break;
- case 'y' :
- $title = substr($title, 0, -1) . 'ies';
- break;
- default :
- $title .= 's';
- }
- }
- }
- return $title;
- }
-
- /**
- * Set object description
- * @param string $description Description for object
- */
- function set_description($description = '') {
- $this->description = strip_tags(trim($description));
- }
-
- /**
- * Retrieve object description
- * @return string Object description
- */
- function get_description() {
- $dir = 'current';
- return $this->get_member_value('description', '','', $dir);
- return $desc;
- }
-
- /*-** Hooks **-*/
-
- /**
- * Retrieve hooks added to object
- * @return array Hooks
- */
- function get_hooks() {
- return $this->get_member_value('hooks', '', array());
- }
-
- /**
- * Add hook for object
- * @see add_filter() for parameter defaults
- * @param $tag
- * @param $function_to_add
- * @param $priority
- * @param $accepted_args
- */
- function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
- //Create new array for tag (if not already set)
- if ( !isset($this->hooks[$tag]) )
- $this->hooks[$tag] = array();
- //Build Unique ID
- if ( is_string($function_to_add) )
- $id = $function_to_add;
- elseif ( is_array($function_to_add) && !empty($function_to_add) )
- $id = strval($function_to_add[count($function_to_add) - 1]);
- else
- $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
- //Add hook
- $this->hooks[$tag][$id] = func_get_args();
- }
-
- /**
- * Convenience method for adding an action for object
- * @see add_filter() for parameter defaults
- * @param $tag
- * @param $function_to_add
- * @param $priority
- * @param $accepted_args
- */
- function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
- }
-
- /**
- * Convenience method for adding a filter for object
- * @see add_filter() for parameter defaults
- * @param $tag
- * @param $function_to_add
- * @param $priority
- * @param $accepted_args
- */
- function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
- $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
- }
-
- /*-** Dependencies **-*/
-
- /**
- * Adds dependency to object
- * @param string $type Type of dependency to add (script, style)
- * @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
- * @see wp_enqueue_script for the following of the parameters
- * @param $handle
- * @param $src
- * @param $deps
- * @param $ver
- * @param $ex
- */
- function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
- $args = func_get_args();
- //Remove type/context from arguments
- $args = array_slice($args, 2);
-
- //Set context
- if ( !is_array($context) ) {
- //Wrap single contexts in an array
- if ( is_string($context) )
- $context = array($context);
- else
- $context = array();
- }
- //Add file to instance property
- $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
- }
-
- /**
- * Add script to object to be added in specified contexts
- * @param array|string $context Array of contexts to add script to page
- * @see wp_enqueue_script for the following of the parameters
- * @param $handle
- * @param $src
- * @param $deps
- * @param $ver
- * @param $in_footer
- */
- function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
- $args = func_get_args();
- //Add file type to front of arguments array
- array_unshift($args, 'scripts');
- call_user_func_array(array(&$this, 'add_dependency'), $args);
- }
-
- /**
- * Retrieve script dependencies for object
- * @return array Script dependencies
- */
- function get_scripts() {
- return $this->get_member_value('scripts', '', array());
- }
-
- /**
- * Add style to object to be added in specified contexts
- * @param array|string $context Array of contexts to add style to page
- * @see wp_enqueue_style for the following of the parameters
- * @param $handle
- * @param $src
- * @param $deps
- * @param $ver
- * @param $in_footer
- */
- function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
- $args = func_get_args();
- array_unshift($args, 'styles');
- call_user_method_array('add_dependency', $this, $args);
- }
-
- /**
- * Retrieve Style dependencies for object
- * @return array Style dependencies
- */
- function get_styles() {
- return $this->get_member_value('styles', '', array());
- }
-}
-
-/**
- * Content Type - Field Types
- * Stores properties for a specific field
- * @package Cornerstone
- * @subpackage Content Types
- * @author Archetyped
- */
-class CNR_Field_Type extends CNR_Content_Base {
- /* Properties */
-
- const USES_DATA = '{data}';
-
- /**
- * Base class name
- * @var string
- */
- var $base_class = 'cnr_field_type';
-
- /**
- * @var array Array of Field types that make up current Field type
- */
- var $elements = array();
-
- /**
- * Structure: Property names stored as keys in group
- * Root
- * -> Group Name
- * -> Property Name => Null
- * Reason: Faster searching over large arrays
- * @var array Groupings of Properties
- */
- var $property_groups = array();
-
- /**
- * @var array Field type layouts
- */
- var $layout = array();
-
- /**
- * @var CNR_Field_Type Parent field type (reference)
- */
- var $parent = null;
-
- /**
- * Object that field is in
- * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
- */
- var $container = null;
-
- /**
- * Object that called field
- * Used to determine field hierarchy/nesting
- * @var CNR_Field|CNR_Field_Type|CNR_Content_Type
- */
- var $caller = null;
-
- /**
- * Legacy Constructor
- */
- function CNR_Field_Type($id = '', $parent = null) {
- $this->__construct($id, $parent);
- }
-
- /**
- * Constructor
- */
- function __construct($id = '', $parent = null) {
- parent::__construct($id);
-
- $this->id = $id;
- $this->set_parent($parent);
- }
-
- /* Getters/Setters */
-
- /**
- * Search for specified member value in field's container object (if exists)
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
- * @param string $name Value to retrieve from member
- * @return mixed Member value if found (Default: empty string)
- */
- function get_container_value($member, $name = '', $default = '') {
- $container =& $this->get_container();
- return $this->get_object_value($container, $member, $name, $default, 'container');
- }
-
- /**
- * Search for specified member value in field's container object (if exists)
- * @param string $member Name of object member to search (e.g. properties, layout, etc.)
- * @param string $name Value to retrieve from member
- * @return mixed Member value if found (Default: empty string)
- */
- function get_caller_value($member, $name = '', $default = '') {
- $caller =& $this->get_caller();
- return $this->get_object_value($caller, $member, $name, $default, 'caller');
- }
-
- /**
- * Sets reference to container object of current field
- * Reference is cleared if no valid object is passed to method
- * @param object $container
- */
- function set_container(&$container) {
- if ( !empty($container) && is_object($container) ) {
- //Set as param as container for current field
- $this->container =& $container;
- } else {
- //Clear container member if argument is invalid
- $this->clear_container();
- }
- }
-
- /**
- * Clears reference to container object of current field
- */
- function clear_container() {
- $this->container = null;
- }
-
- /**
- * Retrieves reference to container object of current field
- * @return object Reference to container object
- */
- function &get_container() {
- $ret = null;
- if ( $this->has_container() )
- $ret =& $this->container;
- return $ret;
- }
-
- /**
- * Checks if field has a container reference
- * @return bool TRUE if field is contained, FALSE otherwise
- */
- function has_container() {
- return !empty($this->container);
- }
-
- /**
- * Sets reference to calling object of current field
- * Any existing reference is cleared if no valid object is passed to method
- * @param object $caller Calling object
- */
- function set_caller(&$caller) {
- if ( !empty($caller) && is_object($caller) )
- $this->caller =& $caller;
- else
- $this->clear_caller();
- }
-
- /**
- * Clears reference to calling object of current field
- */
- function clear_caller() {
- unset($this->caller);
- }
-
- /**
- * Retrieves reference to caller object of current field
- * @return object Reference to caller object
- */
- function &get_caller() {
- $ret = null;
- if ( $this->has_caller() )
- $ret =& $this->caller;
- return $ret;
- }
-
- /**
- * Checks if field has a caller reference
- * @return bool TRUE if field is called by another field, FALSE otherwise
- */
- function has_caller() {
- return !empty($this->caller);
- }
-
- /**
- * Add/Set a property on the field definition
- * @param string $name Name of property
- * @param mixed $value Default value for property
- * @param string|array $group Group(s) property belongs to
- * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
- */
- function set_property($name, $value = '', $group = null) {
- //Do not add if property name is not a string
- if ( !is_string($name) )
- return false;
- //Create property array
- $prop_arr = array();
- $prop_arr['value'] = $value;
- //Add to properties array
- $this->properties[$name] = $value;
- //Add property to specified groups
- if ( !empty($group) ) {
- $this->set_group_property($group, $name);
- }
- return true;
- }
-
- /**
- * Sets multiple properties on field type at once
- * @param array $properties Properties. Each element is an array containing the arguments to set a new property
- * @return boolean TRUE if successful, FALSE otherwise
- */
- function set_properties($properties) {
- if ( !is_array($properties) )
- return false;
- foreach ( $properties as $name => $val) {
- $this->set_property($name, $val);
- }
- }
-
- /**
- * Retreives property from field type
- * @param string $name Name of property to retrieve
- * @return mixed Specified Property if exists (Default: Empty string)
- */
- function get_property($name) {
- $val = $this->get_member_value('properties', $name);
- return $val;
- }
-
- /**
- * Adds Specified Property to a Group
- * @param string|array $group Group(s) to add property to
- * @param string $property Property to add to group
- */
- function set_group_property($group, $property) {
- if ( is_string($group) && isset($this->property_groups[$group][$property]) )
- return;
- if ( !is_array($group) ) {
- $group = array($group);
- }
-
- foreach ($group as $g) {
- $g = trim($g);
- //Initialize group if it doesn't already exist
- if ( !isset($this->property_groups[$g]) )
- $this->property_groups[$g] = array();
-
- //Add property to group
- $this->property_groups[$g][$property] = null;
- }
- }
-
- /**
- * Retrieve property group
- * @param string $group Group to retrieve
- * @return array Array of properties in specified group
- */
- function get_group($group) {
- return $this->get_member_value('property_groups', $group, array());
- }
-
- /**
- * Sets an element for the field type
- * @param string $name Name of element
- * @param CNR_Field_Type $type Reference of field type to use for element
- * @param array $properties Properties for element (passed as keyed associative array)
- * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
- */
- function set_element($name, $type, $properties = array(), $id_prop = 'id') {
- $name = trim(strval($name));
- if ( empty($name) )
- return false;
- //Create new field for element
- $el = new CNR_Field($name, $type);
- //Set container to current field instance
- $el->set_container($this);
- //Add properties to element
- $el->set_properties($properties);
- //Save element to current instance
- $this->elements[$name] =& $el;
- }
-
- /**
- * Add a layout to the field
- * @param string $name Name of layout
- * @param string $value Layout text
- */
- function set_layout($name, $value = '') {
- if ( !is_string($name) )
- return false;
- $name = trim($name);
- $this->layout[$name] = $value;
- return true;
- }
-
- /**
- * Retrieve specified layout
- * @param string $name Layout name
- * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
- * @return string Specified layout text
- */
- function get_layout($name = 'form', $parse_nested = true) {
- //Retrieve specified layout (use $name value if no layout by that name exists)
- $layout = $this->get_member_value('layout', $name, $name);
-
- //Find all nested layouts in current layout
- if ( !empty($layout) && !!$parse_nested ) {
- $ph = $this->get_placeholder_defaults();
-
- while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
- //Iterate through the different types of layout placeholders
- foreach ($ph->match as $tag => $instances) {
- //Iterate through instances of a specific type of layout placeholder
- foreach ($instances as $instance) {
- //Get nested layout
- $nested_layout = $this->get_member_value($instance);
-
- //Replace layout placeholder with retrieved item data
- if ( !empty($nested_layout) )
- $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
- }
- }
- }
- }
-
- return $layout;
- }
-
- /**
- * Checks if specified layout exists
- * Finds layout if it exists in current object or any of its parents
- * @param string $layout Name of layout to check for
- * @return bool TRUE if layout exists, FALSE otherwise
- */
- function has_layout($layout) {
- $ret = false;
- if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
- $layout = $this->get_member_value('layout', $layout, false);
- if ( $layout !== false )
- $ret = true;
- }
-
- return $ret;
- }
-
- /**
- * Checks if layout content is valid
- * Layouts need to have placeholders to be valid
- * @param string $layout_content Layout content (markup)
- * @return bool TRUE if layout is valid, FALSE otherwise
- */
- function is_valid_layout($layout_content) {
- $ph = $this->get_placeholder_defaults();
- return preg_match($ph->pattern_general, $layout_content);
- }
-
- /**
- * Parse field layout with a regular expression
- * @param string $layout Layout data
- * @param string $search Regular expression pattern to search layout for
- * @return array Associative array containing all of the regular expression matches in the layout data
- * Array Structure:
- * root => placeholder tags
- * => Tag instances (array)
- * 'tag' => (string) tag name
- * 'match' => (string) placeholder match
- * 'attributes' => (array) attributes
- */
- function parse_layout($layout, $search) {
- $ph_xml = '';
- $parse_match = '';
- $ph_root_tag = 'ph_root_element';
- $ph_start_xml = '<';
- $ph_end_xml = ' />';
- $ph_wrap_start = '<' . $ph_root_tag . '>';
- $ph_wrap_end = '' . $ph_root_tag . '>';
- $parse_result = false;
-
- //Find all nested layouts in layout
- $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
-
- if ($match_value !== false && $match_value > 0) {
- $parse_result = array();
- //Get all matched elements
- $parse_match = $parse_match[1];
-
- //Build XML string from placeholders
- foreach ($parse_match as $ph) {
- $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
- }
- $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
- //Parse XML data
- $ph_prs = xml_parser_create();
- xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
- xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
- $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
- xml_parser_free($ph_prs);
-
- //Build structured array with all parsed data
-
- unset($parse_result['index'][$ph_root_tag]);
-
- //Build structured array
- $result = array();
- foreach ($parse_result['index'] as $tag => $instances) {
- $result[$tag] = array();
- //Instances
- foreach ($instances as $instance) {
- //Skip instance if it doesn't exist in parse results
- if (!isset($parse_result['values'][$instance]))
- continue;
-
- //Stop processing instance if a previously-saved instance with the same options already exists
- foreach ($result[$tag] as $tag_match) {
- if ($tag_match['match'] == $parse_match[$instance - 1])
- continue 2;
- }
-
- //Init instance data array
- $inst_data = array();
-
- //Add Tag to array
- $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
-
- //Add instance data to array
- $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
-
- //Add match to array
- $inst_data['match'] = $parse_match[$instance - 1];
-
- //Add to result array
- $result[$tag][] = $inst_data;
- }
- }
- $parse_result = $result;
- }
-
- return $parse_result;
- }
-
- /**
- * Retrieves default properties to use when evaluating layout placeholders
- * @return object Object with properties for evaluating layout placeholders
- */
- function get_placeholder_defaults() {
- $ph = new stdClass();
- $ph->start = '{';
- $ph->end = '}';
- $ph->reserved = array('ref' => 'ref_base');
- $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
- $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
- return $ph;
- }
-
- /**
- * Builds HTML for a field based on its properties
- * @param array $field Field properties (id, field, etc.)
- * @param array data Additional data for current field
- */
- function build_layout($layout = 'form', $data = null) {
- $out_default = '';
-
- /* Layout */
-
- //Get base layout
- $out = $this->get_layout($layout);
-
- //Only parse valid layouts
- if ( $this->is_valid_layout($out) ) {
- //Parse Layout
- $ph = $this->get_placeholder_defaults();
-
- //Search layout for placeholders
- while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
- //Iterate through placeholders (tag, id, etc.)
- foreach ( $ph->match as $tag => $instances ) {
- //Iterate through instances of current placeholder
- foreach ( $instances as $instance ) {
- //Process value based on placeholder name
- $target_property = apply_filters('cnr_process_placeholder_' . $tag, '', $this, $instance, $layout, $data);
-
- //Process value using default processors (if necessary)
- if ( '' == $target_property ) {
- $target_property = apply_filters('cnr_process_placeholder', $target_property, $this, $instance, $layout, $data);
- }
-
- //Clear value if value not a string
- if ( !is_scalar($target_property) ) {
- $target_property = '';
- }
- //Replace layout placeholder with retrieved item data
- $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
- }
- }
- }
- } else {
- $out = $out_default;
- }
-
- /* Return generated value */
-
- return $out;
- }
-
- /*-** Static Methods **-*/
-
- /**
- * Returns indacator to use field data (in layouts, property values, etc.)
- */
- function uses_data() {
- return self::USES_DATA;
- }
-
- /**
- * Register a function to handle a placeholder
- * Multiple handlers may be registered for a single placeholder
- * Basically a wrapper function to facilitate adding hooks for placeholder processing
- * @uses add_filter()
- * @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
- * @param callback $handler Function to set as a handler
- * @param int $priority (optional) Priority of handler
- */
- function register_placeholder_handler($placeholder, $handler, $priority = 10) {
- if ( 'all' == $placeholder )
- $placeholder = '';
- else
- $placeholder = '_' . $placeholder;
-
- add_filter('cnr_process_placeholder' . $placeholder, $handler, $priority, 5);
- }
-
- /**
- * Default placeholder processing
- * To be executed when current placeholder has not been handled by another handler
- * @param string $ph_output Value to be used in place of placeholder
- * @param CNR_Field $field Field containing placeholder
- * @param array $placeholder Current placeholder
- * @see CNR_Field::parse_layout for structure of $placeholder array
- * @param string $layout Layout to build
- * @param array $data Extended data for field
- * @return string Value to use in place of current placeholder
- */
- function process_placeholder_default($ph_output, $field, $placeholder, $layout, $data) {
- //Validate parameters before processing
- if ( empty($ph_output) && is_a($field, 'CNR_Field_Type') && is_array($placeholder) ) {
- //Build path to replacement data
- $ph_output = $field->get_member_value($placeholder);
-
- //Check if value is group (properties, etc.)
- //All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
- if (is_array($ph_output)
- && !empty($placeholder['attributes'])
- && is_array($placeholder['attributes'])
- && ($ph = $field->get_placeholder_defaults())
- && $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
- ) {
- /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
-
- //Find items matching criteria in $ph_output
- //Check for group criteria
- //TODO: Implement more robust/flexible criteria handling (2010-03-11: Currently only processes property groups)
- if ( 'properties' == $placeholder['tag'] && ($prop_group = $field->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
- /* Process group */
- $group_out = array();
- //Iterate through properties in group and build string
- foreach ( $prop_group as $prop_key => $prop_val ) {
- $group_out[] = $prop_key . '="' . $field->get_property($prop_key) . '"';
- }
- $ph_output = implode(' ', $group_out);
- }
- } elseif ( is_object($ph_output) && is_a($ph_output, $field->base_class) ) {
- /* Targeted property is actually a nested field */
- //Set caller to current field
- $ph_output->set_caller($field);
- //Build layout for nested element
- $ph_output = $ph_output->build_layout($layout);
- }
- }
-
- return $ph_output;
- }
-
- /**
- * Build Field ID attribute
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_id($ph_output, $field, $placeholder, $layout, $data) {
- //Get attributes
- $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
- return $field->get_id($args);
- }
-
- /**
- * Build Field name attribute
- * Name is formatted as an associative array for processing by PHP after submission
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_name($ph_output, $field, $placeholder, $layout, $data) {
- //Get attributes
- $args = wp_parse_args($placeholder['attributes'], array('format' => 'default'));
- return $field->get_id($args);
- }
-
- /**
- * Retrieve data for field
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_data($ph_output, $field, $placeholder, $layout) {
- $val = $field->get_data();
- if ( !is_null($val) ) {
- $ph_output = $val;
- $attr =& $placeholder['attributes'];
- //Get specific member in value (e.g. value from a specific field element)
- if ( isset($attr['element']) && is_array($ph_output) && ( $el = $attr['element'] ) && isset($ph_output[$el]) )
- $ph_output = $ph_output[$el];
- if ( isset($attr['format']) && 'display' == $attr['format'] )
- $ph_output = nl2br($ph_output);
- }
-
- //Return data
- return $ph_output;
- }
-
- /**
- * Loops over data to build field output
- * Options:
- * data - Dot-delimited path in field that contains data to loop through
- * layout - Name of layout to use for each data item in loop
- * layout_data - Name of layout to use for data item that matches previously-saved field data
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_loop($ph_output, $field, $placeholder, $layout, $data) {
- //Setup loop options
- $attr_defaults = array (
- 'layout' => '',
- 'layout_data' => null,
- 'data' => ''
- );
-
- $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
-
- if ( is_null($attr['layout_data']) ) {
- $attr['layout_data'] =& $attr['layout'];
- }
-
- //Get data for loop
- $path = explode('.', $attr['data']);
- $loop_data = $field->get_member_value($path);
- /*if ( isset($loop_data['value']) )
- $loop_data = $loop_data['value'];
- */
- $out = array();
-
- //Get field data
- $data = $field->get_data();
-
- //Iterate over data and build output
- if ( is_array($loop_data) && !empty($loop_data) ) {
- foreach ( $loop_data as $value => $label ) {
- //Load appropriate layout based on field value
- $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
- //Stop processing if no valid layout is returned
- if ( empty($layout) )
- continue;
- //Prep extended field data
- $data_ext = array('option_value' => $value, 'option_text' => $label);
- $out[] = $field->build_layout($layout, $data_ext);
- }
- }
-
- //Return output
- return implode($out);
- }
-
- /**
- * Returns specified value from extended data array for field
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_data_ext($ph_output, $field, $placeholder, $layout, $data) {
- if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
- $ph_output = strval($data[$key]);
- }
-
- return $ph_output;
- }
-
- /**
- * WP Editor
- * @see CNR_Field_Type::process_placeholder_default for parameter descriptions
- * @return string Placeholder output
- */
- function process_placeholder_rich_editor($ph_output, $field, $placeholder, $layout, $data) {
- $id = $field->get_id( array (
- 'format' => 'attr_id'
- ));
- $settings = array (
- 'textarea_name' => $field->get_id( array (
- 'format' => 'default'
- ))
- );
- ob_start();
- wp_editor($field->get_data(), $id, $settings);
- $out = ob_get_clean();
- return $out;
- }
-
-}
-
-class CNR_Field extends CNR_Field_Type {
-
-}
-
-class CNR_Content_Type extends CNR_Content_Base {
-
- /**
- * Base class for instance objects
- * @var string
- */
- var $base_class = 'cnr_content_type';
-
- /**
- * Indexed array of fields in content type
- * @var array
- */
- var $fields = array();
-
- /**
- * Associative array of groups in conten type
- * Key: Group name
- * Value: object of group properties
- * > description string Group description
- * > location string Location of group on edit form
- * > fields array Fields in group
- * @var array
- */
- var $groups = array();
-
- /* Constructors */
-
- /**
- * Legacy constructor
- * @param string $id Content type ID
- */
- function CNR_Content_Type($id, $parent = false, $properties = null) {
- $args = func_get_args();
- call_user_func_array(array(&$this, '__construct'), $args);
- }
-
- /**
- * Class constructor
- * @param string $id Content type ID
- * @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
- * @param array $properties (optional) Properties to set for content type (Default: none)
- */
- function __construct($id = '', $parent = null, $properties = null) {
- parent::__construct($id, $parent);
-
- //Set properties
- //TODO Iterate through additional arguments and set instance properties
- }
-
- /* Registration */
-
- /**
- * Registers current content type w/CNR
- */
- function register() {
- global $cnr_content_utilities;
- $cnr_content_utilities->register_content_type($this);
- }
-
- /* Getters/Setters */
-
- /**
- * Adds group to content type
- * Groups are used to display related fields in the UI
- * @param string $id Unique name for group
- * @param string $title Group title
- * @param string $description Short description of group's purpose
- * @param string $location Where group will be displayed on post edit form (Default: main)
- * @param array $fields (optional) ID's of existing fields to add to group
- * @return object Group object
- */
- function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
- //Create new group and set properties
- $id = trim($id);
- $this->groups[$id] =& $this->create_group($title, $description, $location);
- //Add fields to group (if supplied)
- if ( !empty($fields) && is_array($fields) )
- $this->add_to_group($id, $fields);
- return $this->groups[$id];
- }
-
- /**
- * Remove specified group from content type
- * @param string $id Group ID to remove
- */
- function remove_group($id) {
- $id = trim($id);
- if ( $this->group_exists($id) ) {
- unset($this->groups[$id]);
- }
- }
-
- /**
- * Standardized method to create a new field group
- * @param string $title Group title (used in meta boxes, etc.)
- * @param string $description Short description of group's purpose
- * @param string $location Where group will be displayed on post edit form (Default: main)
- * @return object Group object
- */
- function &create_group($title = '', $description = '', $location = 'normal') {
- $group = new stdClass();
- $title = ( is_scalar($title) ) ? trim($title) : '';
- $group->title = $title;
- $description = ( is_scalar($description) ) ? trim($description) : '';
- $group->description = $description;
- $location = ( is_scalar($location) ) ? trim($location) : 'normal';
- $group->location = $location;
- $group->fields = array();
- return $group;
- }
-
- /**
- * Checks if group exists
- * @param string $id Group name
- * @return bool TRUE if group exists, FALSE otherwise
- */
- function group_exists($id) {
- $id = trim($id);
- //Check if group exists in content type
- return ( !is_null($this->get_member_value('groups', $id, null)) );
- }
-
- /**
- * Adds field to content type
- * @param string $id Unique name for field
- * @param CNR_Field_Type|string $parent Field type that this field is based on
- * @param array $properties (optional) Field properties
- * @param string $group (optional) Group ID to add field to
- * @return CNR_Field Reference to new field
- */
- function &add_field($id, $parent, $properties = array(), $group = null) {
- //Create new field
- $id = trim(strval($id));
- $field = new CNR_Field($id);
- $field->set_parent($parent);
- $field->set_container($this);
- $field->set_properties($properties);
-
- //Add field to content type
- $this->fields[$id] =& $field;
- //Add field to group
- $this->add_to_group($group, $field->id);
- return $field;
- }
-
- /**
- * Removes field from content type
- * @param string|CNR_Field $field Object or Field ID to remove
- */
- function remove_field($field) {
- $field = CNR_Field_Type::get_id($field);
- if ( !$field )
- return false;
-
- //Remove from fields array
- //$this->fields[$field] = null;
- unset($this->fields[$field]);
-
- //Remove field from groups
- $this->remove_from_group($field);
- }
-
- /**
- * Retrieve specified field in Content Type
- * @param string $field Field ID
- * @return CNR_Field Specified field
- */
- function &get_field($field) {
- if ( $this->has_field($field) ) {
- $field = trim($field);
- $field = $this->get_member_value('fields', $field);
- } else {
- //Return empty field if no field exists
- $field = new CNR_Field('');
- }
- return $field;
- }
-
- /**
- * Checks if field exists in the content type
- * @param string $field Field ID
- * @return bool TRUE if field exists, FALSE otherwise
- */
- function has_field($field) {
- return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
- }
-
- /**
- * Adds field to a group in the content type
- * Group is created if it does not already exist
- * @param string|array $group ID of group (or group parameters if new group) to add field to
- * @param string|array $fields Name or array of field(s) to add to group
- */
- function add_to_group($group, $fields) {
- //Validate parameters
- $group_id = '';
- if ( !empty($group) ) {
- if ( !is_array($group) ) {
- $group = array($group, $group);
- }
-
- $group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
- }
- if ( empty($group_id) || empty($fields) )
- return false;
- //Create group if it doesn't exist
- if ( !$this->group_exists($group_id) ) {
- call_user_func_array($this->m('add_group'), $group);
- }
- if ( ! is_array($fields) )
- $fields = array($fields);
- foreach ( $fields as $field ) {
- unset($fref);
- if ( ! $this->has_field($field) )
- continue;
- $fref =& $this->get_field($field);
- //Remove field from any other group it's in (fields can only be in one group)
- foreach ( array_keys($this->groups) as $group_name ) {
- if ( isset($this->groups[$group_name]->fields[$fref->id]) )
- unset($this->groups[$group_name]->fields[$fref->id]);
- }
- //Add reference to field in group
- $this->groups[$group_id]->fields[$fref->id] =& $fref;
- }
- }
-
- /**
- * Remove field from a group
- * If no group is specified, then field is removed from all groups
- * @param string|CNR_Field $field Field object or ID of field to remove from group
- * @param string $group (optional) Group ID to remove field from
- */
- function remove_from_group($field, $group = '') {
- //Get ID of field to remove or stop execution if field invalid
- $field = CNR_Field_Type::get_id($field);
- if ( !$field )
- return false;
-
- //Remove field from group
- if ( !empty($group) ) {
- //Remove field from single group
- if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
- unset($group->fields[$field]);
- }
- } else {
- //Remove field from all groups
- foreach ( array_keys($this->groups) as $group ) {
- if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
- unset($group->fields[$field]);
- }
- }
- }
- }
-
- /**
- * Retrieve specified group
- * @param string $group ID of group to retrieve
- * @return object Reference to specified group
- */
- function &get_group($group) {
- $group = trim($group);
- //Create group if it doesn't already exist
- if ( ! $this->group_exists($group) )
- $this->add_group($group);
- $group = $this->get_member_value('groups', $group);
- return $group;
- }
-
- /**
- * Retrieve all groups in content type
- * @return array Reference to group objects
- */
- function &get_groups() {
- $groups = $this->get_member_value('groups');
- return $groups;
- }
-
- /**
- * Output fields in a group
- * @param string $group ID of Group to output
- * @return string Group output
- */
- function build_group($group) {
- $out = array();
- $classnames = (object) array(
- 'multi' => 'multi_field',
- 'single' => 'single_field',
- 'elements' => 'has_elements'
- );
-
- //Stop execution if group does not exist
- if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
- $group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
- $classname = array('cnr_attributes_wrap', $group_fields);
- $out[] = '
'; //Wrap all fields in group
-
- //Build layout for each field in group
- foreach ( array_keys($group->fields) as $field_id ) {
- /**
- * CNR_Field_Type
- */
- $field =& $group->fields[$field_id];
- $field->set_caller($this);
- //Start field output
- $id = 'cnr_field_' . $field->get_id();
- $class = array('cnr_attribute_wrap');
- //If single field in group, check if field title matches group
- if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
- $class[] = 'group_field_title';
- //Add flag to indicate that field was loaded on page
- $inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
- $out[] = '';
- $out[] = '
';
- //Build field layout
- $out[] = $field->build_layout();
- //end field output
- $out[] = '
';
- $field->clear_caller();
- }
- $out[] = '
'; //Close fields container
- //Add description if exists
- if ( !empty($group->description) )
- $out[] = '
' . $group->description . '
';
- }
-
- //Return group output
- return implode($out);
- }
-
- /**
- * Set data for a field
- * @param string|CNR_Field $field Reference or ID of Field to set data for
- * @param mixed $value Data to set
- */
- function set_data($field, $value = '') {
- if ( 1 == func_num_args() && is_array($field) )
- $this->data = $field;
- else {
- $field = CNR_Field_Type::get_id($field);
- if ( empty($field) )
- return false;
- $this->data[$field] = $value;
- }
- }
-
- /*-** Admin **-*/
-
- /**
- * Adds meta boxes for post's content type
- * Each group in content type is a separate meta box
- * @param string $type Type of item meta boxes are being build for (post, page, link)
- * @param string $context Location of meta box (normal, advanced, side)
- * @param object $post Post object
- */
- function admin_do_meta_boxes($type, $context, $post) {
- //Add post data to content type
- global $cnr_content_utilities;
- $this->set_data($cnr_content_utilities->get_item_data($post));
-
- //Get Groups
- $groups = array_keys($this->get_groups());
- $priority = 'default';
- //Iterate through groups and add meta box if it fits the context (location)
- foreach ( $groups as $group_id ) {
- $group =& $this->get_group($group_id);
- if ( $context == $group->location && count($group->fields) ) {
- //Format ID for meta box
- $meta_box_id = $this->prefix . '_group_' . $group_id;
- $group_args = array( 'group' => $group_id );
- add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
- }
- }
- }
-
- /**
- * Outputs group fields for a meta box
- * @param object $post Post object
- * @param array $box Meta box properties
- */
- function admin_build_meta_box($post, $box) {
- //Stop execution if group not specified
- if ( !isset($box['args']['group']) )
- return false;
-
- //Get ID of group to output
- $group_id =& $box['args']['group'];
-
- $output = array();
- $output[] = '
- get_title());
- $admin_path = ABSPATH . 'wp-admin/';
- include ($admin_path . 'edit-form-advanced.php');
- }
-
- /**
- * Adds hidden field declaring content type on post edit form
- * @deprecated no longer needed for WP 3.0+
- */
- function admin_page_edit_form() {
- global $post, $plugin_page;
- if ( empty($post) || !$post->ID ) {
- $type = $this->get_type($post);
- if ( ! empty($type) && ! empty($type->id) ) {
- ?>
-
- get_types()), array('post', 'page'))) ) {
- //Get content type definition
- $ct =& $this->get_type($post);
- //Pass processing to content type instance
- $ct->admin_do_meta_boxes($type, $context, $post);
- }
- }
-
- /**
- * Saves field data submitted for current post
- * @param int $post_id ID of current post
- * @param object $post Post object
- */
- function save_item_data($post_id, $post) {
- if ( empty($post_id) || empty($post) || !isset($_POST['cnr']) || !is_array($_POST['cnr']) )
- return false;
- $pdata = $_POST['cnr'];
-
- if ( isset($pdata['attributes']) && is_array($pdata['attributes']) && isset($pdata['fields_loaded']) && is_array($pdata['fields_loaded']) ) {
-
- $prev_data = (array) $this->get_item_data($post_id);
-
- //Remove loaded fields from prev data
- $prev_data = array_diff_key($prev_data, $pdata['fields_loaded']);
-
- //Get current field data
- $curr_data = $pdata['attributes'];
-
- //Merge arrays together (new data overwrites old data)
- if ( is_array($prev_data) && is_array($curr_data) ) {
- $curr_data = array_merge($prev_data, $curr_data);
- }
-
- //Save to database
- update_post_meta($post_id, $this->get_fields_meta_key(), $curr_data);
- }
- //Save content type
- if ( isset($_POST['cnr']['content_type']) ) {
- $type = $_POST['cnr']['content_type'];
- $saved_type = get_post_meta($post_id, $this->get_type_meta_key(), true);
- if ( is_array($saved_type) )
- $saved_type = implode($saved_type);
- if ( $type != $saved_type ) {
- //Continue processing if submitted content type is different from previously-saved content type (or no type was previously set)
- update_post_meta($post_id, $this->get_type_meta_key(), array($type));
- }
- }
- }
-
- /*-** Helpers **-*/
-
- /**
- * Get array of default post types
- * @return array Default post types
- */
- function get_default_post_types() {
- return array('post', 'page', 'attachment', 'revision', 'nav_menu');
- }
-
- /**
- * Checks if post's post type is a standard WP post type
- * @param mixed $post_type Post type (default) or post ID/object to evaluate
- * @see CNR_Content_Utilities::get_type() for possible parameter values
- * @return bool TRUE if post is default type, FALSE if it is a custom type
- */
- function is_default_post_type($post_type) {
- if ( !is_string($post_type) ) {
- $post_type = $this->get_type($post_type);
- $post_type = $post_type->id;
- }
- return in_array($post_type, $this->get_default_post_types());
- }
-
- /**
- * Checks if specified content type has been defined
- * @param string|CNR_Content_Type $type Content type ID or object
- * @return bool TRUE if content type exists, FALSE otherwise
- *
- * @uses array $cnr_content_types
- */
- function type_exists($type) {
- global $cnr_content_types;
- if ( ! is_scalar($type) ) {
- if ( is_a($type, 'CNR_Content_Type') )
- $type = $type->id;
- else
- $type = null;
- }
- return ( isset($cnr_content_types[$type]) );
- }
-
- /**
- * Retrieves content type definition for specified content item (post, page, etc.)
- * If content type does not exist, a new instance object will be created and returned
- * > New content types are automatically registered (since we are looking for registered types when using this method)
- * @param string|object $item Post object, or item type (string)
- * @return CNR_Content_Type Reference to matching content type, empty content type if no matching type exists
- *
- * @uses array $cnr_content_types
- */
- function &get_type($item) {
- //Return immediately if $item is a content type instance
- if ( is_a($item, 'CNR_Content_Type') )
- return $item;
-
- $type = null;
-
- if ( is_string($item) )
- $type = $item;
-
- if ( !$this->type_exists($type) ) {
- $post = $item;
-
- //Check if $item is a post (object or ID)
- if ( $this->util->check_post($post) && isset($post->post_type) ) {
- $type = $post->post_type;
- }
- }
- global $cnr_content_types;
- if ( $this->type_exists($type) ) {
- //Retrieve content type from global array
- $type =& $cnr_content_types[$type];
- } else {
- //Create new empty content type if it does not already exist
- $type = new CNR_Content_Type($type);
- //Automatically register newly initialized content type if it extends an existing WP post type
- if ( $this->is_default_post_type($type->id) )
- $type->register();
- }
-
- return $type;
- }
-
- /**
- * Retrieve content types
- * @return Reference to content types array
- */
- function &get_types() {
- return $GLOBALS['cnr_content_types'];
- }
-
- /**
- * Retrieve meta key for post fields
- * @return string Fields meta key
- */
- function get_fields_meta_key() {
- return $this->util->make_meta_key('fields');
- }
-
- /**
- * Retrieve meta key for post type
- * @return string Post type meta key
- */
- function get_type_meta_key() {
- return $this->util->make_meta_key('post_type');
- }
-
- /**
- * Checks if post contains specified field data
- * @param Object $post (optional) Post to check data for
- * @param string $field (optional) Field ID to check for
- * @return bool TRUE if data exists, FALSE otherwise
- */
- function has_item_data($item = null, $field = null) {
- $ret = $this->get_item_data($item, $field, 'raw', null);
- if ( is_scalar($ret) )
- return ( !empty($ret) || $ret === 0 );
- if ( is_array($ret) ) {
- foreach ( $ret as $key => $val ) {
- if ( !empty($val) || $val === 0 )
- return true;
- }
- }
- return false;
- }
-
- /**
- * Retrieve specified field data from content item (e.g. post)
- * Usage Examples:
- * get_item_data($post_id, 'field_id')
- * - Retrieves field_id data from global $post object
- * - Field data is formatted using 'display' layout of field
- *
- * get_item_data($post_id, 'field_id', 'raw')
- * - Retrieves field_id data from global $post object
- * - Raw field data is returned (no formatting)
- *
- * get_item_data($post_id, 'field_id', 'display', $post_id)
- * - Retrieves field_id data from post matching $post_id
- * - Field data is formatted using 'display' layout of field
- *
- * get_item_data($post_id, 'field_id', null)
- * - Retrieves field_id data from post matching $post_id
- * - Field data is formatted using 'display' layout of field
- * - The default layout is used when no valid layout is specified
- *
- * get_item_data($post_id)
- * - Retrieves full data array from post matching $post_id
- *
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
- * @param string $field ID of field to retrieve
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
- * @param array $attr (optional) Additional attributes to pass along to field object (e.g. for building layout, etc.)
- * @see CNR_Field_Type::build_layout for more information on attribute usage
- * @return mixed Specified field data
- */
- function get_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
- $ret = $default;
-
- //Get item
- $item = get_post($item);
-
- if ( !isset($item->ID) )
- return $ret;
-
- //Get item data
- $data = get_post_meta($item->ID, $this->get_fields_meta_key(), true);
-
- //Get field data
-
- //Set return value to data if no field specified
- if ( empty($field) || !is_string($field) )
- $ret = $data;
- //Stop if no valid field specified
- if ( !isset($data[$field]) ) {
- //TODO Check $item object to see if specified field exists (e.g. title, post_status, etc.)
- return $ret;
- }
-
- $ret = $data[$field];
-
- //Initialize layout value
- $layout_def = 'display';
-
- if ( !is_scalar($layout) || empty($layout) )
- $layout = $layout_def;
-
- $layout = strtolower($layout);
-
- //Check if raw data requested
- if ( 'raw' == $layout )
- return $ret;
-
- /* Build specified layout */
-
- //Get item's content type
- $ct =& $this->get_type($item);
- $ct->set_data($data);
-
- //Get field definition
- $fdef =& $ct->get_field($field);
-
- //Validate layout
- if ( !$fdef->has_layout($layout) )
- $layout = $layout_def;
-
- //Build layout
- $fdef->set_caller($ct);
- $ret = $fdef->build_layout($layout, $attr);
- $fdef->clear_caller();
-
- //Return formatted value
- return $ret;
- }
-
- /**
- * Prints an item's field data
- * @see CNR_Content_Utilities::get_item_data() for more information
- * @param int|object $item(optional) Content item to retrieve field from (Default: null - global $post object will be used)
- * @param string $field ID of field to retrieve
- * @param string $layout(optional) Layout to use when returning field data (Default: display)
- * @param mixed $default (optional) Default value to return in case of errors, etc.
- * @param array $attr Additional attributes to pass to field
- */
- function the_item_data($item = null, $field = null, $layout = null, $default = '', $attr = null) {
- echo apply_filters('cnr_the_item_data', $this->get_item_data($item, $field, $layout, $default, $attr), $item, $field, $layout, $default, $attr);
- }
-
- /**
- * Build Admin URL for specified post type
- * @param string|CNR_Content_Type $type Content type ID or object
- * @param string $action Action to build URL for
- * @param bool $sep_action Whether action should be a separate query variable (Default: false)
- * @return string Admin page URL
- */
- function get_admin_page_url($type, $action = '', $sep_action = false) {
- $url = admin_url('admin.php');
- $url .= '?page=' . $this->get_admin_page_file($type, $action, $sep_action);
- return $url;
- }
-
- function get_edit_item_url($edit_url, $item_id, $context) {
- //Get post type
- $type = $this->get_type($item_id);
- if ( ! $this->is_default_post_type($type->id) && $this->type_exists($type) ) {
- $edit_url = $this->get_admin_page_url($type, 'edit-item', true) . '&post=' . $item_id;
- }
-
- return $edit_url;
- }
-}
-?>
diff --git a/includes/class.content_base.php b/includes/class.content_base.php
new file mode 100644
index 0000000..064e7c7
--- /dev/null
+++ b/includes/class.content_base.php
@@ -0,0 +1,613 @@
+id = $id;
+ if ( is_bool($parent) && $parent )
+ $parent = $id;
+ $this->set_parent($parent);
+ }
+
+ /* Getters/Setters */
+
+ /**
+ * Checks if the specified path exists in the object
+ * @param array $path Path to check for
+ * @return bool TRUE if path exists in object, FALSE otherwise
+ */
+ function path_isset($path = '') {
+ //Stop execution if no path is supplied
+ if ( empty($path) )
+ return false;
+ $args = func_get_args();
+ $path = $this->util->build_path($args);
+ $item =& $this;
+ //Iterate over path and check if each level exists before moving on to the next
+ for ($x = 0; $x < count($path); $x++) {
+ if ( $this->util->property_exists($item, $path[$x]) ) {
+ //Set $item as reference to next level in path for next iteration
+ $item =& $this->util->get_property($item, $path[$x]);
+ //$item =& $item[ $path[$x] ];
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieves a value from object using a specified path
+ * Checks to make sure path exists in object before retrieving value
+ * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
+ * @return mixed Value at specified path
+ */
+ function &get_path_value($path = '') {
+ $ret = '';
+ $path = $this->util->build_path(func_get_args());
+ if ( $this->path_isset($path) ) {
+ $ret =& $this;
+ for ($x = 0; $x < count($path); $x++) {
+ if ( 0 == $x )
+ $ret =& $ret->{ $path[$x] };
+ else
+ $ret =& $ret[ $path[$x] ];
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Search for specified member value in field type ancestors
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
+ * @param string $name Value to retrieve from member
+ * @return mixed Member value if found (Default: empty string)
+ */
+ function get_parent_value($member, $name = '', $default = '') {
+ $parent =& $this->get_parent();
+ return $this->get_object_value($parent, $member, $name, $default, 'parent');
+ }
+
+ /**
+ * Retrieves specified member value
+ * Handles inherited values
+ * Merging corresponding parents if value is an array (e.g. for property groups)
+ * @param string|array $member Member to search. May also contain a path to the desired member
+ * @param string $name Value to retrieve from member
+ * @param mixed $default Default value if no value found (Default: empty string)
+ * @param string $dir Direction to move through hierarchy to find value
+ * Possible Values:
+ * parent (default) - Search through field parents
+ * current - Do not search through connected objects
+ * container - Search through field containers
+ * caller - Search through field callers
+ * @return mixed Specified member value
+ */
+ function get_member_value($member, $name = '', $default = '', $dir = 'parent') {
+ //Check if path to member is supplied
+ $path = array();
+ if ( is_array($member) && isset($member['tag']) ) {
+ if ( isset($member['attributes']['ref_base']) ) {
+ if ( 'root' != $member['attributes']['ref_base'] )
+ $path[] = $member['attributes']['ref_base'];
+ } else {
+ $path[] = 'properties';
+ }
+
+ $path[] = $member['tag'];
+ } else {
+ $path = $member;
+ }
+
+ $path = $this->util->build_path($path, $name);
+ //Set defaults and prepare data
+ $val = $default;
+ $inherit = false;
+ $inherit_tag = '{inherit}';
+
+ /* Determine whether the value must be retrieved from a parent/container object
+ * Conditions:
+ * > Path does not exist in current field
+ * > Path exists and is not an object, but at least one of the following is true:
+ * > Value at path is an array (e.g. properties, elements, etc. array)
+ * > Parent/container values should be merged with retrieved array
+ * > Value at path is a string that inherits from another field
+ * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
+ */
+
+ $deeper = false;
+
+ if ( !$this->path_isset($path) )
+ $deeper = true;
+ else {
+ $val = $this->get_path_value($path);
+ if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
+ $deeper = true;
+ else
+ $deeper = false;
+ }
+ if ( $deeper && 'current' != $dir ) {
+ //Get Parent value (recursive)
+ $ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
+ //Handle inheritance
+ if ( is_array($val) ) {
+ //Combine Arrays
+ if ( is_array($ex_val) )
+ $val = array_merge($ex_val, $val);
+ } elseif ( $inherit !== false ) {
+ //Replace placeholder with inherited string
+ $val = str_replace($inherit_tag, $ex_val, $val);
+ } else {
+ //Default: Set parent value as value
+ $val = $ex_val;
+ }
+ }
+
+ return $val;
+ }
+
+ /**
+ * Search for specified member value in an object
+ * @param object $object Reference to object to retrieve value from
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
+ * @param string $name (optional) Value to retrieve from member
+ * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
+ * @param string $dir Direction to move through hierarchy to find value @see CNR_Field_Type::get_member_value() for possible values
+ * @return mixed Member value if found (Default: $default)
+ */
+ function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
+ $ret = $default;
+ if ( is_object($object) && method_exists($object, 'get_member_value') )
+ $ret = $object->get_member_value($member, $name, $default, $dir);
+ return $ret;
+ }
+
+ /**
+ * Retrieve value from data member
+ * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
+ * @return mixed Value at specified path
+ */
+ function get_data($top = true) {
+ $top = !!$top;
+ $obj = $this;
+ $obj_path = array($this);
+ $path = array();
+ //Iterate through hiearchy to get top-most object
+ while ( !empty($obj) ) {
+ $new = null;
+ //Try to get caller first
+ if ( method_exists($obj, 'get_caller') ) {
+ $checked = true;
+ $new = $obj->get_caller();
+ }
+ //Try to get container if no caller found
+ if ( empty($new) && method_exists($obj, 'get_container') ) {
+ $checked = true;
+ $new = $obj->get_container();
+ }
+
+ $obj = $new;
+
+ //Stop iteration
+ if ( !empty($obj) ) {
+ //Add object to path if it is valid
+ $obj_path[] = $obj;
+ }
+ }
+
+ //Check each object (starting with top-most) for matching data for current field
+
+ //Reverse array
+ $obj_path = array_reverse($obj_path);
+ //Build path for data location
+ foreach ( $obj_path as $obj ) {
+ if ( $this->util->property_exists($obj, 'id') )
+ $path[] = $obj->id;
+ }
+
+ //Iterate through objects
+ while ( !empty($obj_path) ) {
+ //Get next object
+ $obj = array_shift($obj_path);
+ //Shorten path
+ array_shift($path);
+ //Check for value in object and stop iteration if matching data found
+ if ( ($val = $this->get_object_value($obj, 'data', $path, null, 'current')) && !is_null($val) ) {
+ break;
+ }
+ }
+
+ return $val;
+ }
+
+ /**
+ * Sets value in data member
+ * Sets value to data member itself by default
+ * @param mixed $value Value to set
+ * @param string|array $name Name of value to set (Can also be path to value)
+ */
+ function set_data($value, $name = '') {
+ $ref =& $this->get_path_value('data', $name);
+ $ref = $value;
+ }
+
+ /**
+ * Retrieve base_class property
+ * @return string base_class property of current class/instance object
+ */
+ static function get_base_class() {
+ $ret = '';
+ if ( isset($this) )
+ $ret = $this->base_class;
+ else {
+ $ret = CNR_Utilities::get_property(__CLASS__, 'base_class');
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Sets parent object of current instance
+ * Parent objects must be the same object type as current instance
+ * @param string|object $parent Parent ID or reference
+ */
+ function set_parent($parent) {
+ if ( !empty($parent) ) {
+ //Validate parent object
+ if ( is_array($parent) )
+ $parent =& $parent[0];
+
+ //Retrieve reference object if ID was supplied
+ if ( is_string($parent) ) {
+ $parent = trim($parent);
+ //Check for existence of parent
+ $lookup = $this->base_class . 's';
+ if ( isset($GLOBALS[$lookup][$parent]) ) {
+ //Get reference to parent
+ $parent =& $GLOBALS[$lookup][$parent];
+ }
+ }
+
+ //Set reference to parent field type
+ if ( is_a($parent, $this->base_class) ) {
+ $this->parent =& $parent;
+ }
+ }
+ }
+
+ /**
+ * Retrieve field type parent
+ * @return CNR_Field_Type Reference to parent field
+ */
+ function &get_parent() {
+ return $this->parent;
+ }
+
+ /**
+ * Retrieves field ID
+ * @param string|CNR_Field|array $field (optional) Field object or ID of field or options array
+ * @return string|bool Field ID, FALSE if $field is invalid
+ */
+ function get_id($field = null) {
+ $ret = false;
+ if ( ( !is_object($field) || !is_a($field, 'cnr_field_type') ) && isset($this) ) {
+ $field =& $this;
+ }
+
+ if ( is_a($field, CNR_Field_Type::get_base_class()) )
+ $id = $field->id;
+
+ if ( is_string($id) )
+ $ret = trim($id);
+
+ //Setup options
+ $options_def = array('format' => null);
+ //Get options array
+ $num_args = func_num_args();
+ $options = ( $num_args > 0 && ( $last_arg = func_get_arg($num_args - 1) ) && is_array($last_arg) ) ? $last_arg : array();
+ $options = wp_parse_args($options, $options_def);
+ //Check if field should be formatted
+ if ( is_string($ret) && !empty($options['format']) ) {
+ //Clear format option if it is an invalid value
+ if ( is_bool($options['format']) || is_int($options['format']) )
+ $options['format'] = null;
+ //Setup values
+ $wrap = array('open' => '[', 'close' => ']');
+ if ( isset($options['wrap']) && is_array($options['wrap']) )
+ $wrap = wp_parse_args($options['wrap'], $wrap);
+ $wrap_trailing = ( isset($options['wrap_trailing']) ) ? !!$options['wrap_trailing'] : true;
+ switch ( $options['format'] ) {
+ case 'attr_id' :
+ $wrap = (array('open' => '_', 'close' => '_'));
+ $wrap_trailing = false;
+ break;
+ }
+ $c = $field->get_caller();
+ $field_id = array($ret);
+ while ( !!$c ) {
+ //Add ID of current field to array
+ if ( isset($c->id) && is_a($c, $this->base_class) )
+ $field_id[] = $c->id;
+ $c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
+ }
+
+ //Add prefix to ID value
+ $field_id[] = 'attributes';
+
+ //Convert array to string
+ return $field->prefix . $wrap['open'] . implode($wrap['close'] . $wrap['open'], array_reverse($field_id)) . ( $wrap_trailing ? $wrap['close'] : '');
+ }
+ return $ret;
+ }
+
+ /**
+ * Set object title
+ * @param string $title Title for object
+ * @param string $plural Plural form of title
+ */
+ function set_title($title = '', $plural = '') {
+ $this->title = strip_tags(trim($title));
+ if ( isset($plural) )
+ $this->title_plural = strip_tags(trim($plural));
+ }
+
+ /**
+ * Retrieve object title
+ * @param bool $plural TRUE if plural title should be retrieved, FALSE otherwise (Default: FALSE)
+ */
+ function get_title($plural = false) {
+ $dir = 'current';
+ //Singular
+ if ( !$plural )
+ return $this->get_member_value('title', '','', $dir);
+ //Plural
+ $title = $this->get_member_value('title_plural', '', '', $dir);
+ if ( empty($title) ) {
+ //Use singular title for plural base
+ $title = $this->get_member_value('title', '', '', $dir);
+ //Determine technique for making title plural
+ //Get last letter
+ if ( !empty($title) ) {
+ $tail = substr($title, -1);
+ switch ( $tail ) {
+ case 's' :
+ $title .= 'es';
+ break;
+ case 'y' :
+ $title = substr($title, 0, -1) . 'ies';
+ break;
+ default :
+ $title .= 's';
+ }
+ }
+ }
+ return $title;
+ }
+
+ /**
+ * Set object description
+ * @param string $description Description for object
+ */
+ function set_description($description = '') {
+ $this->description = strip_tags(trim($description));
+ }
+
+ /**
+ * Retrieve object description
+ * @return string Object description
+ */
+ function get_description() {
+ $dir = 'current';
+ return $this->get_member_value('description', '','', $dir);
+ return $desc;
+ }
+
+ /*-** Hooks **-*/
+
+ /**
+ * Retrieve hooks added to object
+ * @return array Hooks
+ */
+ function get_hooks() {
+ return $this->get_member_value('hooks', '', array());
+ }
+
+ /**
+ * Add hook for object
+ * @see add_filter() for parameter defaults
+ * @param $tag
+ * @param $function_to_add
+ * @param $priority
+ * @param $accepted_args
+ */
+ function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+ //Create new array for tag (if not already set)
+ if ( !isset($this->hooks[$tag]) )
+ $this->hooks[$tag] = array();
+ //Build Unique ID
+ if ( is_string($function_to_add) )
+ $id = $function_to_add;
+ elseif ( is_array($function_to_add) && !empty($function_to_add) )
+ $id = strval($function_to_add[count($function_to_add) - 1]);
+ else
+ $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
+ //Add hook
+ $this->hooks[$tag][$id] = func_get_args();
+ }
+
+ /**
+ * Convenience method for adding an action for object
+ * @see add_filter() for parameter defaults
+ * @param $tag
+ * @param $function_to_add
+ * @param $priority
+ * @param $accepted_args
+ */
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
+ }
+
+ /**
+ * Convenience method for adding a filter for object
+ * @see add_filter() for parameter defaults
+ * @param $tag
+ * @param $function_to_add
+ * @param $priority
+ * @param $accepted_args
+ */
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
+ }
+
+ /*-** Dependencies **-*/
+
+ /**
+ * Adds dependency to object
+ * @param string $type Type of dependency to add (script, style)
+ * @param array|string $context When dependency will be added (@see CNR_Utilities::get_action() for possible contexts)
+ * @see wp_enqueue_script for the following of the parameters
+ * @param $handle
+ * @param $src
+ * @param $deps
+ * @param $ver
+ * @param $ex
+ */
+ function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
+ $args = func_get_args();
+ //Remove type/context from arguments
+ $args = array_slice($args, 2);
+
+ //Set context
+ if ( !is_array($context) ) {
+ //Wrap single contexts in an array
+ if ( is_string($context) )
+ $context = array($context);
+ else
+ $context = array();
+ }
+ //Add file to instance property
+ $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
+ }
+
+ /**
+ * Add script to object to be added in specified contexts
+ * @param array|string $context Array of contexts to add script to page
+ * @see wp_enqueue_script for the following of the parameters
+ * @param $handle
+ * @param $src
+ * @param $deps
+ * @param $ver
+ * @param $in_footer
+ */
+ function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
+ $args = func_get_args();
+ //Add file type to front of arguments array
+ array_unshift($args, 'scripts');
+ call_user_func_array(array(&$this, 'add_dependency'), $args);
+ }
+
+ /**
+ * Retrieve script dependencies for object
+ * @return array Script dependencies
+ */
+ function get_scripts() {
+ return $this->get_member_value('scripts', '', array());
+ }
+
+ /**
+ * Add style to object to be added in specified contexts
+ * @param array|string $context Array of contexts to add style to page
+ * @see wp_enqueue_style for the following of the parameters
+ * @param $handle
+ * @param $src
+ * @param $deps
+ * @param $ver
+ * @param $in_footer
+ */
+ function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
+ $args = func_get_args();
+ array_unshift($args, 'styles');
+ call_user_method_array('add_dependency', $this, $args);
+ }
+
+ /**
+ * Retrieve Style dependencies for object
+ * @return array Style dependencies
+ */
+ function get_styles() {
+ return $this->get_member_value('styles', '', array());
+ }
+}
\ No newline at end of file
diff --git a/includes/class.content_type.php b/includes/class.content_type.php
new file mode 100644
index 0000000..80a9837
--- /dev/null
+++ b/includes/class.content_type.php
@@ -0,0 +1,402 @@
+ description string Group description
+ * > location string Location of group on edit form
+ * > fields array Fields in group
+ * @var array
+ */
+ var $groups = array();
+
+ /* Constructors */
+
+ /**
+ * Class constructor
+ * @param string $id Content type ID
+ * @param string|bool $parent (optional) Parent to inherit properties from (Default: none)
+ * @param array $properties (optional) Properties to set for content type (Default: none)
+ */
+ function __construct($id = '', $parent = null, $properties = null) {
+ parent::__construct($id, $parent);
+
+ //Set properties
+ //TODO Iterate through additional arguments and set instance properties
+ }
+
+ /* Registration */
+
+ /**
+ * Registers current content type w/CNR
+ */
+ function register() {
+ global $cnr_content_utilities;
+ $cnr_content_utilities->register_content_type($this);
+ }
+
+ /* Getters/Setters */
+
+ /**
+ * Adds group to content type
+ * Groups are used to display related fields in the UI
+ * @param string $id Unique name for group
+ * @param string $title Group title
+ * @param string $description Short description of group's purpose
+ * @param string $location Where group will be displayed on post edit form (Default: main)
+ * @param array $fields (optional) ID's of existing fields to add to group
+ * @return object Group object
+ */
+ function &add_group($id, $title = '', $description = '', $location = 'normal', $fields = array()) {
+ //Create new group and set properties
+ $id = trim($id);
+ $this->groups[$id] =& $this->create_group($title, $description, $location);
+ //Add fields to group (if supplied)
+ if ( !empty($fields) && is_array($fields) )
+ $this->add_to_group($id, $fields);
+ return $this->groups[$id];
+ }
+
+ /**
+ * Remove specified group from content type
+ * @param string $id Group ID to remove
+ */
+ function remove_group($id) {
+ $id = trim($id);
+ if ( $this->group_exists($id) ) {
+ unset($this->groups[$id]);
+ }
+ }
+
+ /**
+ * Standardized method to create a new field group
+ * @param string $title Group title (used in meta boxes, etc.)
+ * @param string $description Short description of group's purpose
+ * @param string $location Where group will be displayed on post edit form (Default: main)
+ * @return object Group object
+ */
+ function &create_group($title = '', $description = '', $location = 'normal') {
+ $group = new stdClass();
+ $title = ( is_scalar($title) ) ? trim($title) : '';
+ $group->title = $title;
+ $description = ( is_scalar($description) ) ? trim($description) : '';
+ $group->description = $description;
+ $location = ( is_scalar($location) ) ? trim($location) : 'normal';
+ $group->location = $location;
+ $group->fields = array();
+ return $group;
+ }
+
+ /**
+ * Checks if group exists
+ * @param string $id Group name
+ * @return bool TRUE if group exists, FALSE otherwise
+ */
+ function group_exists($id) {
+ $id = trim($id);
+ //Check if group exists in content type
+ return ( !is_null($this->get_member_value('groups', $id, null)) );
+ }
+
+ /**
+ * Adds field to content type
+ * @param string $id Unique name for field
+ * @param CNR_Field_Type|string $parent Field type that this field is based on
+ * @param array $properties (optional) Field properties
+ * @param string $group (optional) Group ID to add field to
+ * @return CNR_Field Reference to new field
+ */
+ function &add_field($id, $parent, $properties = array(), $group = null) {
+ //Create new field
+ $id = trim(strval($id));
+ $field = new CNR_Field($id);
+ $field->set_parent($parent);
+ $field->set_container($this);
+ $field->set_properties($properties);
+
+ //Add field to content type
+ $this->fields[$id] =& $field;
+ //Add field to group
+ $this->add_to_group($group, $field->id);
+ return $field;
+ }
+
+ /**
+ * Removes field from content type
+ * @param string|CNR_Field $field Object or Field ID to remove
+ */
+ function remove_field($field) {
+ if ( $field instanceof CNR_Field_Type ) {
+ $field = $field->get_id();
+ }
+ if ( !is_string($field) || empty($field) )
+ return false;
+
+ //Remove from fields array
+ //$this->fields[$field] = null;
+ unset($this->fields[$field]);
+
+ //Remove field from groups
+ $this->remove_from_group($field);
+ }
+
+ /**
+ * Retrieve specified field in Content Type
+ * @param string $field Field ID
+ * @return CNR_Field Specified field
+ */
+ function &get_field($field) {
+ if ( $this->has_field($field) ) {
+ $field = trim($field);
+ $field = $this->get_member_value('fields', $field);
+ } else {
+ //Return empty field if no field exists
+ $field = new CNR_Field('');
+ }
+ return $field;
+ }
+
+ /**
+ * Checks if field exists in the content type
+ * @param string $field Field ID
+ * @return bool TRUE if field exists, FALSE otherwise
+ */
+ function has_field($field) {
+ return ( !is_string($field) || empty($field) || is_null($this->get_member_value('fields', $field, null)) ) ? false : true;
+ }
+
+ /**
+ * Adds field to a group in the content type
+ * Group is created if it does not already exist
+ * @param string|array $group ID of group (or group parameters if new group) to add field to
+ * @param string|array $fields Name or array of field(s) to add to group
+ */
+ function add_to_group($group, $fields) {
+ //Validate parameters
+ $group_id = '';
+ if ( !empty($group) ) {
+ if ( !is_array($group) ) {
+ $group = array($group, $group);
+ }
+
+ $group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
+ }
+ if ( empty($group_id) || empty($fields) )
+ return false;
+ //Create group if it doesn't exist
+ if ( !$this->group_exists($group_id) ) {
+ call_user_func_array($this->m('add_group'), $group);
+ }
+ if ( ! is_array($fields) )
+ $fields = array($fields);
+ foreach ( $fields as $field ) {
+ unset($fref);
+ if ( ! $this->has_field($field) )
+ continue;
+ $fref =& $this->get_field($field);
+ //Remove field from any other group it's in (fields can only be in one group)
+ foreach ( array_keys($this->groups) as $group_name ) {
+ if ( isset($this->groups[$group_name]->fields[$fref->id]) )
+ unset($this->groups[$group_name]->fields[$fref->id]);
+ }
+ //Add reference to field in group
+ $this->groups[$group_id]->fields[$fref->id] =& $fref;
+ }
+ }
+
+ /**
+ * Remove field from a group
+ * If no group is specified, then field is removed from all groups
+ * @param string|CNR_Field $field Field object or ID of field to remove from group
+ * @param string $group (optional) Group ID to remove field from
+ */
+ function remove_from_group($field, $group = '') {
+ //Get ID of field to remove or stop execution if field invalid
+ if ( $field instanceof CNR_Field_Type ) {
+ $field = $field->get_id();
+ }
+ if ( !is_string($field) || empty($field) )
+ return false;
+
+ //Remove field from group
+ if ( !empty($group) ) {
+ //Remove field from single group
+ if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
+ unset($group->fields[$field]);
+ }
+ } else {
+ //Remove field from all groups
+ foreach ( array_keys($this->groups) as $group ) {
+ if ( ($group =& $this->get_group($group)) && isset($group->fields[$field]) ) {
+ unset($group->fields[$field]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve specified group
+ * @param string $group ID of group to retrieve
+ * @return object Reference to specified group
+ */
+ function &get_group($group) {
+ $group = trim($group);
+ //Create group if it doesn't already exist
+ if ( ! $this->group_exists($group) )
+ $this->add_group($group);
+ $group = $this->get_member_value('groups', $group);
+ return $group;
+ }
+
+ /**
+ * Retrieve all groups in content type
+ * @return array Reference to group objects
+ */
+ function &get_groups() {
+ $groups = $this->get_member_value('groups');
+ return $groups;
+ }
+
+ /**
+ * Output fields in a group
+ * @param string $group ID of Group to output
+ * @return string Group output
+ */
+ function build_group($group) {
+ $out = array();
+ $classnames = (object) array(
+ 'multi' => 'multi_field',
+ 'single' => 'single_field',
+ 'elements' => 'has_elements'
+ );
+
+ //Stop execution if group does not exist
+ if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
+ $group_fields = ( count($group->fields) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->fields) ) && ( $f =& $group->fields[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
+ $classname = array('cnr_attributes_wrap', $group_fields);
+ $out[] = '
'; //Wrap all fields in group
+
+ //Build layout for each field in group
+ foreach ( array_keys($group->fields) as $field_id ) {
+ /**
+ * CNR_Field_Type
+ */
+ $field =& $group->fields[$field_id];
+ $field->set_caller($this);
+ //Start field output
+ $id = 'cnr_field_' . $field->get_id();
+ $class = array('cnr_attribute_wrap');
+ //If single field in group, check if field title matches group
+ if ( count($group->fields) == 1 && $group->title == $field->get_property('label') )
+ $class[] = 'group_field_title';
+ //Add flag to indicate that field was loaded on page
+ $inc = 'cnr[fields_loaded][' . $field->get_id() . ']';
+ $out[] = '';
+ $out[] = '
';
+ //Build field layout
+ $out[] = $field->build_layout();
+ //end field output
+ $out[] = '
';
+ $field->clear_caller();
+ }
+ $out[] = '
'; //Close fields container
+ //Add description if exists
+ if ( !empty($group->description) )
+ $out[] = '
' . $group->description . '
';
+ }
+
+ //Return group output
+ return implode($out);
+ }
+
+ /**
+ * Set data for a field
+ * @param string|CNR_Field $field Reference or ID of Field to set data for
+ * @param mixed $value Data to set
+ */
+ function set_data($field, $value = '') {
+ if ( 1 == func_num_args() && is_array($field) )
+ $this->data = $field;
+ else {
+ if ( $field instanceof CNR_Field_Type ) {
+ $field = $field->get_id();
+ }
+ if ( !is_string($field) || empty($field) )
+ return false;
+ $this->data[$field] = $value;
+ }
+ }
+
+ /*-** Admin **-*/
+
+ /**
+ * Adds meta boxes for post's content type
+ * Each group in content type is a separate meta box
+ * @param string $type Type of item meta boxes are being build for (post, page, link)
+ * @param string $context Location of meta box (normal, advanced, side)
+ * @param object $post Post object
+ */
+ function admin_do_meta_boxes($type, $context, $post) {
+ //Add post data to content type
+ global $cnr_content_utilities;
+ $this->set_data($cnr_content_utilities->get_item_data($post));
+
+ //Get Groups
+ $groups = array_keys($this->get_groups());
+ $priority = 'default';
+ //Iterate through groups and add meta box if it fits the context (location)
+ foreach ( $groups as $group_id ) {
+ $group =& $this->get_group($group_id);
+ if ( $context == $group->location && count($group->fields) ) {
+ //Format ID for meta box
+ $meta_box_id = $this->prefix . '_group_' . $group_id;
+ $group_args = array( 'group' => $group_id );
+ add_meta_box($meta_box_id, $group->title, $this->m('admin_build_meta_box'), $type, $context, $priority, $group_args);
+ }
+ }
+ }
+
+ /**
+ * Outputs group fields for a meta box
+ * @param object $post Post object
+ * @param array $box Meta box properties
+ */
+ function admin_build_meta_box($post, $box) {
+ //Stop execution if group not specified
+ if ( !isset($box['args']['group']) )
+ return false;
+
+ //Get ID of group to output
+ $group_id =& $box['args']['group'];
+
+ $output = array();
+ $output[] = '