Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
johnvanbreda committed Sep 25, 2024
2 parents df5638d + f6aaa93 commit 211620c
Show file tree
Hide file tree
Showing 36 changed files with 612 additions and 115 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# Version 9.4.0
*2024-09-23*

* Adds features to trigger reports that allow them to directly send emails, e.g. for replying to
partially complete reports of Asian Hornets.
* Adds fields to the `groups` entity (for iRecord Activities) for the following:
* Controlling if blogs are enabled and, if so, whether any member can post a blog or only admins.
See https://github.com/BiologicalRecordsCentre/iRecord/issues/1703.
* Defining if a group is a container for other groups, e.g. a project divided by years. See
https://github.com/BiologicalRecordsCentre/iRecord/issues/1678.
* Defining if a group is contained by another group.
* Update the `library/groups/find_group_by_url` report to include information about container and
contained groups. See https://github.com/BiologicalRecordsCentre/iRecord/issues/1678.
* Bugfixes for the new bulk edit tool. See https://github.com/BiologicalRecordsCentre/iRecord/issues/1673.
* Updated download field formats to support sensitive record download requirements. See
https://github.com/BiologicalRecordsCentre/iRecord/issues/1714 and the columns documentation at
https://indicia-docs.readthedocs.io/en/latest/site-building/iform/helpers/elasticsearch-report-helper.html#elasticsearchreporthelper-datagrid.
* Bugfix for the handling of the current common name when AddSynonym operations are processed by
the UKSI History processor. See https://github.com/Indicia-Team/warehouse/pull/522.
* Update the `library/groups/group_list` report to include a full text search parameter and also so
that setting the parameter `userFilterMode=joinable` excludes groups you are already a member of.
* The Elasticsearch special field handler for "sitename" now supports additional options -
`obscureifsensitive` - shows a warning message instead of the site name if sensitive and
`showifsensitive` - displays the full site name for sensitive records (only if the user has
access to full precision sensitive data).
* In Elasticsearch, sensitive or private records have their site names replaced by '!' to
distinguish them from records where there is no site name given. See
https://github.com/BiologicalRecordsCentre/iRecord/issues/1714.

# Version 9.3.0
*2024-08-19*

* Adds `search_code` to parameters of Rest endpoint, `services/rest/taxa/search`
and includes it in the response.

