diff --git a/.hgignore b/.gitignore similarity index 100% rename from .hgignore rename to .gitignore diff --git a/api/app/Http/Controllers/KerkdienstController.php b/api/app/Http/Controllers/KerkdienstController.php index 0ec3c94..f9826d2 100644 --- a/api/app/Http/Controllers/KerkdienstController.php +++ b/api/app/Http/Controllers/KerkdienstController.php @@ -47,7 +47,7 @@ QUERY; */ public function download() { $file = '/var/audio/podcast/6fm-kerkdienst.mp3'; - return response()->download($file, "6FM_Gemist-Kerkdienst.mp3"); + return response()->download($file, "NH_Gooi_Gemist-Kerkdienst.mp3"); } /** diff --git a/api/app/Http/Controllers/MenuController.php b/api/app/Http/Controllers/MenuController.php new file mode 100644 index 0000000..59f5c28 --- /dev/null +++ b/api/app/Http/Controllers/MenuController.php @@ -0,0 +1,30 @@ +select('SELECT id, title, link, is_special FROM site_menu WHERE ((visible_from IS NULL AND visible_to IS NULL) OR (visible_from <= NOW() AND visible_to >= NOW()))'); + $items = []; + foreach($query as $item) + { + $items[$item->id] = ['title' => $item->title, 'link' => $item->link, 'special' => $item->is_special]; + } + + return $items; + } +} diff --git a/api/app/Http/Controllers/NewsController.php b/api/app/Http/Controllers/NewsController.php index ac564b5..35378e6 100644 --- a/api/app/Http/Controllers/NewsController.php +++ b/api/app/Http/Controllers/NewsController.php @@ -1,17 +1,26 @@ EXTERNAL_NEWS_API = env('EXTERNAL_NEWS_API', '//'); + } + + /** + * RSS-feed van alle podcasts + */ + public function rss(Request $request) { + $page = (int)$request->get('page', 1); + if($page <= 0) { + return abort(400); + } + + $podcasts = $this->retrieveNewsItems($page, $count = 20, $filter = null, $params = []); + $view = view('rss.news')->with('news', $podcasts)->with('url', $request->url())->with('page', $page); + return response($view)->header('Content-Type', 'application/xml'); } /** @@ -45,9 +68,42 @@ QUERY; if($count <= 0 || $page <= 0) { return abort(400); } - + + $result = $this->retrieveNewsItems($page, $count, $filter, []); + return response()->json(['page' => $page, 'count' => $count, 'news' => $result]); + } + /** + * Lijst van alle nieuwsberichten voor de kabelkrant + */ + public function tvlist(Request $request) { + $count = (int)$request->get('aantal', 15); + if($count <= 0) { + return abort(400); + } + + $params = ['target' => self::$TV_TARGET, 'category' => self::$NEWS_CATEGORY, 'secondarycategory' => self::$EXTERNAL_NEWS_CATEGORY]; + $newsItems = app('db')->select(self::$BASE_SQL + . ' ORDER BY COALESCE(`news`.`pubupdatedt`, `news`.`creationdt`) DESC' + . ' LIMIT 0, ' . (int)$count, $params); + + $result = array(); + foreach($newsItems as $newsItem) { + $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $newsItem->id]); + $newsItem = new \Model\NewsItem($newsItem, $pictures); + $newsItem->content = strip_tags($newsItem->content); + if($newsItem->images && count($newsItem->images) > 1) { + $newsItem->images = [$newsItem->images[0]]; + } + + $result[] = $newsItem; + } + + return response()->json(['news' => $result]); + } + + public function retrieveNewsItems($page, $count, $filter, $params) { $filterSql = ""; - $params = ['category' => self::$NEWS_CATEGORY]; + $params = ['target' => self::$WEBSITE_TARGET, 'category' => self::$NEWS_CATEGORY, 'secondarycategory' => self::$EXTERNAL_NEWS_CATEGORY]; if($filter) { foreach($filter as $field => $value) { $paramName = preg_replace('/[^A-Za-z0-9]/', '', $field); @@ -59,7 +115,7 @@ QUERY; $start = ($page - 1) * $count; $newsItems = app('db')->select(self::$BASE_SQL . $filterSql - . ' ORDER BY `published` DESC' + . ' ORDER BY COALESCE(`news`.`pubupdatedt`, `news`.`creationdt`) DESC' . ' LIMIT ' . (int)$start . ', ' . (int)$count, $params); $result = array(); @@ -70,7 +126,7 @@ QUERY; $result[] = new \Model\NewsItem($newsItem, $pictures); } - return response()->json(['page' => $page, 'count' => $count, 'news' => $result]); + return $result; } public function regionlist(Request $request, $region) { @@ -83,23 +139,20 @@ QUERY; public function findnews(Request $request, $query) { $page = (int)$request->get('pagina', 1); + $count = (int)$request->get('aantal', 15); if($page <= 0) { return abort(400); } - + + $start = ($page - 1) * $count; $query = strip_tags(urldecode($query)); - $searchRange = [ - (new \DateTimeImmutable())->setDate(date('Y') + 1 - $page, 1, 1), - (new \DateTimeImmutable())->setDate(date('Y') + 2 - $page, 1, 1)]; - - $newsItems = app('db')->select(self::$BASE_SQL - . ' AND `news`.`creationdt` >= :startRange AND `news`.`creationdt` <= :endRange' - . ' AND MATCH(`content`.`title`, `content`.`content`) AGAINST(:query IN BOOLEAN MODE)' - . ' ORDER BY `published` DESC' - . ' LIMIT 0, 20', [ + $newsItems = app('db')->select($s=self::$BASE_SQL + . ' AND MATCH(`content`.`title`, `content`.`content`) AGAINST(:query IN NATURAL LANGUAGE MODE)' + . ' ORDER BY COALESCE(`news`.`pubupdatedt`, `news`.`creationdt`) DESC' + . ' LIMIT ' . $start . ', ' . $count, [ 'category' => self::$NEWS_CATEGORY, - 'startRange' => $searchRange[0]->format('Y-m-d'), - 'endRange' => $searchRange[1]->format('Y-m-d'), + 'secondarycategory' => self::$EXTERNAL_NEWS_CATEGORY, + 'target' => self::$WEBSITE_TARGET, 'query' => $query ]); @@ -124,7 +177,7 @@ QUERY; $today = new \DateTime('today'); $agendaItems = app('db')->select(self::$BASE_SQL . ' AND `news`.`enddt` >= :today ' - . ' ORDER BY `news`.`startdt` ASC, `news`.`enddt` ASC', ['category' => self::$CALENDAR_CATEGORY, 'today' => $today]); + . ' ORDER BY `news`.`startdt` ASC, `news`.`enddt` ASC', ['target' => self::$WEBSITE_TARGET, 'category' => self::$CALENDAR_CATEGORY, 'secondarycategory' => 0, 'today' => $today]); $result = array(); foreach($agendaItems as $agendaItem) { @@ -138,46 +191,174 @@ QUERY; } + /** + * Vacatures ophalen + */ + public function jobslist(Request $request) { + $today = new \DateTime('today'); + $vacatures = app('db')->select(self::$BASE_SQL + . ' AND (`news`.`enddt` = 0 OR `news`.`enddt` >= :today) ' + . ' ORDER BY `news`.`startdt` ASC, `news`.`enddt` ASC', ['target' => self::$WEBSITE_TARGET, 'category' => self::$JOBS_CATEGORY, 'secondarycategory' => 0, 'today' => $today]); + + $result = array(); + foreach($vacatures as $vacature) { + // Note: content is stored in the database with an additional addslashes() - don't ask why, just remove it :) + $vacature->content = $vacature->content; + $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $vacature->id]); + $result[] = new \Model\JobOpening($vacature, $pictures); + } + + return response()->json(['jobs' => $result]); + } + + /** + * Beelden ophalen + */ + public function featuredimageslist(Request $request) { + $count = (int)$request->get('aantal', 1000); + $page = (int)$request->get('pagina', 1); + $startIndex = ($page - 1) * $count; + $today = new \DateTime('today'); + $items = app('db')->select(self::$BASE_SQL + . ' AND (`news`.`enddt` = 0 OR `news`.`enddt` >= :today) ' + . ' ORDER BY `news`.`startdt` ASC, `news`.`enddt` ASC' + . ' LIMIT ' . (int)$startIndex . ', ' . (int)$count, + ['target' => self::$WEBSITE_TARGET, 'category' => self::$FEATIMG_CATEGORY, 'secondarycategory' => 0, 'today' => $today]); + + $result = array(); + foreach($items as $item) { + // Note: content is stored in the database with an additional addslashes() - don't ask why, just remove it :) + $item->content = $item->content; + $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $item->id]); + $result[] = new \Model\NewsItem($item, $pictures); + } + + return response()->json(['items' => $result]); + } + + /** + * Specifiek nieuwsbericht ophalen + */ + public function jobsitem($id) { + $item = $this->itemFromCategory(self::$JOBS_CATEGORY, $id); + if(!$item) { + return abort(404); + } + + $data = array( + 'item' => new \Model\JobOpening($item['data'], $item['images'], null)); + + return response()->json($data); + } + + /** + * Blogitems ophalen + */ + public function bloglist() { + $blogs = app('db')->select( 'SELECT id, title, description, news_category, start_date, end_date, is_active ' + . ' FROM liveblog ORDER BY is_active DESC' ); + $result = array(); + foreach($blogs as $blog) { + $result[] = new \Model\Blog($blog); + } + + return response()->json($result); + } + + /** + * Blogitems ophalen + */ + public function blogitemlist(Request $request, $id) { + $blog_data = app('db')->select('SELECT id, title, description, news_category, start_date, end_date, is_active ' + . ' FROM liveblog WHERE id = :id', ['id' => (int)$id]); + if(count($blog_data) != 1) { + return abort(400); + } + + $blog = new \Model\Blog($blog_data[0]); + $count = (int)$request->get('aantal', 15); + $page = (int)$request->get('pagina', 1); + if($count <= 0 || $page <= 0) { + return abort(400); + } + + $startIndex = ($page - 1) * $count; + $blogItems = app('db')->select($sql = self::$BASE_SQL + . ' AND `news`.`startdt` >= :start AND `news`.`startdt` <= :end' + . ' ORDER BY COALESCE(`news`.`pubupdatedt`, `news`.`creationdt`) DESC' + . ' LIMIT ' . (int)$startIndex . ', ' . (int)$count, + ['target' => self::$WEBSITE_TARGET, 'category' => $blog->news_category, 'secondarycategory' => 0, 'start' => $blog->start_date, 'end' => $blog->end_date]); + + $result = array(); + foreach($blogItems as $blogItem) { + // Note: content is stored in the database with an additional addslashes() - don't ask why, just remove it :) + $blogItem->content = $blogItem->content; + $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $blogItem->id]); + if($blogItem->podcast_id > 0) + { + $podcast = app('db')->select(PodcastController::$BASE_SQL . ' AND `podcast`.`id` = :podcastId', ['podcastId' => $blogItem->podcast_id]); + $podcast = count($podcast) ? $podcast[0] : null; + } + else + { + $podcast = null; + } + + $result[] = new \Model\NewsItem($blogItem, $pictures, $podcast); + } + + return response()->json(['page' => $page, 'count' => $count, 'blog' => $blog, 'items' => $result]); + } + + /** * Populaire berichten ophalen */ public function popularNews(Request $request) { - return $this->popularInCategory($request, self::$NEWS_CATEGORY); + return $this->popularInCategory($request, self::$NEWS_CATEGORY, self::$EXTERNAL_NEWS_CATEGORY); } public function popularCalendar(Request $request) { return $this->popularInCategory($request, self::$CALENDAR_CATEGORY); } - protected function popularInCategory(Request $request, $category) { + protected function popularInCategory(Request $request, $category, $secondarycategory = -1) { $count = (int)$request->get('aantal', 5); + $page = (int)$request->get('pagina', 1); + $start = ($page - 1) * $count; $recent = app('db')->select(<<= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL :interval DAY) - AND `news`.`category` = :category AND `content`.`target` = 1 AND `news`.`active` = 1 AND `content`.`active` = 1 + AND (`news`.`category` = :category OR `news`.`category` = :secondarycategory) AND `content`.`target` = 1 AND `news`.`active` = 1 AND `content`.`active` = 1 GROUP BY news.id ORDER BY visitors DESC - LIMIT 0, :count + LIMIT :start, :count QUERY , [ 'type' => $category == self::$NEWS_CATEGORY ? 'nieuws' + : ( $category == self::$EXTERNAL_NEWS_CATEGORY ? 'nieuws' : ($category == self::$CALENDAR_CATEGORY ? 'agenda' - : ( "" ) ), + : ( "" ) ) ), 'interval' => $category == self::$NEWS_CATEGORY ? 30 + : ($category == self::$EXTERNAL_NEWS_CATEGORY ? 30 : ($category == self::$CALENDAR_CATEGORY ? 0 - : 30 ), + : 30 ) ), 'category' => $category, + 'secondarycategory' => $secondarycategory, + 'start' => $start, 'count' => $count ]); $result = array(); foreach($recent as $item) { - $newsItem = app('db')->select(self::$BASE_SQL . ' AND `news`.`id` = :newsId', ['category' => $category, 'newsId' => $item->id])[0]; - $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $newsItem->id]); - $result[] = new \Model\NewsItem($newsItem, $pictures); + $newsItem = app('db')->select(self::$BASE_SQL . ' AND `news`.`id` = :newsId', ['target' => self::$WEBSITE_TARGET, 'category' => $category, 'secondarycategory' => $secondarycategory, 'newsId' => $item->id]); + if(count($newsItem)) { + $pictures = app('db')->select(self::$LOAD_IMAGES, ['newsId' => $newsItem[0]->id]); + $result[] = new \Model\NewsItem($newsItem[0], $pictures); + } } return response()->json($result); @@ -187,8 +368,25 @@ QUERY * Specifiek nieuwsbericht ophalen */ public function newsitem($id) { - $item = $this->itemFromCategory(self::$NEWS_CATEGORY, $id); - return $item ? response()->json(new \Model\NewsItem($item['data'], $item['images'], $item['podcast'])) : abort(404); + $item = $this->itemFromCategory(array(self::$NEWS_CATEGORY, self::$EXTERNAL_NEWS_CATEGORY), $id); + if(!$item) { + return abort(404); + } + + $data = array('version' => 1, + 'news' => new \Model\NewsItem($item['data'], $item['images'], $item['podcast'])); + + if($externalId = (int)$item['data']->external_id) { + try { + $externalSource = json_decode(file_get_contents($url = $this->EXTERNAL_NEWS_API . 'article/' . (int)$externalId)); + if($externalSource) { + $data['version'] = 2; + $data['source'] = $externalSource->article; + } + } catch(\Exception $ex) { $data['error'] = array('source' => $url, 'exception' => $ex->getMessage()); } + } + + return response()->json($data); } /** @@ -200,9 +398,16 @@ QUERY } private function itemFromCategory($category, $id) { + $params = ['id' => (int)$id, 'target' => self::$WEBSITE_TARGET]; + if(is_array($category)) { + $params['category'] = $category[0]; + $params['secondarycategory'] = $category[1]; + } else { + $params['category'] = $category; + $params['secondarycategory'] = 0; + } $newsItem = app('db')->select(self::$BASE_SQL - . ' AND `news`.`id` = :id LIMIT 0, 1', - ['category' => $category, 'id' => (int)$id]); + . ' AND `news`.`id` = :id LIMIT 0, 1', $params); if(count($newsItem) != 1) { return null; diff --git a/api/app/Http/Controllers/ProgramController.php b/api/app/Http/Controllers/ProgramController.php index d561525..9a5ae2a 100644 --- a/api/app/Http/Controllers/ProgramController.php +++ b/api/app/Http/Controllers/ProgramController.php @@ -1,5 +1,4 @@ clone $start, 'end' => clone $end, 'program' => new \Model\Program($program)]; + $schedule[] = ['id' => $scheduleid, 'start' => clone $start, 'end' => clone $end, 'program' => new \Model\Program($program)]; } } @@ -53,7 +52,9 @@ QUERY; $startDay = $start->format('N'); $endDay = $end->format('N'); $schedule = []; - + + if($DEBUG) print "Creating schedule between {$start->format('Y-m-d H:i')} and {$end->format('Y-m-d H:i')}
\n"; + $START = 1; $END = -1; $WEEK = new \DateInterval('P7D'); $DAY = new \DateInterval('P1D'); $WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" /* == 0 and 7 */]; @@ -61,34 +62,56 @@ QUERY; ['enddate' => $end->format('Y-m-d'), 'startdate' => $start->format('Y-m-d')]); $scheduleChanges = new \SplPriorityQueue(); $active = []; + foreach($scheduleItems as $item) { if($item->startdate) { $item->startdate = new \DateTimeImmutable($item->startdate); } if($item->enddate) { $item->enddate = (new \DateTimeImmutable($item->enddate))->add($DAY); } + // Find the start time and end time of the last occurrence on or before the start time - $itemStart = new \DateTime("last " . $WEEKDAYS[$item->startday] . " " . $start->format('Y-m-d') . " " . $item->starttime); - $itemEnd = new \DateTime("last " . $WEEKDAYS[$item->endday] . " " . $start->format('Y-m-d') . " " . $item->endtime); + $itemStart = new \DateTimeImmutable("last " . $WEEKDAYS[$item->startday] . " " . $start->format('Y-m-d') . " " . $item->starttime); + $itemEnd = new \DateTimeImmutable("last " . $WEEKDAYS[$item->endday] . " " . $start->format('Y-m-d') . " " . $item->endtime); + if($itemEnd <= $itemStart) { - $itemEnd->add($WEEK); + $itemEnd = $itemEnd->add($WEEK); } // If the first occurrence is completely before the period of interest, look at the next week while($itemEnd < $start) { - $itemStart->add($WEEK); - $itemEnd->add($WEEK); + $itemStart = $itemStart->add($WEEK); + $itemEnd = $itemEnd->add($WEEK); } - // Create a list in which all start and end times are separate entries - $scheduleChanges->insert( [$START, $itemStart, $item], [-$itemStart->getTimestamp(), -$START] ); - $scheduleChanges->insert( [$END, $itemEnd, $item], [-$itemEnd->getTimestamp(), -$END] ); + if($DEBUG) print "Have a schedule item for {$item->name} {$item->suffix} from {$itemStart->format('Y-m-d H:i')} to {$itemEnd->format('Y-m-d H:i')}
\n"; + while($itemStart <= $end) { + if($DEBUG) { + print "Considering {$item->name} {$item->suffix} (#$item->scheduleid) at {$itemStart->format('Y-m-d H:i')} to {$itemEnd->format('Y-m-d H:i')}.
\n"; + if($item->startdate) print "-- (Item start is {$item->startdate->format('Y-m-d H:i')}, end is ". ($item->enddate ? $item->enddate->format('Y-m-d H:i') : "never") . ")
\n"; + } + + if($item->startdate && $itemStart < $item->startdate) { + if($DEBUG) print "-- Skipping {$item->name} {$item->suffix} (#$item->scheduleid) at {$itemStart->format('Y-m-d H:i')} because it is before the schedule instance start.
\n"; + } else if($item->enddate && $itemStart >= $item->enddate) { + if($DEBUG) print "-- Skipping {$item->name} {$item->suffix} (#$item->scheduleid) at {$itemStart->format('Y-m-d H:i')} because it is after the schedule instance end.
\n"; + } else { + // if($DEBUG) print "-- Adding {$item->name} {$item->suffix} (#$item->scheduleid) at {$itemStart->format('Y-m-d H:i')}
\n"; + // Create a list in which all start and end times are separate entries + $scheduleChanges->insert( [$START, $itemStart, $item], [-$itemStart->getTimestamp(), -$START] ); + $scheduleChanges->insert( [$END, $itemEnd, $item], [-$itemEnd->getTimestamp(), -$END] ); + } + + // Also keep track of all priorities seen + $active[$item->priority] = null; + + $itemStart = $itemStart->add($WEEK); + $itemEnd = $itemEnd->add($WEEK); + } - // Also keep track of all priorities seen - $active[$item->priority] = null; } if($DEBUG) { - print "

Schedule changes - initial state