diff --git a/blocks/rss_client/block_rss_client.php b/blocks/rss_client/block_rss_client.php
index 3f3e292f175..b745aef7a67 100644
--- a/blocks/rss_client/block_rss_client.php
+++ b/blocks/rss_client/block_rss_client.php
@@ -61,19 +61,6 @@
protected function get_footer($feedrecords) {
$footer = null;
- if ($this->config->block_rss_client_show_channel_link) {
- global $CFG;
- require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
-
- $feedrecord = array_pop($feedrecords);
- $feed = new moodle_simplepie($feedrecord->url);
- $channellink = new moodle_url($feed->get_link());
-
- if (!empty($channellink)) {
- $footer = new block_rss_client\output\footer($channellink);
- }
- }
-
if ($this->hasfailedfeeds) {
if (has_any_capability(['block/rss_client:manageownfeeds', 'block/rss_client:manageanyfeeds'], $this->context)) {
if ($footer === null) {
@@ -104,6 +91,15 @@
return $this->content;
}
+ $managefeedfooterlink = '';
+ if (has_any_capability(['block/rss_client:manageanyfeeds', 'block/rss_client:manageownfeeds'], $this->context)) {
+ $managefeedfooterlink = html_writer::link(
+ new moodle_url('/blocks/rss_client/managefeeds.php', ['courseid' => $this->page->course->id]),
+ get_string('managefeeds', 'block_rss_client'),
+ ['class' => 'btn btn-primary', 'role' => 'button'],
+ );
+ }
+
if (!isset($this->config)) {
// The block has yet to be configured - just display configure message in
// the block if user has permission to configure it
@@ -112,6 +108,8 @@
$this->content->text = get_string('feedsconfigurenewinstance2', 'block_rss_client');
}
+ $this->content->footer = $managefeedfooterlink;
+
return $this->content;
}
@@ -156,6 +154,8 @@
$this->content->footer = $renderer->render_footer($footer);
}
+ $this->content->footer .= $managefeedfooterlink;
+
return $this->content;
}
@@ -259,6 +259,12 @@
}
}
+ // Feed channel link.
+ if ($this->config->block_rss_client_show_channel_link) {
+ $channellink = $simplepiefeed->get_link();
+ $feed->set_channellink($channellink ? new moodle_url($channellink) : null);
+ }
+
return $feed;
}
diff --git a/blocks/rss_client/classes/output/feed.php b/blocks/rss_client/classes/output/feed.php
index 28ea2cf3594..61b9ae8f995 100644
--- a/blocks/rss_client/classes/output/feed.php
+++ b/blocks/rss_client/classes/output/feed.php
@@ -44,6 +44,13 @@ class feed implements \renderable, \templatable {
*/
protected $title = null;
+ /**
+ * The feed's channel link.
+ *
+ * @var string|null
+ */
+ protected ?string $channellink;
+
/**
* An array of renderable feed items
*
@@ -78,11 +85,13 @@ class feed implements \renderable, \templatable {
* @param string $title The title of the RSS feed
* @param boolean $showtitle Whether to show the title
* @param boolean $showimage Whether to show the channel image
+ * @param string|null $channellink The channel link of the RSS feed
*/
- public function __construct($title, $showtitle = true, $showimage = true) {
+ public function __construct($title, $showtitle = true, $showimage = true, ?string $channellink = null) {
$this->title = $title;
$this->showtitle = $showtitle;
$this->showimage = $showimage;
+ $this->channellink = $channellink;
}
/**
@@ -97,6 +106,7 @@ class feed implements \renderable, \templatable {
'title' => $this->showtitle ? $this->title : null,
'image' => null,
'items' => array(),
+ 'channellink' => $this->channellink ?? null,
);
if ($this->showimage && $this->image) {
@@ -131,6 +141,15 @@ class feed implements \renderable, \templatable {
return $this->title;
}
+ /**
+ * Set the feed channel link.
+ *
+ * @param \moodle_url|null $channellink the URL to the channel website.
+ */
+ public function set_channellink(?\moodle_url $channellink) {
+ $this->channellink = $channellink;
+ }
+
/**
* Add an RSS item
*
diff --git a/blocks/rss_client/classes/output/footer.php b/blocks/rss_client/classes/output/footer.php
index c864df3d686..7ff348ad289 100644
--- a/blocks/rss_client/classes/output/footer.php
+++ b/blocks/rss_client/classes/output/footer.php
@@ -100,7 +100,6 @@ class footer implements \renderable, \templatable {
*/
public function export_for_template(\renderer_base $output) {
$data = new \stdClass();
- $data->channellink = clean_param($this->channelurl, PARAM_URL);
if ($this->manageurl) {
$data->hasfailedfeeds = true;
$data->manageurl = clean_param($this->manageurl, PARAM_URL);
diff --git a/blocks/rss_client/edit_form.php b/blocks/rss_client/edit_form.php
index 6f481abf83d..dc6e9ef5b2c 100644
--- a/blocks/rss_client/edit_form.php
+++ b/blocks/rss_client/edit_form.php
@@ -22,6 +22,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir .'/simplepie/moodle_simplepie.php');
+
/**
* Form for editing RSS client block instances.
*
@@ -29,24 +33,36 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_rss_client_edit_form extends block_edit_form {
+
+ /** @var stdClass|null The new RSS feed URL object. */
+ private ?stdClass $newrss = null;
+
protected function specific_definition($mform) {
global $CFG, $DB, $USER;
// Fields for editing block contents.
$mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
- $mform->addElement('selectyesno', 'config_display_description', get_string('displaydescriptionlabel', 'block_rss_client'));
- $mform->setDefault('config_display_description', 0);
+ $radiogroup = [
+ $mform->createElement('radio', 'config_method',
+ get_string('configmethodexisting', 'block_rss_client'), null, 'existing'),
+ $mform->createElement('radio', 'config_method',
+ get_string('configmethodnew', 'block_rss_client'), null, 'new'),
+ ];
+ $mform->addGroup(
+ elements: $radiogroup,
+ name: 'config_method_group',
+ separator: [' '],
+ appendName: false,
+ );
+ $mform->setDefault('config_method', 'existing');
- $mform->addElement('text', 'config_shownumentries', get_string('shownumentrieslabel', 'block_rss_client'), array('size' => 5));
- $mform->setType('config_shownumentries', PARAM_INT);
- $mform->addRule('config_shownumentries', null, 'numeric', null, 'client');
- if (!empty($CFG->block_rss_client_num_entries)) {
- $mform->setDefault('config_shownumentries', $CFG->block_rss_client_num_entries);
- } else {
- $mform->setDefault('config_shownumentries', 5);
- }
+ // Add new RSS feed.
+ $mform->addElement('text', 'config_feedurl', get_string('feedurl', 'block_rss_client'));
+ $mform->setType('config_feedurl', PARAM_URL);
+ $mform->hideIf('config_feedurl', 'config_method', 'ne', 'new');
+ // Select existing RSS feed.
$insql = '';
$params = array('userid' => $USER->id);
if (!empty($this->block->config) && !empty($this->block->config->rssid)) {
@@ -68,21 +84,33 @@ class block_rss_client_edit_form extends block_edit_form {
if ($rssfeeds) {
$select = $mform->addElement('select', 'config_rssid', get_string('choosefeedlabel', 'block_rss_client'), $rssfeeds);
$select->setMultiple(true);
-
+ $mform->hideIf('config_rssid', 'config_method', 'ne', 'existing');
} else {
$mform->addElement('static', 'config_rssid_no_feeds', get_string('choosefeedlabel', 'block_rss_client'),
get_string('nofeeds', 'block_rss_client'));
+ $mform->hideIf('config_rssid_no_feeds', 'config_method', 'ne', 'existing');
}
- if (has_any_capability(array('block/rss_client:manageanyfeeds', 'block/rss_client:manageownfeeds'), $this->block->context)) {
- $mform->addElement('static', 'nofeedmessage', '',
- '' .
- get_string('feedsaddedit', 'block_rss_client') . '');
- }
+ // Subheading: Display settings for RSS feed.
+ $startsubheading = '
';
+ $mform->addElement('html', $startsubheading . get_string('displaysettings', 'block_rss_client') . $endsubheading);
$mform->addElement('text', 'config_title', get_string('uploadlabel'));
$mform->setType('config_title', PARAM_NOTAGS);
+ $mform->addElement('selectyesno', 'config_display_description', get_string('displaydescriptionlabel', 'block_rss_client'));
+ $mform->setDefault('config_display_description', 0);
+
+ $mform->addElement('text', 'config_shownumentries', get_string('shownumentrieslabel', 'block_rss_client'), ['size' => 5]);
+ $mform->setType('config_shownumentries', PARAM_INT);
+ $mform->addRule('config_shownumentries', null, 'numeric', null, 'client');
+ if (!empty($CFG->block_rss_client_num_entries)) {
+ $mform->setDefault('config_shownumentries', $CFG->block_rss_client_num_entries);
+ } else {
+ $mform->setDefault('config_shownumentries', 5);
+ }
+
$mform->addElement('selectyesno', 'config_block_rss_client_show_channel_link', get_string('clientshowchannellinklabel', 'block_rss_client'));
$mform->setDefault('config_block_rss_client_show_channel_link', 0);
@@ -90,6 +118,91 @@ class block_rss_client_edit_form extends block_edit_form {
$mform->setDefault('config_block_rss_client_show_channel_image', 0);
}
+ /**
+ * Overriding the get_data function to insert a new RSS ID.
+ */
+ public function get_data(): ?stdClass {
+ $data = parent::get_data();
+ // Force the 'existing` method as a default.
+ $data->config_method = 'existing';
+ // Sanitize the title to prevent XSS (Cross-Site Scripting) attacks by encoding special characters into HTML entities.
+ $data->config_title = htmlspecialchars($data->config_title, ENT_QUOTES, 'utf-8');
+ // If the new RSS is not empty then add the ID to the config_rssid.
+ if ($data && $this->newrss) {
+ $data->config_rssid[] = $this->newrss->id;
+ }
+ return $data;
+ }
+
+ /**
+ * Overriding the definition_after_data to empty the input.
+ */
+ public function definition_after_data(): void {
+ parent::definition_after_data();
+ $mform =& $this->_form;
+ // If form is not submitted then empty the feed URL.
+ if (!$this->is_submitted()) {
+ $mform->getElement('config_feedurl')->setValue('');
+ }
+ }
+
+ /**
+ * Overriding the validation to validate the RSS URL and store it to the database.
+ *
+ * If there are no errors, insert the new feed to the database and store the object in
+ * the private property so it can be saved to the RSS block config.
+ *
+ * @param array $data Data from the form.
+ * @param array $files Files form the form.
+ * @return array of errors from validation.
+ */
+ public function validation($data, $files): array {
+ global $USER, $DB;
+ $errors = parent::validation($data, $files);
+
+ if ($data['config_method'] === "new") {
+ // If the "New" method is selected and the feed URL is not empty, then proceed.
+ if ($data['config_feedurl']) {
+ if (!filter_var($data['config_feedurl'], FILTER_VALIDATE_URL)) {
+ $errors['config_feedurl'] = get_string('couldnotfindloadrssfeed', 'block_rss_client');
+ return $errors;
+ }
+ try {
+ $rss = new moodle_simplepie();
+ // Set timeout for longer than normal to try and grab the feed.
+ $rss->set_timeout(10);
+ $rss->set_feed_url($data['config_feedurl']);
+ $rss->set_autodiscovery_cache_duration(0);
+ $rss->set_autodiscovery_level(moodle_simplepie::LOCATOR_ALL);
+ $rss->init();
+ if ($rss->error()) {
+ $errors['config_feedurl'] = get_string('couldnotfindloadrssfeed', 'block_rss_client');
+ } else {
+ // Return URL without quoting.
+ $discoveredurl = new moodle_url($rss->subscribe_url());
+ $theurl = $discoveredurl->out(false);
+ // Save the RSS to the database.
+ $this->newrss = new stdClass;
+ $this->newrss->userid = $USER->id;
+ $this->newrss->title = $rss->get_title();
+ $this->newrss->description = $rss->get_description();
+ $this->newrss->url = $theurl;
+ $newrssid = $DB->insert_record('block_rss_client', $this->newrss);
+ $this->newrss->id = $newrssid;
+ }
+ } catch (Exception $e) {
+ $errors['config_feedurl'] = get_string('couldnotfindloadrssfeed', 'block_rss_client');
+ }
+ } else {
+ // If the "New" method is selected, but the feed URL is empty, then raise error.
+ $errors['config_feedurl'] = get_string('err_required', 'form');
+ }
+
+ }
+
+ return $errors;
+ }
+
/**
* Display the configuration form when block is being added to the page
*
diff --git a/blocks/rss_client/lang/en/block_rss_client.php b/blocks/rss_client/lang/en/block_rss_client.php
index 06facda0af2..310ca7d5cce 100644
--- a/blocks/rss_client/lang/en/block_rss_client.php
+++ b/blocks/rss_client/lang/en/block_rss_client.php
@@ -27,17 +27,21 @@ $string['addheadlineblock'] = 'Add RSS headline block';
$string['addnew'] = 'Add new';
$string['addnewfeed'] = 'Add a new feed';
$string['cannotmakemodification'] = 'You are not allowed to make modifications to this RSS feed at this time.';
+$string['choosefeedlabel'] = 'Select the feeds to display in this block';
$string['clientchannellink'] = 'Source site...';
$string['clientnumentries'] = 'The default number of entries to show per feed.';
-$string['clientshowchannellinklabel'] = 'Should a link to the original site (channel link) be displayed? (Note that if no feed link is supplied in the news feed then no link will be shown) :';
+$string['clientshowchannellinklabel'] = 'Show source link if available';
$string['clientshowimagelabel'] = 'Show channel image if available :';
$string['configblock'] = 'Configure this block';
+$string['configmethodexisting'] = 'Select existing RSS feed';
+$string['configmethodnew'] = 'Add new RSS feed';
$string['couldnotfindfeed'] = 'Could not find feed with id';
$string['couldnotfindloadrssfeed'] = 'Could not find or load the RSS feed.';
$string['customtitlelabel'] = 'Custom title (leave blank to use title supplied by feed):';
$string['deletefeedconfirm'] = 'Are you sure you want to delete this feed?';
$string['disabledrssfeeds'] = 'RSS feeds are disabled';
-$string['displaydescriptionlabel'] = 'Display each link\'s description?';
+$string['displaydescriptionlabel'] = 'Show descriptions for entries';
+$string['displaysettings'] = 'Display settings for RSS feed';
$string['editafeed'] = 'Edit a feed';
$string['editfeeds'] = 'Edit, subscribe or unsubscribe from RSS/Atom news feeds';
$string['editnewsfeeds'] = 'Edit news feeds';
@@ -54,11 +58,10 @@ $string['feedsaddedit'] = 'Add/edit feeds';
$string['feedsconfigurenewinstance'] = 'Click here to configure this block to display RSS feeds.';
$string['feedsconfigurenewinstance2'] = 'Click the edit icon above to configure this block to display RSS feeds.';
$string['feedupdated'] = 'News feed updated';
-$string['feedurl'] = 'Feed URL';
+$string['feedurl'] = 'RSS link';
$string['findmorefeeds'] = 'Find more RSS feeds';
-$string['choosefeedlabel'] = 'Choose the feeds which you would like to make available in this block:';
$string['managefeeds'] = 'Manage all my feeds';
-$string['nofeeds'] = 'There are no RSS feeds defined for this site.';
+$string['nofeeds'] = 'There are no existing RSS feeds configured for this site. You can add one choosing \'Add new RSS feed\'.';
$string['numentries'] = 'Entries per feed';
$string['pickfeed'] = 'Pick a news feed';
$string['pluginname'] = 'Remote RSS feeds';
@@ -81,7 +84,7 @@ $string['rss_client:manageownfeeds'] = 'Manage own RSS feeds';
$string['rss_client:myaddinstance'] = 'Add a new Remote RSS feeds block to Dashboard';
$string['seeallfeeds'] = 'See all feeds';
$string['sharedfeed'] = 'Shared feed';
-$string['shownumentrieslabel'] = 'Max number entries to show per block.';
+$string['shownumentrieslabel'] = 'Entries to display';
$string['submitters'] = 'Who will be allowed to define new RSS feeds? Defined feeds are available for any page on your site.';
$string['submitters2'] = 'Submitters';
$string['timeout'] = 'Time in minutes before an RSS feed expires in cache. Note that this time defines the minimum time before expiry; the feed will be refreshed in cache on the next cron execution after expiry. Recommended values are 30 mins or greater.';
diff --git a/blocks/rss_client/templates/block.mustache b/blocks/rss_client/templates/block.mustache
index 6cc2c71ea07..8dca0217c8c 100644
--- a/blocks/rss_client/templates/block.mustache
+++ b/blocks/rss_client/templates/block.mustache
@@ -55,7 +55,8 @@
"permalink": "https://www.example.com/my-cat-story.html",
"datepublished": "12 January 2016, 9:12 pm"
}
- ]
+ ],
+ "channellink": "https://www.example.com"
},
{
"title": "News from around my kitchen",
@@ -81,7 +82,8 @@
"permalink": "https://www.example.com/oven-smoke.html",
"datepublished": "13 January 2016, 8:25 pm"
}
- ]
+ ],
+ "channellink": "https://www.example.com"
}
]
}
diff --git a/blocks/rss_client/templates/channel_image.mustache b/blocks/rss_client/templates/channel_image.mustache
index f20166e53d7..230f8d31f5d 100644
--- a/blocks/rss_client/templates/channel_image.mustache
+++ b/blocks/rss_client/templates/channel_image.mustache
@@ -42,7 +42,7 @@
{{/link}}
-
+
{{#link}}
diff --git a/blocks/rss_client/templates/feed.mustache b/blocks/rss_client/templates/feed.mustache
index a69f3e82888..7600bb9fa11 100644
--- a/blocks/rss_client/templates/feed.mustache
+++ b/blocks/rss_client/templates/feed.mustache
@@ -55,7 +55,8 @@
"permalink": "https://www.example.com/my-cat-story.html",
"datepublished": "12 January 2016, 9:12 pm"
}
- ]
+ ],
+ "channellink": "https://www.example.com"
}
}}
{{$image}}
@@ -77,3 +78,9 @@
{{/items}}
{{/items}}
+
+{{#channellink}}
+
+{{/channellink}}
diff --git a/blocks/rss_client/templates/footer.mustache b/blocks/rss_client/templates/footer.mustache
index dd5d0fe2b39..d3fb1c471fa 100644
--- a/blocks/rss_client/templates/footer.mustache
+++ b/blocks/rss_client/templates/footer.mustache
@@ -30,13 +30,10 @@
Example context (json):
{
- "channellink": "https://www.example.com/feeds/rss"
+ "hasfailedfeeds": true,
+ "manageurl": "http://moodle.web/blocks/rss_client/managefeeds.php?courseid=1"
}
}}
-{{#channellink}}
- {{#str}} clientchannellink, block_rss_client {{/str}}
- {{#hasfailedfeeds}}
{{/hasfailedfeeds}}
-{{/channellink}}
{{#hasfailedfeeds}}
- {{#str}} failedfeeds, block_rss_client {{/str}}
-{{/hasfailedfeeds}}
\ No newline at end of file
+ {{#str}} failedfeeds, block_rss_client {{/str}}
+{{/hasfailedfeeds}}
diff --git a/blocks/rss_client/tests/behat/block_rss_client_frontpage.feature b/blocks/rss_client/tests/behat/block_rss_client_frontpage.feature
new file mode 100644
index 00000000000..36eddaf2970
--- /dev/null
+++ b/blocks/rss_client/tests/behat/block_rss_client_frontpage.feature
@@ -0,0 +1,48 @@
+@block @block_rss_client
+Feature: Enable RSS client block menu on the frontpage
+ In order to enable the RSS client block on the frontpage
+ As an admin
+ I can add RSS client block to the frontpage
+
+ Background:
+ Given I log in as "admin"
+ When I navigate to "Plugins > Blocks > Manage blocks" in site administration
+ Then I enable "rss_client" "block" plugin
+ And the following "blocks" exist:
+ | blockname | contextlevel | reference | pagetypepattern | defaultregion |
+ | rss_client | System | 1 | site-index | side-pre |
+
+ @javascript
+ Scenario: Configuring the RSS block on the frontpage
+ Given I log in as "admin"
+ And I am on site homepage
+ And I turn editing mode on
+ And "Remote news feed" "block" should exist
+ And I configure the "Remote news feed" block
+ And I should see "There are no existing RSS feeds configured for this site. You can add one choosing 'Add new RSS feed'."
+
+ # Test filling in an empty URL in the input.
+ And I click on "Add new RSS feed" "radio"
+ And I press "Save changes"
+ And I should see "You must supply a value here."
+
+ # Test filling in with a non-valid URL in the input.
+ And I set the field "config_feedurl" to "https://example.com/notvalid.rss"
+ And I press "Save changes"
+ And I should see "Could not find or load the RSS feed."
+
+ # Test filling in with the correct URL in the input.
+ And I set the field "config_feedurl" to "https://www.nasa.gov/rss/dyn/breaking_news.rss"
+ And I set the field "config_block_rss_client_show_channel_link" to "Yes"
+ And I press "Save changes"
+ And I should see "NASA"
+ And I should see "Source site..."
+
+ # Test the existence of the available feeds.
+ When I configure the "NASA" block
+ Then I should see "NASA" in the "Select the feeds to display in this block" "select"
+ And I click on "Cancel" "button" in the "Configure NASA block" "dialogue"
+
+ # Test the Manage all my feeds page.
+ And I click on "Manage all my feeds" "link"
+ And I should see "NASA"