From af4f1366a08f6f42d6f2c3302bd0ad24b30cc49b Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Fri, 23 Oct 2020 22:54:30 +0000 Subject: [PATCH] Adding lazy-load version of feed component to load data using AJAX after page content has been rendered. --- www/plugins/jasonwilliams/feed/Plugin.php | 39 +++ .../assets/javascript/lazyloadshortfeed.js | 10 + .../jasonwilliams/feed/classes/SocialApis.php | 287 ++++++++++++++++++ .../feed/components/ChannelList.php | 41 +++ .../feed/components/LazyLoadShortFeed.php | 47 +++ .../feed/components/ShortFeed.php | 79 +++++ .../jasonwilliams/feed/components/TagList.php | 26 ++ .../feed/components/channellist/default.htm | 5 + .../components/lazyloadshortfeed/default.htm | 4 + .../components/lazyloadshortfeed/feed.htm | 9 + .../feed/components/shortfeed/default.htm | 9 + .../feed/components/taglist/default.htm | 5 + .../feed/controllers/Channels.php | 19 ++ .../jasonwilliams/feed/controllers/Feed.php | 19 ++ .../controllers/channels/_list_toolbar.htm | 19 ++ .../controllers/channels/_reorder_toolbar.htm | 3 + .../controllers/channels/config_form.yaml | 10 + .../controllers/channels/config_list.yaml | 12 + .../controllers/channels/config_reorder.yaml | 4 + .../feed/controllers/channels/create.htm | 46 +++ .../feed/controllers/channels/index.htm | 1 + .../feed/controllers/channels/preview.htm | 22 ++ .../feed/controllers/channels/reorder.htm | 8 + .../feed/controllers/channels/update.htm | 54 ++++ .../feed/controllers/feed/_list_toolbar.htm | 19 ++ .../controllers/feed/_reorder_toolbar.htm | 3 + .../feed/controllers/feed/config_form.yaml | 10 + .../feed/controllers/feed/config_list.yaml | 12 + .../feed/controllers/feed/config_reorder.yaml | 4 + .../feed/controllers/feed/create.htm | 46 +++ .../feed/controllers/feed/index.htm | 1 + .../feed/controllers/feed/preview.htm | 22 ++ .../feed/controllers/feed/reorder.htm | 8 + .../feed/controllers/feed/update.htm | 54 ++++ .../jasonwilliams/feed/lang/en-ca/lang.php | 6 + .../jasonwilliams/feed/models/Channels.php | 33 ++ .../jasonwilliams/feed/models/FeedItem.php | 65 ++++ .../jasonwilliams/feed/models/Links.php | 35 +++ .../jasonwilliams/feed/models/Settings.php | 14 + .../jasonwilliams/feed/models/Tags.php | 40 +++ .../feed/models/channels/columns.yaml | 10 + .../feed/models/channels/fields.yaml | 21 ++ .../feed/models/feeditem/columns.yaml | 9 + .../feed/models/feeditem/fields.yaml | 20 ++ .../feed/models/settings/fields.yaml | 51 ++++ www/plugins/jasonwilliams/feed/plugin.yaml | 20 ++ .../jasonwilliams_feed_initial_setup.php | 57 ++++ .../jasonwilliams/feed/updates/version.yaml | 3 + .../jason-williamsca/assets/css/resume.css | 24 -- .../jason-williamsca/assets/css/theme.css | 89 +++++- .../assets/images/loading.svg | 1 + .../assets/javascript/common.js | 17 ++ .../assets/javascript/feedlayout.js | 45 +++ .../assets/javascript/resume.js | 25 +- .../assets/javascript/tooltips.js | 3 - .../jason-williamsca/layouts/default.htm | 3 + .../jason-williamsca/layouts/sidesubmenu.htm | 2 + www/themes/jason-williamsca/pages/contact.htm | 12 +- www/themes/jason-williamsca/pages/feed.htm | 41 ++- .../jason-williamsca/pages/maintenance.htm | 29 ++ .../jason-williamsca/pages/pgp-public-key.htm | 17 ++ www/themes/jason-williamsca/pages/resume.htm | 35 ++- .../jason-williamsca/pages/shrapnel.htm | 6 + www/themes/jason-williamsca/pages/test.htm | 8 + www/themes/jason-williamsca/pages/uh-oh.htm | 15 + www/themes/jason-williamsca/pages/welcome.htm | 13 + .../partials/contactform/default.htm | 12 +- .../partials/feed/placeholder.htm | 5 + .../partials/feed/template.htm | 45 +++ .../jason-williamsca/partials/pgpkey.htm | 32 ++ .../jason-williamsca/partials/social.htm | 21 +- 71 files changed, 1753 insertions(+), 88 deletions(-) create mode 100644 www/plugins/jasonwilliams/feed/Plugin.php create mode 100644 www/plugins/jasonwilliams/feed/assets/javascript/lazyloadshortfeed.js create mode 100644 www/plugins/jasonwilliams/feed/classes/SocialApis.php create mode 100644 www/plugins/jasonwilliams/feed/components/ChannelList.php create mode 100644 www/plugins/jasonwilliams/feed/components/LazyLoadShortFeed.php create mode 100644 www/plugins/jasonwilliams/feed/components/ShortFeed.php create mode 100644 www/plugins/jasonwilliams/feed/components/TagList.php create mode 100644 www/plugins/jasonwilliams/feed/components/channellist/default.htm create mode 100644 www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/default.htm create mode 100644 www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/feed.htm create mode 100644 www/plugins/jasonwilliams/feed/components/shortfeed/default.htm create mode 100644 www/plugins/jasonwilliams/feed/components/taglist/default.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/Channels.php create mode 100644 www/plugins/jasonwilliams/feed/controllers/Feed.php create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/_list_toolbar.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/_reorder_toolbar.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/config_form.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/config_list.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/config_reorder.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/create.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/index.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/preview.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/reorder.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/channels/update.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/_list_toolbar.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/_reorder_toolbar.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/config_form.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/config_list.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/config_reorder.yaml create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/create.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/index.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/preview.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/reorder.htm create mode 100644 www/plugins/jasonwilliams/feed/controllers/feed/update.htm create mode 100644 www/plugins/jasonwilliams/feed/lang/en-ca/lang.php create mode 100644 www/plugins/jasonwilliams/feed/models/Channels.php create mode 100644 www/plugins/jasonwilliams/feed/models/FeedItem.php create mode 100644 www/plugins/jasonwilliams/feed/models/Links.php create mode 100644 www/plugins/jasonwilliams/feed/models/Settings.php create mode 100644 www/plugins/jasonwilliams/feed/models/Tags.php create mode 100644 www/plugins/jasonwilliams/feed/models/channels/columns.yaml create mode 100644 www/plugins/jasonwilliams/feed/models/channels/fields.yaml create mode 100644 www/plugins/jasonwilliams/feed/models/feeditem/columns.yaml create mode 100644 www/plugins/jasonwilliams/feed/models/feeditem/fields.yaml create mode 100644 www/plugins/jasonwilliams/feed/models/settings/fields.yaml create mode 100644 www/plugins/jasonwilliams/feed/plugin.yaml create mode 100644 www/plugins/jasonwilliams/feed/updates/jasonwilliams_feed_initial_setup.php create mode 100644 www/plugins/jasonwilliams/feed/updates/version.yaml create mode 100644 www/themes/jason-williamsca/assets/images/loading.svg create mode 100644 www/themes/jason-williamsca/assets/javascript/common.js create mode 100644 www/themes/jason-williamsca/assets/javascript/feedlayout.js delete mode 100644 www/themes/jason-williamsca/assets/javascript/tooltips.js create mode 100644 www/themes/jason-williamsca/pages/maintenance.htm create mode 100644 www/themes/jason-williamsca/pages/pgp-public-key.htm create mode 100644 www/themes/jason-williamsca/pages/shrapnel.htm create mode 100644 www/themes/jason-williamsca/pages/test.htm create mode 100644 www/themes/jason-williamsca/pages/uh-oh.htm create mode 100644 www/themes/jason-williamsca/partials/feed/placeholder.htm create mode 100644 www/themes/jason-williamsca/partials/feed/template.htm create mode 100644 www/themes/jason-williamsca/partials/pgpkey.htm diff --git a/www/plugins/jasonwilliams/feed/Plugin.php b/www/plugins/jasonwilliams/feed/Plugin.php new file mode 100644 index 0000000..39252a4 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/Plugin.php @@ -0,0 +1,39 @@ + [ + 'label' => 'Feed', + 'description' => 'Manage API keys and settings related to the feed plugin', + 'icon' => 'icon-newspaper-o', + 'class' => 'JasonWilliams\Feed\Models\Settings', + 'order' => 500, + ] + ]; + } + + public function registerComponents() + { + return [ + '\JasonWilliams\Feed\Components\ShortFeed' => 'ShortFeed', + '\JasonWilliams\Feed\Components\LazyLoadShortFeed' => 'LazyLoadShortFeed', + '\JasonWilliams\Feed\Components\ChannelList' => 'ChannelList', + '\JasonWilliams\Feed\Components\TagList' => 'TagList' + ]; + } + + public function registerSchedule($schedule) + { + $schedule->call(function() { + SocialApis::updateTwitter(); + SocialApis::updateInstagram(); + SocialApis::updateFoursquare(); + })->everyThirtyMinutes(); + } +} diff --git a/www/plugins/jasonwilliams/feed/assets/javascript/lazyloadshortfeed.js b/www/plugins/jasonwilliams/feed/assets/javascript/lazyloadshortfeed.js new file mode 100644 index 0000000..c091444 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/assets/javascript/lazyloadshortfeed.js @@ -0,0 +1,10 @@ +$(() => { + var obj = {}; + if (typeof renderPartial !== 'string') obj = { '@feed': '#feedarea' } + else obj[renderPartial] = '#feedarea' + + $.request('LazyLoadShortFeed::onUpdateRequested', { + update: obj, + complete: () => { $(window).trigger('feedLoaded') } + }) +}) diff --git a/www/plugins/jasonwilliams/feed/classes/SocialApis.php b/www/plugins/jasonwilliams/feed/classes/SocialApis.php new file mode 100644 index 0000000..a71820b --- /dev/null +++ b/www/plugins/jasonwilliams/feed/classes/SocialApis.php @@ -0,0 +1,287 @@ + 0) + { + foreach ($tweets as $tweet) + { + // Skip tweets created by IFTTT if they have an image attached (because they came from Instagram) + if (!isset($tweet['retweet_status']) && strpos($tweet['source'], "IFTTT") && isset($tweet['entities']['media'])) continue; + + // Skip any tweets created by Foursquare + if (!isset($tweet['retweet_status']) && strpos($tweet['source'], "Foursquare")) continue; + + $extradata = array(); + + // Get the timestamp before we go down a layer to retweets + $timestamp = strtotime($tweet['created_at']); + + // Set extra data if this is a retweet + if (isset($tweet['retweeted_status'])) + { + $tweet = $tweet['retweeted_status']; + $extradata['opuser'] = $tweet['user']['screen_name']; + $extradata['opname'] = $tweet['user']['name']; + $extradata['opimg'] = $tweet['user']['profile_image_url_https']; + } + + // Set extra data if there's an attached image + if (isset($tweet['entities']['media'])) $extradata['img'] = $tweet['entities']['media'][0]['media_url_https']; + + // Convert any extra data into a JSON object + $extradata = (sizeof($extradata) > 0) ? json_encode($extradata) : ""; + + // Create a new FeedItem + $feeditem = new FeedItem; + $feeditem->timestamp = $timestamp; + $feeditem->channel_id = 2; + $feeditem->title = $tweet['full_text']; + $feeditem->link = "https://twitter.com/i/web/status/".$tweet['id_str']; + $feeditem->extra = $extradata; + $feeditem->save(); + + // Are there links to include? + if (sizeof($tweet['entities']['urls']) > 0) + { + foreach ($tweet['entities']['urls'] as $url) + { + $feeditem->links()->create([ + 'addr' => $url['url'], + 'display' =>$url['display_url'] + ]); + } + } + + // Are there tags to include? + if (sizeof($tweet['entities']['hashtags'])) + { + foreach ($tweet['entities']['hashtags'] as $tag) + { + $feeditem->tags()->create([ + 'tag' => $tag['text'] + ]); + } + } + } + + // Set the latest item ID + Settings::set('twitter_latest_tweet', $tweets[sizeof($tweets) - 1]['id_str']); + } + } + + // Update the database with the latest instagram posts + public static function updateInstagram() + { + $posts = self::fetchInstagram(); + + if (sizeof($posts) > 0) + { + foreach ($posts as $post) + { + // Skip videos and albums + if ($post['media_type'] != "IMAGE") continue; + + // Create a new FeedItem + $feeditem = new FeedItem; + $feeditem->timestamp = strtotime($post['timestamp']); + $feeditem->channel_id = 3; + $feeditem->title = (isset($post['caption'])) ? $post['caption'] : ""; + $feeditem->link = $post['permalink']; + $feeditem->extra = json_encode(['img' => $post['media_url']]); + $feeditem->save(); + + // Are there tags to include? + if (isset($post['caption']) && strpos($post['caption'], '#')) + { + preg_match_all('%\B#\w*[a-zA-Z]+\w*%', $post['caption'], $hashtags); + foreach ($hashtags[0] as $tag) + { + $feeditem->tags()->create([ + 'tag' => substr($tag, 1) + ]); + } + } + } + + // Set the latest item ID + Settings::set('instagram_latest_post', strtotime(end($posts)['timestamp'])); + } + } + + // Update the database with the latest foursquare posts + public static function updateFoursquare() + { + $posts = self::fetchFoursquare(); + + if (sizeof($posts) > 0) + { + foreach ($posts as $post) + { + $extradata = [ + 'lat' => $post['venue']['location']['lat'], + 'lng' => $post['venue']['location']['lng'], + 'address' => [] + ]; + + // Build the address + if (isset($post['venue']['location']['address'])) $extradata['address'][] = $post['venue']['location']['address']; + if (isset($post['venue']['location']['city'])) $extradata['address'][] = $post['venue']['location']['city']; + if (isset($post['venue']['location']['country'])) $extradata['address'][] = $post['venue']['location']['country']; + $extradata['address'] = implode(', ', $extradata['address']); + + // Create a new FeedItem + $feeditem = new FeedItem; + $feeditem->timestamp = $post['createdAt']; + $feeditem->channel_id = 6; + $feeditem->title = $post['venue']['name']; + $feeditem->body = (isset($post['shout'])) ? $post['shout'] : ""; + $feeditem->extra = json_encode($extradata); + $feeditem->save(); + + // Are there tags to include? + if (isset($post['shout']) && strpos($post['shout'], '#')) + { + preg_match_all('%\B#\w*[a-zA-Z]+\w*%', $post['shout'], $hashtags); + foreach ($hashtags[0] as $tag) + { + $feeditem->tags()->create([ + 'tag' => substr($tag, 1) + ]); + } + } + } + + // Set the latest item ID + Settings::set('foursquare_latest_post', end($posts)['createdAt']); + } + } + + // Fetch and return the latest tweets + private static function fetchTwitter() + { + // Set the API endpoint + $endpoint = "https://api.twitter.com/1.1/statuses/user_timeline.json"; + + // Set request parameteres + $req_params = array( + 'screen_name' => Settings::get('twitter_username'), + 'count' => 200, + 'since_id' => Settings::get('twitter_latest_tweet'), + 'tweet_mode' => 'extended', + 'exclude_replies' => true, + 'include_rts' => true + ); + + // Set oauth parameters + self::$authobject = array( + 'oauth_token' => Settings::get('twitter_access_token'), + 'oauth_consumer_key' => Settings::get('twitter_consumer_key'), + 'oauth_nonce' => time(), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => time(), + 'oauth_version' => '1.0' + ); + + // Add parameters to oauth array + self::$authobject = array_merge(self::$authobject, $req_params); + + // Generate the oauth signautre + $base_string = self::buildBaseString($endpoint, "GET", self::$authobject); + $composite_key = rawurlencode(Settings::get('twitter_consumer_secret'))."&".rawurlencode(Settings::get('twitter_access_token_secret')); + self::$authobject['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_string, $composite_key, true)); + + // Create the URL by adding the parameters to the endpoint + $endpoint = $endpoint."?".http_build_query($req_params); + + $tweets = array_reverse(json_decode(Http::get($endpoint, function($http) + { + $http->header(self::buildAuthorizationHeader(self::$authobject)); + }), true)); + + return $tweets; + } + + // Fetch and return the latest instagram posts + private static function fetchInstagram() + { + $endpoint = "https://graph.instagram.com/me/media"; + + // Set the fields we want back + $fields = [ + 'id', + 'ig-id', + 'caption', + 'media_type', + 'media_url', + 'permalink', + 'timestamp' + ]; + + // Create the URL by adding the parameters to the endpoint + $endpoint = $endpoint."?".http_build_query([ + 'access_token' => Settings::get('instagram_access_token'), + 'fields' => implode(',', $fields), + 'limit' => 100 + ]); + + $posts = json_decode(Http::get($endpoint), true); + $posts = array_reverse($posts['data']); + + foreach($posts as $postkey => $post) + { + if (strtotime($post['timestamp']) <= Settings::get('instagram_latest_post')) unset($posts[$postkey]); + } + + return $posts; + } + + // Fetch and return the latest foursquare checkins + private static function fetchFoursquare() + { + $endpoint = "https://api.foursquare.com/v2/users/self/checkins"; + + // Create the URL by adding the parameters to the endpoint + $endpoint = $endpoint."?".http_build_query([ + 'oauth_token' => Settings::get('foursquare_access_token'), + 'v' => date('Ymd'), + 'limit' => 250, + 'sort' => 'oldestfirst', + 'afterTimestamp' => Settings::get('foursquare_latest_post') + 1 + ]); + + $posts = json_decode(Http::get($endpoint), true); + return $posts['response']['checkins']['items']; + } + + // Function to build a base string for the twitter API + private static function buildBaseString($baseURI, $method, $params) { + $r = array(); + ksort($params); + foreach ($params as $key => $value) { + $r[] = $key.'='.rawurlencode($value); + } + return $method.'&'.rawurlencode($baseURI).'&'.rawurlencode(implode('&', $r)); + } + + // Function to build the OAuth authorization header for twitter + private static function buildAuthorizationHeader($oauth) { + $r = 'Authorization: OAuth '; + $values = array(); + foreach($oauth as $key=>$value) + $values[] = $key.'="'.rawurlencode($value).'"'; + $r .= implode(', ', $values); + return $r; + } + + private static $authobject; +} diff --git a/www/plugins/jasonwilliams/feed/components/ChannelList.php b/www/plugins/jasonwilliams/feed/components/ChannelList.php new file mode 100644 index 0000000..4d188eb --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/ChannelList.php @@ -0,0 +1,41 @@ + 'Channel List', + 'description' => 'Displays a list of feed channels.' + ]; + } + + public function defineProperties() + { + return [ + 'includeEmpty' => [ + 'title' => 'Include Empty?', + 'description' => 'Include channels with no posts?', + 'type' => 'checkbox', + 'default' => true + ] + ]; + } + + public function onRun() + { + $this->page['channels'] = Channels::orderBy('id')->withCount('feeditems')->get(); + + // If applicable, remove empty channels + if (!$this->property('includeEmpty')) + { + foreach($this->page['channels'] as $key => $value) + { + if (!$value->feeditems_count) unset($this->page['channels'][$key]); + } + } + } +} diff --git a/www/plugins/jasonwilliams/feed/components/LazyLoadShortFeed.php b/www/plugins/jasonwilliams/feed/components/LazyLoadShortFeed.php new file mode 100644 index 0000000..5b3cd8c --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/LazyLoadShortFeed.php @@ -0,0 +1,47 @@ + 'Short Feed (Lazy Load)', + 'description' => 'Displays a mini-feed of the most recent feed items, retrieved after other page content has been loaded' + ]; + } + + public function defineProperties() + { + return [ + 'maxItems' => [ + 'title' => 'Max items', + 'description' => 'How many feed items should be displayed?', + 'default' => 10, + 'type' => 'string', + 'validationPattern' => '^[0-9]+$', + 'validationMessage' => 'The Max items property must be numeric' + ], + 'renderPartial' => [ + 'title' => 'Render partial', + 'description' => 'The path to the partial that will be used as a template to render feed items. @feed is provided by the plugin itself.', + 'default' => '@feed', + 'type' => 'string' + ] + ]; + } + + public function onRun() + { + $this->page['renderPartial'] = $this->property('renderPartial'); + $this->addJs('/plugins/jasonwilliams/feed/assets/javascript/lazyloadshortfeed.js'); + } + + public function onUpdateRequested() + { + date_default_timezone_set('America/Edmonton'); + $this->page['posts'] = FeedItem::orderBy('timestamp', 'desc')->take($this->property('maxItems'))->get(); + } +} diff --git a/www/plugins/jasonwilliams/feed/components/ShortFeed.php b/www/plugins/jasonwilliams/feed/components/ShortFeed.php new file mode 100644 index 0000000..748acf0 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/ShortFeed.php @@ -0,0 +1,79 @@ + 'Short Feed', + 'description' => 'Displays a mini-feed of the most recent feed items, loaded with other page content' + ]; + } + + public function defineProperties() + { + return [ + 'channelFilter' => [ + 'title' => 'Channels', + 'description' => 'A comma-separated list of channels to include in feed.', + 'type' => 'string', + 'default' => '' + ], + 'tagFilter' => [ + 'title' => 'Tag', + 'description' => 'The tag to display in the feed.', + 'type' => 'string', + 'default' => '' + ], + 'maxItems' => [ + 'title' => 'Max items', + 'description' => 'How many feed items should be displayed?', + 'default' => 10, + 'type' => 'string', + 'validationPattern' => '^[0-9]+$', + 'validationMessage' => 'The Max items property must be numeric' + ] + ]; + } + + public function onRun() + { + date_default_timezone_set('America/Edmonton'); + + // Set up the results query + $results = FeedItem::orderBy('timestamp', 'desc')->take($this->property('maxItems')); + + // Do we need to filter based on a tag? + if ($this->property('tagFilter') != null) + { + $results->join('jasonwilliams_feed_tags', 'jasonwilliams_feed_.id', '=', 'jasonwilliams_feed_tags.feed_item_id'); + $results->where('tag', $this->property('tagFilter')); + } + + // Do we need to filter based on the channel? + if ($this->property('channelFilter') != null && $this->property('channelFilter') != 'all') + { + $this->channelfilter = explode(',', $this->property('channelFilter')); + + $results->whereHas('channel', function($q) { + $firstchannel = true; + + foreach ($this->channelfilter as $channel) + { + if ($firstchannel) $q->where('slug', $channel); + else $q->orWhere('slug', $channel); + + $firstchannel = false; + } + }); + } + + $this->page['posts'] = $results->get(); + } +} diff --git a/www/plugins/jasonwilliams/feed/components/TagList.php b/www/plugins/jasonwilliams/feed/components/TagList.php new file mode 100644 index 0000000..5dfbe47 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/TagList.php @@ -0,0 +1,26 @@ + 'Tag List', + 'description' => 'Displays a list of feed tags.' + ]; + } + + public function defineProperties() + { + return []; + } + + public function onRun() + { + $this->page['tags'] = Tags::groupBy('tag')->select(Db::raw('tag, count(*) as count'))->orderBy('count', 'desc')->get(); + } +} diff --git a/www/plugins/jasonwilliams/feed/components/channellist/default.htm b/www/plugins/jasonwilliams/feed/components/channellist/default.htm new file mode 100644 index 0000000..e00ee5f --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/channellist/default.htm @@ -0,0 +1,5 @@ + diff --git a/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/default.htm b/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/default.htm new file mode 100644 index 0000000..c4e17b7 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/default.htm @@ -0,0 +1,4 @@ +
Loading...
+{% put scripts %} + +{% endput %} diff --git a/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/feed.htm b/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/feed.htm new file mode 100644 index 0000000..4f01416 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/lazyloadshortfeed/feed.htm @@ -0,0 +1,9 @@ + diff --git a/www/plugins/jasonwilliams/feed/components/shortfeed/default.htm b/www/plugins/jasonwilliams/feed/components/shortfeed/default.htm new file mode 100644 index 0000000..4f01416 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/shortfeed/default.htm @@ -0,0 +1,9 @@ + diff --git a/www/plugins/jasonwilliams/feed/components/taglist/default.htm b/www/plugins/jasonwilliams/feed/components/taglist/default.htm new file mode 100644 index 0000000..1cf10e0 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/components/taglist/default.htm @@ -0,0 +1,5 @@ + diff --git a/www/plugins/jasonwilliams/feed/controllers/Channels.php b/www/plugins/jasonwilliams/feed/controllers/Channels.php new file mode 100644 index 0000000..45cff0b --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/Channels.php @@ -0,0 +1,19 @@ + + + + + diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/_reorder_toolbar.htm b/www/plugins/jasonwilliams/feed/controllers/channels/_reorder_toolbar.htm new file mode 100644 index 0000000..4c3425d --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/_reorder_toolbar.htm @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/config_form.yaml b/www/plugins/jasonwilliams/feed/controllers/channels/config_form.yaml new file mode 100644 index 0000000..3b4696e --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/config_form.yaml @@ -0,0 +1,10 @@ +name: Channels +form: $/jasonwilliams/feed/models/channels/fields.yaml +modelClass: jasonwilliams\feed\Models\Channels +defaultRedirect: jasonwilliams/feed/channels +create: + redirect: 'jasonwilliams/feed/channels/update/:id' + redirectClose: jasonwilliams/feed/channels +update: + redirect: jasonwilliams/feed/channels + redirectClose: jasonwilliams/feed/channels diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/config_list.yaml b/www/plugins/jasonwilliams/feed/controllers/channels/config_list.yaml new file mode 100644 index 0000000..1a8662d --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/config_list.yaml @@ -0,0 +1,12 @@ +title: Channels +modelClass: jasonwilliams\feed\Models\Channels +list: $/jasonwilliams/feed/models/channels/columns.yaml +recordUrl: 'jasonwilliams/feed/channels/update/:id' +noRecordsMessage: 'backend::lang.list.no_records' +recordsPerPage: 20 +showSetup: true +showCheckboxes: true +toolbar: + buttons: list_toolbar + search: + prompt: 'backend::lang.list.search_prompt' diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/config_reorder.yaml b/www/plugins/jasonwilliams/feed/controllers/channels/config_reorder.yaml new file mode 100644 index 0000000..1c1aa6c --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/config_reorder.yaml @@ -0,0 +1,4 @@ +title: Channels +modelClass: jasonwilliams\feed\Models\Channels +toolbar: + buttons: reorder_toolbar diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/create.htm b/www/plugins/jasonwilliams/feed/controllers/channels/create.htm new file mode 100644 index 0000000..30c1cee --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/create.htm @@ -0,0 +1,46 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
+ formRender() ?> +
+ +
+
+ + + + + +
+
+ + + + +

fatalError)) ?>

