announcements_feed.inc
Announcements feed helper functions.
File
-
modules/
announcements_feed/ announcements_feed.inc
View source
<?php
/**
* @file
* Announcements feed helper functions.
*/
/**
* Returns the list of announcements.
*
* @return array
* A build array with announcements.
*/
function announcements_feed_get_announcements() {
drupal_set_title(t('Community announcements'));
drupal_add_css(drupal_get_path('module', 'announcements_feed') . '/announcements_feed.css', array(
'group' => CSS_DEFAULT,
'every_page' => TRUE,
));
try {
$announcements = announcements_feed_get_all_announcements();
} catch (Exception $e) {
drupal_set_message(t('An error occurred while parsing the announcements feed, check the logs for more information.'), 'error');
return array();
}
$build = array();
foreach ($announcements as $announcement) {
$key = $announcement['featured'] ? '#featured' : '#standard';
$build[$key][] = $announcement;
}
$build = array_merge($build, array(
'#theme' => 'announcements_feed',
'#count' => count($announcements),
'#feed_link' => variable_get('announcements_feed_link', ANNOUNCEMENTS_FEED_DEFAULT_LINK),
));
return $build;
}
/**
* Generate an array of announcements with keys.
*
* @return array
* An array of announcements.
*/
function announcements_feed_get_all_announcements() {
$announcements = announcements_feed_fetch();
$announcements_feed = array();
foreach ($announcements as $announcement) {
$announcements_feed[] = array(
'id' => $announcement['id'],
'title' => $announcement['title'],
'link' => $announcement['url'],
'date_modified' => $announcement['date_modified'],
'date_published' => $announcement['date_published'],
'teaser' => $announcement['content_html'],
'version' => $announcement['_drupalorg']['version'],
'featured' => (bool) $announcement['_drupalorg']['featured'],
);
}
return $announcements_feed;
}
/**
* Fetches the feed either from a local cache or fresh remotely.
*
* The feed follows the "JSON Feed" format:
* - https://www.jsonfeed.org/version/1.1/
*
* The structure of an announcement item in the feed is:
* - id: Id.
* - title: Title of the announcement.
* - content_html: Announcement teaser.
* - url: URL
* - date_modified: Last updated timestamp.
* - date_published: Created timestamp.
* - _drupalorg.featured: 1 if featured, 0 if not featured.
* - _drupalorg.version: Target version of Drupal, as a Composer version.
*
* @param bool $force
* (optional) Whether to always fetch new items or not. Defaults to FALSE.
*
* @return array
* An array of announcements from the feed relevant to the Drupal version.
* The array is empty if there were no matching announcements. If an error
* occurred while fetching/decoding the feed, it is thrown as an exception.
*
* @throws Exception
*/
function announcements_feed_fetch($force = FALSE) {
$announcements = cache_get('announcements_feed');
if ($force || empty($announcements)) {
$announcements_feed_json_url = variable_get('announcements_feed_json_url', ANNOUNCEMENTS_FEED_DEFAULT_JSON_URL);
$response = drupal_http_request($announcements_feed_json_url);
if ($response->code == 200) {
$feeds = json_decode($response->data, TRUE);
if (!isset($feeds['items'])) {
watchdog('announcements_feed', 'The feed format is not valid.', NULL, WATCHDOG_ERROR);
throw new Exception('Announcements feed JSON format is invalid');
}
$announcements = array();
if ($feeds['items']) {
$announcements = $feeds['items'];
}
$announcements = array_filter($announcements, 'announcements_feed_filter_announcements');
cache_set('announcements_feed', $announcements, 'cache', REQUEST_TIME + variable_get('announcements_feed_max_age', ANNOUNCEMENTS_FEED_DEFAULT_MAX_AGE));
}
else {
watchdog('announcements_feed', 'The feed failed to fetch with an error code: @code, error message: @message.', array(
'@code' => $response->code,
'@message' => $response->error,
), WATCHDOG_ERROR);
throw new Exception($response->error, $response->code);
}
}
else {
$announcements = $announcements->data;
}
// The drupal.org endpoint is sorted by created date in descending order.
// We will limit the announcements based on the configuration limit.
$announcements_feed_limit = variable_get('announcements_feed_limit', ANNOUNCEMENTS_FEED_DEFAULT_LIMIT);
$announcements = array_slice($announcements, 0, $announcements_feed_limit);
// For the remaining announcements, put all the featured announcements
// before the rest.
uasort($announcements, 'announcements_feed_sort_featured');
return $announcements;
}
/**
* Sort the elements of announcements_feed by values in comparison function.
*/
function announcements_feed_sort_featured($a, $b) {
$a_value = (int) $a['_drupalorg']['featured'];
$b_value = (int) $b['_drupalorg']['featured'];
if ($a_value == $b_value) {
return 0;
}
return $a_value < $b_value ? -1 : 1;
}
/**
* Filter the announcements relevant to the Drupal version used with valid URL controlled by drupal.org.
*
* @param array $announcement
* Announcement feed array item to check.
*
* @return bool
* Return TRUE if $announcement is relevant and the URL is valid.
*/
function announcements_feed_filter_announcements($announcement) {
$announcement_url = '';
$announcement_version = '';
if (!empty($announcement['url'])) {
$announcement_url = $announcement['url'];
}
if (!empty($announcement['_drupalorg']['version'])) {
$announcement_version = $announcement['_drupalorg']['version'];
}
return announcements_feed_validate_url($announcement_url) && announcements_feed_is_relevant_item($announcement_version);
}
/**
* Check whether the version given is relevant to the Drupal version used.
*
* @param string $version
* Version to check.
*
* @return bool
* Return TRUE if the version matches Drupal version.
*/
function announcements_feed_is_relevant_item($version) {
if ($version == '*') {
return TRUE;
}
// Split the version if received in || formats.
$version_patterns = '/\\|\\|/';
$all_versions = preg_split($version_patterns, $version);
// The operation is optional and defaults to equals.
$p_op = '(?P<operation>!=|\\^|==|=|<|<=|>|>=|<>)?';
$operations = '=';
// Extracts major version from version string like 7, 8, 9.
$p_major = '(?P<major>\\d+)';
// Extracts minor version from version string.
$p_minor = '(?P<minor>(?:\\d+|x)(?:-[A-Za-z]+\\d+)?)';
foreach ($all_versions as $version) {
if (preg_match("/^\\s*{$p_op}\\s*{$p_major}(\\.{$p_minor})?/", $version, $matches)) {
$feed_version = $matches['major'];
if (!empty($matches['minor'])) {
$feed_version = $matches['major'] . '.' . $matches['minor'];
}
if (!empty($matches['operation'])) {
$operations = $matches['operation'];
if ($operations == '^') {
$operations = '>=';
}
}
if (isset($operations) && version_compare(VERSION, $feed_version, $operations)) {
return TRUE;
}
}
}
return FALSE;
}
/**
* Check whether a link is controlled by drupal.org.
*
* @param string $url
* URL to check.
*
* @return bool
* Return TRUE if the URL is controlled by drupal.org.
*/
function announcements_feed_validate_url($url) {
if (empty($url)) {
return FALSE;
}
$host = parse_url($url, PHP_URL_HOST);
// First character can only be a letter or a digit.
// @see https://www.rfc-editor.org/rfc/rfc1123#page-13
return $host && preg_match('/^([a-zA-Z0-9][a-zA-Z0-9\\-_]*\\.)?drupal\\.org$/', $host);
}
Functions
Title | Deprecated | Summary |
---|---|---|
announcements_feed_fetch | Fetches the feed either from a local cache or fresh remotely. | |
announcements_feed_filter_announcements | Filter the announcements relevant to the Drupal version used with valid URL controlled by drupal.org. | |
announcements_feed_get_all_announcements | Generate an array of announcements with keys. | |
announcements_feed_get_announcements | Returns the list of announcements. | |
announcements_feed_is_relevant_item | Check whether the version given is relevant to the Drupal version used. | |
announcements_feed_sort_featured | Sort the elements of announcements_feed by values in comparison function. | |
announcements_feed_validate_url | Check whether a link is controlled by drupal.org. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.