# Version 9.2.0
*2024-06-17*

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Indicia Warehouse [![Build Status](https://travis-ci.com/Indicia-Team/warehouse.svg?branch=master)](https://travis-ci.com/Indicia-Team/warehouse)
# Indicia Warehouse

This is the repository for the Indicia Warehouse, the server-side component of Indicia, the online wildlife recording
toolkit. Indicia accelerates development of wildlife recording websites and mobile applications. Documentation is
Expand Down
4 changes: 2 additions & 2 deletions application/config/version.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
*
* @var string
*/
$config['version'] = '9.3.3';
$config['version'] = '9.4.0';

/**
* Version release date.
*
* @var string
*/
$config['release_date'] = '2024-09-02';
$config['release_date'] = '2024-09-25';

/**
* Link to the code repository downloads page.
Expand Down
49 changes: 35 additions & 14 deletions application/controllers/attribute_by_survey.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/

/**
* Controller providing the ability to configure the list of attributes joined to a survey.
* Controller for relationship between custom attributes and surveys.
*/
class Attribute_By_Survey_Controller extends Indicia_Controller {
private $_survey = NULL;
Expand Down Expand Up @@ -83,8 +83,10 @@ public function edit($id) {
}

/**
* Handle the layout_update action, which uses $_POST data to find a list of commands
* for re-ordering the controls.
* Handle the layout_update action.
*
* Which uses $_POST data to find a list of commands for re-ordering the
* controls.
*/
public function layout_update() {
// Get the survey id from the segments in the URI.
Expand Down Expand Up @@ -223,8 +225,13 @@ protected function editViewName() {
}

/**
* Setup the values to be loaded into the edit view. For this class, we need to explode the
* items out of the validation_rules field, which our base class can do.
* Setup the values to be loaded into the edit view.
*
* For this class, we need to explode the items out of the validation_rules
* field, which our base class can do.
*
* @return array
* Key value pairs of data for the edit view.
*/
protected function getModelValues() {
$r = parent::getModelValues();
Expand Down Expand Up @@ -308,8 +315,12 @@ protected function prepareOtherViewData(array $values) {
return $otherData;
}

/**
* Override save handler to format validation rules correctly.
*/
public function save() {
// Build the validation_rules field from the set of controls that are associated with it.
// Build the validation_rules field from the set of controls that are
// associated with it.
$rules = [];
$ruleNames = ([
'required',
Expand Down Expand Up @@ -340,12 +351,20 @@ public function save() {
parent::save();
}

/**
* Determine the page to return to after saving.
*
* @return string
* Path to the page to return to.
*/
protected function get_return_page() {
$surveyPostKey = $this->type.'_attributes_website:restrict_to_survey_id';
$surveyPostKey = $this->type . '_attributes_website:restrict_to_survey_id';
if (isset($_POST[$surveyPostKey])) {
return 'attribute_by_survey/'.$_POST[$surveyPostKey].'?type='.$this->type;
} else {
// If $_POST data not available, then just return to the survey list. Shouldn't really happen.
return 'attribute_by_survey/' . $_POST[$surveyPostKey] . '?type=' . $this->type;
}
else {
// If $_POST data not available, then just return to the survey list.
// Shouldn't really happen.
return 'survey';
}
}
Expand All @@ -356,7 +375,8 @@ protected function get_return_page() {
protected function defineEditBreadcrumbs() {
$this->page_breadcrumbs[] = html::anchor('survey', 'Survey datasets');
$survey = ORM::Factory('survey', $this->model->restrict_to_survey_id);
$this->page_breadcrumbs[] = html::anchor('/attribute_by_survey/'.$this->model->restrict_to_survey_id.'?type='.$this->type, 'Attributes for '.$survey->title);
$this->page_breadcrumbs[] = html::anchor(
"/attribute_by_survey/$survey->id?type=$this->type", "Attributes for $survey->title");
$this->page_breadcrumbs[] = $this->model->caption();
}

Expand All @@ -371,8 +391,10 @@ protected function page_authorised() {
$this->_website_id = $survey->website_id;
}
return in_array($this->_website_id, $this->auth_filter['values']);
} else
}
else {
return true;
}
}

/**
Expand All @@ -385,5 +407,4 @@ protected function getSurvey() {
return $this->_survey;
}


}
}
24 changes: 21 additions & 3 deletions application/controllers/forgotten_password.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
*/
class Forgotten_Password_Controller extends Indicia_Controller {

/**
* Set up the main controller page.
*/
public function index() {

if ($this->auth->logged_in()) {
Expand Down Expand Up @@ -59,10 +62,25 @@ public function index() {
}
}

/**
* Return true if the user can login to the warehouse.
*
* User's only have login rughts if they have site editor role or higher, or
* core admin.
*
* @param ORM $user
* User object.
*
* @return bool
* True if the user is allowed to login.
*/
public function check_can_login($user) {
if (is_null($user->core_role_id) && ORM::factory('users_website')
->where('user_id', $user->id)->where('site_role_id IS NOT ', NULL)->find_all() === 0) {
$this->template->content->error_message = $_POST['UserID'] . ' does not have permission to log on to this website';
if (is_null($user->core_role_id)
&& ORM::factory('users_website')
->where('user_id', $user->id)
->where('site_role_id IS NOT ', NULL)
->find_all() === 0) {
$this->template->content->error_message = "$_POST[UserID] does not have permission to log on to this website";
return FALSE;
}
return TRUE;
Expand Down
3 changes: 3 additions & 0 deletions application/controllers/location_comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ protected function getDefaults() {
*
* After saving a comment you are returned to the location entry which has
* the comment.
*
* @return string
* Page path to return to.
*/
protected function get_return_page() {
if (array_key_exists('location_comment:location_id', $_POST)) {
Expand Down
14 changes: 6 additions & 8 deletions application/controllers/logout.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@
* @link https://github.com/indicia-team/warehouse
*/

/*
/**
* Provides application support for logging users out.
*/
class Logout_Controller extends Indicia_Controller {

/*
* description: Logs the current user out of the application. Destroys the current session
* parameters: None expected.
*/
public function index()
{
/**
* Logs the current user out of the application. Destroys the current session.
*/
public function index() {
$this->auth->logout(TRUE);
url::redirect();
}

}
}
104 changes: 103 additions & 1 deletion application/controllers/scheduled_tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ protected function checkTriggers() {
$params = json_decode($trigger->params_json, TRUE);
$reportEngine = new ReportEngine();
// Get parameter for last run specific to this trigger.
$params['date'] = variable::get("trigger_last_run-$trigger->id", $this->lastRunDate);
$params['date'] = variable::get("trigger_last_run-$trigger->id", $this->lastRunDate, FALSE);
$currentTime = time();
try {
$data = $reportEngine->requestReport($trigger->trigger_template_file . '.xml', 'local', 'xml', $params);
Expand Down Expand Up @@ -239,6 +239,13 @@ protected function checkTriggers() {
],
$digestMode
);
$this->doTriggerImmediateEmails(
$trigger->name,
[
'headings' => $parsedData['headingData'],
'data' => $parsedData['websiteRecordData'],
]
);
}
// Remember when this specific trigger last ran.
variable::set("trigger_last_run-$trigger->id", date('c', $currentTime));
Expand Down Expand Up @@ -354,6 +361,101 @@ private function doDirectTriggerNotifications($triggerName, array $data, $digest
}
}

/**
* Send emails for trigger reports that directly define an email to send.
*
* These emails can be sent directly, bypassing the notifications system and
* therefore can be sent to anyone not just registered users. E.g. a thank
* you email to anonymous recorders.
*
* @param string $triggerName
* Name of the trigger which fired.
* @param array $data
* Info regarding the trigger report columns and associated retrieved data.
*/
private function doTriggerImmediateEmails($triggerName, array $data) {
if (count($data['data']) === 0 || !in_array('email_to', $data['headings'])) {
return;
}
$emailConfig = Kohana::config('email');
if (!isset($emailConfig['address'])) {
self::msg('Email address not provided in email configuration', 'error');
return;
}
$colIndexes = [];
$sysCols = ['email_to', 'email_subject', 'email_body', 'email_name'];
foreach ($sysCols as $col) {
if (($colIdx = array_search($col, $data['headings'])) !== FALSE) {
$colIndexes[$col] = $colIdx;
}
}
$defaultSubject = empty(kohana::config('email.notification_subject'))
? kohana::lang('misc.notification_subject')
: kohana::config('email.notification_subject');
$emails = [];
foreach ($data['data'] as $records) {
foreach ($records as $record) {
if (!empty($record[$colIndexes['email_to']])) {
$to = $record[$colIndexes['email_to']];
$name = empty($colIndexes['email_name']) || empty($record[$colIndexes['email_name']])
? $to
: $record[$colIndexes['email_name']];
if (!isset($emails["$to $name"])) {
$emails["$to $name"] = [];
}

$subject = empty($colIndexes['email_subject']) || empty($record[$colIndexes['email_subject']])
? $defaultSubject
: $record[$colIndexes['email_subject']];
if (empty($colIndexes['email_body']) || empty($record[$colIndexes['email_body']])) {
// Email body not provided, so construct it from the other columns.
$body = '';
foreach ($data['headings'] as $idx => $colTitle) {
// Skip the functional email columns.
if (!in_array($colTitle, $sysCols)) {
$body[] = '<h2>' . htmlspecialchars($colTitle) . '</h2>';
$body[] = '<p>' . htmlspecialchars($record[$idx]) . '</p>';
}
}
}
else {
$body = $record[$colIndexes['email_body']];
}
// Aggregate emails per email address, so we don't send multiple.
$emails["$to $name"][] = [
'to' => $to,
'name' => $name,
'subject' => $subject,
'body' => $body,
];
}
}
}
$swift = email::connect();
// Now send the emails as a digest so each recipient only gets one email.
foreach ($emails as $infoList) {
// If a single email for this recipient we can use the subject, otherwise
// we use a generic subject and put each email subject in as a subtitle.
$subject = count($infoList) === 1 ? $infoList[0]['subject'] : $defaultSubject;
$emailContent = '';
foreach ($infoList as $infoItem) {
if (count($infoList) > 1) {
$emailContent .= '<h1>' . htmlspecialchars($infoItem['subject']) . '</h1>';
}
$emailContent .= '<div>' . $infoItem['body'] . '</div>';
}
$message = new Swift_Message(
$subject,
"<html>$emailContent</html>",
'text/html'
);
$recipients = new Swift_RecipientList();
$recipients->addTo($infoList[0]['to'], $infoList[0]['name']);
// Send the email.
$swift->send($message, $recipients, $emailConfig['address']);
}
}

/**
* Create email digests for trigger notifications.
*
Expand Down
4 changes: 2 additions & 2 deletions application/controllers/taxa_taxon_list.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public function __construct() {
$this->columns = [
'taxon' => '',
'authority' => '',
'taxon_group' => 'Taxon Group',
'taxon_group' => 'Taxon group',
'language' => '',
'taxonomic_sort_order' => 'Sort Order',
'taxonomic_sort_order' => 'Sort order',
];
$this->pagetitle = "Species";
}
Expand Down
2 changes: 1 addition & 1 deletion application/i18n/en_GB/form_error_messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
'default' => 'Invalid input.',
],
'centroid_sref_system' => [
'required' => 'The centorid spatial reference system must be supplied.',
'required' => 'The centroid spatial reference system must be supplied.',
'default' => 'Invalid input.',
],
'external_key' => [
Expand Down
Loading

0 comments on commit 211620c

Please sign in to comment.