+

+ \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/index.htm b/www/plugins/jasonwilliams/feed/controllers/channels/index.htm new file mode 100644 index 0000000..ea43a36 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/index.htm @@ -0,0 +1 @@ +listRender() ?> diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/preview.htm b/www/plugins/jasonwilliams/feed/controllers/channels/preview.htm new file mode 100644 index 0000000..a5fd5a0 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/preview.htm @@ -0,0 +1,22 @@ + + + + +fatalError): ?> + +
+ formRenderPreview() ?> +
+ + +

fatalError) ?>

+ + +

+ + + +

\ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/reorder.htm b/www/plugins/jasonwilliams/feed/controllers/channels/reorder.htm new file mode 100644 index 0000000..cf71506 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/reorder.htm @@ -0,0 +1,8 @@ + + + + +reorderRender() ?> \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/channels/update.htm b/www/plugins/jasonwilliams/feed/controllers/channels/update.htm new file mode 100644 index 0000000..9dbbff2 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/channels/update.htm @@ -0,0 +1,54 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
+ formRender() ?> +
+ +
+
+ + + + + + + +
+
+ + + +

fatalError)) ?>

+

+ \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/_list_toolbar.htm b/www/plugins/jasonwilliams/feed/controllers/feed/_list_toolbar.htm new file mode 100644 index 0000000..1ce9228 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/_list_toolbar.htm @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/_reorder_toolbar.htm b/www/plugins/jasonwilliams/feed/controllers/feed/_reorder_toolbar.htm new file mode 100644 index 0000000..db14605 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/_reorder_toolbar.htm @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/config_form.yaml b/www/plugins/jasonwilliams/feed/controllers/feed/config_form.yaml new file mode 100644 index 0000000..b3ee563 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/config_form.yaml @@ -0,0 +1,10 @@ +name: Feed +form: $/jasonwilliams/feed/models/feeditem/fields.yaml +modelClass: jasonwilliams\feed\Models\FeedItem +defaultRedirect: jasonwilliams/feed/feed +create: + redirect: 'jasonwilliams/feed/feed/update/:id' + redirectClose: jasonwilliams/feed/feed +update: + redirect: jasonwilliams/feed/feed + redirectClose: jasonwilliams/feed/feed diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/config_list.yaml b/www/plugins/jasonwilliams/feed/controllers/feed/config_list.yaml new file mode 100644 index 0000000..d5fc028 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/config_list.yaml @@ -0,0 +1,12 @@ +title: Feed +modelClass: jasonwilliams\feed\Models\FeedItem +list: $/jasonwilliams/feed/models/feeditem/columns.yaml +recordUrl: 'jasonwilliams/feed/feed/update/:id' +noRecordsMessage: 'backend::lang.list.no_records' +recordsPerPage: 20 +showSetup: true +showCheckboxes: true +toolbar: + buttons: list_toolbar + search: + prompt: 'backend::lang.list.search_prompt' diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/config_reorder.yaml b/www/plugins/jasonwilliams/feed/controllers/feed/config_reorder.yaml new file mode 100644 index 0000000..329b69f --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/config_reorder.yaml @@ -0,0 +1,4 @@ +title: Feed +modelClass: jasonwilliams\feed\Models\FeedItem +toolbar: + buttons: reorder_toolbar diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/create.htm b/www/plugins/jasonwilliams/feed/controllers/feed/create.htm new file mode 100644 index 0000000..84028f4 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/create.htm @@ -0,0 +1,46 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
+ formRender() ?> +
+ +
+
+ + + + + +
+
+ + + + +

