Changeset 606


Ignore:
Timestamp:
Sep 25, 2007 12:42:01 AM (6 years ago)
Author:
dkg
Message:

JPDD: completely refactored broadcast e-mails.

Location:
trunk/jpdd
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/jpdd/class.dkg.site.php

    r603 r606  
    100100                                              'table' => $this->_users['tablename'], 
    101101                                              'sort' => $this->_users['username'])); 
     102      $this->addClassMapEntry('broadcast', array('classname' => 'DKG_Broadcast',  
     103                                              'filename' => 'class.dkg.broadcast.php', 
     104                                              'table' => 'broadcast', 
     105                                              'sort' => 'id DESC')); 
    102106      $this->addClassMapEntry('privilege', array('classname' => 'DKG_Privilege',  
    103107                                              'filename' => 'class.dkg.privilege.php', 
     
    197201 
    198202    function getAllowedActions() { 
    199       return array('edit', 'login', 'logout', 'resetpass', 'newacct', 'broadcast'); 
     203      return array('edit', 'login', 'logout', 'resetpass', 'newacct', 'send', 'copy'); 
    200204    } 
    201205     
     
    983987          return $this->getLoginForm(); // login form shows logout form if currently logged in. 
    984988        } 
     989      } elseif ($this->_action == 'copy') { 
     990        if ($this->isEmpty($this->_type) || $this->isEmpty($this->_identifier))  
     991          $this->error('Nothing to copy'); 
     992        if (!$this->canCreate($this->_type)) 
     993          return $this->permissionDenied('Creating '.$this->_type.' not allowed.'); 
     994 
     995        // must be able to edit and create in order to copy? 
     996        $item = $this->getItem($this->_type, $this->_identifier); 
     997        // unset the ID. 
     998        $item->_id = NULL; 
     999        return $item->getCreationForm(); 
     1000         
    9851001      } elseif ($this->_action == 'edit') { 
    9861002        if ($this->isEmpty($this->_type)) { 
     
    10111027          } 
    10121028        } 
    1013       } elseif ($this->_action == 'broadcast') { 
     1029      } elseif ($this->_action == 'send') { 
    10141030        if ($this->isAuthenticated() && $this->_authenticated_user->HasAllOfThesePrivileges('Send Broadcast')) { 
    1015           if ($_SERVER['REQUEST_METHOD'] == 'POST') { 
    1016             // verify form token, or just quit: 
    1017             if (!$this->verifyFormToken()) { 
    1018               $this->addWarning('Bad form token mismatch!'); 
    1019               return ''; 
    1020             } 
    1021             if ($_POST['submit'] == 'Send') { 
    1022               return $this->sendBroadcastEmails(); 
    1023             } else { 
    1024               return $this->getBroadcastEmailForm(); 
    1025             } 
     1031          $item = $this->getItem('broadcast', $this->_identifier); 
     1032          if ($_SERVER['REQUEST_METHOD'] != 'POST') { 
     1033            $this->addWarning('You must POST all broadcast sending pages.'); 
     1034          } else if ($this->_type != 'broadcast') { 
     1035            $this->addWarning('You can only send broadcast items'); 
     1036          } else if (!$this->verifyFormToken()) { 
     1037            $this->addWarning('Bad form token mismatch!'); 
    10261038          } else { 
    1027             return $this->getBroadcastEmailForm(); 
     1039            // need to actually send the damn e-mails here. 
     1040            $item->sendEmailsToOutstandingPeople(); 
    10281041          } 
     1042          return $item->getDetailView(); 
    10291043        } else 
    10301044          return $this->permissionDenied('Broadcast not allowed.'); 
     
    10341048    } 
    10351049 
    1036     function getBroadcastSelectors() { 
    1037       // override to return an array that maps internal keys to 
    1038       // visible labels and SQL statements.  For example: 
    1039       return array('all' => array('description' => 'Every account with a confirmed e-mail', 
    1040                                   'sql' => 'SELECT * FROM person WHERE pass IS NOT NULL')); 
    1041     } 
    1042  
    1043     function getAllowedTemplates() { 
    1044       // override to return an array that maps selectors to 
    1045       // array('description' = "human readable", 'function' => actual 
    1046       // php function) 
    1047       return array('[[NAME]]' => array('description' => 'The person\'s name', 
    1048                                        'function' => create_function('$x', 'return $x->getTitle();')), 
    1049                    // FIXME: during the preview display, this feature 
    1050                    // actually allows the previewer to see the reset 
    1051                    // link for the user being previewed.  This is a 
    1052                    // security flaw. 
    1053                    '[[PASSWORD_RESET]]' => array('description' => 'A link that lets the person change hir password', 
    1054                                        'function' => create_function('$x', 'global $dkg_site; return $dkg_site->handleResetPassRequest($x->_email, false);'))); 
    1055     } 
    1056      
    1057     function getAllowedTemplateLegend() { 
    1058       $temps = $this->getAllowedTemplates(); 
    1059       return "<div class=\"template-legend\">Allowed personalization terms:<dl>\n". 
    1060         join('', array_map(create_function('$x,$y', 'return "<dt>".$x."</dt>\n<dd>".$y["description"]."</dd>\n";'), 
    1061                            array_keys($temps), $temps))."</dl>\n</div>\n"; 
    1062     } 
    1063      
    1064     function getBroadcastSelectorInput($selector_name = NULL) { 
    1065       $selectors = $this->getBroadcastSelectors(); 
    1066       return '<select name="selector"> 
    1067 '.join("\n", array_map(create_function('$v, $l', 'global $dkg_site; return "<option value=\"".$v."\" ".($v == "'.$selector_name.'" ? "selected" : "").">".$l["description"]." (".$dkg_site->getValueFromSQL("SELECT COUNT(*) AS count FROM (".$l["sql"].") AS selector", "count").")</option>";'), array_keys($selectors), $selectors)).' 
    1068 </select> 
    1069 '; 
    1070     } 
    1071      
    1072     // $txt can be either a string or an array of strings.  the return 
    1073     // value will match. 
    1074     function applyBroadcastTemplate($txt, $user) { 
    1075       $temps = $this->getAllowedTemplates(); 
    1076       $vals = array(); 
    1077       reset($temps); 
    1078       while(list($temp,$x) = each($temps)) { 
    1079         $vals[] = $x['function']($user); 
    1080       } 
    1081       return str_ireplace(array_keys($temps), $vals, $txt); 
    1082     } 
    1083  
    1084     function sendBroadcastEmails() { 
    1085       if ($_SERVER['REQUEST_METHOD'] != 'POST') { 
    1086         $this->addWarning('You cannot send broadcasts with a GET request.'); 
    1087         return ''; 
    1088       } 
    1089  
    1090       $subj = $_POST['subject']; 
    1091       $content = $_POST['content']; 
    1092       $personal = ($_POST['personal'] == 'personal'); 
    1093        
    1094       $selector_name = $_POST['selector']; 
    1095       $selectors = $this->getBroadcastSelectors(); 
    1096       if (!array_key_exists($selector_name, $selectors)) { 
    1097         $this->addWarning('You chose an unavailable selector!'); 
    1098         return ''; 
    1099       } 
    1100       $selector = $selectors[$selector_name]; 
    1101  
    1102       // store this broadcast template in the database: 
    1103       $bid = $this->insertBroadcast($subj, $content, $selector_name); 
    1104  
    1105       // we're gonna grab some peops: 
    1106       $map = $this->prepClass('person'); 
    1107       $cname = $map['classname']; 
    1108       $objs = $this->getSeriesFromSQL($selector['sql'], $cname); 
    1109       $attempted = 0; 
    1110       $succeeded = 0; 
    1111       $skipped = array(); 
    1112       reset($objs); 
    1113       while(list(,$obj) = each($objs)) { 
    1114         $to = $obj->_email; 
    1115         if (is_null($to) || !is_null($this->isValid('email', $to))) { 
    1116           $skipped[] = $obj; 
    1117         } else { 
    1118           $subject = trim(str_replace("\n", '', $this->applyBroadcastTemplate($subj, $obj))); 
    1119           $body = trim(wordwrap($this->applyBroadcastTemplate($content, $obj))); 
    1120           $attempted++; 
    1121           if ($this->mail('broadcast', $to, $subject, $body, array(), $personal, $bid)) 
    1122             $succeeded++; 
    1123         } 
    1124       } 
    1125       return $succeeded.' out of '.$attempted.' mails sent.'. 
    1126         (count($skipped) > 0 ? '<br/>Did not try the following people because they had no valid e-mail address: <ul>'. 
    1127          join('', array_map(create_function('$x', 'return "<li>".$x->getLinkedTitle()."\n";'), $skipped)).'</ul>': ''); 
    1128     } 
     1050 
    11291051 
    11301052    function getFromAddressSelector($personal = false) { 
     
    11331055<label><input name="personal" type="radio" value="personal" '.($personal ? 'checked' : '').'>'.htmlentities(trim(str_replace("\n", '', $this->_authenticated_user->getTitle().' <'.$this->_authenticated_user->_email.'>'))).'</label> 
    11341056</div>'; 
    1135     } 
    1136  
    1137     function getBroadcastEmailForm() { 
    1138       $subj = ''; 
    1139       $content = ''; 
    1140       $preview = ''; 
    1141       $ret = ''; 
    1142       $selector_name = ''; 
    1143       $from = trim(str_replace("\n", '', $this->getSiteName().' <'.$this->_site_email_from.'>')); 
    1144       $personal = false; 
    1145  
    1146       if ($_SERVER['REQUEST_METHOD'] == 'POST') { 
    1147         if ($this->_actually_send_email) 
    1148           $this->addWarning('Sending e-mail is actually enabled.  Please be careful with this!'); 
    1149         else  
    1150           $ret .= '<div>Sending e-mail is currently disabled.  Messages will be logged, but not actually sent.</div>'; 
    1151         $subj = $_POST['subject']; 
    1152         $content = $_POST['content']; 
    1153         $personal = (($_POST['personal'] == 'personal') && $this->isAuthenticated()); 
    1154  
    1155         if ($personal) 
    1156           $from = trim(str_replace("\n", '', $this->_authenticated_user->getTitle().' <'.$this->_authenticated_user->_email.'>')); 
    1157            
    1158          
    1159         $selector_name = $_POST['selector']; 
    1160         $selectors = $this->getBroadcastSelectors(); 
    1161         if (!array_key_exists($selector_name, $selectors)) { 
    1162           $this->addWarning('You chose an unavailable selector!'); 
    1163           return ''; 
    1164         } 
    1165         $selector = $selectors[$selector_name]; 
    1166          
    1167         $preview = '<fieldset><legend>Preview Example:</legend>'; 
    1168          
    1169         // we're gonna grab a person: 
    1170         $map = $this->prepClass('person'); 
    1171         $cname = $map['classname']; 
    1172         $data = $this->getSingletonFromSQL($selector['sql'].' LIMIT 1', false); 
    1173         if (is_null($data)) { 
    1174           $preview .= '<span class="error">There are no selectors which match <q>'.$selector['description'].'</q></span>'; 
    1175         } else { 
    1176           $obj = new $cname(array('data' => $data)); 
    1177           $preview .= '<pre>To: '.$obj->_email.' 
    1178 From: '.htmlentities($from).' 
    1179 Subject: '.htmlentities(trim(str_replace("\n", '', $this->applyBroadcastTemplate($subj, $obj)))).' 
    1180  
    1181 '.htmlentities(trim(wordwrap($this->applyBroadcastTemplate($content, $obj)))).'</pre>'; 
    1182         } 
    1183         $preview .= '</fieldset>'; 
    1184       } elseif (!$this->isEmpty($this->_type)) { 
    1185         $template = $this->getSingletonFromSQL('SELECT * FROM broadcast WHERE id = '.(int)$this->_type); 
    1186         $subj = $template['subj']; 
    1187         $content = $template['body']; 
    1188       } 
    1189  
    1190       $alternates = $this->getMultiSeriesFromSQLByValue('SELECT broadcast.id AS id,MAX(subj) AS subj,DATE(whattime) AS date, COUNT(*) AS count from broadcast JOIN mail_log ON (broadcast_id = broadcast.id) GROUP BY broadcast.id,DATE(whattime) ORDER BY broadcast.id DESC', 'id'); 
    1191       if (count($alternates) > 0) { 
    1192         $altout = '<div class="collapse" id="previous-broadcasts"><div class="title">Previous Broadcast E-mails:</div><div class="notes">click message subject to see or resubmit text from these messages</div><ul>'; 
    1193         reset($alternates); 
    1194         while(list($id,$temp) = each($alternates)) { 
    1195           $altout .= '<li'.((!$this->isEmpty($this->_type)) && ($this->_type == $id) ? ' class="selected"' : '' ).'><a href="'.$this->Path('broadcast', (int)$id).'">'.htmlentities($temp[0]['subj']).'</a><ul>'. 
    1196             join("\n", array_map(create_function('$x', 'return $x["date"]."<div class=\"notes\">[sent to: ".$x["count"].($x["count"] == 1 ? " person" : " people")."]</div>";'), $temp)).'</ul></li>'; 
    1197         } 
    1198         $altout .= '</ul></div>'; 
    1199         $ret .= $altout; 
    1200       } 
    1201  
    1202       return $ret.'<form name="broadcast" action="'.$this->Path('broadcast').'" method="post"> 
    1203 '.$this->getFormTokenHiddenInput().' 
    1204 <fieldset><legend>Send to:</legend> 
    1205 '.$this->getBroadcastSelectorInput($selector_name).' 
    1206 </fieldset> 
    1207 '.$preview.' 
    1208 <fieldset><legend>Message Content:</legend> 
    1209 '.$this->getAllowedTemplateLegend().' 
    1210 From: '.$this->getFromAddressSelector($personal).'<br /> 
    1211 <label>Subject:<br /> 
    1212 <input name="subject" type="text" size="60" value="'.htmlentities($subj).'" /><br /> 
    1213 <label>Body:<br /> 
    1214 <textarea name="content" rows="20" cols="72">'.htmlentities($content). 
    1215         '</textarea> 
    1216 </label><br/> 
    1217 <input type="submit" class="submit" name="submit" value="Preview"/> 
    1218 '.($_SERVER['REQUEST_METHOD'] == 'POST' ? '<input class="submit" type="submit" name="submit" value="Send"/>' : '').' 
    1219 </fieldset> 
    1220 </form>'; 
    12211057    } 
    12221058 
     
    12891125        } 
    12901126 
    1291     function insertBroadcast($subj, $body, $selector) { 
    1292        $this->executeSQL('INSERT INTO broadcast (subj, body, selector, sender_id) VALUES ('.$this->escStr($subj).', '. 
    1293                          $this->escStr($body).', '. 
    1294                          $this->escStr($selector).', '. 
    1295                          (int)$this->_authenticated_user->getID().')'); 
    1296        // return the last id from the log: 
    1297        return (int)$this->getValueFromSQL('SELECT currval(\'broadcast_id_seq\') AS broadcast_id', 'broadcast_id'); 
    1298     } 
    1299  
    13001127    function log($data) { 
    13011128       $this->executeSQL('INSERT INTO log (data, backtrace, servervars) VALUES ('.$this->escStr($data).', '. 
  • trunk/jpdd/class.jpdd.php

    r605 r606  
    2424                                              'table' => 'person', 
    2525                                              'sort' => 'lower(last_name),lower(first_name)')); 
     26      // likewise for DKG_Broadcast: 
     27      $this->addClassMapEntry('broadcast', array('classname' => 'JPDD_Broadcast',  
     28                                              'filename' => 'class.jpdd.broadcast.php', 
     29                                              'table' => 'broadcast', 
     30                                              'sort' => 'id DESC')); 
    2631      $this->addClassMapEntry('category', array('classname' => 'JPDD_Category',  
    2732                                                'filename' => 'class.jpdd.category.php', 
     
    5661      $this->_stylesheets[] = 'jpdd.css'; 
    5762    } 
    58  
    59  
    60     function getBroadcastSelectors() { 
    61       // override to return an array that maps internal keys to 
    62       // visible labels and SQL statements.  For example: 
    63       $roles = $this->getAll('role'); 
    64       $rsel = array(); // the per-role selectors 
    65       reset($roles); 
    66       while(list(,$role) = each($roles)) { 
    67         $rsel['role:'.$role->getID()] = array('description' => 'All people acting as '.$role->getTitle(), 
    68                                               'sql' => 'SELECT person.* FROM person JOIN person_role ON (person_id = person.id) WHERE role_id = '.$role->getID(). 
    69                                               ' AND event_id = '.$this->getActiveEventID()); 
    70       } 
    71       // get a list of all  
    72  
    73       return array_merge(array('presenters' => array('description' => 'Workshop Presenters', 
    74                                                      'sql' => 'SELECT person.* FROM person JOIN presenter ON (person.id = presenter.person_id) JOIN workshop ON (workshop_id = workshop.id) WHERE event_id = '.$this->getActiveEventID()), 
    75                                'attendees' => array('description' => 'Workshop Attendees', 
    76                                                     'sql' => 'SELECT person.* FROM person JOIN audience ON (person.id = audience.person_id) JOIN workshop ON (workshop_id = workshop.id) WHERE event_id = '.$this->getActiveEventID()), 
    77                                'jpdd_admins' => array('description' => 'People named Gillmor or Tashlik', 
    78                                               'sql' => 'SELECT * FROM person WHERE last_name IN (\'Gillmor\', \'Tashlik\')')), 
    79                          $rsel, 
    80                          parent::getBroadcastSelectors() 
    81                          ); 
    82     } 
    83  
    84     function getAllowedTemplates() { 
    85       // override to return an array that maps selectors to 
    86       // array('description' = "human readable", 'function' => actual 
    87       // php function) 
    88       return array_merge(parent::getAllowedTemplates(), 
    89                          // FIXME: these are pretty broken, actually, particularly if there is more than one workshop being attended. 
    90                          array('[[WORKSHOP]]' => array('description' => 'The workshop the person is attending (or "no workshop")', 
    91                                                        'function' => create_function('$x', '$ws = $x->getM2MPeers("workshop", "attendance"); if (count($ws) == 0) return "no workshop"; return join(", ", array_map(create_function(\'$x\', \'return html_entity_decode($x->getTitle());\'), $ws));')), 
    92                                '[[ROOM]]' => array('description' => 'The room number of the attended workshop (e.g. "Room 143" or "no room")', 
    93                                                    'function' => create_function('$x', '$ws = $x->getM2MPeers("workshop", "attendance"); if (count($ws) == 0) return "no room"; return join(", ", array_map(create_function(\'$x\', \'$rr = $x->getAssignedRoom(); return (is_null($rr) ? "no room" : "Room ".$rr->getTitle());\'), $ws));'))) 
    94                          ); 
    95     } 
    96  
    9763 
    9864    function getNavLink($title,$targ) { 
  • trunk/jpdd/config.inc.php

    r560 r606  
    3232  // if this is set to false, no e-mail will actually go out, though 
    3333  // it will be written to the log.  Defaults to true. 
    34   'actually_send_email' => true, 
     34  'actually_send_email' => false, 
    3535   
    3636  // signup closed message: if not null, the signups will be closed, 
  • trunk/jpdd/sql/db.sql

    r598 r606  
    925925 
    926926ALTER TABLE attendance_requirements ADD CONSTRAINT attendence_requirements_category_event_unique UNIQUE (category_id,event_id); 
     927 
     928-- remember whether each broadcast was sent out as a personalized 
     929-- mail, (false means it was sent out from the site-wide e-mail address): 
     930ALTER TABLE broadcast ADD COLUMN personal boolean; 
     931UPDATE broadcast SET personal = true;  
     932ALTER TABLE broadcast ALTER COLUMN personal SET NOT NULL; 
     933 
     934-- we're allowing editing of broadcast templates now: 
     935GRANT UPDATE ON broadcast TO "www-data"; 
Note: See TracChangeset for help on using the changeset viewer.