Commit 63e0484d authored by Nathan Lewis's avatar Nathan Lewis

Improvements to notification system (Bug #1299993)

- Each activity type can specify a default notification method. They default
  to 'email' to remain backwards compatible.
- Each activity type can specify if it is allowed to be set to 'none'. Defaults
  to 'allowed' for backwards compatibility.
- Removed 'required' from notification settings - it didn't make sense, and the
  change above deals with this in a better way.
- The site wide defaults for each activity type can be edited in
  Site options -> Notification settings. These are applied to new users or
  whenever a user does not have the appropriate usr_activity_preference records.
- Removed 'Default notification method' as it's functionality is now covered by
  the change above.
- There is a separate help next to each activity type to explain what messages
  will be affected by the setting.

Change-Id: I131cdeefbeaa8e43688aefd9d770fc8cb9bceea8
Signed-off-by: default avatarNathan Lewis <nathan.lewis@totaralms.com>
parent 626fe28a
......@@ -20,50 +20,7 @@ define('TITLE', get_string('notifications'));
require_once('pieforms/pieform.php');
require_once(get_config('libroot') . 'activity.php');
$activitytypes = get_records_array('activity_type', 'admin', 0);
if ($USER->get('admin') || $USER->is_institutional_admin()) {
$admintypes = get_records_array('activity_type', 'admin', 1);
$activitytypes = array_merge($activitytypes , $admintypes);
}
$notifications = plugins_installed('notification');
$elements = array();
$options = array();
foreach ($notifications as $n) {
$options[$n->name] = get_string('name', 'notification.' . $n->name);
}
foreach ($activitytypes as $type) {
$dv = $USER->get_activity_preference($type->id);
if (empty($dv)) {
$dv = call_static_method(generate_activity_class_name($type->name, $type->plugintype, $type->pluginname), 'default_notification_method');
}
if (!empty($type->plugintype)) {
$section = $type->plugintype . '.' . $type->pluginname;
}
else {
$section = 'activity';
}
if ($dv == 'email' && !isset($maildisabledmsg) && get_account_preference($USER->get('id'),'maildisabled')) {
$SESSION->add_error_msg(get_string('maildisableddescription', 'account', get_config('wwwroot') . 'account/index.php'), false);
$maildisabledmsg = true;
}
$elements['activity_'.$type->id] = array(
'defaultvalue' => $dv,
'type' => 'select',
'title' => get_string('type' . $type->name, $section),
'options' => $options,
'rules' => array(
'required' => true
)
);
if (!empty($type->admin)) {
$elements['activity_'.$type->id]['rules']['required'] = false;
$elements['activity_'.$type->id]['options']['none'] = get_string('none');
}
}
$elements = get_notification_settings_elements($USER);
$elements['submit'] = array(
'type' => 'submit',
......@@ -76,7 +33,7 @@ $prefsform = pieform(array(
'method' => 'post',
'jsform' => true,
'renderer' => 'table',
'plugintype ' => 'core',
'plugintype' => 'core',
'pluginname' => 'account',
'elements' => $elements,
));
......@@ -88,16 +45,9 @@ $smarty->assign('PAGEHEADING', get_config('dropdownmenu') ? get_string('settings
$smarty->display('form.tpl');
function activityprefs_submit(Pieform $form, $values) {
global $activitytypes, $admintypes, $USER;
$userid = $USER->get('id');
foreach ($activitytypes as $type) {
if ($values['activity_'.$type->id] == 'none') {
$USER->set_activity_preference($type->id, null);
}
else {
$USER->set_activity_preference($type->id, $values['activity_'.$type->id]);
}
}
global $USER;
save_notification_settings($values, $USER);
$form->json_reply(PIEFORM_OK, get_string('prefssaved', 'account'));
}
......@@ -20,6 +20,7 @@ require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once('pieforms/pieform.php');
require_once('searchlib.php');
require_once('antispam.php');
require_once(get_config('libroot') . 'activity.php');
define('TITLE', get_string('siteoptions', 'admin'));
$langoptions = get_languages();
......@@ -33,13 +34,7 @@ $searchpluginoptions = get_search_plugins();
$countries = getoptions_country();
$notificationmethods = array();
foreach (array_keys(plugins_installed('notification')) as $n) {
$notificationmethods[$n] = get_string('name', 'notification.' . $n);
}
if (!$notificationdefault = get_config('defaultnotificationmethod')) {
$notificationdefault = isset($notificationmethods['email']) ? 'email' : 'internal';
}
$notificationelements = get_notification_settings_elements(null, true);
$spamtraps = available_spam_traps();
$siteoptionform = array(
......@@ -545,17 +540,15 @@ $siteoptionform = array(
'disabled' => in_array('noreplyaddress', $OVERRIDDEN),
'help' => true,
),
'defaultnotificationmethod' => array(
'type' => 'select',
'title' => get_string('defaultnotificationmethod', 'admin'),
'description' => get_string('defaultnotificationmethoddescription', 'admin'),
'defaultvalue' => $notificationdefault,
'disabled' => in_array('defaultnotificationmethod', $OVERRIDDEN),
'options' => $notificationmethods,
'help' => true,
),
),
),
'notificationsettings' => array(
'type' => 'fieldset',
'collapsible' => true,
'collapsed' => true,
'legend' => get_string('notificationsettings', 'admin'),
'elements' => $notificationelements,
),
'generalsettings' => array(
'type' => 'fieldset',
'collapsible' => true,
......@@ -747,7 +740,7 @@ function siteoptions_submit(Pieform $form, $values) {
'remoteavatars', 'userscanhiderealnames', 'antispam', 'spamhaus', 'surbl', 'anonymouscomments',
'recaptchaonregisterform', 'recaptchapublickey', 'recaptchaprivatekey', 'loggedinprofileviewaccess', 'disableexternalresources',
'proxyaddress', 'proxyauthmodel', 'proxyauthcredentials', 'smtphosts', 'smtpport', 'smtpuser', 'smtppass', 'smtpsecure',
'noreplyaddress', 'defaultnotificationmethod', 'homepageinfo', 'showprogressbar', 'showonlineuserssideblock', 'onlineuserssideblockmaxusers',
'noreplyaddress', 'homepageinfo', 'showprogressbar', 'showonlineuserssideblock', 'onlineuserssideblockmaxusers',
'registerterms', 'licensemetadata', 'licenseallowcustom', 'allowmobileuploads', 'creategroups', 'createpublicgroups', 'allowgroupcategories', 'wysiwyg',
'staffreports', 'staffstats', 'userscandisabledevicedetection', 'watchlistnotification_delay',
'masqueradingreasonrequired', 'masqueradingnotified', 'searchuserspublic',
......@@ -811,6 +804,8 @@ function siteoptions_submit(Pieform $form, $values) {
ArtefactTypeFolder::change_public_folder_name($oldlanguage, $values['lang']);
}
save_notification_settings($values, null, true);
// If they've changed the search plugin, give the new plugin a chance to initialize.
if ($oldsearchplugin != $values['searchplugin']) {
// Call the old search plugin's sitewide cleanup method
......
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Feedback</h3>
<p>Notification of any feedback or comments received on any pages, artefacts or journals within your ePortfolio.</p>
\ No newline at end of file
......@@ -60,6 +60,8 @@ class PluginArtefactComment extends PluginArtefact {
'name' => 'feedback',
'admin' => 0,
'delay' => 0,
'allownonemethod' => 1,
'defaultmethod' => 'email',
)
);
}
......
......@@ -436,7 +436,7 @@ class User {
*/
public function get_activity_preference($key) {
$activityprefs = $this->get('activityprefs');
return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
return array_key_exists($key, $activityprefs) ? $activityprefs[$key] : false;
}
/** @todo document this method */
......
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>New forum post</h3>
<p>Notification of a new topic or reply in a forum you are subscribed to.</p>
\ No newline at end of file
......@@ -319,7 +319,9 @@ EOF;
(object)array(
'name' => 'newpost',
'admin' => 0,
'delay' => 1
'delay' => 1,
'allownonemethod' => 1,
'defaultmethod' => 'email',
)
);
}
......
......@@ -536,8 +536,7 @@ $string['emailsmtpsecuressl'] = 'SSL';
$string['emailsmtpsecuretls'] = 'TLS';
$string['emailnoreplyaddress'] = 'System mail address';
$string['emailnoreplyaddressdescription'] = 'Emails come out as from this address';
$string['defaultnotificationmethod'] = 'Default notification method';
$string['defaultnotificationmethoddescription'] = 'Notification method for new users';
$string['notificationsettings'] = 'Notification settings';
......
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Contact us</h3>
<p>Messages to administrators that are sent via the Contact us form.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Group message</h3>
<p>Automatically generated by the system, e.g. user request for joining a group.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Institution message</h3>
<p>Automatically generated by the system, e.g. institution confirmation message, institution removal message, institution request sent to administrator.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>System message</h3>
<p>Automatically generated by the system or sent to you by one of the site administrators, e.g. account confirmation message.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Objectionable content</h3>
<p>Messages to administrators that contain complaints by users about objectionable content.</p>
<p>Pages and artefacts can be flagged as containing objectionable content.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Message from other users</h3>
<p>Sent to you directly from other users of the site.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>New page access</h3>
<p>Notification that you or one of your groups have been given access to a new or existing non-public page. You do not receive notifications about pages accessible to all logged-in users and the public.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Virus flag release</h3>
<p>Messages to administrators about files that were released by the virus scanner.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Repeat virus upload</h3>
<p>Messages to administrators about users who repeatedly upload virus-infected files. Virus checking must be turned on.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Watchlist</h3>
<p>Notification of activity on any artefact, page, group or journal you are monitoring.</p>
\ No newline at end of file
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Notifications</h3>
<h3>Notification types</h3>
<p>You may select how you receive notifications for each activity type.</p>
<h4>Notification types</h4>
<p><strong>Inbox:</strong> your notifications area will display a list of activity notifications received.</p>
<p><strong>Email:</strong> your primary email address will receive an email each time the activity occurs.</p>
<p><strong>Email digest:</strong> your primary email address will receive an email each day with a list of the activities of the last 24 hours.</p>
<p><strong>Note:</strong> If you select either email option, all activities will also be recorded in your notifications log as they happen. They will all be marked as already read. These notifications automatically expire and are removed from your log after 60 days.</p>
<h4>Activity types</h4>
<p><strong>System messages:</strong> automatically generated by the system or sent to you by one of the site administrators.</p>
<p><strong>Messages from other users:</strong> sent to you directly from other users of the site.</p>
<p><strong>Feedback:</strong> notification of any feedback or comments received on any pages, artefacts or journals within your ePortfolio.</p>
<p><strong>Watchlist:</strong> notification of activity on any artefact, page, group or journal you are monitoring.</p>
<p><strong>New page access:</strong> notification that you or one of your groups have been given access to a new or existing non-public page.</p>
......@@ -132,21 +132,6 @@ function activity_get_users($activitytype, $userids=null, $userobjs=null, $admin
}
function activity_default_notification_method() {
static $method = null;
if (is_null($method)) {
if (!$method = get_config('defaultnotificationmethod')) {
if (in_array('email', array_keys(plugins_installed('notification')))) {
$method = 'email';
}
else {
$method = 'internal';
}
}
}
return $method;
}
/**
* this function inserts a default set of activity preferences for a given user
* id
......@@ -154,21 +139,18 @@ function activity_default_notification_method() {
function activity_set_defaults($eventdata) {
$user_id = $eventdata['id'];
$activitytypes = get_records_array('activity_type', 'admin', 0);
$method = activity_default_notification_method();
foreach ($activitytypes as $type) {
insert_record('usr_activity_preference', (object)array(
'usr' => $user_id,
'activity' => $type->id,
'method' => $method,
'method' => $type->defaultmethod,
));
}
}
function activity_add_admin_defaults($userids) {
$activitytypes = get_records_array('activity_type', 'admin', 1);
$method = activity_default_notification_method();
foreach ($activitytypes as $type) {
foreach ($userids as $id) {
......@@ -176,7 +158,7 @@ function activity_add_admin_defaults($userids) {
insert_record('usr_activity_preference', (object)array(
'usr' => $id,
'activity' => $type->id,
'method' => $method,
'method' => $type->defaultmethod,
));
}
}
......@@ -578,6 +560,7 @@ abstract class ActivityType {
protected $activity_queue_id;
protected $overridemessagecontents;
protected $parent;
protected $defaultmethod;
public function get_id() {
if (!isset($this->id)) {
......@@ -586,7 +569,15 @@ abstract class ActivityType {
}
return $this->id;
}
public function get_default_method() {
if (!isset($this->defaultmethod)) {
$tmp = activity_locate_typerecord($this->get_id());
$this->defaultmethod = $tmp->defaultmethod;
}
return $this->defaultmethod;
}
public function get_type() {
$prefix = 'ActivityType';
return strtolower(substr(get_class($this), strlen($prefix)));
......@@ -685,7 +676,16 @@ abstract class ActivityType {
$user->lang = get_config('lang');
}
if (empty($user->method)) {
$user->method = call_static_method(get_class($this), 'default_notification_method');
// If method is not set then either the user has selected 'none' or their setting has not been set (so use default).
if (record_exists('usr_activity_preference', 'usr', $user->id, 'activity', $this->get_id())) {
// The user specified 'none' as their notification type.
return;
}
$user->method = $this->get_default_method();
if (empty($user->method)) {
// The default notification type is 'none' for this activity type.
return;
}
}
// always do internal
......@@ -794,10 +794,6 @@ abstract class ActivityType {
}
return 0;
}
public static function default_notification_method() {
return activity_default_notification_method();
}
}
......@@ -1464,3 +1460,122 @@ function activitylist_html($type='all', $limit=10, $offset=0) {
return $result;
}
/**
* Get a table of elements that can be used to set notification settings for the specified user, or for the site defaults.
*
* @param object $user whose settings are being displayed or...
* @param bool $sitedefaults true if the elements should be loaded from the site default settings.
* @return array of elements suitable for adding to a pieforms form.
*/
function get_notification_settings_elements($user = null, $sitedefaults = false) {
global $SESSION;
if ($user == null && !$sitedefaults) {
throw new SystemException("Function get_notification_settings_elements requires a user or sitedefaults must be true");
}
if ($sitedefaults || $user->get('admin') || $user->is_institutional_admin()) {
$activitytypes = get_records_array('activity_type', '', '', 'id');
}
else {
$activitytypes = get_records_array('activity_type', 'admin', 0, 'id');
}
$notifications = plugins_installed('notification');
$elements = array();
$options = array();
foreach ($notifications as $n) {
$options[$n->name] = get_string('name', 'notification.' . $n->name);
}
$maildisabledmsg = false;
foreach ($activitytypes as $type) {
if ($sitedefaults) {
$dv = $type->defaultmethod;
}
else {
$dv = $user->get_activity_preference($type->id);
if ($dv === false) {
$dv = $type->defaultmethod;
}
}
if (empty($dv)) {
$dv = 'none';
}
if (!empty($type->plugintype)) {
$section = $type->plugintype . '.' . $type->pluginname;
}
else {
$section = 'activity';
}
if (!$sitedefaults && $dv == 'email' && !isset($maildisabledmsg) && get_account_preference($user->get('id'), 'maildisabled')) {
$SESSION->add_error_msg(get_string('maildisableddescription', 'account', get_config('wwwroot') . 'account/index.php'), false);
$maildisabledmsg = true;
}
if (empty($type->plugintype)) {
$key = "activity_{$type->name}";
}
else {
$key = "activity_{$type->name}_{$type->plugintype}_{$type->pluginname}";
}
$elements[$key] = array(
'defaultvalue' => $dv,
'type' => 'select',
'title' => get_string('type' . $type->name, $section),
'options' => $options,
'help' => true,
);
$elements[$key]['helpformname'] = 'activityprefs';
if (empty($type->plugintype)) {
$elements[$key]['helpplugintype'] = 'core';
$elements[$key]['helppluginname'] = 'account';
}
else {
$elements[$key]['helpplugintype'] = $type->plugintype;
$elements[$key]['helppluginname'] = $type->pluginname;
}
if ($type->allownonemethod) {
$elements[$key]['options']['none'] = get_string('none');
}
}
return $elements;
}
/**
* Save the notification settings.
*
* @param array $values returned from submitting a pieforms form.
* @param object $user whose settings are being updated or...
* @param bool $sitedefaults true if the elements should be saved to the site default settings.
*/
function save_notification_settings($values, $user = null, $sitedefaults = false) {
if ($user == null && !$sitedefaults) {
throw new SystemException("Function save_notification_settings requires a user or sitedefaults must be true");
}
if ($sitedefaults || $user->get('admin') || $user->is_institutional_admin()) {
$activitytypes = get_records_array('activity_type');
}
else {
$activitytypes = get_records_array('activity_type', 'admin', 0);
}
foreach ($activitytypes as $type) {
if (empty($type->plugintype)) {
$key = "activity_{$type->name}";
}
else {
$key = "activity_{$type->name}_{$type->plugintype}_{$type->pluginname}";
}
$value = $values[$key] == 'none' ? null : $values[$key];
if ($sitedefaults) {
execute_sql("UPDATE {activity_type} SET defaultmethod = ? WHERE id = ?", array($value, $type->id));
}
else {
$user->set_activity_preference($type->id, $value);
}
}
}
......@@ -314,6 +314,8 @@
<FIELD NAME="name" TYPE="char" LENGTH="50" NOTNULL="true" />
<FIELD NAME="admin" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="delay" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" />
<FIELD NAME="allownonemethod" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" />
<FIELD NAME="defaultmethod" TYPE="char" LENGTH="255" NOTNULL="false" DEFAULT="email" />
<FIELD NAME="plugintype" TYPE="char" LENGTH="25" NOTNULL="false" />
<FIELD NAME="pluginname" TYPE="char" LENGTH="255" NOTNULL="false" />
</FIELDS>
......@@ -608,7 +610,7 @@
<FIELDS>
<FIELD NAME="usr" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="activity" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="method" TYPE="char" LENGTH="255" NOTNULL="true" />
<FIELD NAME="method" TYPE="char" LENGTH="255" NOTNULL="false" />
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="usr,activity" />
......
......@@ -3249,5 +3249,29 @@ function xmldb_core_upgrade($oldversion=0) {
change_field_default($table, $field);
}
if ($oldversion < 2014041600) {
// Add allownonemethod and defaultmethod fields to activity_type table.
$table = new XMLDBTable('activity_type');
$field = new XMLDBField('allownonemethod');
$field->setAttributes(XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null, null, null, 1, 'delay');
if (!field_exists($table, $field)) {
add_field($table, $field);
}
$field = new XMLDBField('defaultmethod');
$field->setAttributes(XMLDB_TYPE_CHAR, 255, null, null, null, null, null, 'email', 'allownonemethod');
if (!field_exists($table, $field)) {
add_field($table, $field);
}
// Allow null method in usr_activity_preference.
// Null indicates "none", no record indicates "not yet set" so use the default.
$table = new XMLDBTable('usr_activity_preference');
$field = new XMLDBField('method');
$field->setAttributes(XMLDB_TYPE_CHAR, 255, null, null, null, null, null, null);
change_field_notnull($table, $field);
}
return $status;
}
......@@ -879,16 +879,16 @@ function core_install_firstcoredata_defaults() {
// install the activity types
$activitytypes = array(
array('maharamessage', 0, 0),
array('usermessage', 0, 0),
array('watchlist', 0, 1),
array('viewaccess', 0, 1),
array('contactus', 1, 1),
array('objectionable', 1, 1),
array('virusrepeat', 1, 1),
array('virusrelease', 1, 1),
array('institutionmessage', 0, 0),
array('groupmessage', 0, 1),
array('maharamessage', 0, 0, 0, 'email'),
array('usermessage', 0, 0, 0, 'email'),
array('watchlist', 0, 1, 1, 'email'),
array('viewaccess', 0, 1, 1, 'email'),
array('contactus', 1, 1, 1, 'email'),
array('objectionable', 1, 1, 1, 'email'),
array('virusrepeat', 1, 1, 1, 'email'),
array('virusrelease', 1, 1, 1, 'email'),
array('institutionmessage', 0, 0, 1, 'email'),
array('groupmessage', 0, 1, 1, 'email'),
);
foreach ($activitytypes as $at) {
......@@ -896,6 +896,8 @@ function core_install_firstcoredata_defaults() {
$a->name = $at[0];
$a->admin = $at[1];
$a->delay = $at[2];
$a->allownonemethod = $at[3];
$a->defaultmethod = $at[4];
insert_record('activity_type', $a);
}
......
......@@ -135,9 +135,6 @@ function change_language($userid, $oldlang, $newlang) {
* @param string $method notification method to set.
*/
function set_activity_preference($userid, $activity, $method) {
if (empty($method)) {
return delete_records('usr_activity_preference', 'activity', $activity, 'usr', $userid);
}
if (record_exists('usr_activity_preference', 'usr', $userid, 'activity', $activity)) {
set_field('usr_activity_preference', 'method', $method, 'usr', $userid, 'activity', $activity);
}
......
......@@ -15,7 +15,7 @@ $config = new stdClass();
// See https://wiki.mahara.org/index.php/Developer_Area/Version_Numbering_Policy
// For upgrades on stable branches, increment the version by one. On master, use the date.
$config->version = 2014041401;
$config->version = 2014041600;
$config->release = '1.10.0dev';
$config->minupgradefrom = 2009022600;
$config->minupgraderelease = '1.1.0 (release tag 1.1.0_RELEASE)';
......
......@@ -1868,9 +1868,10 @@ function get_help_icon($plugintype, $pluginname, $form, $element, $page='', $sec
}
function pieform_get_help(Pieform $form, $element) {
return get_help_icon($form->get_property('plugintype'),
$form->get_property('pluginname'),
$form->get_name(), $element['name']);
$plugintype = isset($element['helpplugintype']) ? $element['helpplugintype'] : $form->get_property('plugintype');
$pluginname = isset($element['helppluginname']) ? $element['helppluginname'] : $form->get_property('pluginname');
$formname = isset($element['helpformname']) ? $element['helpformname'] : $form->get_name