Notification System for Membership Websites Using Formidable Pro

I am sharing here my experience of building an announcement system based on Formidable Pro (currently at version 2.14).

The notification system is created for membership websites where admins can send personalized notifications to single or multiple users.

Components:
1. The form is a simple form with a textarea and 2 checkboxes. Leave the checkboxes empty but check the “Use separate values” checkbox in their properties
Capture1

2. The admin view and page will allow admins to create, edit and delete notifications
Capture2

3. The public view will allow recipients to read and “mark as read” the notifications
Capture3

4. The custom code that is explained below:

Populate user fields (Recipients and Read by in my case or field_id 124 and 212)

//for fields with separate values update field definition so it can render correctly in views
function update_bulk_options_db($list,$ids) {
	global $wpdb;
	$id = implode(",",$ids);
	$options = array();
	$j=0;
	foreach ($list as $key=>$val) {
		$j=$j+1;
		$options[$j]=array('value'=>(string)$key,'label'=>$val);
	}
	$save= serialize($options);
	$wpdb->query(
			"UPDATE wp_frm_fields
			 SET options = '$save'
			 WHERE id in ($id)
			" 
	);
	return;
}
//populate user related fields with users. here I use s2member plugin for role management
add_filter('frm_setup_new_fields_vars', 'paperplus_populate_users', 20, 2);
add_filter('frm_setup_edit_fields_vars', 'paperplus_populate_users', 20, 2); //use this function on edit too
function paperplus_populate_users($values, $field){
	if($field->id == 124||$field->id == 212){
		$users1q = new WP_User_Query(array( 'role' => 'subscriber' ));
		$operators = $users1q->results;
		$users2q = new WP_User_Query(array( 'role' => 's2member_level1' ));	
		$managers = $users2q->results;
		$users3q = new WP_User_Query(array( 'role' => 'administrator' ));	
		$admins = $users3q->get_results();
		$users = array_unique(array_merge($operators, $managers, $admins));	
		unset($values['options']);
		$values['options'] = array();
		foreach($users as $u){
		       $values['options'][$u->ID] = $u->display_name;
		}
		update_bulk_options_db ($values['options'], array(124,212));
		$values['use_key'] = true; 
	}
	return $values;
}

After adding this snippet to your functions.php file in your active theme you will get prepopulated checkboxes with current users registered for the subscriber, s2member_level1 and administrator roles. Of course you will use any number of roles you like or all existing users, just format properly the WP_User_Query

Filter the notifications view so that only notifications sent to current user will be displayed

Of course, a notification can be sent to several users at once. We need to filter entries that have current user id in the recipients list

//filter the notifications view if the user is in the list.
add_filter('frm_where_filter', 'filter_anunturi', 10, 2);
function filter_anunturi($where, $args){
	if ( $args['display']->ID == 82){ //change 82 with your view id 
		$user_id = get_current_user_id();
		if ($user_id > 0) {
			$where = "(FIND_IN_SET('" . $user_id . "', fn_parse_ser_array(meta_value))>0 and fi.id='124')";
		}
	}
	return $where;
}

You notice the mysql function call fn_parse_ser_array that is a custom made function. You need phpmyadmin access and rights to create functions on your database. This function is the mysql version of the PHP function unserialize.

You may try this mysql command:

BEGIN
declare cnt int;
declare i int;
declare retstr varchar(2000); 
declare mystr varchar(2000);
-- declare str varchar(2000);
if instr(str,"{")>0 then
    set str = right(str, length(str)-instr(str,"{"));
    set str = replace(str,"}","");
    -- set retstr = str;
    
    set cnt = subStringCount2(str,";");
    
    -- select @str;
    
    set i = 1;
    set retstr = '';
    WHILE i< =cnt 
    DO
    
    if mod(i,2)=0  then
         set mystr = SPLIT_STR2(str,";",i);
         set mystr = replace(mystr, '"','');
         set mystr = SUBSTRING_INDEX(mystr, ':',-1);
         if length(retstr)>0 then 
            set retstr = concat(retstr, ',', mystr);
        else
            set retstr = mystr;
        end if;
    end if;
      set i = i +1;
    END while;
else
	set retstr = str;
end if;
return retstr;

END

Also, we would want to hide the notifications that recipients have already marked as read. First idea is to use a negate condition on field 212 but… it does not work. In fact

$where = "(FIND_IN_SET('" . $user_id . "', fn_parse_ser_array(meta_value))=0 and fi.id='212')";

would work on its own but this will never work:

$where = "(FIND_IN_SET('" . $user_id . "', fn_parse_ser_array(meta_value))>0 and fi.id='124') AND (FIND_IN_SET('" . $user_id . "', fn_parse_ser_array(meta_value))=0 and fi.id='212')";

Therefore my solution was to hide the read notifications in the table itself by using DataTables jQuery plugin

Mark as read system for notifications

This system will both hide notifications for users that read them and will show admins who actually read their notifications

First add this snippet to functions.php file then add the [mark_as_read entry_id='[id]’] shortcode into the last column of the notifications table inside the view

//mark notifications as read by users
add_shortcode('mark_as_read','function_mark_as_read');
function function_mark_as_read($atts) {
	$entry = $atts["entry_id"];
	$val = FrmProEntriesController::get_field_value_shortcode(array('field_id' => 212, 'entry' => $entry, 'show' => 1));
	if ($val)
		$val = explode(",",str_replace(" ","",$val));
	else
		$val = array();
	if (in_array(get_current_user_id(),$val))
		return "Read";
	else
		array_push($val,get_current_user_id());
	//$val = array_unique($val);
	$new_val = "[" . implode(",",$val) . "]";
	//return FrmProEntriesController::entry_update_field(array('id' => $entry, 'field_id' => 212, 'label' => 'Mark as read', 'class' => '', 'value' => $new_val, 'message' => 'Succes!'));
	return 'Mark as read';
}

Note that the attempt to use Formidable API failed in this case because instead of this new value [1,2,3] it will always insert this new value ‘[1,2,3]’ messing the checkbox values in the database… (maybe this is a Formidable but at this moment). So I had to copy the behaviour and hardcode it in the last line.

In the datatables initialization we need to add the searchCols filtering as in the next example:

jQuery(document).ready(function() {jQuery('.ppl_table').DataTable({
"language": {
    "url": "//cdn.datatables.net/plug-ins/1.10.9/i18n/Romanian.json"
  },
  "searchCols": [
    null,
    null,
    { "search": "Mark" }
  ]
} );} );

Download

Below you can copy this exported definition of the form and views for your reference and save it as xml file, then import it into Formidable plugin. Please check the fields id that may change, also in the previous section I translated Mark and Read, in the original definitions they are in Romanian language

< ?xml version="1.0" encoding="UTF-8" ?>

	Paper Plus
	Sat, 03 Oct 2015 11:06:59 +0000

	
13 < ![CDATA[2yx0no]]> < ![CDATA[Anunturi personalizate]]> < ![CDATA[]]> 2015-08-05 08:42:57 0 0 0 1 < ![CDATA[{"submit_value":"Trimite","success_action":"redirect","success_msg":"Your responses were successfully submitted. Thank you!","show_form":0,"akismet":"","no_save":0,"ajax_load":0,"form_class":"","custom_style":"1","before_html":"[form_name]< \/legend>\r\n[if form_name]

[form_name]< \/h3>[\/if form_name]\r\n[if form_description]

< ![CDATA[published]]> 0 120 < ![CDATA[9iabnr]]> < ![CDATA[Mesaj nou]]> < ![CDATA[]]> < ![CDATA[textarea]]> < ![CDATA[]]> 0 13 < ![CDATA[]]> < ![CDATA[{"size":"","max":"5","label":"","blank":"This field cannot be blank.","required_indicator":"*","invalid":"","separate_value":0,"clear_on_focus":0,"default_blank":0,"classes":"frm_first frm_third","custom_html":"
\r\n 124 < ![CDATA[9dqxn5]]> < ![CDATA[Destinatari]]> < ![CDATA[]]> < ![CDATA[checkbox]]> < ![CDATA[]]> 1 13 < ![CDATA[{"1":{"value":"12","label":"anca1 Nume"},"2":{"value":"13","label":"QUADRANT TEST"},"3":{"value":"3","label":"Anca Badetoiu"},"4":{"value":"2","label":"Emanuel I"},"5":{"value":"1","label":"Richard Vencu"}}]]> < ![CDATA[{"size":"","max":"","label":"","blank":"This field cannot be blank.","required_indicator":"*","invalid":"","separate_value":"1","clear_on_focus":0,"default_blank":0,"classes":"frm_third","custom_html":"
\r\n 212 < ![CDATA[dlqcx6]]> < ![CDATA[Marcat ca citit de]]> < ![CDATA[]]> < ![CDATA[checkbox]]> < ![CDATA[]]> 2 13 < ![CDATA[{"1":{"value":"12","label":"anca1 Nume"},"2":{"value":"13","label":"QUADRANT TEST"},"3":{"value":"3","label":"Anca Badetoiu"},"4":{"value":"2","label":"Emanuel I"},"5":{"value":"1","label":"Richard Vencu"}}]]> < ![CDATA[{"size":"","max":"","label":"","blank":"This field cannot be blank.","required_indicator":"*","invalid":"","separate_value":"1","clear_on_focus":0,"default_blank":0,"classes":"frm_third","custom_html":"
\r\n 211 < ![CDATA[j52ux0]]> < ![CDATA[User ID]]> < ![CDATA[]]> < ![CDATA[user_id]]> < ![CDATA[]]> 3 13 < ![CDATA[]]> < ![CDATA[{"size":"","max":"","label":"","blank":"","required_indicator":"*","invalid":"","separate_value":0,"clear_on_focus":0,"default_blank":0,"classes":"","custom_html":"","custom_field":"","post_field":"","taxonomy":"category","exclude_cat":0,"slide":0,"form_select":"","show_hide":"show","any_all":"any","align":"block","hide_field":[],"hide_field_cond":["=="],"hide_opt":[],"star":0,"ftypes":[],"data_type":"select","restrict":0,"start_year":2000,"end_year":2020,"read_only":0,"admin_only":"","locale":"","attach":false,"minnum":0,"maxnum":9999,"step":1,"clock":12,"start_time":"00:00","end_time":"23:59","unique":0,"use_calc":0,"calc":"","calc_dec":"","dyn_default_value":"","multiple":0,"unique_msg":"","autocom":0,"format":"","repeat":0,"add_label":"Add","remove_label":"Remove","conf_field":"","conf_input":"","conf_desc":"","conf_msg":"The entered values do not match","other":0}]]> Anunturi Personalizate < ![CDATA[emi]]> < ![CDATA[[120][created-at][mark_as_read entry_id='[id]']]]> < ![CDATA[]]> 82 2015-08-06 20:51:18 2015-08-06 17:51:18 closed closed anunturi-personalizate publish 0 0 frm_display 0 _edit_lock < ![CDATA[1443870262:1]]> _edit_last 1 frm_param < ![CDATA[entry]]> frm_dyncontent < ![CDATA[]]> frm_insert_loc < ![CDATA[none]]> frm_type < ![CDATA[id]]> frm_show_count < ![CDATA[all]]> frm_form_id 13 frm_post_id < ![CDATA[]]> frm_options < ![CDATA[{"date_field_id":"created_at","edate_field_id":"","repeat_event_field_id":"","repeat_edate_field_id":"","before_content":"\r\n","no_rt":"1","after_content":"< \/tbody>< \/table> Anunturi Personalizate Admin < ![CDATA[emi]]> < ![CDATA[]]>< ![CDATA[]]>842015-08-06 21:27:422015-08-06 18:27:42closedclosedanunturi-personalizate-adminpublish00frm_display0 _edit_lock < ![CDATA[1443730443:1]]> _edit_last 1 frm_param < ![CDATA[entry]]> frm_dyncontent < ![CDATA[]]> frm_insert_loc < ![CDATA[none]]> frm_type < ![CDATA[id]]> frm_show_count < ![CDATA[all]]> frm_form_id 13 frm_post_id < ![CDATA[]]> frm_options < ![CDATA[{"date_field_id":"created_at","edate_field_id":"","repeat_event_field_id":"","repeat_edate_field_id":"","before_content":"
Mesajul< \/th>Data< \/th>Marcheaza ca citit< \/th>\r\n< \/tr>< \/thead>
[120][124][212][created-at format="Y-m-d H:i"][editlink location="front" label="Editeaza" page_id=78][deletelink label="Sterge" confirm="Esti sigur ca vrei sa stergi acest mesaj?..."]
\r\n","no_rt":"1","after_content":"< \/tbody>< \/table> Email Notification < ![CDATA[rvencu]]> < ![CDATA[{"email_to":"office@paperplus.ro","cc":"","bcc":"","reply_to":"","from":"[sitename] <[admin_email]>","email_subject":"Un nou anunt a fost publicat pe situl de comenzi","email_message":"[default-message]","inc_user_info":"1","event":["create"],"conditions":{"send_stop":"send","any_all":"any"}}]]> < ![CDATA[email]]> 77 2015-08-05 11:42:57 2015-08-05 08:42:57 closed closed 13_email_77 publish 0 13 frm_form_actions 0 _wp_old_slug < ![CDATA[13_email_]]>
Mesajul< \/th>Destinatari< \/th>Citit de< \/th>Data< \/th>Editare< \/th>Stergere< \/th>\r\n< \/tr>< \/thead>