The notification plugin in SobiPro is sending emails that are triggered upon some events (entry update, entry approval an so on).
SobiPro entry edit, approval and other functions do not work when CMandrill plugin is enabled. Notification that is triggered by Ajax call within SobiPro gets stack, the page shows just loading progress in window overlay. When I disable CMandrill plugin it works OK.
I've attached the helper files from SobiPro notification plugin for you to take a look in hope that you will provide some hints on how to fix this issue.
I would also like to see feature that would allow us to exclude CMandrill for certain components as pointed in other forum posts.
Thank you for great plugin.
<?php
/**
* @version: $Id: helper.php 3591 2013-07-24 11:26:13Z Radek Suski $
* @package: Notifications Application
* @author
* Name: Sigrid Suski & Radek Suski, Sigsiu.NET GmbH
* Email: sobi[at]sigsiu.net
* Url: http://www.Sigsiu.NET
* @copyright Copyright (C) 2006 - 2013 Sigsiu.NET GmbH (http://www.Sigsiu.NET). All rights reserved.
* @license GNU/GPL Version 3
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3
* as published by the Free Software Foundation, and under the additional terms according section 7 of GPL v3.
* See http://www.gnu.org/licenses/gpl.html and http://sobipro.sigsiu.net/licenses.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* $Date: 2013-07-24 13:26:13 +0200 (Mi, 24 Jul 2013) $
* $Revision: 3591 $
* $Author: Radek Suski $
*/
defined( 'SOBIPRO' ) || exit( 'Restricted access' );
/**
* @author Radek Suski
* @version 1.0
*/
abstract class SPNotificationHelper
{
const debug = false;
public static function Trigger( $action, $args, $triggers, $handlers = array() )
{
self::debug( "Incoming request. Action: {$action}", __FUNCTION__ );
$action = preg_split( '/([A-Z][a-z]+)/', $action, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
$subject = $action[ 0 ];
array_shift( $action );
$a = null;
if ( count( $action ) ) {
foreach ( $action as $p ) {
$a = $a . $p;
}
}
switch ( strtolower( $subject ) ) {
case 'entry':
$params = array();
self:ayment( $a, $args, $triggers[ 'payment' ], $params );
self::entry( $a, $args, $triggers[ 'entry' ], $params );
break;
default:
$customHandler = self::checkHandler( strtolower( $subject ), $handlers );
if ( !( $customHandler ) ) {
self::custom( $a, $args, $triggers[ strtolower( $subject ) ] );
}
else {
self::customHandler( $a, $args, $triggers[ strtolower( $subject ) ], $customHandler, strtolower( $subject ) );
}
break;
}
}
private static function customHandler( $action, $args, $triggers, $handler, $subject )
{
$messages = array();
foreach ( $triggers as $trigger => $label ) {
if ( strstr( $trigger, $action ) ) {
$settings = array();
if ( method_exists( $handler, 'prepareMessageSettings' ) ) {
$handler->prepareMessageSettings( $trigger, $settings, $args[ 0 ] );
}
else {
$settings = self::loadMessages( $trigger, $subject, $args[ 0 ] );
}
if ( count( $settings ) && self::messageEnabled( $settings[ 'enabled' ] ) ) {
if ( method_exists( $handler, 'prepareMessageArgs' ) ) {
$handler->prepareMessageArgs( $trigger, $settings, $args[ 0 ] );
}
foreach ( $settings as $k => $v ) {
if ( method_exists( $handler, 'parseMessage' ) ) {
$v = $handler->parseMessage( $trigger, $args[ 0 ] );
}
$messages[ $trigger ][ $k ] = self:arse( $v, $args[ 0 ] );
}
}
}
}
self::sendMessages( $messages );
}
private static function checkHandler( $subject, $handlers )
{
if ( isset( $handlers[ $subject ] ) ) {
if ( SPLoader::translatePath( $handlers[ $subject ] ) ) {
$handler = SPFactory::Instance( $handlers[ $subject ] );
if ( method_exists( $handler, 'parseMessage' ) || method_exists( $handler, 'prepareMessageArgs' ) || method_exists( $handler, 'prepareMessageSettings' ) ) {
return $handler;
}
}
}
return false;
}
private static function payment( $action, &$args, $triggers, &$params )
{
if ( !( $args[ 0 ] instanceof SPEntry ) ) {
$sid = $args[ 0 ];
}
else {
$sid = $args[ 0 ]->get( 'id' );
}
$entry = SPFactory::Entry( $sid );
$args[ 0 ] = $entry;
$payment = SPFactory:ayment()->summary( $entry->get( 'id' ) );
$methods = array();
$HTMLmethods = array();
Sobi::Trigger( 'AppPaymentMessage', 'Send', array( &$methods, $args[ 0 ], &$payment ) );
Sobi::Trigger( 'AppPaymentMessage', 'Send', array( &$HTMLmethods, $args[ 0 ], &$payment, true ) );
$payment[ 'methods' ] = $methods;
$payment[ 'html_methods' ] = $HTMLmethods;
$params = array(
'entry' => $entry,
'user' => SPFactory::user(),
'payment' => $payment,
'author' => SPFactory::Instance( 'cms.base.user', $args[ 0 ]->get( 'owner' ) )
);
if ( $action == 'AfterSave' && SPFactory:ayment()->count( $entry->get( 'id' ) ) ) {
$messages = array();
foreach ( $triggers as $trigger => $label ) {
$settings = self::loadMessages( $trigger, 'payment', $params );
if ( !( count( $settings ) ) ) {
// in case we still have some old stuff
$settings = self::loadMessages( $trigger, null, $params );
}
if ( count( $settings ) && self::messageEnabled( $settings[ 'enabled' ] ) ) {
foreach ( $settings as $k => $v ) {
$messages[ $trigger ][ $k ] = self:arse( $v, $params );
}
$messages[ $trigger ][ 'sid' ] = $entry->get( 'id' );
}
}
self::sendMessages( $messages );
}
}
private static function entry( $action, $args, $triggers, &$params )
{
$messages = array();
$state = 0;
/* get the right message id */
if ( $action == 'AfterChangeState' ) {
foreach ( $triggers as $trigger => $label ) {
if ( !( strstr( $trigger, $action . ( $args[ 1 ] ? '.on' : '.off' ) ) ) ) {
unset( $triggers[ $trigger ] );
}
}
} /* if the action is "Un-Approve" entry - do nothing */
elseif ( $action == 'AfterApprove' && !( $args[ 1 ] ) ) {
return true;
}
/* determine if saving a new entry or updating an existing one */
if ( ( SPRequest::sid() || SPRequest::int( 'entry_id', 'post' ) ) && ( $args[ 0 ]->get( 'updatedTime' ) != $args[ 0 ]->get( 'createdTime' ) ) ) {
$action = str_replace( 'Save', 'Update', $action );
}
foreach ( $triggers as $trigger => $label ) {
if ( strstr( $trigger, $action ) ) {
// this is the new method with subject
$settings = self::loadMessages( $trigger, 'entry', $params );
if ( !( count( $settings ) ) ) {
// in case we still have some old stuff
$settings = self::loadMessages( $trigger, null, $params );
}
if ( count( $settings ) && self::messageEnabled( $settings[ 'enabled' ] ) ) {
foreach ( $settings as $k => $v ) {
$messages[ $trigger ][ $k ] = self:arse( $v, $params );
}
$messages[ $trigger ][ 'sid' ] = $args[ 0 ]->get( 'id' );
}
}
}
self::sendMessages( $messages );
}
private static function parse( $txt, $params )
{
// self::debug( "Parsing text. {$txt}", __FUNCTION__ );
preg_match_all( '/{([a-zA-Z0-9\-_\:\.\[\],]+)}/', $txt, $placeHolders );
if ( count( $placeHolders[ 1 ] ) ) {
SPLang::load( 'SpApp.notifications' );
foreach ( $placeHolders[ 1 ] as $placeHolder ) {
/* handle special application placeholders */
$replacement = null;
switch ( $placeHolder ) {
case 'entry.url':
if ( isset( $params[ 'entry' ] ) ) {
$pid = $params[ 'entry' ]->get( 'primary' );
if ( $pid ) {
$replacement = Sobi::Url( array( 'title' => Sobi::Cfg( 'sef.alias', true ) ? $params[ 'entry' ]->get( 'nid' ) : $params[ 'entry' ]->get( 'name' ), 'pid' => $pid, 'sid' => $params[ 'entry' ]->get( 'id' ) ), false, true, true, true );
}
else {
$replacement = Sobi::Url( array( 'title' => Sobi::Cfg( 'sef.alias', true ) ? $params[ 'entry' ]->get( 'nid' ) : $params[ 'entry' ]->get( 'name' ), 'sid' => $params[ 'entry' ]->get( 'id' ) ), false, true, true, true );
}
}
break;
case 'entry.edit_url':
if ( isset( $params[ 'entry' ] ) ) {
$replacement = Sobi::Url( array( 'task' => 'entry.edit', 'sid' => $params[ 'entry' ]->get( 'id' ) ), false, true, true, true );
}
break;
case 'entry.state':
if ( isset( $params[ 'entry' ] ) ) {
$replacement = ( $params[ 'entry' ]->get( 'state' ) == 0 ) ? Sobi::Txt( 'NOTA.ENTRY_DISABLED' ) : Sobi::Txt( 'NOTA.ENTRY_ENABLED' );
}
break;
case 'entry.approval':
if ( isset( $params[ 'entry' ] ) ) {
$replacement = ( $params[ 'entry' ]->get( 'approved' ) == 0 ) ? Sobi::Txt( 'NOTA.ENTRY_UNAPPROVED' ) : Sobi::Txt( 'NOTA.ENTRY_APPROVED' );
}
break;
case 'payment.methods.out':
if ( isset( $params[ 'payment' ] ) && isset( $params[ 'payment' ][ 'methods' ] ) ) {
foreach ( $params[ 'payment' ][ 'methods' ] as $method ) {
$replacement .= $method[ 'title' ] . '<hr/>';
$replacement .= $method[ 'content' ];
}
}
break;
case 'payment.methods.html':
if ( isset( $params[ 'payment' ] ) && isset( $params[ 'payment' ][ 'html_methods' ] ) ) {
foreach ( $params[ 'payment' ][ 'html_methods' ] as $method ) {
$replacement .= $method[ 'title' ] . '<hr/>';
$replacement .= $method[ 'content' ];
}
}
break;
default:
if ( strstr( $placeHolder, 'payment.columns' ) && isset( $params[ 'payment' ][ 'positions' ] ) && count( $params[ 'payment' ][ 'positions' ] ) ) {
preg_match_all( '/:\[([a-zA-Z0-9,]+)\]/', $placeHolder, $cols );
if ( count( $cols[ 1 ] ) ) {
$cols = explode( ',', $cols[ 1 ][ 0 ] );
if ( count( $cols ) ) {
$replacement .= "\n" . '<div style="min-width: 600px;">';
$w = 100 / count( $cols );
foreach ( $cols as $col ) {
$replacement .= "\n\t" . '<div style="width:' . $w . '%; float:left; font-weight:bold; text-align: center;">';
$replacement .= Sobi::Txt( 'NOTA.PAYMENT_' . strtoupper( $col ) );
$replacement .= "\n\t" . '</div>';
}
$replacement .= "\n\t" . '<div style="clear:both;"></div>';
foreach ( $params[ 'payment' ][ 'positions' ] as $data ) {
if ( count( $data ) ) {
foreach ( $cols as $col ) {
$replacement .= "\n\t" . '<div style="width:' . $w . '%; float:left; text-align: center;">';
if ( isset( $data[ $col ] ) ) {
$replacement .= $data[ $col ];
}
$replacement .= /*"\n\t".*/
'</div>';
}
$replacement .= "\n\t" . '<div style="clear:both;"></div>';
}
}
$replacement .= "\n" . '</div>';
}
}
}
break;
}
if ( strlen( $replacement ) ) {
$txt = str_replace( '{' . $placeHolder . '}', ( string )$replacement, $txt );
}
}
}
return preg_replace( '/{([a-zA-Z0-9\-_\:\.\[\],]+)}/', null, SPLang::replacePlaceHolders( $txt, $params ) );
}
private static function custom( $action, $args, $triggers )
{
$messages = array();
foreach ( $triggers as $trigger => $label ) {
if ( strstr( $trigger, $action ) ) {
$settings = self::loadMessages( $trigger, null, $args[ 0 ] );
if ( count( $settings ) && self::messageEnabled( $settings[ 'enabled' ] ) ) {
foreach ( $settings as $k => $v ) {
$messages[ $trigger ][ $k ] = self:arse( $v, $args[ 0 ] );
}
}
}
}
self::sendMessages( $messages );
}
private function stripTags( $txt )
{
$txt = str_replace( '<hr/>', "\n-------------------------------------\n", $txt );
$txt = str_replace( '<li>', "\n - ", $txt );
$txt = str_replace( '</div>', "\n", $txt );
$txt = str_replace( '</p>', "\n", $txt );
return strip_tags( $txt );
}
private static function messageEnabled( $setting )
{
return ( defined( 'SOBIPRO_ADM' ) ) ? ( ( $setting == 1 ) || ( $setting == 3 ) ) : ( $setting == 1 ) || ( $setting == 2 );
}
private static function sendMessages( $messages )
{
if ( count( $messages ) ) {
self::debug( $messages, __FUNCTION__ );
foreach ( $messages as $message ) {
$send = false;
$err = 'none';
if ( !( $message[ 'html' ] ) ) {
$message[ 'body' ] = self::stripTags( $message[ 'body' ] );
}
if ( self::validMail( $message[ 'fromMail' ] ) && self::validMail( $message[ 'toMail' ] ) ) {
$mail = SPFactory::Instance( 'services.mail' );
$mail->setSender( array( $message[ 'fromMail' ], $message[ 'from' ] ) );
$mail->setSubject( $message[ 'subject' ] );
$mail->setBody( $message[ 'body' ] );
// doesn't works in J16 - no idea why
// $mail->addRecipient( ( strlen( $message[ 'to' ] ) ? "{$message[ 'to' ]} <{$message[ 'toMail' ]}>" : $message[ 'toMail' ] ) );
$mail->AddAddress( $message[ 'toMail' ], $message[ 'to' ] );
$mail->IsHTML( $message[ 'html' ] );
$mail->addCC( explode( ',', $message[ 'cc' ] ) );
$mail->addBCC( explode( ',', $message[ 'bcc' ] ) );
$send = $mail->Send();
if ( $send instanceof Exception ) {
$err = $send->getMessage();
$send = false;
}
else {
$send = true;
}
unset( $mail );
}
else {
$err = SPLang::e( 'NO_VALID_EMAIL_ADDRESS_FOUND' );
Sobi::Error( 'Notifications', $err, SPC::WARNING, 0, __LINE__, __FILE__ );
}
SPFactory::db()
->insert(
'spdb_notifications',
array(
'mid' => null,
'sid' => ( isset( $message[ 'sid' ] ) ? $message[ 'sid' ] : 0 ),
'mailFrom' => $message[ 'fromMail' ],
'mailFromName' => $message[ 'from' ],
'mailTo' => $message[ 'toMail' ],
'mailToName' => $message[ 'to' ],
'mailSubject' => $message[ 'subject' ],
'mailBody' => $message[ 'body' ],
'mailCC' => $message[ 'cc' ],
'mailBCC' => $message[ 'bcc' ],
'mailDate' => 'FUNCTION:NOW()',
'mailHTML' => $message[ 'html' ],
'error' => $err,
'send' => $send,
'uid' => Sobi::My( 'id' ),
'pid' => Sobi::Section(),
)
);
if ( $send ) {
if ( SPRequest::cmd( 'spsid' ) ) {
SPFactory::message()->setReport( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'toMail' ], '' ), SPRequest::cmd( 'spsid' ), SPC::SUCCESS_MSG );
if ( $message[ 'cc' ] ) {
SPFactory::message()->setReport( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'cc' ], '[CC]' ), SPRequest::cmd( 'spsid' ), SPC::SUCCESS_MSG );
}
if ( $message[ 'bcc' ] ) {
SPFactory::message()->setReport( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'bcc' ], '[BCC]' ), SPRequest::cmd( 'spsid' ), SPC::SUCCESS_MSG );
}
}
elseif( SPFactory::user()->isAdmin() ) {
SPFactory::message()->success( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'toMail' ], '' ), false );
if ( $message[ 'cc' ] ) {
SPFactory::message()->success( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'cc' ], '[CC]' ), false );
}
if ( $message[ 'bcc' ] ) {
SPFactory::message()->success( Sobi::Txt( 'NOTA.STATUS_EMAIL_SENT', $message[ 'bcc' ], '[BCC]' ), false );
}
}
}
else {
if ( SPRequest::cmd( 'spsid' ) ) {
if ( $err ) {
SPFactory::message()->setReport( Sobi::Txt( 'NOTA.STATUS_EMAIL_NOT_SENT', $message[ 'toMail' ], $err ), SPRequest::cmd( 'spsid' ), SPC::ERROR_MSG );
}
else {
SPFactory::message()->setReport( Sobi::Txt( 'NOTA.STATUS_EMAIL_NOT_SENT', $message[ 'toMail' ], 'Unknown Error' ), SPRequest::cmd( 'spsid' ), SPC::ERROR_MSG );
}
}
elseif( SPFactory::user()->isAdmin() ) {
if ( $err ) {
SPFactory::message()->error( Sobi::Txt( 'NOTA.STATUS_EMAIL_NOT_SENT', $message[ 'toMail' ], $err ), false );
}
else {
SPFactory::message()->error( Sobi::Txt( 'NOTA.STATUS_EMAIL_NOT_SENT', $message[ 'toMail' ], 'Unknown Error' ), false );
}
}
}
}
}
}
private static function customMailList( &$def, $args )
{
if ( strlen( $def[ 'customList' ] ) ) {
$file = SPLoader::translatePath( 'etc.napp_cfg.' . $def[ 'customList' ], 'front', true, 'xml', false );
if ( $file ) {
$xml = DOMDocument::load( $file );
$root = $xml->getElementsByTagName( 'notifications' )->item( 0 );
/** @var DOMNode $node */
foreach ( $root->childNodes as $node ) {
if ( $node->nodeName == 'rule' ) {
$trigger = $node->attributes->getNamedItem( 'trigger' ) ? $node->attributes->getNamedItem( 'trigger' )->nodeValue : null;
$value = $node->attributes->getNamedItem( 'value' ) ? $node->attributes->getNamedItem( 'value' )->nodeValue : null;
if ( $trigger === null || self::compareArgs( $args, $trigger, $value ) ) {
/** @var DOMNode $recipient */
$ccs = array();
if ( $def[ 'cc' ] ) {
$ccs = explode( ',', $def[ 'cc' ] );
}
$bccs = array();
if ( $def[ 'bcc' ] ) {
$bccs = explode( ',', $def[ 'bcc' ] );
}
foreach ( $node->childNodes as $recipient ) {
switch ( $recipient->nodeName ) {
case 'to':
$def[ 'toMail' ] = $recipient
->attributes
->getNamedItem( 'mail' )
->nodeValue;
if ( $recipient->attributes->getNamedItem( 'name' ) ) {
$def[ 'to' ] = $recipient
->attributes
->getNamedItem( 'name' )
->nodeValue;
}
break;
case 'cc':
$ccs[ ] = $recipient
->attributes
->getNamedItem( 'mail' )
->nodeValue;
break;
case 'bcc':
$bccs[ ] = $recipient
->attributes
->getNamedItem( 'mail' )
->nodeValue;
break;
}
}
$def[ 'cc' ] = implode( ',', array_unique( $ccs ) );
$def[ 'bcc' ] = implode( ',', array_unique( $bccs ) );
}
}
}
}
}
}
private static function compareArgs( $args, $trigger, $value )
{
$trigger = explode( '.', $trigger );
$var = $args;
// xml true / false
if ( strtolower( $value ) == 'true' ) {
$value = true;
}
elseif ( strtolower( $value ) == 'true' ) {
$value = false;
}
if ( count( $trigger ) ) {
foreach ( $trigger as $property ) {
if ( ( $var instanceof SPDBObject ) || ( method_exists( $var, 'get' ) ) ) {
if ( strstr( $property, 'field_' ) && $var instanceof SPEntry ) {
try {
$v = $var->getField( $property )->data();
if ( !( $v ) ) {
$v = $var->getField( $property )->getRaw();
}
$var = $v;
} catch ( SPException $x ) {
Sobi::Error( 'Notifications', $x->getMessage(), SPC::WARNING, 0, __LINE__, __FILE__ );
}
}
elseif ( ( $property == 'name' ) && ( $var instanceof SPEntry ) && !( strlen( $var->get( $property ) ) ) ) {
$var = $var->getField( ( int )Sobi::Cfg( 'entry.name_field' ) )->data();
}
else {
$var = $var->get( $property );
}
}
elseif ( is_array( $var ) && isset( $var[ $property ] ) ) {
$var = $var[ $property ];
}
elseif ( $var instanceof stdClass ) {
$var = $var->$property;
}
}
}
if ( is_string( $var ) && $var == $value ) {
return true;
}
elseif ( is_array( $var ) && ( isset( $var[ $value ] ) || array_key_exists( $value, $var ) ) ) {
return true;
}
// if we just checking if there was anything
elseif ( $value === null ) {
if ( is_string( $var ) && strlen( $var ) ) {
return true;
}
elseif ( is_array( $var ) && count( $var ) ) {
return true;
}
}
else {
return false;
}
}
private function validMail( $mail )
{
return preg_match( '/[a-z0-9\.\-\_]+@[a-z0-9\.\-\_]{2,}\.[a-z]{2,5}/i', $mail );
}
private static function loadMessages( $nid, $subject = null, $args = null )
{
static $data = array();
if ( $subject ) {
$nid = $subject . '.' . $nid;
}
if ( isset( $data[ $nid ] ) ) {
return $data[ $nid ];
}
self::debug( "Loading messages. Nid: {$nid}", __FUNCTION__ );
SPFactory::registry()->loadDBSection( 'notifications' );
$setting = Sobi::Reg( 'notifications.settings.params' );
if ( strlen( $setting ) ) {
$setting = SPConfig::unserialize( $setting );
}
self::debug( $setting, __FUNCTION__ );
if ( is_array( $setting ) && isset( $setting[ Sobi::Section() ] ) && isset( $setting[ Sobi::Section() ][ $nid ] ) ) {
$setting = $setting[ Sobi::Section() ][ $nid ];
}
elseif ( is_array( $setting ) && isset( $setting[ -1 ] ) && isset( $setting[ -1 ][ $nid ] ) ) {
$setting = $setting[ -1 ][ $nid ];
}
$ids = array(
'from' => '{cfg:mail.fromname}',
'fromMail' => '{cfg:mail.from}',
'to' => '{user.name}',
'toMail' => '{user.email}',
'subject' => 'Message Subject',
'body' => 'Message Body',
'cc' => null,
'bcc' => null,
'html' => true,
'enabled' => false,
'customList' => null,
);
foreach ( $ids as $id => $v ) {
$value = SPLang::getValue( $nid . '.' . $id, 'application', Sobi::Section() );
if ( !( strlen( $value ) ) ) {
$value = SPLang::getValue( $nid . '.' . $id, 'application', -1 );
}
if ( strlen( $value ) ) {
$ids[ $id ] = $value;
}
else {
if ( isset( $setting[ $id ] ) ) {
$ids[ $id ] = $setting[ $id ];
}
else {
$ids[ $id ] = $v;
}
}
}
self::customMailList( $ids, $args );
self::debug( $ids, __FUNCTION__ );
$data[ $nid ] = $ids;
return $ids;
}
private static function debug( $msg, $f )
{
if ( self::debug ) {
SPConfig::debOut( '[ ' . date( DATE_RFC822 ) . ' ] [ ' . $f . ' ] ', false, false, true );
SPConfig::debOut( $msg, false, false, true );
}
}
}