diff --git a/src/configs/defaults.ini b/src/configs/defaults.ini index 785979b79..ba7d73c09 100644 --- a/src/configs/defaults.ini +++ b/src/configs/defaults.ini @@ -11,6 +11,10 @@ allowDuplicate=0 allowOpenPhotoLogin=1 cdnPrefix="" +[feed] +pageSize=20 +photoSize=100x100xCR + [secrets] passwordSalt="salt_for_passwords" diff --git a/src/html/assets/themes/beisel/templates/template.php b/src/html/assets/themes/beisel/templates/template.php index 5ae84a660..52afd2fc3 100644 --- a/src/html/assets/themes/beisel/templates/template.php +++ b/src/html/assets/themes/beisel/templates/template.php @@ -9,6 +9,7 @@ + config->site->mode === 'dev') { ?> diff --git a/src/html/assets/themes/beisel2.0/templates/template.php b/src/html/assets/themes/beisel2.0/templates/template.php index 154321e33..57bf7ee7d 100644 --- a/src/html/assets/themes/beisel2.0/templates/template.php +++ b/src/html/assets/themes/beisel2.0/templates/template.php @@ -17,6 +17,7 @@ + diff --git a/src/html/assets/themes/default/templates/template.php b/src/html/assets/themes/default/templates/template.php index 6b76129e4..e6f98702f 100644 --- a/src/html/assets/themes/default/templates/template.php +++ b/src/html/assets/themes/default/templates/template.php @@ -15,6 +15,7 @@ + diff --git a/src/libraries/controllers/FeedActivityController.php b/src/libraries/controllers/FeedActivityController.php new file mode 100644 index 000000000..9e83afcc9 --- /dev/null +++ b/src/libraries/controllers/FeedActivityController.php @@ -0,0 +1,194 @@ + + */ +class FeedActivityController extends ApiActivityController +{ + /** + * Call the parent constructor + * + * @return void + */ + public function __construct() + { + parent::__construct(); + $this->activity = new Activity; + + $this->theme = getTheme(); + $this->template->utility = $this->utility; + $this->template->url = $this->url; + } + + /** + * Retrieve a list of the user's photo uploads from the remote datasource. + * The $filterOpts are values from the path but can also be in _GET. + * /photos/page-2/tags-favorites.json is identical to /photos.json?page=2&tags=favorites + * + * @param string $filterOpts Options on how to filter the list of photos. + * @return string Standard JSON envelope + */ + public function list_($filterOpts = null) + { + $args = func_get_args(); + $feed_format = $args[1]; + if ($feed_format === null) + $feed_format = 'atom'; + + // parse parameters in request + extract($this->parseFilters($filterOpts)); + $activities = $this->activity->list_($filters, $pageSize); + if(isset($_GET['groupBy'])) + $activities = $this->groupActivities($activities, $_GET['groupBy']); + else + $activities = $this->groupActivities($activities, 'hour'); + + if($activities !== false) { + $last_activity = current($activities); + $last_activity_item = current($last_activity); + $last_updated_timestamp = $last_activity_item['data']['dateUploaded']; + } else { + $last_updated_timestamp = time(); + } + + if($this->config->user) + { + $author_email = $this->config->user->email; + if ($this->config->user->name) + $author_name = ''; + else + $author_name = $this->utility->getEmailHandle($author_email, false); + } + else + { + $author_email = ''; + $author_name = ''; + } + + if($this->config->site->baseUrl) + $site_base_url = $this->config->site->baseUrl; + else + $site_base_url = ''; + + if($feed_format == 'atom') + { + $data = array( + 'title' => getConfig()->get('titles')->default, + 'link' => $site_base_url . '/activities/list.atom', + 'updated' => gmdate('Y-m-d\TH:i:s\Z', $last_updated_timestamp), + 'author' => array( + 'name' => $author_name, + 'email' => $author_email + ), + 'base_url' => $site_base_url, + 'id' => $site_base_url . '/' + ); + + header('Content-type: application/atom+xml'); + + $this->theme->display($this->utility->getTemplate('feed-atom.php'), array('items' => $this->prepareFeedItems($activities, $site_base_url), 'data' => $data)); + } + + exit(0); + } + + protected function prepareFeedItems($activities, $site_base_url) + { + $feed_items = array(); + $photoSize = $this->config->feed->photoSize; + if ($photoSize === null) + $photoSize = '100x100xCR'; + + if (count($activities) > 0) { + foreach ($activities as $activity_title => $activity) { + $feed_item = array(); + + $titles = array(); + $photos = array(); + $tags = array(); + + foreach ($activity as $item) { + // TODO: support other activity types in the future? -- mv + if ($item['type'] == 'photo-upload') { + $data = $item['data']; + + if (isset($data['title'])) + $titles[] = $data['title']; + else + $titles[] = $data['filenameOriginal']; + + $photo = array( + 'url' => $site_base_url . $this->url->photoView($data['id'], null, false), + 'src' => $data[sprintf('path%s', $photoSize)], + 'title' => $data['title'], + 'description' => $data['description'], + ); + + $photos[] = $photo; + + // add tags + $tags = array_merge($tags, $data['tags']); + + // use the first photo's url and upload date for the item + if (!isset($feed_item['link'])) { + $feed_item['link'] = $photo['url']; + $feed_item['updated'] = $data['dateUploaded']; + $feed_item['license'] = $data['license']; + } + } + } + + $feed_item['title'] = implode(', ', $titles); + $feed_item['photos'] = $photos; + $feed_item['tags'] = array_unique($tags); + + $feed_items[] = $feed_item; + } + } + + return $feed_items; + } + + protected function parseFilters($filterOpts) + { + $pageSize = $this->config->feed->pageSize; + if ($pageSize === null) + $pageSize = 20; + + $filters = array('sortBy' => 'dateCreated,desc', 'groupBy' => 'hour', 'type' => 'photo-upload'); + if($filterOpts !== null) + { + $filterOpts = (array)explode('/', $filterOpts); + foreach($filterOpts as $value) + { + $dashPosition = strpos($value, '-'); + if(!$dashPosition) + continue; + + $parameterKey = substr($value, 0, $dashPosition); + $parameterValue = substr($value, ($dashPosition+1)); + switch($parameterKey) + { + case 'pageSize': + $pageSize = intval($parameterValue); + break; + case 'type': + $filters['type'] = $value; + default: + $filters[$parameterKey] = $parameterValue; + break; + } + } + } + // merge path parameters with GET parameters. GET parameters override + if(isset($_GET['pageSize']) && intval($_GET['pageSize']) == $_GET['pageSize']) + $pageSize = intval($_GET['pageSize']); + $filters = array_merge($filters, $_GET); + + // force only public items + $filters['permission'] = 0; + + return array('filters' => $filters, 'pageSize' => $pageSize); + } +} diff --git a/src/libraries/dependencies.php b/src/libraries/dependencies.php index 03019a624..7ba2e411d 100644 --- a/src/libraries/dependencies.php +++ b/src/libraries/dependencies.php @@ -33,6 +33,7 @@ require $pathsObj->controllers . '/OAuthController.php'; require $pathsObj->controllers . '/ApiWebhookController.php'; require $pathsObj->controllers . '/WebhookController.php'; +require $pathsObj->controllers . '/FeedActivityController.php'; // libraries require $pathsObj->external . '/aws/sdk.class.php'; diff --git a/src/libraries/models/Url.php b/src/libraries/models/Url.php index b7d69a9d3..87b412ce7 100644 --- a/src/libraries/models/Url.php +++ b/src/libraries/models/Url.php @@ -84,6 +84,19 @@ public function photosUpload($write = true) return $utilityObj->returnValue('/photos/upload', $write); } + public function photosFeed($options = null, $feed_format = 'atom', $write = true) + { + $utilityObj = new Utility; + if(empty($options)) + { + return $utilityObj->returnValue(sprintf('/photos/list.%s', $feed_format), $write); + } + else + { + return $utilityObj->returnValue(sprintf('/photos/%s/list.%s', $options, $feed_format), $write); + } + } + public function tagsView($write = true) { $utilityObj = new Utility; diff --git a/src/libraries/routes-feed.php b/src/libraries/routes-feed.php new file mode 100644 index 000000000..9b47a08c5 --- /dev/null +++ b/src/libraries/routes-feed.php @@ -0,0 +1,8 @@ +get('/activities/?(.+)?/list.(atom|rss)', array('FeedActivityController', 'list_'), EpiApi::external); // retrieve activities (/activities/list.atom) diff --git a/src/libraries/routes.php b/src/libraries/routes.php index 28aa56d2b..905a4045e 100644 --- a/src/libraries/routes.php +++ b/src/libraries/routes.php @@ -1,5 +1,6 @@ get('paths')->libraries . '/routes-api.php'; +require $configObj->get('paths')->libraries . '/routes-feed.php'; /* * Home page, optionally redirects if the theme doesn't have a front.php diff --git a/src/templates/feed-atom.php b/src/templates/feed-atom.php new file mode 100644 index 000000000..b58cfc84f --- /dev/null +++ b/src/templates/feed-atom.php @@ -0,0 +1,46 @@ +?xml version="1.0" encoding="utf-8"?> + + + + + + utility->safe($data['author']['name']); ?> + utility->safe($data['author']['email']); ?> + + + + The OpenPhoto Project + + config->keywords->default) { ?> + config->keywords->default); ?> + + + + + + + + utility->safe($item['title']); ?> + + + + + + + + utility->safe($photo['description']); ?> + + + + ]]> + utility->licenseName($item['license']); ?> + 0) { ?> + + "/> + + + + + +
utility->safe($photo['description']); ?>