Building Mobile Appsin WordPress
Presenter: Trevor MillsWeb: topquark.comEmail: [email protected]: @topquarkyPhone: +1(416)579-3655
1Tuesday, November 8, 2011
About me...
Studied Engineering, specializing in Quantum Physics at University of Toronto
Programming Web Sites since 2003
Working with WordPress since 2010
Bass player in the Juno Award-Winning band Digging Roots
2Tuesday, November 8, 2011
Web Development in 2011
WordPress maturity with Version 3.x
15% of the top 1,000,000 sites run WordPress
Mobile is overtaking desktop usage
Mobile apps are where it’s at!
3Tuesday, November 8, 2011
Web vs. Native AppsProprietary languages
Long process for App Store
Different versions for different devices
NATIVE APPS
WEB APPS
Web languages - HTML, CSS & Javascript
No app store, run from browser
One version for all devices
4Tuesday, November 8, 2011
Bridging the Phone Gap
PhoneGap is an HTML5 app platform that allows you to author
native applications with web technologies and get access to
APIs and app stores. PhoneGap leverages web technologies
developers already know best... HTML and JavaScript.
*
* ACQUIRED BY ADOBE IN OCTOBER 2011
5Tuesday, November 8, 2011
WPTouch & WPTouch Pro
Native App Builders
WordPress Mobile Pack
WordPress as Mobile App
JAMES G. PEARCE
6Tuesday, November 8, 2011
Javascript FrameworksJames PearceSr Director, Developer Relations
7Tuesday, November 8, 2011
Javascript FrameworksJames PearceSr Director, Developer Relations
David Kaneda
7Tuesday, November 8, 2011
Javascript FrameworksJames PearceSr Director, Developer Relations
David KanedaCreative Director
SENCHA TOUCH IS THE MOST ADVANCED JAVASCRIPT FRAMEWORK AVAILABLE FOR BUILDING NATIVE-FEELING, CROSS-DEVICE WEB APPS
7Tuesday, November 8, 2011
An Actual Example...
http://topquark.com/wordcamp/app/2011-toronto
THE WORDCAMP APP
8Tuesday, November 8, 2011
An Actual Example...
Data is managed in WordPress
App is rendered by a WordPress plugin
Runs on Sencha Touch
http://topquark.com/wordcamp/app/2011-toronto
THE WORDCAMP APP
8Tuesday, November 8, 2011
Features of The Conference App
Works offline (using HTML5 LocalStorage)
Includes Twitter feed and Google map
Allows you to favourite speakers & sessions
Includes Sponsors
9Tuesday, November 8, 2011
How the app......bypasses the WordPress theme
...gets data from WordPress
...is able to work offline
TAKE A DEEP BREATH......LET’S GET INTO CODE!
10Tuesday, November 8, 2011
Bypassing WP Theme
add_filter('option_rewrite_rules','conf_app_rewrite_rules');function conf_app_rewrite_rules($rules){! $p = conf_app_get_option('path');! // don't allow hijacking bloginfo('url')! if ($p == ''){! ! return $rules;! }!! $i = 'index.php?';!! $r[$p.'/?$'] = $i.QUERY_VAR.'=on'; // the app! $r[$p.'/proposals/?$'] = $i.QUERY_VAR.'=on&'.DATA_VAR.'=proposals'; // sessions data! $r[$p.'/speakers/?$'] = $i.QUERY_VAR.'=on&'.DATA_VAR.'=speakers'; // speakers data! $r[$p.'/manifest/?$'] = $i.QUERY_VAR.'=on&'.MANIFEST_VAR.'=on'; // cache manifest! $r[$p.'/appscript/?$'] = $i.QUERY_VAR.'=on&'.APPSCRIPT_VAR.'=on'; // app js !! // I want the CONF_APP rules to appear at the beginning - ! // thereby taking precedence over other rules! $rules = $r + $rules;!! return $rules;}
Use the option_rewrite_rules filter to add your own rewrite rules. Note: Permalinks must be on for this to work
11Tuesday, November 8, 2011
Bypassing WP ThemeUse the query_vars filter to register your own valid query vars.
add_filter('query_vars','conf_app_query_vars');function conf_app_query_vars($query_vars){! $query_vars[] = QUERY_VAR;! $query_vars[] = DATA_VAR;! $query_vars[] = MANIFEST_VAR;! $query_vars[] = APPSCRIPT_VAR;! return $query_vars;}
12Tuesday, November 8, 2011
Bypassing WP ThemeUse the pre_get_posts action to build the pieces of your app.
add_action( 'pre_get_posts', 'conf_app_template_redirect' );function conf_app_template_redirect(){! if ($q = get_query_var(QUERY_VAR)) {! ! // Instantiate the containers! ! conf_app_load_topquark();! ! global $topquark_objects;! ! $tq = & $topquark_objects;! !! ! switch(true){! ! case get_query_var(DATA_VAR) != '': $include = 'the-data/index.php'; break;! ! case get_query_var(MANIFEST_VAR) != '': $include = 'the-manifest/index.php'; break;! ! case get_query_var(APPSCRIPT_VAR) != '': $include = 'the-app/the-app.js.php'; break;! ! default: $include = 'the-app/index.php'; break;! ! }! !! ! include($include);! ! exit();! }}
13Tuesday, November 8, 2011
Ext.regModel('Proposal', {! fields: ["id","title","url","description","day","time","end_time","pretty_time","date","topics","room","proposal_type","sponsor","speakers","proposals"]});Ext.regModel('Speaker', {! fields: ["id","first_name","last_name","name","position","affiliation","bio","twitter","url","photo","website","pretty_website","proposals","group","members","hide_from_lineup"]});
Getting Data from WPSencha Touch requires a data MODEL (the fields)
add_filter('get_models','the_models');function the_models($Models){! if (is_the_conference_schedule_published()){! ! $Models['Proposal'] = array();! ! $Models['Proposal']['fields'] = array(! ! ! 'id', !! ! 'title', !! 'url', ! ! ! 'description','day', ! ! 'time', ! ! ! 'end_time', ! 'pretty_time','date', ! ! ! 'topics', ! 'room', ! ! 'proposal_type', ! ! ! 'sponsor', ! 'speakers', ! 'proposals'! ! );! }!! if (is_the_conference_lineup_published()){! ! $Models['Speaker'] = array();! ! $Models['Speaker']['fields'] = array(! ! ! 'id', !! ! 'first_name', 'last_name', ! ! ! 'name', ! ! 'position', ! 'affiliation', ! ! ! 'bio', ! ! 'twitter', ! 'url', ! ! ! 'photo',! ! 'website', ! 'pretty_website', ! ! ! 'proposals', 'group', ! ! 'members', ! ! ! 'hide_from_lineup'! ! );! }!! return apply_filters('the_models',$Models);}
$Models = apply_filters('get_models',array());foreach ($Models as $key => $model){! echo "Ext.regModel('$key', {\n";! $sep = "\t";! foreach ($model as $what => $details){! ! echo $sep."$what: ";! ! echo anti_escape(json_encode($details));! ! echo "\n";! ! $sep = "\t,";! }! echo "});\n";}
PHP - defining models PHP - Rendering Javascript
JS - Rendered Sencha Touch Javascript
14Tuesday, November 8, 2011
the_conf_app.SpeakerStore = new Ext.data.Store({! model: "Speaker"! ,sorters: "last_name"! ,getGroupString: function(r){! ! return r.get('last_name')[0];}! ,proxy: {"type":"scripttag","url":"http:\/\/topquark.com\/wordcamp\/app\/speakers","reader":{"type":"json","root":"speakers"}}});the_conf_app.SessionListStore = new Ext.data.Store({! model: "Proposal"! ,sorters: "time"! ,getGroupString: function(r){! ! return r.get('pretty_time');}! ,proxy: {"type":"scripttag","url":"http:\/\/topquark.com\/wordcamp\/app\/proposals","reader":{"type":"json","root":"proposals"}}});
$Stores = apply_filters('get_stores',array());foreach ($Stores as $key => $store){! echo "the_conf_app.$key = new Ext.data.Store({\n";! $sep = "\t";! foreach ($store as $what => $details){! ! echo $sep."$what: ";! ! echo anti_escape(json_encode($details));! ! echo "\n";! ! $sep = "\t,";! }! echo "});\n";}
add_filter('get_stores','the_stores');function the_stores($Stores){! $app_path = conf_app_get_option('path');! $u = get_bloginfo('url').'/'.$app_path.'/'; $s = $Stores; ! if (is_the_conference_lineup_published()){! ! $s['SpeakerStore'] = array();! ! $s['SpeakerStore']['model'] = 'Speaker';! ! $s['SpeakerStore']['sorters'] = 'last_name';! ! $s['SpeakerStore']['getGroupString'] = ! ! ! do_not_escape('function(r){! ! ! ! return r.get(\'last_name\')[0];}');! ! $s['SpeakerStore']['proxy'] = array(! ! ! 'type' => 'scripttag','url' => $u.'speakers',! ! ! 'reader' => array('type' => 'json', ! ! ! ! ! ! ! ! 'root' => 'speakers')! ! );! }! if (is_the_conference_schedule_published()){! ! $s['SessionListStore'] = array();! ! $s['SessionListStore']['model'] = 'Proposal';! ! $s['SessionListStore']['sorters'] = 'time';! ! $s['SessionListStore']['getGroupString'] = ! ! ! do_not_escape('function(r){! ! ! ! return r.get(\'pretty_time\');}');! ! $s['SessionListStore']['proxy'] = array(! ! ! 'type' => 'scripttag','url' => $u.'proposals',! ! ! 'reader' => array('type' => 'json', ! ! ! ! ! ! ! ! 'root' => 'proposals')! ! );! }! return apply_filters('the_stores',$s);}
Getting Data from WPSencha Touch requires a data STORE (the proxy)
PHP - defining stores PHP - Rendering Javascript
JS - Rendered Sencha Touch Javascript
15Tuesday, November 8, 2011
! global $topquark_objects;! $tq = & $topquark_objects;!! switch(get_query_var(DATA_VAR)){! case 'proposals':! ! $Shows = $tq->getPublished('Shows',$tq->year);! ! $output['proposals'] = array();! ! foreach ($Shows as $ShowID => $Show){! ! ! $show = array();! ! ! $show['id'] = intval($Show->getParameter('ShowID'));! ! ! $show['description'] = $Show->getParameter('ShowDescription');! ! ! // ... etc.! ! ! $output['proposals'][] = apply_filters('data_proposal',$show,$Show);! ! }! ! break;! case 'speakers':! ! $FestivalArtists = $tq->getPublished('FestivalArtists',$tq->year);! ! $output['speakers'] = array();! ! foreach ($FestivalArtists as $ArtistID => $Artist){! ! ! $artist = array();! ! ! $artist['id'] = intval($ArtistID);! ! ! $artist['last_name'] = $Artist['ArtistLastName'];! ! ! // ... etc.! ! ! $output['speakers'][] = apply_filters('data_speaker',$artist,$Artist);! ! }! ! break;! } ! header("Content-type: text/javascript");! $c = $_GET['callback'];! echo ($c ? "$c(" : '').json_encode($output).($c ? ")" : '').';';! exit();
Getting Data from WPThe data feed from WordPress should be JSONP
16Tuesday, November 8, 2011
Taking the App OfflineHTML5 provides two features to make this possible:
Cache Manifest & LocalStorage
Specify URLs to cache
Apps get 5MB for free
Saves Key/Value pairs
Value can be an array
17Tuesday, November 8, 2011
CACHE MANIFEST# Version 1# The Main App fileshttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/css/oreilly.csshttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/css/the-app.csshttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/sencha-touch.jshttp://topquark.com/wordcamp/app/2011-toronto/globalshttp://topquark.com/wordcamp/app/2011-toronto/appscripthttp://maps.google.com/maps/api/js?sensor=true
# The App Imageshttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/img/startup_640.pnghttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/img/startup.pnghttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/img/icon_57.pnghttp://topquark.com/wordcamp/wp-content/plugins/the-conference-app/the-app/resources/img/icon_114.pnghttp://topquark.com/wp-content/uploads/2011/06/TopQuarkLogo.png
# The Speaker Imageshttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/3c25b65d5c556ac17411fc851a327032_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/94bea2f1df084ceb07b862ff54934a79_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/d34fafa6a12b463c3bc35be3bbea10db_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/ce6de75934ad22c941a0771f94547514_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/da3d4e7e92dd3c7e5d0bbc01dbd2357c_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/0d66c8bf6b3b2ec9b0653b44f9858ce4_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/2731301837d70cee240f91c9e1ad3efb_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/ce3a4f5f44731fc26a3a952769c4dad7_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/7b8ff059b9a4504dfbaebd4dd190466e_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/19370a2484593358d3d903edfbcc2e6a_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/be5eafe3d6436ed959570d7632fc6344_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/1d23db2f33a765c1c0e91d45f0ddb0f7_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/4016ee41be5d3de4e1d675192cc85d82_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/38cde14ef1dd685a75cc3c332b040b65_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/3a934626afc553ad160fbb6455d34822_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/a2e0cc1151f2fcc3ceff89aa19a3963c_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/94477372feb76bff3ca7851575e715ed_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/16aeda409d2a00b51ac4c5058a3a8435_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/8c4edcb507deaa9b56743fbed520c823_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/797179aceab9977c950fe6b4475fff77_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/2ef1ba8ffbff6277210bbb2dc6210605_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/e59dee8a3306722a0d13be6370770151_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/1299e8d336a2d5faa4fc440d07ea44f8_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/6eca4708c14b4aae041335e251dd3b12_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/de6185b0ef8ac590004efa009873a2bc_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/3014f80ae412cb62a1f1688b50d1a89a_thumbhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralArtistImages/f1a15c65645488a1bcdf5ea87f68460d_thumb
http://topquark.com/wordcamp/wp-content/plugins/wordcamp-mods/the-app/css/wordcamp.csshttp://topquark.com/wordcamp/wp-content/plugins/wordcamp-mods/the-app/css/images/top-quark-banner.png# The Sponsor Imageshttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/telus_logo-200.jpghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/GB-logo-2-e1317100064535_thumb.gifhttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/jetpack-e1317100184729.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/sidebar-logo-wcd-copy-e1317100643375.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/make-web-not-war-200_thumb.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/Headshots_Logo_200.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/topquark.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/weever_wplogo_thumb.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/freshbooks247x120.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/newbox-logo2.pnghttp://topquark.com/wordcamp/wp-content/blogs.dir/5/files/assets/galleries/GeneralSponsorImages/Habaneros_thumb.png
#Everything ElseNETWORK:*
Taking the App OfflineThe Application Cache (Cache Manifest)
DECLARING IT... <html manifest="http://topquark.com/wordcamp/app/manifest">
GENERATING IT...<?php header('Content-type: text/cache-manifest'); ?>CACHE MANIFEST# Version <?php echo conf_app_get_option('manifest_version'); ?>
# The Main App files# ...etc.
# The Speaker Images# ...etc.
<?php do_action('the_conference_app_print_manifest'); ?>
#Everything ElseNETWORK:*
RESULT...
18Tuesday, November 8, 2011
Taking the App OfflineThe Application Cache (Cache Manifest) - Updating & Resetting
UPDATINGApp Cache refreshes from server only if manifest file changes
window.applicationCache.addEventListener('updateready', function(){this.swapCache(); window.location.reload();}, false);
RESETTINGChrome has chrome://appcache-internals/
19Tuesday, November 8, 2011
Taking the App OfflineUsing LocalStorage to store the data
add_filter('get_offline_stores','the_offline_stores');function the_stores($Stores){! $app_path = conf_app_get_option('path');! $u = get_bloginfo('url').'/'.$app_path.'/'; $s = $Stores; ! if (is_the_conference_lineup_published()){! ! $s['SpeakerStore'] = array();! ! $s['SpeakerStore']['model'] = 'Speaker';! ! $s['SpeakerStore']['sorters'] = 'last_name';! ! $s['SpeakerStore']['getGroupString'] = ! ! ! do_not_escape('function(r){! ! ! ! return r.get(\'last_name\')[0];}');! ! $s['SpeakerStore']['proxy'] = array(! ! 'type' => 'localstorage',! ! 'id' => 'Speaker_'.substr(md5(get_bloginfo('url')),0,5)! ! );! }! if (is_the_conference_schedule_published()){! ! $s['SessionListStore'] = array();! ! $s['SessionListStore']['model'] = 'Proposal';! ! $s['SessionListStore']['sorters'] = 'time';! ! $s['SessionListStore']['getGroupString'] = ! ! ! do_not_escape('function(r){! ! ! ! return r.get(\'pretty_time\');}');! ! $s['SessionListStore']['proxy'] = array(! ! 'type' => 'localstorage',! ! 'id' => 'Proposal_'.substr(md5(get_bloginfo('url')),0,5)! ! );! }! return apply_filters('the_offline_stores',$s);}
PHP - defining stores PHP - Rendering Javascript
JS - Rendered Sencha Touch Javascript
$Stores = apply_filters('get_offline_stores',array());foreach ($Stores as $key => $store){! echo "the_conf_app.Off$key = new Ext.data.Store({\n";! $sep = "\t";! foreach ($store as $what => $details){! ! echo $sep."$what: ";! ! echo anti_escape(json_encode($details));! ! echo "\n";! ! $sep = "\t,";! }! echo "});\n";}
the_conf_app.OffSpeakerStore = new Ext.data.OfflineStore({! model: "Speaker"! ,sorters: "last_name"! ,getGroupString: function(r){return r.get('last_name')[0];}! ,proxy: {"type":"localstorage","id":"Speaker_8c0c2"}});the_conf_app.OffSessionListStore = new Ext.data.OfflineStore({! model: "Proposal"! ,sorters: "time"! ,getGroupString: function(r){return r.get('pretty_time');}! ,proxy: {"type":"localstorage","id":"Proposal_8c0c2"}});
20Tuesday, November 8, 2011
Taking the App OfflineLocalStorage - Updating & Resetting
UPDATINGUse timestamps to see if a store has new data
If there is new data, load the Online Store from the server
Then...refresh the Offline Store with the data from the Online Store
RESETTINGChrome has chrome://settings/cookies
21Tuesday, November 8, 2011
Wouldn’t it be nice if...
...there were a WordPress plugin to do this dirty work?
22Tuesday, November 8, 2011
Introducing...BY:
...NOW IN THE REPOSITORY
22Tuesday, November 8, 2011
The App Maker
Creates an Apps custom post_type
Build the app using shortcodes
Upload splash images, icon & stylesheet as attachments
Lots of hooks to build customizations
No ads (unless you want to put them in)
23Tuesday, November 8, 2011
Hello World App/**In the beginning, there was an app...And the coder declared, let there be options:
* _is_debug_on = false (default) or true --+ sends Javascript messages to console.log() via a maybelog() call. Loads debug version of Sencha library.* _is_using_manifest = false (default) or true --+ saves data to the user's device. An app gets 5MB for free without having to ask the user for permission.* transition = slide (default), fade, pop, flip, cube, wipe (buggy) --+ the CSS3 transition to use between pages* manifest_version = (string) --+ a version number for the manifest file. Useful for forcing new manifest load.
****************************************************/[the_app transition=cube]
http://topquark.com/wordcamp/apps/hello-world
24Tuesday, November 8, 2011
/***************************************************Then the coder said, let there be [ app_item ]may it render HTML5 markup in a variety of ways
* _is_default = false (default) or true --+ makes this item the first one that appears.* title = (string) --+ the title of page. Also the title on the bottom toolbar icon.* icon = action, add, arrow_down, arrow_left, arrow_right, arrow_up, compose, delete, organize, refresh, reply, search, settings, star (default), trash, maps, locate, home* post = (int) --+ $post->ID of any WordPress post (optional)* ]]Any HTML5 markup [[ or_wordpress_shortcode ]] can be contenticized [[/ --+ create a page from scratch. (note: markup must pass WordPress editor valid elements)***************************************************/[app_item title="Testing" icon=arrow_up]This is some content with <strong>markup</strong> and <a href="http://topquark.com">a link</a>[/app_item][app_item post=1] [/app_item] +-- Note the space
Hello World App
http://topquark.com/wordcamp/apps/hello-world
24Tuesday, November 8, 2011
Hello World App
http://topquark.com/wordcamp/apps/hello-world
24Tuesday, November 8, 2011
/*********************************************************At this point, the coder paused for a smoke, whence hepredicted someone might want to group items together toappear as a list, and so let there be [ app_item_wrapper ]
* title = (string) --+ the title of page. Also the title on the bottom toolbar icon.* icon = action, add, arrow_down, arrow_left, arrow_right, arrow_up, compose, delete, organize, refresh, reply, search, settings, star (default), trash, maps, locate, home* _is_default = false (default) or true --+ makes this item the first one that appears.***************************************************/[app_item_wrapper title="Excellent" icon=star] [app_item title="Testing" icon=compose]This is some content with <strong>markup</strong> and <a href="http://topquark.com">a link</a>[/app_item] [app_item post=1] [/app_item] +-- Note the space[/app_item_wrapper]
Hello World App
http://topquark.com/wordcamp/apps/hello-world-2
25Tuesday, November 8, 2011
Hello World App
http://topquark.com/wordcamp/apps/hello-world-2
25Tuesday, November 8, 2011
[the_app]
[app_posts post_type=records title=Discography icon=settings]
[app_posts title="Events" _is_default=true icon=action category_name=events orderby=date_gmt order=asc grouped=true group_by=month indexbar=false list_template='<span class="title">{title}</span><span class="date secondary">{date:date("F jS")}</span>']
A Non-Trivial App
http://patrickgrahampercussion.com/apps/my-app/
26Tuesday, November 8, 2011
A Non-Trivial App
http://patrickgrahampercussion.com/apps/my-app/
add_filter('the_app_post_output','patrick_app_post_output',10,2);function patrick_app_post_output($o,$post){! if (get_query_var(APP_DATA_VAR) == 'post' and $o['_EventStartDate'] != ''){! ! $o['date'] = date("M d Y",strtotime($o['_EventStartDate'][0]));! ! $o['date_gmt'] = $o['_EventStartDate'][0]; if (strtotime($o['date_gmt']) < time()) return false;! }! return $o;}
26Tuesday, November 8, 2011
A Non-Trivial App
http://patrickgrahampercussion.com/apps/my-app/
26Tuesday, November 8, 2011
A Non-Trivial App
http://patrickgrahampercussion.com/apps/my-app/
26Tuesday, November 8, 2011
Presenter: Trevor MillsWeb: topquark.comEmail: [email protected]: @topquarkyPhone: +1(416)579-3655
http://borealisrecords.com/apps/catalogue
27Tuesday, November 8, 2011