Changeset 1974927
- Timestamp:
- 11/15/2018 01:51:05 PM (7 years ago)
- Location:
- ecampaign/branches/maintenance
- Files:
-
- 2 edited
-
Ecampaign.class.php (modified) (26 diffs)
-
EcampaignPetition.class.php (modified) (19 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ecampaign/branches/maintenance/Ecampaign.class.php
r1851632 r1974927 15 15 */ 16 16 17 class EcampaignSession 18 { 19 public 20 $classPath, // path and class name of declaring class 21 $formID = NULL, 22 $templateFields = [], // array of fields populated by parseTemplate() 23 $testMode = NULL, // determines whether messages are delivered and to where 24 $fieldSet = NULL; // bindings - posted values in associative array 25 26 function __construct($postID, $formID) 27 { 28 $this->fieldSet = new stdClass(); 29 $this->fieldSet->postID = $this->postID = $postID; 30 $this->formID = $formID; 31 } 32 } 33 34 17 35 include_once dirname(__FILE__) . '/EcampaignField.class.php'; 18 36 include_once dirname(__FILE__) . '/EcampaignLog.class.php'; … … 21 39 class Ecampaign 22 40 { 41 public $session = NULL; 42 public $validAjaxMethods = array(); // methods in this class that ajax calls can access protected $log ; 43 protected $defaultTemplate; // set by extending classes 44 protected $submitEnabled = true; // prevents attempts to send/sign before any (postcode) lookups 45 protected $styleClass = 'notset'; // class attribute given to outer wrapper of form 46 protected $testMode = NULL ; 47 23 48 static function help($anchor) 24 49 { … … 27 52 return "<a target='_blank' href='$path$helpFile$anchor' title='$anchor: Open ecampaign help page in another window'>". __("More help")."</a>" ; 28 53 } 29 30 static $formList, $allFields = array(); 31 32 protected $classPath, // path and class name of declaring class 33 $testMode, // determines whether messages are delivered and to where 34 $cannedFields = null, // array of hard coded fields and label names 35 $templateFields, // array of fields after template processing 36 $submitEnabled = true, // prevents attempts to send/sign before any (postcode) lookups 37 $defaultTemplate, // set by extending classes 38 $styleClass = 'notset'; // class attribute given to outer wrapper of form 39 40 public $validAjaxMethods = array(), // methods in this class that ajax calls can access 41 $fieldSet ; // name/value pairs recieved in POST 54 static $allControllers=[]; 55 42 56 43 57 const ecCounter = 'ecCounter' ; // number of people taking action, counter value is saved in metadata in post … … 69 83 sReferer = 'referer', 70 84 sPostID = 'postID', 71 85 sFormID = 'formID', 86 72 87 sFriendEmail = 'friendEmail', 73 88 sFriendSend = 'friendSend' , … … 81 96 * @param unknown_type $atts all the attribute that follow </campaign> 82 97 * @param unknown_type $messageBody the text between the <ecampaign> and </ecampaign> 83 * @return unknown_type 84 */ 85 86 87 function __construct() 88 { 89 $this->testMode = new EcampaignTestMode(); 90 $this->log = new EcampaignLog(); 91 $this->classPath = get_class($this); 98 * @return 99 */ 100 101 102 function __construct($session) 103 { 104 $this->session = $session; 105 $this->log = EcampaignLog::get(); 92 106 $this->bodyTrailer = "" ; 93 if (!isset(self::$formList)) self::$formList = array(); 94 } 95 96 /* 97 function initializeCannedFieldsNext() 98 { 99 $this->cannedFields = 100 101 " { " .self::sTo. " label = ". __('To'). " min=10 size=70 } ". 102 " { " .self::sSubject." label = ". __(''). " min=10 size=70 } ". 103 " { " .self::sBody. " label = ". __(''). " min=10 size=70 } ". 104 105 ""; 106 } 107 */ 108 109 107 } 108 109 110 110 function initializeCannedFields() 111 111 { 112 $this->cannedFields =array(112 return array( 113 113 self::sTo => array(__('To')), // field lengths irrelevant 114 114 self::sSubject => array(null, "data-min='10' size='70'"), … … 121 121 self::sCity => array(__('City'), "data-min='4' size='10'"), 122 122 self::sPostcode => array(__('Postcode'), "data-min='4' size='10'"), 123 self::sUKPostcode => array(__('Postcode'), "data-min='4' size=' 10'", 'validateUKPostcode'),123 self::sUKPostcode => array(__('Postcode'), "data-min='4' size='7'", 'validateUKPostcode'), 124 124 self::sZipcode => array(__('Zipcode'), "data-min='5' size='10'", 'validateZipcode'), 125 125 self::sState => array(__('State'), "data-min='2' size='2'"), // tx, ca 126 126 self::sCountry => array(__('Country'), "data-min='2' size='15'"), // us, uk 127 self::sVeriCode => array(__('Code'), "data-min='4' size='4'" ),128 self::sCaptcha => array(__('Captcha'), "data-min='4' size='4'"), 127 self::sVeriCode => array(__('Code'), "data-min='4' size='4'", 'autoSubmit'), 128 self::sCaptcha => array(__('Captcha'), "data-min='4' size='4'"), 129 129 self::sSend => array(__('Send')), 130 130 self::sSign => array(__('Sign the petition')), … … 148 148 * {subject* min=20 flavor='chocolate chip' Lorem ipsum dolor sit amet} 149 149 * {body rows=8 cols='70' Ut enim ad minim veniam} 150 * 151 * @layout template from inside the short code or the form 152 * @pageAttributes short code attributes which can be used to ovveride default values 153 * @cannedFields set of built in fields with preset names and field sizes 150 154 * 151 155 * @return array of EcampaignField … … 153 157 const regexParseTemplate = '${(\w+)(\*)?((?:\s+[\w][\w-]+=(?:[%]?[\w]+|[\'\"\”][^\'\"\”]+[\'\"\”]))*)\s*([^}]*)}$' ; 154 158 155 function parseTemplate($layout, $pageAttributes)159 static function parseTemplate($layout, $pageAttributes, $cannedFields) 156 160 { 157 161 $parsedFields = array(); … … 165 169 $efield = new EcampaignField ; 166 170 $efield->wholeField = $parsedFields[0][$i]; 171 $efield->isCustom = false; 167 172 $noun = $parsedFields[1][$i]; 168 173 169 // use aliases for just these two fields, full name use everywhere else174 // map template labels to field names 170 175 if ($noun == 'name') $noun = 'visitorName'; 171 176 if ($noun == 'email') $noun = 'visitorEmail'; 172 177 173 178 $efield->name = $noun ; 174 179 175 $knownField = @$this->cannedFields[$noun];176 177 if (! isset($knownField))180 $knownField = array_key_exists($noun, $cannedFields) ? $cannedFields[$noun] : null ; 181 182 if (!$knownField) 178 183 { 179 184 $knownField = array($noun,''); // ignore fields like %xyz we don't recognise, they will stay in the text … … 188 193 $efield->save =@ $attributeMap['save']; $attributeMap['save'] = null; 189 194 $efield->type = @$attributeMap['type']; 190 $efield->attributes = EcampaignField::serializeAttributes($attributeMap);191 195 $efield->mandatory = $parsedFields[2][$i] == "*"; 192 $ efield->value = $value = trim($parsedFields[4][$i]);196 $value = trim($parsedFields[4][$i]); 193 197 $efield->validator = @$knownField[2]; 194 195 if (!empty($value)) 198 199 // this is inconsistent behaviour but just for checkboxes 200 // the label is taken from the value i.e. the text inside the {} 201 // and the initial value (checked or otherwise) has to be set using the checked attribute 202 if (in_array($noun, [Ecampaign::sCheckbox1, Ecampaign::sCheckbox2])) 203 { 204 $efield->type = 'checkbox'; 205 $efield->label = $value ; 206 $value = in_array('checked', $attributeMap) ? 'on' : 'off' ; 207 unset($attributeMap['checked']); 208 } 209 if (!empty($value)) # only if checkbox is true 196 210 { 197 211 $efield->definition = true ; … … 203 217 $efield->value = @$pageAttributes->$noun ; 204 218 } 205 219 $efield->attributes = EcampaignField::serializeAttributes($attributeMap); 220 206 221 $templateFields[$noun] = $efield ; 207 222 } … … 211 226 212 227 /** 213 * Generate html for one or more forms228 * Generate html for one instance of plugin 214 229 * @return html string 215 230 */ 216 231 217 232 function createPage($pageAttributes, $pageBody) 218 { 219 $this->initializeCannedFields(); 220 $this->testMode = new EcampaignTestMode($pageAttributes->testMode); 233 { 221 234 if (empty($pageBody)) 222 throw new Exception(__("there is no text between [ecampaign] and [/ecampaign] or the closing [/ecampaign] is missing")); 223 224 if (empty($pageAttributes->campaignEmail)) 225 $pageAttributes->campaignEmail = get_option('ec_campaignEmail'); 226 235 throw new Exception(__("there is no text between [ecampaign] and [/ecampaign] or the closing [/ecampaign] is missing")); 236 237 $cannedFields = $this->initializeCannedFields(); 238 239 $this->session->testMode = new EcampaignTestMode($pageAttributes->testMode); 240 $this->session->fieldSet->referer = @$_SERVER["HTTP_REFERER"]; 241 $this->session->fieldSet->campaignEmail = $pageAttributes->campaignEmail ? 242 $pageAttributes->campaignEmail : get_option('ec_campaignEmail'); 243 227 244 $nonce = wp_create_nonce('ecampaign'); 228 $referer = @$_SERVER["HTTP_REFERER"]; 229 $postID = get_the_ID(); 245 246 $hiddenFields = 247 "<input type='hidden' name='_ajax_nonce' value='{$nonce}' /> 248 <input type='hidden' name='postID' value='{$this->session->postID}' />\n" ; //required 230 249 231 $hiddenFields = 232 "<input type='hidden' name='_ajax_nonce' value='{$nonce}' /> 233 <input type='hidden' name='referer' value='{$referer}'/> 234 <input type='hidden' name='postID' value='{$postID}'/>"; 235 236 $form = null ; $pageParts = preg_split("$<hr[^/]*/>$", $pageBody); 237 238 /** 239 * Only body of message defined. using default template. 240 */ 241 242 switch(count($pageParts)) 243 { 244 case 2 : // legacy mode, two forms, target email in top form, friends email in bottom form 245 { 246 $error = array() ; 247 248 if (empty($pageAttributes->targetEmail)) 249 $error[] = sprintf(__('%s attribute not set.'), 'targetEmail') ; 250 251 if (empty($pageAttributes->targetSubject)) 252 $error[] = sprintf(__('%s attribute not set.'), 'targetSubject') ; 253 254 if (empty($pageAttributes->friendSubject)) 255 $error[] = sprintf(__('%s attribute not set.'), 'friendSubject') ; 256 257 if (count($error) > 0) 258 { 259 $errorText = implode('</p><p>', $error); 260 die(__('ecampaign: page not setup correctly') ." ". self::help()."#setup" 261 ."<p>$errorText</p>" 262 ."<div style='border:1px solid red; padding:5px'> <code>$pageBody</code></div>"); 263 } 264 265 // this is legacy mode and it's messy 266 267 $this->classPath = 'EcampaignTarget'; // bodge 268 if (empty($pageAttributes->to)) $pageAttributes->to = $pageAttributes->targetEmail ; 269 if (empty($pageAttributes->subject)) $pageAttributes->subject = $pageAttributes->targetSubject ; 270 $pageAttributes->body = $pageParts[0] ; 271 // Convert the clean \r\n characters into html breaks so its in the same 272 // format as if the form were embedded in the post. 273 $template = preg_replace("$[\r\n]+$", "<br/>", get_option('ec_layout')); 274 $form = self::createForm($template, "ecform ec-target", $pageAttributes, $hiddenFields); 275 276 $this->classPath = 'EcampaignFriend'; // bodge 277 $pageAttributes->subject = $pageAttributes->friendSubject ; 278 $pageAttributes->body = $pageParts[1] . "<p>". get_permalink() . "</p>"; 279 $pageAttributes->hidden = true ; 280 // Convert the clean \r\n characters into html breaks so its in the same 281 // format as if the form were embedded in the post. 282 $template = preg_replace("$[\r\n]+$", "<br/>", get_option('ec_friendsLayout')); 283 $form .= self::createForm($template, "ecform ec-friend", $pageAttributes, $hiddenFields); 284 $sections[] = $form; 285 break ; 286 } 287 288 case 1 : 289 default : 290 { 291 if (4 < preg_match_all('$[{}]$' ,$pageParts[0], $matches)) 292 { // single form using the template inside the post 293 $template = preg_replace('$[\r\n]+$', '', $pageParts[0]); // loose existing CRLF 294 $pageAttributes->body = null ; // the body is contained in the template 295 } 296 else 297 { 298 $template = preg_replace("$[\r\n]+$", "<br/>", $this->defaultTemplate); 299 $pageAttributes->body = $pageParts[0]; 300 } 301 if (empty($pageAttributes->to)) $pageAttributes->to = $pageAttributes->targetEmail ; 302 $form = self::createForm($template, $this->styleClass, $pageAttributes, $hiddenFields); 303 $sections[] = $form; 304 break ; 305 } 306 $hiddenFields = "" ; // only needed on first form 307 } 308 return "<!-- http://www.wordpress.org/plugins/ecampaign -->\r\n" . $form ; 309 } 310 311 /** 312 * 313 * @param $template Must be at least 4 fileds to be accepted. 314 * @param $id 315 * @param $pageAttributes 316 * @param $hiddenFields 317 */ 318 319 function createForm($template, $styleClass, $pageAttributes, $hiddenFields) 320 { 321 $id = EcampaignField::nextID(); 322 self::$formList[$id] = array(); 323 self::$formList[$id]['classPath'] = $this->classPath; 324 325 // wrap all the lines in the template in DIV.ECROW, 326 // this is less flexible but makes layout in IE7 less troublesome. 327 // 328 // $rows = explode("\r\n", $template); 329 // $html = "<div class='ecrow'>" . implode("</div>\r\n<div class='ecrow'>", $rows) ."</div>" ; 330 331 $templateFields = self::parseTemplate($html=$template, $pageAttributes); 250 251 if (array_key_exists(self::sVeriCode, $_GET)) { 252 $this->session->fieldSet->{self::sVeriCode} = $_GET[self::sVeriCode]; 253 $hiddenFields .= "<input type='hidden' name='autoSubmit' value='1' />\n"; // would rather modify element atrribute 254 } 255 256 if (count(preg_split("$<hr[^/]*/>$", $pageBody)) > 1) 257 throw new Exception( 258 'Multiple forms within a single pair of shortcode brackets are no longer supported. ' 259 .'Please use two pairs of shortcode brackets'); 260 if (4 < preg_match_all('$[{}]$' ,$pageBody, $matches)) 261 { // single form using the template inside the post 262 $template = preg_replace('$[\r\n]+$', '', $pageBody); // loose existing CRLF 263 $pageAttributes->body = null ; // the body is contained in the template 264 } 265 else 266 { 267 $template = preg_replace("$[\r\n]+$", "<br/>", $this->defaultTemplate); 268 $pageAttributes->body = filter_var($pageBody, FILTER_SANITIZE_STRING); 269 } 270 $form = $this->createForm($template, $this->styleClass, $pageAttributes, $hiddenFields, $cannedFields); 271 272 return "<!-- http://www.wordpress.org/plugins/ecampaign start -->\r\n" 273 . $form . "\r\n" 274 . "<!-- http://www.wordpress.org/plugins/ecampaign end -->\r\n" ; 275 } 276 277 /** 278 * 279 * @param $template specifies the fields 280 * @param $styleClass class attribute given to outer wrapper of form 281 * @param $pageAttributes contains shortcode attributes 282 * @param $hiddenFields contains formID etc 283 * @param $cannedFields definition of fields, label, length, etc 284 */ 285 286 function createForm($template, $styleClass, $pageAttributes, $hiddenFields, $cannedFields) 287 { 288 $templateFields = self::parseTemplate($html=$template, $pageAttributes, $cannedFields); 332 289 333 290 if (count($templateFields) == 0) … … 338 295 please go to $settingsLink and check that templates. <br/><br/>".$template)); 339 296 } 340 297 // populate the template //TODO, priority not right 341 298 foreach ($templateFields as $noun => $efield) 342 299 { 300 if (!empty($this->session->fieldSet->$noun)) // put saved session values into rewritten form 301 $efield->value = $this->session->fieldSet->$noun ; 302 303 $this->session->fieldSet->$noun = $efield->value ; 343 304 $snippet = $this->createField($noun, $efield, $pageAttributes, $templateFields); 344 305 if (isset($snippet)) … … 357 318 358 319 unset($pageAttributes->body); // no need to serialize it (and it can change 359 // if URL attached to bottom of friends body changes) 360 $form['pageAttributes'] = $pageAttributes ; 361 $form['template'] = $templateFields ; 362 $form['testMode'] = $this->testMode ; 363 364 $this->updateFormList(get_the_ID(), $id, $form); 365 366 $displayClass = $pageAttributes->hidden ? "hidden" : "" ; 367 return "<div id='$id' class='$styleClass $displayClass' >" 368 . $html . $hiddenFields 369 . "</div>\r\n" ; 370 } 371 372 /** 373 * store all the form data in post meta because the ajax POST 374 * handler doesn't have access to the page text or plugin attributes etc 375 * Data only updated if it's changed. 376 * Note there is double serialization. 377 * 378 * @param unknown_type $postID 379 * @param unknown_type $formID 380 * @param unknown_type $form 381 */ 382 383 function updateFormList($postID, $formID, $form) 384 { 385 386 $formSerialized = serialize($form); 387 388 $formListSerialized = get_post_meta($postID, 'formList', true); 389 if (empty($formListSerialized)) 390 { 391 $formList = array(); // creating a new list. 392 } 393 else 394 { 395 $formList = unserialize($formListSerialized); 396 } 397 $oldFormSerialized = @$formList[$formID]; 398 if (!empty($oldFormSerialized)) 399 { 400 if ($formSerialized === $oldFormSerialized) 401 { 402 return ; // version stored is identical 403 } 404 } 405 $formList[$formID] = $formSerialized; 406 update_post_meta($postID, 'formList', serialize($formList)); 407 $this->log->write("formUpdate", array(), "postID:$postID formID:$formID"); 320 unset($this->session->fieldSet->verificationCode); 321 // if URL attached to bottom of friends body changes) 322 $this->session->pageAttributes = $pageAttributes ; // will be serialized, need for form processing 323 $this->session->templateFields = $templateFields ; // will be serialized, need for form processing 324 325 $displayStyle = $pageAttributes->hidden ? "display:none" : "" ; 326 $formID = $this->session->formID ; 327 return "<div id='$formID' class='$styleClass' style='$displayStyle'>" 328 . $html . "\r\n". $hiddenFields . "\r\n</div>" ; 408 329 } 409 330 … … 415 336 */ 416 337 function revealNextApplicableForm($response) 417 { 418 $currentID = $_POST['formID']; 419 420 if (!is_array(self::$formList)) 421 throw new Exception("Session data missing or corrupt"); 422 338 { 339 $lastID = NULL ; 423 340 $selector = array(); 424 $forms = self::$formList;425 341 426 342 // restore all forms including ones that are wrapped 427 343 // in other ecampaign tags on the same page. 428 429 foreach($forms as $ID => $serializedForm) 430 { 344 345 ksort(self::$allControllers); 346 foreach(self::$allControllers as $formID => $session) 347 { 348 431 349 if (isset($lastID)) // reveal all subesquent forms 432 350 { 433 $form = unserialize($serializedForm); 434 $ecampaign2 = _createFromClassPath($form['classPath']); 435 // this is a bodge, copy properties from one to other class 436 $ecampaign2->restoreForm($this->fieldSet->postID, $ID); // overwrites self::$formList 437 $ecampaign2->fieldSet = $this->fieldSet; // need to access posted data 438 if ($ecampaign2->filterVisitors()) 351 $session->fieldSet = $this->session->fieldSet; // carry over existing posted fields 352 // if ($nextEcampaignForm->filterVisitors()) // overidden method NEEDS FIXING 439 353 { 440 $selector[] = "#$ ID " ; // reveal this form354 $selector[] = "#$formID " ; // reveal this form 441 355 } 442 356 } 443 if ($ currentID == $ID)444 $lastID = $ currentID ;357 if ($formID == $session->formID) 358 $lastID = $formID ; 445 359 } 446 360 if (is_array($response)) … … 467 381 { 468 382 return true ; 469 }470 471 472 /**473 * Invoked prior to handling an ajax submit474 * Rearley called so not concerned about time to unserialize475 */476 477 function restoreForm($postID, $formID)478 {479 $formListSerialized = get_post_meta($postID, 'formList', true);480 if (empty($formListSerialized))481 {482 throw new Exception ("Unable to recall form list for postID $postID");483 }484 self::$allFields = array();485 self::$formList = unserialize($formListSerialized);486 foreach (self::$formList as $ID => $formSerialized)487 {488 if (empty($formSerialized))489 {490 throw new Exception ("Unable to recall form for postID $postID formID $formID");491 }492 $form = unserialize($formSerialized);493 if ($formID == $ID)494 {495 $this->pageAttributes = $form['pageAttributes'];496 $this->templateFields = $form['template'];497 $this->testMode = $form['testMode'];498 }499 else500 self::$allFields = array_merge(self::$allFields, $form['template']);501 }502 // take values from current form and override values503 self::$allFields = array_merge(self::$allFields, $this->templateFields);504 505 if (empty($this->templateFields))506 {507 throw new Exception ("Unable to recall form for postID $postID formID $formID");508 }509 383 } 510 384 … … 520 394 function createField($noun, $efield, $pageAttributes) 521 395 { 396 $testMode = $this->session->testMode ; 522 397 switch($noun) { 523 398 524 399 case self::sTo : 525 $recipientsBrokenUp = self::breakupEmail( 526 $this->testMode->isDiverted()? $pageAttributes->campaignEmail: $efield->value); 527 400 $emailList = new EmailList($efield->value); 401 $recipients = $emailList->divert($testMode, $pageAttributes->campaignEmail)->asAntiSpammedHTMLString(); 528 402 $settingsUrl = admin_url("options-general.php?page=ecampaign"); 529 $helpOutOfTestMode = $this->testMode->is(EcampaignTestMode::sNormal) ? "" : "<span id='text-test-mode'> [{$this->testMode->toString()} <a href='{$settingsUrl}')>change</a>]</span>" ; 530 $html = "<label id='lab-to'>$efield->label:</label><span id='recipients-email' class='ecinputwrap'>$recipientsBrokenUp</span>$helpOutOfTestMode" ; 531 break ; 532 /* 533 case self::sSubject : 534 $html = $efield->writeField(null); 535 break ; 536 */ 403 $helpOutOfTestMode = $testMode->isNormal() ? "" : "<span id='text-test-mode'> [{$testMode->toString()} <a href='{$settingsUrl}')>change</a>]</span>" ; 404 $html = "<label id='lab-to'>$efield->label:</label><span id='recipients-email' class='ecinputwrap'>$recipients</span>$helpOutOfTestMode" ; 405 break ; 406 537 407 case self::sBody : 538 408 if (isset($this->bodyTrailer)) // set in EcampaignFriends to carry url of post 539 409 $efield->value .= $this->bodyTrailer ; 540 $efield->value = strip_tags($this->replaceParagraphTagsWithNewlines($efield->value));410 $efield->value = strip_tags(Ecampaign::replaceParagraphTagsWithNewlines($efield->value)); 541 411 $html = $efield->writeTextArea(); 542 412 unset($efield->value) ; // to prevent if from being serialized … … 562 432 563 433 case self::sVeriCode : 564 $efield->wrapper = 'eccode hidden';434 $efield->wrapper = empty($efield->value) ? 'eccode hidden' : 'eccode' ; 565 435 $html = $efield->writeField(); 566 436 break ; … … 570 440 case self::sFriendSend : 571 441 $efield->attributes .= $this->submitEnabled ? "" : " disabled='disabled'" ; 572 $efield->attributes .= " onclick=\"return ecam.onClickSubmit(this, '$ this->classPath', '$noun');\" ";442 $efield->attributes .= " onclick=\"return ecam.onClickSubmit(this, '$noun');\" "; 573 443 $efield->name = 'submit'; 574 444 $html = "<div class='ecsend'>".$efield->writeButton()."</div><div class='ecstatus'></div>" ; … … 595 465 case self::sCampaignEmail : 596 466 $efield->isCustom =false ; 597 $html = @$efield->definition ? "" : self::breakupEmail($efield->value);467 $html = isset($efield->definition) ? "" : $efield->value; 598 468 break ; 599 469 600 470 case self::sSuccessMessage : // have to remove any para tags, new lines are just ignored 601 471 $efield->isCustom =false ; 602 $efield->value = $this->replaceParagraphTagsWithNewlines($efield->value);472 $efield->value = Ecampaign::replaceParagraphTagsWithNewlines($efield->value); 603 473 $html = $efield->definition ? "" : $efield->value; 604 474 break ; 605 606 475 607 476 case self::sCheckbox1 : 608 477 case self::sCheckbox2 : 609 $efield->type='checkbox' ; 478 $html = $efield->writeCheckBox() ; 479 break ; 610 480 611 481 default : // handles name, email, zipcode, postcode etc 612 613 // this is inconsistent behaviour but just for checkboxes 614 // the label is taken from the value i.e. the text inside the {} 615 // and the initial value (checked or otherwise) has to be set using the checked attribute 616 617 if ($efield->type=='checkbox') 618 { 619 $efield->label = $efield->value ; $efield->value = null; 620 $html = $efield->writeCheckBox(); 621 break ; 622 } 623 else 624 { 625 $html = $efield->writeField(); 626 break ; 627 } 482 $html = $efield->writeField(); 628 483 } 629 484 return $html ; 630 485 } 631 486 632 633 634 487 // <label for="send-formName" class="labeloverlay sendoverlay labeloverlayhidden" >Name</label> 635 488 489 function mergeBindings() 490 { 491 return (Object) array_merge( 492 (array) $this->session->pageAttributes, 493 (array) $this->session->fieldSet, 494 (array) EcampaignField::getMap($this->session->templateFields, true) // from $_REQUEST() 495 ); 496 } 497 636 498 /** 637 499 * Convert </p> and <br/> tags into CR-LF and strip out any adjacent CR and LF that happen to be there … … 650 512 } 651 513 652 653 654 514 /** 655 515 * break up string of emails separated by commas … … 659 519 * @return string containing emails separated by comma and a space. 660 520 */ 661 static function breakupEmail ($emailsBetweenCommas)521 static function breakupEmailUNUSED($emailsBetweenCommas) 662 522 { 663 523 $html = "" ; … … 671 531 return $html; 672 532 } 673 674 675 /**676 * email addresses returned from the to: field in the browser677 * *all* of which are in 1 of 2 formats678 * 1. mailbox@domain679 * 2. first-name last-name <mailbox@domain>680 * and are expected to be separated by commas but not assuming that681 *682 * Cannot find any simple way of parsing emails in both formats683 * in one expression which doesn't involve fiddling with the parser results.684 *685 * @param $emailString686 * @return string containing emails separated by any whitespace character.687 */688 static function parseEmailRecipients($emailString)689 {690 $parsedFields = array();691 $num = preg_match_all('$<[\s]*([^>]+)[\s]*>$', $emailString, $parsedFields);692 if ($num > 0)693 return $parsedFields[1]; // ie just the email addresses inside < and >694 695 $num = preg_match_all('$[,\s]*([^<>,\s]+)$', $emailString, $parsedFields);696 return $parsedFields[1]; // return every word with 1 or more chars697 }698 699 533 700 534 /** … … 727 561 $phpmailer->CharSet = apply_filters( 'wp_mail_charset', get_bloginfo( 'charset' )); 728 562 // add the originating URL so abuse can be tracked back to specific web page 729 $phpmailer->addCustomHeader("X-ecampaign-URL: {$_SERVER["HTTP_REFERER"]}");563 @$phpmailer->addCustomHeader("X-ecampaign-URL: {$_SERVER["HTTP_REFERER"]}"); 730 564 return $phpmailer ; 731 565 } … … 748 582 throw new Exception("$fieldName: ". __("rejected by FILTER_VALIDATE_EMAIL")." : {$recipient}"); 749 583 } 750 }751 else // for older PHP versions752 {753 $validEmail = preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $recipient);754 if (!validEmail)755 throw new Exception("$fieldName: ". __("invalid email address")." : {$recipient}");756 584 } 757 585 … … 774 602 } 775 603 } 604 605 606 class EmailList 607 { 608 public $items = [] ; 609 const regexEmailList = '$(?P<name>[^@<>.,]+)\s<(?P<email>[^@\s\,<>]+@[^@\s\,<>]+)>|(?P<emailOnly>[^@\s\,<>]+@[^@\s\,<>]+)$'; 610 // https://regex101.com/r/typ8tD/ test strings 611 function __construct($stringOfEmails) 612 { 613 $stringOfEmails1 = 'john.smith@ymail.com, Gillian Green <gillgreen@ymail.com>,Fred Smith<Fred@outlook.com>, <sue@sue.com>'; 614 $num = preg_match_all(self::regexEmailList, $stringOfEmails, $eList); 615 if ($num < 1) 616 throw new Exception(__('Unable to parse email string: '). $stringOfEmails); 617 $this->items = EmailList::transpose($eList); 618 // rename 'emailOnly' keys as 'email' keys 619 foreach($this->items as $key => $ite) 620 { 621 if (!empty($this->items[$key]['emailOnly'])) 622 { 623 $this->items[$key]['email'] = $this->items[$key]['emailOnly']; unset($this->items[$key]['emailOnly']); 624 } 625 } 626 } 627 628 static function transpose($ar) { 629 $ar2 = [] ; 630 foreach($ar[0] as $ik => $notused) 631 foreach ($ar as $ok => $notused) 632 $ar2[$ik][$ok] = $ar[$ok][$ik] ; 633 return $ar2 ; 634 } 635 636 function divert($testMode, $campaignEmail) 637 { 638 if ($testMode->isDiverted()) 639 { 640 $this->items[0]['email'] = $campaignEmail; 641 $this->items = array_slice($this->items, 0, 1); // create new truncated list 642 } 643 return $this ; 644 } 645 646 function asString($antiSpammedHTML = False) 647 { 648 $items = []; 649 foreach ($this->items as $email) 650 { 651 $html = empty($email['name']) ? $email['email'] : $email['name'] . " <" . $email['email'] . ">"; 652 if ($antiSpammedHTML) 653 $html = str_replace("@", "<span class='confuseSpammers'>@</span>", htmlspecialchars($html)); 654 $items[] = $html; 655 } 656 return (join(',',$items)); 657 } 658 659 function asAntiSpammedHTMLString() 660 { 661 return $this->asString(/* $antiSpammedHTML = */ True); 662 } 663 } -
ecampaign/branches/maintenance/EcampaignPetition.class.php
r1700889 r1974927 13 13 class EcampaignPetition extends Ecampaign 14 14 { 15 const jsRevealNextForm = "revealNextForm";16 const passwordLength = 4 ;17 function __construct()18 {19 parent::__construct();20 $this->defaultTemplate = get_option('ec_petitionLayout'); 15 const verificationCodeLength = 4 ; 16 function __construct($session) 17 { 18 parent::__construct($session); 19 $this->session->classPath = "EcampaignPetition"; 20 $this->defaultTemplate = get_option('ec_petitionLayout'); 21 21 $this->styleClass = 'ecform ec-target'; 22 22 $this->validAjaxMethods[] = "sign" ; 23 23 $this->validAjaxMethods[] = "send" ; 24 $this->validAjaxMethods[] = "verify" ; 24 25 } 25 26 … … 27 28 private function preProcess() 28 29 { 29 $desiredFields = array_merge(array(self::sPostID, self::sVisitorName, self::sVisitorEmail, self::sCampaignEmail, self::sReferer), 30 array_keys($targetFields = $this->templateFields)); 31 32 $controlFields = array(self::sPostID => new EcampaignField(self::sPostID), 33 self::sReferer => new EcampaignField(self::sReferer)); 34 35 $fieldSet = $this->fieldSet = EcampaignField::requestPartialMap($desiredFields, array_merge($controlFields, self::$allFields)); 36 37 $fieldSet->permalink = get_permalink($fieldSet->postID); 38 $fieldSet->recipients = self::parseEmailRecipients($_POST['recipientsEmail']) ; 30 $this->session->fieldSet = $fieldSet = $this->mergeBindings(); 39 31 40 32 self::validateEmail($fieldSet->campaignEmail, "campaignEmail"); // throws exception if fails. … … 49 41 self::validateEmail($fieldSet->visitorEmail, "visitorEmail"); // throws exception if fails. 50 42 51 $address = new EcampaignString(array($fieldSet->address1,$fieldSet->address2,$fieldSet->address3,43 @$address = new EcampaignString(array($fieldSet->address1,$fieldSet->address2,$fieldSet->address3, 52 44 $fieldSet->city,$fieldSet->postcode,$fieldSet->ukpostcode, 53 45 trim("{$fieldSet->state} {$fieldSet->zipcode}"),$fieldSet->country)); … … 55 47 $fieldSet->postalAddress = $address->removeEmptyFields(); 56 48 57 $this->infoMap = array(58 "refer rer: " . $fieldSet->referer,49 @$this->infoMap = array( 50 "referer: " . $fieldSet->referer, 59 51 "remote: " . "{$_SERVER['REMOTE_HOST']} {$_SERVER['REMOTE_ADDR']}", 60 52 "user-agent: " . $_SERVER['HTTP_USER_AGENT']); 61 62 foreach ($t argetFields as $f) //save all the custom fields53 54 foreach ($this->session->templateFields as $f) //save all the custom fields FIXME 63 55 { 64 56 if ($f->isCustom && (!empty($f->value) || isset($f->mandatory))) … … 66 58 } 67 59 68 /* catcha and verification need to work together easily if only for test p Uurposes.60 /* catcha and verification need to work together easily if only for test purposes. 69 61 * verification field not displayed until captcha entered so don't check captcha again 70 62 * not least because i think captcha codes only work once. … … 73 65 $userSuppliedVerificationCode = $_POST[self::sVeriCode]; 74 66 75 if (array_key_exists(self::sCaptcha, $ targetFields) && empty($userSuppliedVerificationCode))67 if (array_key_exists(self::sCaptcha, $fieldSet) && empty($userSuppliedVerificationCode)) 76 68 { 77 69 $captchadir = get_option('ec_captchadir'); … … 85 77 } 86 78 } 87 88 if (array_key_exists(self::sVeriCode, $targetFields)) 79 80 $verifiedUser = $this->log->recordExists(EcampaignLog::tVerified, $fieldSet->visitorEmail); 81 82 if (!$verifiedUser && (get_option('ec_verifyEmail') || array_key_exists(self::sVeriCode, $targetFields))) 89 83 { 90 84 // nonce creation lifted from wordpress pluggable.php 91 85 // verification code is different on each site for same email addresses 92 86 93 $i = wp_nonce_tick(); 94 $hashCodes = array(); 95 $action = $fieldSet->targetEmail . $fieldSet->visitorEmail; 96 // Nonce generated 0-12 hours ago 97 $hashCodes[] = substr(wp_hash($i . $action, 'nonce'), -12, self::passwordLength); 98 // Nonce generated 12-24 hours ago 99 $hashCodes[] = substr(wp_hash(($i - 1) . $action, 'nonce'), -12, self::passwordLength); 87 88 $action = get_bloginfo("name") . $fieldSet->visitorEmail; 89 $hashCode = substr(wp_hash($action, 'nonce'), -self::verificationCodeLength); 100 90 101 91 if (empty($userSuppliedVerificationCode)) … … 107 97 * @return unknown_type 108 98 */ 109 $fieldSet->code = $hashCode s[0];99 $fieldSet->code = $hashCode; 110 100 $success = $this->sendEmailToSiteVisitor('ec_verificationEmail', $fieldSet); 111 101 $this->log->write(EcampaignLog::tVerify, $fieldSet, "code:". $fieldSet->code); //todo report error 112 102 return array("success" => $success, //"getCode" => true, 113 "callbackJS" => 'revealVerificationField', 114 "msg" => __("Please check your email. Please enter the random characters in the empty code field above.")); 103 "selector" => '.eccode', 104 "callbackJS" => 'revealForm', 105 "msg" => __("Please check your email including your spam folder. We have sent you a link to verify your email address.")); 115 106 } 116 107 117 if ((0 != strcasecmp($userSuppliedVerificationCode,$hashCodes[0])) 118 && (0 != strcasecmp($userSuppliedVerificationCode,$hashCodes[1]))) 108 if (0 != strcasecmp($userSuppliedVerificationCode,$hashCode)) 119 109 { 120 110 return array("success" => false, … … 129 119 // and the count may fall behind. 130 120 131 // add_post_meta( $fieldSet->postID, self::$counter, 0, true);132 $count = get_post_meta($ fieldSet->postID, self::ecCounter, true);133 update_post_meta($ fieldSet->postID, self::ecCounter, $count+1);121 // add_post_meta( ->postID, self::$counter, 0, true); 122 $count = get_post_meta($this->session->postID, self::ecCounter, true); 123 update_post_meta($this->session->postID, self::ecCounter, $count+1); 134 124 135 125 // setup success message 136 $this->successMessage = $this-> templateFields[self::sSuccessMessage]->value;126 $this->successMessage = $this->session->templateFields[self::sSuccessMessage]->value; 137 127 if (empty($this->successMessage)) 138 128 { … … 148 138 return $response; 149 139 150 $fieldSet = $this-> fieldSet;140 $fieldSet = $this->session->fieldSet; 151 141 152 142 if (get_option('ec_preventDuplicateActions')) … … 169 159 */ 170 160 161 function verify() 162 { 163 return $this->send(); 164 } 165 171 166 function send() 172 167 { … … 175 170 return $response; 176 171 177 $fieldSet = $this-> fieldSet;172 $fieldSet = $this->session->fieldSet; 178 173 179 174 $mailer = self::getPhpMailer(); 180 175 $mailer->Subject = $fieldSet->subject; 181 176 $mailer->From = $fieldSet->campaignEmail; // changes 24-Jun-2016 ja 182 $mailer->FromName = '';183 177 $mailer->FromName = get_bloginfo("name"); 178 $mailer->AddBCC($fieldSet->visitorEmail, $fieldSet->visitorName); 184 179 $mailer->AddReplyTo($fieldSet->visitorEmail, $fieldSet->visitorName); 185 $mailer->AddBCC($fieldSet->visitorEmail, $fieldSet->visitorName); // copy of email for site visitor186 //$mailer->AddBCC($fieldSet->campaignEmail, ''); // copy of email for campaign187 188 foreach ($fieldSet->recipients as $recipient)189 {190 self::validateEmail($recipient, "recipientEmail");191 $mailer->AddAddress($recipient);192 if (empty($fieldSet->target))193 $fieldSet->target = $recipient;194 }180 181 $emailList = new EmailList($this->session->fieldSet->to); 182 $emailList->divert($this->session->testMode, $this->session->fieldSet->campaignEmail); 183 184 foreach ($emailList->items as $recipient) 185 { 186 self::validateEmail($recipient['email'], "recipientEmail"); 187 empty($recipient['name']) ? $mailer->AddAddress($recipient['email']) : $mailer->AddAddress($recipient['email'], $recipient['name']); 188 } 189 $firstRecipient = $emailList->items[0]['email'] ; 195 190 if (get_option('ec_preventDuplicateActions')) 196 if ($this->log->recordExists(EcampaignLog::tSend, $fieldSet->visitorEmail, $fieldSet->target, $fieldSet->postID)) 197 { 198 $response = array("success" => false, 199 "msg" => __("You have already sent an email about this issue to $fieldSet->target")) ; 200 return $response ; 201 } 191 if ($this->log->recordExists(EcampaignLog::tSend, $fieldSet->visitorEmail, $firstRecipient, $fieldSet->postID)) 192 { 193 $response = array("success" => false, 194 "msg" => __("You have already sent an email about this issue to $firstRecipient")) ; 195 return $response ; 196 } 197 202 198 $text = new EcampaignString(); 203 199 $text->add($fieldSet->body) // this has been trimmed … … 208 204 $mailer->Body = $text->asBlock(); 209 205 210 array_unshift($this->infoMap,'recipients: ' . ($recipientString = implode(', ', $fieldSet->recipients))); 211 212 $delivery = $this->testMode->isSuppressed() ? 1 : ($mailer->Send() ? 2 : 0); 206 207 array_unshift($this->infoMap,'recipients: ' . $recipientString = $emailList->asString(True)); 208 209 $delivery = $this->session->testMode->isSuppressed() ? 1 : ($mailer->Send() ? 2 : 0); 213 210 214 211 if ($delivery == 0) 215 212 throw new Exception(__("unable to send email to") . " {$recipientString}, {$mailer->ErrorInfo}"); 216 213 217 if (!$this-> testMode->isNormal())218 $this->infoMap[] = 'test mode: ' . $this-> testMode->toString();214 if (!$this->session->testMode->isNormal()) 215 $this->infoMap[] = 'test mode: ' . $this->session->testMode->toString(); 219 216 220 217 $this->log->write(EcampaignLog::tSend, $fieldSet, $this->infoMap); … … 222 219 223 220 // forward a similar version of the email to the campaign but add the checkfields that they have clicked 224 225 if (true) 221 // Disabled 2-nov-2018 because it doubles number of emails, reduces throughput etc. 222 223 if (false) 226 224 { 227 225 $mailer->ClearAllRecipients(); 228 226 $mailer->AddAddress($fieldSet->campaignEmail); 229 $mailer->Subject = "$fieldSet->ch1 : $fieldSet->ch2 : " . $fieldSet->subject ; 227 $mailer->Subject = (isset($fieldSet->checkbox1)? "on:" : " :") 228 . (isset($fieldSet->checkbox2)? "on:" : " :") 229 . $fieldSet->subject ; 230 230 231 231 $this->infoMap[] = " "; $this->infoMap[] = $mailer->Body; 232 232 $mailer->Body = implode("\r\n", $this->infoMap); 233 233 234 $delivery |= $this-> testMode->isSuppressed() ? 1 : ($mailer->Send() ? 2 : 0);234 $delivery |= $this->session->testMode->isSuppressed() ? 1 : ($mailer->Send() ? 2 : 0); 235 235 236 236 if ($delivery == 0) … … 238 238 } 239 239 $sMsg = $this->successMessage . ($delivery == 2 ? "" : " Delivery suppressed (code:$delivery) "); 240 $response = array("success" => true, 240 /* $response = array("success" => true, // not used 241 241 "msg" => $sMsg, 242 242 "callbackJS" => self::jsRevealNextForm); 243 243 */ 244 244 return $this->revealNextApplicableForm(array("success" => true, "msg" => $sMsg)); 245 245 } … … 250 250 $mailer = self::getPhpMailer(); 251 251 $mailer->From = $fieldSet->campaignEmail ; 252 $mailer->FromName = get_bloginfo("name");252 $mailer->FromName = $fieldSet->blogName = get_bloginfo("name"); 253 253 $mailer->AddAddress($fieldSet->visitorEmail, $fieldSet->visitorName); 254 254 … … 259 259 $fieldSet->subject = $post->post_title ; 260 260 } 261 $fieldSet->permalink = get_permalink($this->session->postID); 262 $fieldSet->emailVerificationLink = $fieldSet->permalink.'?verificationCode='.$fieldSet->code; // ja 27-sep-2018 FIXIT 263 $fieldSet->blogname = get_bloginfo("name"); 261 264 $mailer->Subject = self::replaceTokens($optionName."Subject", $fieldSet); 262 265 $mailer->Body = self::replaceTokens($optionName."Body", $fieldSet); 263 266 264 267 if (empty($mailer->Subject) || empty($mailer->Body)) 265 return false ; 268 { 269 throw new Exception(__("Subject or Body of email address verfication email is empty. Check ecampaign settings")); 270 } 266 271 267 272 $success = $mailer->Send(); … … 285 290 { 286 291 $name = $match[1]; 287 return isset($this-> fieldSet->$name) ? $this->fieldSet->$name :"xxxxxx" ;292 return isset($this->session->fieldSet->$name) ? $this->session->fieldSet->$name :"xxxxxx" ; 288 293 } 289 294 … … 302 307 $list = _createFromClassPath($listClassPath); 303 308 304 $list->subscribe($this-> templateFields, $fieldSet);309 $list->subscribe($this->session->templateFields, $fieldSet); 305 310 306 311 $this->log->write("subscribe", $fieldSet, $this->infoMap);
Note: See TracChangeset
for help on using the changeset viewer.