fatalError)) ?>

+

+ \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/index.htm b/www/plugins/jasonwilliams/feed/controllers/feed/index.htm new file mode 100644 index 0000000..ea43a36 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/index.htm @@ -0,0 +1 @@ +listRender() ?> diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/preview.htm b/www/plugins/jasonwilliams/feed/controllers/feed/preview.htm new file mode 100644 index 0000000..34d964a --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/preview.htm @@ -0,0 +1,22 @@ + + + + +fatalError): ?> + +
+ formRenderPreview() ?> +
+ + +

fatalError) ?>

+ + +

+ + + +

\ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/reorder.htm b/www/plugins/jasonwilliams/feed/controllers/feed/reorder.htm new file mode 100644 index 0000000..459cc3c --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/reorder.htm @@ -0,0 +1,8 @@ + + + + +reorderRender() ?> \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/controllers/feed/update.htm b/www/plugins/jasonwilliams/feed/controllers/feed/update.htm new file mode 100644 index 0000000..bf4f6b0 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/controllers/feed/update.htm @@ -0,0 +1,54 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
+ formRender() ?> +
+ +
+
+ + + + + + + +
+
+ + + +

fatalError)) ?>

+

+ \ No newline at end of file diff --git a/www/plugins/jasonwilliams/feed/lang/en-ca/lang.php b/www/plugins/jasonwilliams/feed/lang/en-ca/lang.php new file mode 100644 index 0000000..0d04f01 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/lang/en-ca/lang.php @@ -0,0 +1,6 @@ + [ + 'name' => 'Social Feed', + 'description' => 'Captures social media posts from a variety of services and consolidates them into a single feed.' + ] +]; diff --git a/www/plugins/jasonwilliams/feed/models/Channels.php b/www/plugins/jasonwilliams/feed/models/Channels.php new file mode 100644 index 0000000..5d677c9 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/Channels.php @@ -0,0 +1,33 @@ + ['jasonwilliams\feed\Models\FeedItem', 'key' => 'channel_id'] + ]; +} diff --git a/www/plugins/jasonwilliams/feed/models/FeedItem.php b/www/plugins/jasonwilliams/feed/models/FeedItem.php new file mode 100644 index 0000000..b1cb2e4 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/FeedItem.php @@ -0,0 +1,65 @@ + 'jasonwilliams\feed\Models\Links', + 'tags' => 'jasonwilliams\feed\Models\Tags' + ]; + + public $belongsTo = [ + 'channel' => ['jasonwilliams\feed\Models\Channels', 'key' => 'channel_id'] + ]; + + public function afterFetch() + { + // Add clickable hashtags to the title field + $this->title = preg_replace('/#(\w*[a-zA-Z]+\w*)/', '#$1', $this->title); + + // Add a friendlytime field + $this->friendlytime = date('M j, Y', $this->timestamp); + + // Expand extras to an object + if ($this->extra) $this->extra = json_decode($this->extra); + + // Add twitter user links + if ($this->channel_id == 2) $this->title = preg_replace('/@([a-zA-Z0-9_]+)/', '@$1', $this->title); + + // Add instagram user links + if ($this->channel_id == 3) $this->title = preg_replace('/@([a-zA-Z0-9_]+)/', '@$1', $this->title); + + // Add foursquare user Links + if ($this->channel_id == 6 && !empty($this->body)) $this->body = preg_replace('/@([a-zA-Z0-9_]+)/', '@$1', $this->body); + + // Find and replace links + foreach($this->links as $link) + { + $this->title = str_replace($link->addr, "addr."\">".$link->display."", $this->title); + } + } +} diff --git a/www/plugins/jasonwilliams/feed/models/Links.php b/www/plugins/jasonwilliams/feed/models/Links.php new file mode 100644 index 0000000..d12c73a --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/Links.php @@ -0,0 +1,35 @@ + 'jasonwilliams\feed\Models\FeedItem' + ]; +} diff --git a/www/plugins/jasonwilliams/feed/models/Settings.php b/www/plugins/jasonwilliams/feed/models/Settings.php new file mode 100644 index 0000000..1975cf7 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/Settings.php @@ -0,0 +1,14 @@ + ['jasonwilliams\feed\Models\FeedItem', 'key' => 'feed_item_id'] + ]; + + public function afterFetch() + { + //dd($this->feeditem); + } +} diff --git a/www/plugins/jasonwilliams/feed/models/channels/columns.yaml b/www/plugins/jasonwilliams/feed/models/channels/columns.yaml new file mode 100644 index 0000000..cc0e2af --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/channels/columns.yaml @@ -0,0 +1,10 @@ +columns: + id: + label: 'Channel ID' + type: number + sortable: true + title: + label: Title + type: text + searchable: true + sortable: false diff --git a/www/plugins/jasonwilliams/feed/models/channels/fields.yaml b/www/plugins/jasonwilliams/feed/models/channels/fields.yaml new file mode 100644 index 0000000..0f34f6d --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/channels/fields.yaml @@ -0,0 +1,21 @@ +fields: + title: + label: 'Channel Title' + span: left + type: text + slug: + label: Slug + span: right + preset: + field: title + type: slug + type: text + icon: + label: Icon + span: left + type: text + comment: 'Icon code from FontAwesome' + colour: + label: 'Icon Colour' + span: auto + type: colorpicker diff --git a/www/plugins/jasonwilliams/feed/models/feeditem/columns.yaml b/www/plugins/jasonwilliams/feed/models/feeditem/columns.yaml new file mode 100644 index 0000000..0ae8fbe --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/feeditem/columns.yaml @@ -0,0 +1,9 @@ +columns: + title: + label: 'Post Title' + type: text + searchable: true + timestamp: + label: 'UNIX Timestamp' + type: number + sortable: true diff --git a/www/plugins/jasonwilliams/feed/models/feeditem/fields.yaml b/www/plugins/jasonwilliams/feed/models/feeditem/fields.yaml new file mode 100644 index 0000000..0b628d3 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/feeditem/fields.yaml @@ -0,0 +1,20 @@ +fields: + title: + label: 'Post Title' + span: full + type: text + body: + label: 'Post Body' + size: '' + span: full + type: richeditor + timestamp: + label: 'UNIX Timestamp' + span: auto + type: number + channel: + label: Channel + nameFrom: title + descriptionFrom: description + span: auto + type: relation diff --git a/www/plugins/jasonwilliams/feed/models/settings/fields.yaml b/www/plugins/jasonwilliams/feed/models/settings/fields.yaml new file mode 100644 index 0000000..7c5293c --- /dev/null +++ b/www/plugins/jasonwilliams/feed/models/settings/fields.yaml @@ -0,0 +1,51 @@ +fields: + twitter_section: + label: Twitter API Settings + type: section + twitter_username: + label: Username + description: Your twitter username, excluding the @ + span: left + twitter_latest_tweet: + label: Latest Tweet ID + description: The ID of the most recent tweet already fetched and stored + span: right + twitter_consumer_key: + label: Consumer Key + description: Your consumer key for accessing the twitter API + span: left + twitter_consumer_secret: + label: Consumer Key Secret + description: Your consumer key secret for accessing the twitter API + span: right + twitter_access_token: + label: Access Token + description: Your access token for accessing the twitter API + span: left + twitter_access_token_secret: + label: Access Token Secret + description: Your access token secret for accessing the twitter API + span: right + + instagram_section: + label: Instagram API Settings + type: section + instagram_access_token: + label: Access Token + description: Your access token for accessing the instagram API + span: full + instagram_latest_post: + label: Latest Post Timestamp + description: The unix timestamp of the most recent post already fetched and stored + span: left + + foursquare_section: + label: Foursquare API Settings + type: section + foursquare_access_token: + description: Your access token for accessing the foursquare API + span: full + foursquare_latest_post: + label: Latest Checkin Timestamp + description: The unix timestamp of the most recent checkin already fetched and stored + span: left diff --git a/www/plugins/jasonwilliams/feed/plugin.yaml b/www/plugins/jasonwilliams/feed/plugin.yaml new file mode 100644 index 0000000..01b072e --- /dev/null +++ b/www/plugins/jasonwilliams/feed/plugin.yaml @@ -0,0 +1,20 @@ +plugin: + name: 'jasonwilliams.feed::lang.plugin.name' + description: 'jasonwilliams.feed::lang.plugin.description' + author: 'Jason Williams' + icon: oc-icon-newspaper-o + homepage: 'https://ja.son-williams.ca/' +navigation: + main-menu-item: + label: Feed + url: jasonwilliams/feed/feed + icon: icon-newspaper-o + sideMenu: + side-menu-item: + label: 'Social Feed' + url: jasonwilliams/feed/feed + icon: icon-sitemap + side-menu-item2: + label: 'Social Channels' + url: jasonwilliams/feed/channels + icon: icon-sitemap diff --git a/www/plugins/jasonwilliams/feed/updates/jasonwilliams_feed_initial_setup.php b/www/plugins/jasonwilliams/feed/updates/jasonwilliams_feed_initial_setup.php new file mode 100644 index 0000000..50e0d95 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/updates/jasonwilliams_feed_initial_setup.php @@ -0,0 +1,57 @@ +engine = 'InnoDB'; + $table->increments('id')->unsigned(); + $table->integer('timestamp')->unsigned(); + $table->integer('channel_id')->unsigned(); + $table->string('title', 512)->nullable(); + $table->string('link', 256)->nullable(); + $table->text('body')->nullable(); + $table->text('extra'); + }); + + Schema::create('jasonwilliams_feed_tags', function($table) + { + $table->engine = 'InnoDB'; + $table->increments('id')->unsigned(); + $table->string('tag', 128); + $table->integer('feed_item_id')->unsigned(); + }); + + Schema::create('jasonwilliams_feed_links', function($table) + { + $table->engine = 'InnoDB'; + $table->increments('id')->unsigned(); + $table->string('addr', 256); + $table->string('display', 256); + $table->integer('feed_item_id')->unsigned(); + }); + + Schema::create('jasonwilliams_feed_channels', function($table) + { + $table->engine = 'InnoDB'; + $table->increments('id')->unsigned(); + $table->string('title', 64); + $table->string('slug', 64); + $table->string('icon', 128)->nullable(); + $table->string('colour', 7)->nullable(); + }); + } + + public function down() + { + Schema::dropIfExists('jasonwilliams_feed_'); + Schema::dropIfExists('jasonwilliams_feed_tags'); + Schema::dropIfExists('jasonwilliams_feed_links'); + Schema::dropIfExists('jasonwilliams_feed_channels'); + } +} diff --git a/www/plugins/jasonwilliams/feed/updates/version.yaml b/www/plugins/jasonwilliams/feed/updates/version.yaml new file mode 100644 index 0000000..dc46b65 --- /dev/null +++ b/www/plugins/jasonwilliams/feed/updates/version.yaml @@ -0,0 +1,3 @@ +1.0.1: + - 'Initialize plugin and create database tables.' + - jasonwilliams_feed_initial_setup.php diff --git a/www/themes/jason-williamsca/assets/css/resume.css b/www/themes/jason-williamsca/assets/css/resume.css index ce75600..c78d2de 100644 --- a/www/themes/jason-williamsca/assets/css/resume.css +++ b/www/themes/jason-williamsca/assets/css/resume.css @@ -1,23 +1,7 @@ -h1.master { - margin: 0; - font-family: var(--title-font); - font-size: 3.5rem; - word-spacing: -0.15em; -} - -h2 { - font-size: 1.5rem; -} - h1 + h2, h2:first-child { margin-top: 0; } -h3 { - font-size: 1rem; - color: var(--dark-alt-accent-color); -} - section h1:first-child { margin-top: 0; } @@ -26,14 +10,6 @@ section + section { margin-top: 3rem; } -.main { - font-size: 1.2rem; -} - -.card { - margin-bottom: 2rem; -} - .card ul { margin: 0; padding-left: 1rem; diff --git a/www/themes/jason-williamsca/assets/css/theme.css b/www/themes/jason-williamsca/assets/css/theme.css index 1755dce..a225392 100644 --- a/www/themes/jason-williamsca/assets/css/theme.css +++ b/www/themes/jason-williamsca/assets/css/theme.css @@ -1,7 +1,7 @@ /* Variables */ :root { - --main-bg-color: #E7D9D1; /*#F0E4D1; #E8D5B5; #e7d9d1;*/ - --main-accent-color: #302b44; + --main-bg-color: #F2EDEA; + --main-accent-color: #302B44; --light-accent-color: #686283; --xlight-accent-color: #88839C; --xxlight-accent-color: #FFFFFF; @@ -87,6 +87,22 @@ footer { } /* Standard element styles */ +h1.master { + margin: 0; + font-family: var(--title-font); + font-size: 3.5rem; + word-spacing: -0.15em; +} + +h2 { + font-size: 1.5rem; +} + +h3 { + font-size: 1rem; + color: var(--dark-alt-accent-color); +} + a { color: var(--alt-accent-color); text-decoration: underline; @@ -98,6 +114,10 @@ a.icon-link { color: var(--accent-color); } +a.btn { + text-decoration: none; +} + a:hover { color: var(--dark-alt-accent-color); text-decoration: none; @@ -142,6 +162,18 @@ html::-webkit-scrollbar-thumb { color: var(--xxlight-accent-color); } +.main, .card p.main, .card ul.main { + font-size: 1.2rem; +} + +.col-container { + margin: 0 -15px; +} + +.card { + margin-bottom: 2rem; +} + .social-icons { font-size: 0; } @@ -177,6 +209,57 @@ html::-webkit-scrollbar-thumb { font-size: 0.9rem; } +.card .feedcard-footer p, .card .feedcard-footer a { + margin-bottom: 0; + font-size: 0.75rem; + color: rgba(0, 0, 0, 0.5); + text-decoration: none; +} + +.card .feedcard-footer img { + height: 0.75rem; +} + +.card .feedcard-footer .feedcard-itemtype { + width: 33%; + text-align: right; +} + +.card .feedcard-footer .feedcard-attribution { + width: 67%; +} + .altaccent { color: var(--alt-accent-color); -} \ No newline at end of file +} + +/* Tag cloud styles */ +div.jqcloud { + font-size: 0.5rem; + line-height: normal; +} + +div.jqcloud a { + color: inherit; + font-size: inherit; + text-decoration: none +} + +div.jqcloud a:hover { + color: var(--dark-alt-accent-color); +} + +div.jqcloud span.w10 {font-size:550%; color: rgba(176, 92, 113, 1);} +div.jqcloud span.w9 {font-size:500%; color: rgba(176, 92, 113, 0.95);} +div.jqcloud span.w8 {font-size:450%; color: rgba(176, 92, 113, 0.9);} +div.jqcloud span.w7 {font-size:400%; color: rgba(176, 92, 113, 0.85);} +div.jqcloud span.w6 {font-size:350%; color: rgba(176, 92, 113, 0.8);} +div.jqcloud span.w5 {font-size:300%; color: rgba(176, 92, 113, 0.75);} +div.jqcloud span.w4 {font-size:250%; color: rgba(176, 92, 113, 0.7);} +div.jqcloud span.w3 {font-size:200%; color: rgba(176, 92, 113, 0.65);} +div.jqcloud span.w2 {font-size:150%; color: rgba(176, 92, 113, 0.6);} +div.jqcloud span.w1 {font-size:100%; color: rgba(176, 92, 113, 0.55);} + +div.jqcloud{overflow:hidden;position:relative} + +div.jqcloud span{padding:0} \ No newline at end of file diff --git a/www/themes/jason-williamsca/assets/images/loading.svg b/www/themes/jason-williamsca/assets/images/loading.svg new file mode 100644 index 0000000..71533d9 --- /dev/null +++ b/www/themes/jason-williamsca/assets/images/loading.svg @@ -0,0 +1 @@ + diff --git a/www/themes/jason-williamsca/assets/javascript/common.js b/www/themes/jason-williamsca/assets/javascript/common.js new file mode 100644 index 0000000..c3f46ea --- /dev/null +++ b/www/themes/jason-williamsca/assets/javascript/common.js @@ -0,0 +1,17 @@ +$(() => { + // Enable any tooltips on the page + $('[data-toggle="tooltip"]').tooltip() + + // Enable masonry layout + $('.masonry').each(function() { + var opts = { itemSelector: '.masonry-item' } + if ($(this).hasClass('masonry-horizontal')) opts.horizontalOrder = true + + $(this).masonry(opts) + }) + + // Resize textareas to fit their content + $('textarea').each(function() { + $(this).height($(this)[0].scrollHeight) + }) +}); \ No newline at end of file diff --git a/www/themes/jason-williamsca/assets/javascript/feedlayout.js b/www/themes/jason-williamsca/assets/javascript/feedlayout.js new file mode 100644 index 0000000..48c3bf3 --- /dev/null +++ b/www/themes/jason-williamsca/assets/javascript/feedlayout.js @@ -0,0 +1,45 @@ +(function($) { + "use strict"; // Start of use strict + + // If the feed is loaded immediadely on page load (i.e. we're not using ajax), reset the layout on each image load + $('.masonry img').each(function() { + $(this).one('load', () => { + $('.masonry').masonry() + }) + }) + + // Apply masonry layout to feed cards after content (including images) has been loaded + $(window).on('feedLoaded', function() { + if (!feedItemClass) feedItemClass = 'col-sm-12 col-md-6 col-xl-4' + + $('.masonry-item').addClass(feedItemClass) + $('.show-onfeedloaded').removeClass('d-none') + + $('.masonry').masonry('reloadItems') + $('.masonry').masonry() + + // Every time an image loads, reset the layout + $('.masonry img').each(function() { + $(this).one('load', () => { + $('.masonry').masonry() + }) + }) + }) + + // If there's tag data set once the page is loaded, create a tag cloud + $(function() { + if(typeof tagData !== 'undefined') { + var cloudWords = [] + + tagData.forEach((tag) => { + cloudWords.push({text: tag.tag, weight: tag.count, link: '/feed/all/' + tag.tag}) + }) + + $('#tagcloud').jQCloud(cloudWords, { + autoResize: true, + removeOverflowing: true, + delay: 2 + }) + } + }) +})(jQuery); // End of use strict \ No newline at end of file diff --git a/www/themes/jason-williamsca/assets/javascript/resume.js b/www/themes/jason-williamsca/assets/javascript/resume.js index ca8e3b2..62fe3f4 100644 --- a/www/themes/jason-williamsca/assets/javascript/resume.js +++ b/www/themes/jason-williamsca/assets/javascript/resume.js @@ -1,23 +1,22 @@ (function($) { "use strict"; // Start of use strict - // Apply masonry layout to cards - $('.masonry').each(function() { - var opts = { itemSelector: '.masonry-wrapper' }; - if ($(this).children().length < 6) opts.horizontalOrder = true; - $(this).masonry(opts); - }); - // Add reorder icon to .card-inline - $('.card-inline').prepend('
'); + $('.card-inline').prepend('
') - $('.card-inline .inline-icon').click(function() { - var that = $(this); - that.closest('.card').toggleClass('inline').closest('.masonry').masonry(); + // Define a function to toggle the view + const toggleBulletView = function() { + var that = $(this) + that.closest('.card').toggleClass('inline').closest('.masonry').masonry() setTimeout(() => { - that.tooltip('hide'); + that.tooltip('hide') }, 2000); - }); + return false + } + + // Add the function to clicks on the menu button, and/or taps anywhere on the card + $('.card-inline .inline-icon').on('click', toggleBulletView) + $('.card-inline').on('touchstart', toggleBulletView) // Smooth scrolling using jQuery easing $('a.js-scroll-trigger[href*="#"]:not([href="#"])').click(function() { diff --git a/www/themes/jason-williamsca/assets/javascript/tooltips.js b/www/themes/jason-williamsca/assets/javascript/tooltips.js deleted file mode 100644 index 7fee50f..0000000 --- a/www/themes/jason-williamsca/assets/javascript/tooltips.js +++ /dev/null @@ -1,3 +0,0 @@ -$(() => { - $('[data-toggle="tooltip"]').tooltip() -}); \ No newline at end of file diff --git a/www/themes/jason-williamsca/layouts/default.htm b/www/themes/jason-williamsca/layouts/default.htm index cc69960..ecc9cf6 100644 --- a/www/themes/jason-williamsca/layouts/default.htm +++ b/www/themes/jason-williamsca/layouts/default.htm @@ -45,6 +45,7 @@ description = "Default Layout"
  • Résumé
  • Feed
  • Contact Me
  • +
  • PGP Public Key
  • {% component 'CurrentInfo' %}

    Made with in #YYC

    @@ -63,6 +64,8 @@ description = "Default Layout" {% framework extras %} + + {% scripts %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/layouts/sidesubmenu.htm b/www/themes/jason-williamsca/layouts/sidesubmenu.htm index 32c9273..248788c 100644 --- a/www/themes/jason-williamsca/layouts/sidesubmenu.htm +++ b/www/themes/jason-williamsca/layouts/sidesubmenu.htm @@ -43,6 +43,8 @@ description = "Page with a Submenu" {% framework extras %} + + {% scripts %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/contact.htm b/www/themes/jason-williamsca/pages/contact.htm index e537bcf..99f94ec 100644 --- a/www/themes/jason-williamsca/pages/contact.htm +++ b/www/themes/jason-williamsca/pages/contact.htm @@ -28,18 +28,22 @@ emails_date_format = "Y-m-d"

    Contact Me

    The simplest and quickest way to contact me is by completing the form below.

    - {% component 'contactform' %} + {% component 'contactform' %}

    Old School

    Sometimes the more traditional ways of getting in touch are just better...

    -

    IMG

    -

    4th Floor, 1207 11 Ave SW,
    - Calgary, AB, T3C 0M5

    +

    +

    3699 63 Ave NW,
    + Calgary, AB, T3J 0G7

    (825) 257-5025

    New Cool

    ...and sometimes it's better to keep up with the times.

    {% partial 'social' nocode = true nocontact = true %}
    +
    +

    Encryption

    +

    If you wish, you may use my PGP public key to encrypt anything that you send me.

    +
    \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/feed.htm b/www/themes/jason-williamsca/pages/feed.htm index d76fde2..210730f 100644 --- a/www/themes/jason-williamsca/pages/feed.htm +++ b/www/themes/jason-williamsca/pages/feed.htm @@ -1,6 +1,43 @@ title = "Feed" -url = "/feed" +url = "/feed/:channel?/:tag?/" layout = "default" is_hidden = 0 + +[ShortFeed] +channelFilter = "{{ :channel }}" +tagFilter = "{{ :tag }}" +maxItems = 50 + +[ChannelList] +includeEmpty = 0 + +[TagList] == -

    Feed

    \ No newline at end of file +addJs('https://cdnjs.cloudflare.com/ajax/libs/jqcloud/1.0.3/jqcloud.min.js'); + $this->addJs('assets/javascript/feedlayout.js'); + //$this->addCss('https://cdnjs.cloudflare.com/ajax/libs/jqcloud/1.0.3/jqcloud.min.css'); +} +?> +== +
    +
    +

    Feed

    +
    + {% partial 'feed/template' feeditemclass='col-sm-12 col-md-6 col-xl-4' %} +
    +
    +
    +

    Channels

    + {% component 'ChannelList' %} + +

    Tags

    +
    +
    +
    + +{% put scripts %} + +{% endput %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/maintenance.htm b/www/themes/jason-williamsca/pages/maintenance.htm new file mode 100644 index 0000000..6b4d0f3 --- /dev/null +++ b/www/themes/jason-williamsca/pages/maintenance.htm @@ -0,0 +1,29 @@ +title = "ja.son-williams.ca" +url = "/maintenance" +is_hidden = 0 +== + + + + +son-williams.ca + + + + +
    +
    +

    ja.son-williams.ca

    +
    + +
    + + \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/pgp-public-key.htm b/www/themes/jason-williamsca/pages/pgp-public-key.htm new file mode 100644 index 0000000..0e02b65 --- /dev/null +++ b/www/themes/jason-williamsca/pages/pgp-public-key.htm @@ -0,0 +1,17 @@ +title = "PGP Public Key" +url = "/pgp" +layout = "default" +is_hidden = 0 +== +
    +
    +

    PGP Public Key

    + {% partial 'pgpkey' %} +
    +
    +

    About

    +

    If you wish, you may use my PGP public key here to encrypt anything that you send me. Of course I'll need a way to get my hands on your public key in order to be able to decrypt it, so please make sure you provide that or that you've published it to a well-known public directory like keyserver.pgp.com.

    +

    Contact Me

    + {% partial 'social' noinfrequent = true nocode = true %} +
    +
    \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/resume.htm b/www/themes/jason-williamsca/pages/resume.htm index ffab230..eb8cd45 100644 --- a/www/themes/jason-williamsca/pages/resume.htm +++ b/www/themes/jason-williamsca/pages/resume.htm @@ -9,7 +9,6 @@ function onStart() $this['sideMenu'] = 'resumemenu'; $this->addCss('assets/css/resume.css'); $this->addJs('https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js'); - $this->addJs('https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js'); $this->addJs('assets/javascript/resume.js'); } ?> @@ -31,8 +30,8 @@ function onStart()

    Skills & Expertise

    -
    -
    +
    +

    Product Management

    @@ -49,7 +48,7 @@ function onStart()
    -
    +

    Project Management

    @@ -69,7 +68,7 @@ function onStart()
    -
    +

    Lean Six-Sigma

    @@ -86,7 +85,7 @@ function onStart()
    -
    +

    Business Analysis

    @@ -103,7 +102,7 @@ function onStart()
    -
    +

    Business Technology

    @@ -162,7 +161,7 @@ function onStart()

    My primary focus was leading projects within the retail arm of the bank, I also acted as a single point of contact representing the retail banking business unit on large-scale, enterprise wide initiatives, playing a leading role in stakeholder, resource and organizational change management.

    - March 2013 - September 2017 + November 2014 - September 2017
    @@ -185,7 +184,7 @@ function onStart()

    Acted as the primary point of contact for small and medium business customers based throughout the UK and beyond, providing banking support and services. Assisted with transactional queries both simple and complex. Sold and advised clients on a wide variety of products and services. Demonstrated a commitment to providing industry leading levels of customer service, and received multiple individual awards for excellent customer satisfaction scores.

    - March 2013 - Present + March 2010 - July 2010
    @@ -263,10 +262,10 @@ function onStart()

    Press

    -
    -
    +
    +
    - +

    Connected RPA Delivers A Digital Workforce

    Computerworld · June 2019

    @@ -276,9 +275,9 @@ function onStart()
    -
    +
    - +

    This Canadian financial institution disrupted their industry with AI!

    AI Week · August 2018

    @@ -288,9 +287,9 @@ function onStart()
    -
    +
    - +

    Why ATB Financial moved its entire organization to G Suite

    TechRepublic · March 2018

    @@ -300,9 +299,9 @@ function onStart()
    -
    +
    - +

    High-tech bank helps Edmonton's homeless access financial services

    CBC · September 2017

    diff --git a/www/themes/jason-williamsca/pages/shrapnel.htm b/www/themes/jason-williamsca/pages/shrapnel.htm new file mode 100644 index 0000000..7d2a520 --- /dev/null +++ b/www/themes/jason-williamsca/pages/shrapnel.htm @@ -0,0 +1,6 @@ +title = "Shrapnel" +url = "/feed/shrapnel/:post/" +layout = "default" +is_hidden = 0 +== +

    Shrapnel Post

    \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/test.htm b/www/themes/jason-williamsca/pages/test.htm new file mode 100644 index 0000000..9b95b34 --- /dev/null +++ b/www/themes/jason-williamsca/pages/test.htm @@ -0,0 +1,8 @@ +title = "Test" +url = "/test" +is_hidden = 0 + +[ShortFeed] +maxItems = 10 +== +{% component 'ShortFeed' %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/uh-oh.htm b/www/themes/jason-williamsca/pages/uh-oh.htm new file mode 100644 index 0000000..1279949 --- /dev/null +++ b/www/themes/jason-williamsca/pages/uh-oh.htm @@ -0,0 +1,15 @@ +title = "Uh oh" +url = "/404" +layout = "default" +is_hidden = 0 +== +
    +
    +

    Uh oh

    + +
    +
    +

    Well this is embarassing for one of us. The page you asked for is nowhere to be found. Bad link? Mistyped address? I'm not sure.

    +

    There's not really all that much stuff here, so you could always try making a selection from the menu at the top right of the page. If you think something's wrong, contact me. +

    +
    \ No newline at end of file diff --git a/www/themes/jason-williamsca/pages/welcome.htm b/www/themes/jason-williamsca/pages/welcome.htm index bda6c62..a0b6c89 100644 --- a/www/themes/jason-williamsca/pages/welcome.htm +++ b/www/themes/jason-williamsca/pages/welcome.htm @@ -2,6 +2,17 @@ title = "Welcome" url = "/" layout = "default" is_hidden = 0 + +[LazyLoadShortFeed] +maxItems = 5 +renderPartial = "feed/template" +== +addJs('assets/javascript/feedlayout.js'); +} +?> ==
    @@ -15,5 +26,7 @@ is_hidden = 0

    Feed

    + {% partial 'feed/placeholder' feeditemclass='col-sm-12 col-md-6' %} + View More...
    \ No newline at end of file diff --git a/www/themes/jason-williamsca/partials/contactform/default.htm b/www/themes/jason-williamsca/partials/contactform/default.htm index 9a4171f..211bef8 100644 --- a/www/themes/jason-williamsca/partials/contactform/default.htm +++ b/www/themes/jason-williamsca/partials/contactform/default.htm @@ -7,24 +7,14 @@ description = "Contact Me Form"
    -
    -
    - -
    -
    @@ -33,6 +23,6 @@ description = "Contact Me Form"
    - + \ No newline at end of file diff --git a/www/themes/jason-williamsca/partials/feed/placeholder.htm b/www/themes/jason-williamsca/partials/feed/placeholder.htm new file mode 100644 index 0000000..bb30432 --- /dev/null +++ b/www/themes/jason-williamsca/partials/feed/placeholder.htm @@ -0,0 +1,5 @@ +== +
    +{% put scripts %} + +{% endput %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/partials/feed/template.htm b/www/themes/jason-williamsca/partials/feed/template.htm new file mode 100644 index 0000000..eb22113 --- /dev/null +++ b/www/themes/jason-williamsca/partials/feed/template.htm @@ -0,0 +1,45 @@ +== +{% if feeditemclass is empty %} + {% set feeditemclass = 'masonry-item' %} +{% else %} + {% set feeditemclass = 'masonry-item ' ~ feeditemclass %} +{% endif %} + +{% if posts is empty %} +

    No feed items have been found.

    +{% endif %} + +{% for post in posts %} +
    +
    + {% if post.extra.img %} + + {% elseif post.extra.lat %} + + + {% endif %} +
    + {% if post.channel.slug == 'twitter' or post.channel.slug == 'instagram' %} + {% if post.title | striptags | length < 100 %} +

    {{ post.title | raw | nl2br }}

    + {% elseif post.title | striptags | length < 150 %} +

    {{ post.title | raw | nl2br }}

    + {% else %} +

    {{ post.title | raw | nl2br }}

    + {% endif %} + {% elseif post.channel.slug == 'foursquare' %} + {% if post.body %}

    {{ post.body | raw | nl2br }}

    {% endif %} +

    {{ post.title }}

    +

    {{ post.extra.address }}

    + {% endif %} + +
    +
    +
    +{% endfor %} \ No newline at end of file diff --git a/www/themes/jason-williamsca/partials/pgpkey.htm b/www/themes/jason-williamsca/partials/pgpkey.htm new file mode 100644 index 0000000..763feec --- /dev/null +++ b/www/themes/jason-williamsca/partials/pgpkey.htm @@ -0,0 +1,32 @@ +== + \ No newline at end of file diff --git a/www/themes/jason-williamsca/partials/social.htm b/www/themes/jason-williamsca/partials/social.htm index 19debba..be899cd 100644 --- a/www/themes/jason-williamsca/partials/social.htm +++ b/www/themes/jason-williamsca/partials/social.htm @@ -1,12 +1,5 @@ description = "Social Media Links" == -addJs('assets/javascript/tooltips.js'); -} -?> -==