Coder Social home page Coder Social logo

notify-plugin's Introduction

Notification engine

Plugin is currently in Beta status. Proceed with caution.

Adds support for sending notifications across a variety of different channels, including mail, SMS and Slack.

Notifications are managed in the back-end area by navigating to Settings > Notification rules.

Notification workflow

When a notification fires, it uses the following workflow:

  1. Plugin registers associated actions, conditions and events using registerNotificationRules
  2. A notification class is bound to a system event using Notifier::bindEvent
  3. A system event is fired Event::fire
  4. The parameters of the event are captured, along with any global context parameters
  5. A command is pushed on the queue to process the notification Queue::push
  6. The command finds all notification rules using the notification class and triggers them
  7. The notification conditions are checked and only proceed if met
  8. The notification actions are triggered

Here is an example of a plugin registering notification rules. The groups definition will create containers that are used to better organise events. The presets definition specifies notification rules defined by the system.

public function registerNotificationRules()
{
    return [
        'events' => [
            \RainLab\User\NotifyRules\UserActivatedEvent::class,
        ],
        'actions' => [
            \RainLab\User\NotifyRules\SaveToDatabaseAction::class,
        ],
        'conditions' => [
            \RainLab\User\NotifyRules\UserAttributeCondition::class
        ],
        'groups' => [
            'user' => [
                'label' => 'User',
                'icon' => 'icon-user'
            ],
        ],
        'presets' => '$/rainlab/user/config/notify_presets.yaml',
    ];
}

Here is an example of triggering a notification. The system event rainlab.user.activate is bound to the UserActivatedEvent class.

// Bind to a system event
\RainLab\Notify\Classes\Notifier::bindEvents([
    'rainlab.user.activate' => \RainLab\User\NotifyRules\UserActivatedEvent::class
]);

// Fire the system event
Event::fire('rainlab.user.activate', [$this]);

Here is an example of registering context parameters, which are available globally to all notifications.

\RainLab\Notify\Classes\Notifier::instance()->registerCallback(function($manager) {
    $manager->registerGlobalParams([
        'user' => Auth::getUser()
    ]);
});

Here is an example of an event preset:

# ===================================
#  Event Presets
# ===================================

welcome_email:
    name: Send welcome email to user
    event: RainLab\User\NotifyRules\UserRegisteredEvent
    items:
        - action: RainLab\Notify\NotifyRules\SendMailTemplateAction
          mail_template: rainlab.user::mail.welcome
          send_to_mode: user
    conditions:
        - condition: RainLab\Notify\NotifyRules\ExecutionContextCondition
          subcondition: environment
          operator: is
          value: dev
          condition_text: Application environment <span class="operator">is</span> dev

Creating Event classes

An event class is responsible for preparing the parameters passed to the conditions and actions. The static method makeParamsFromEvent will take the arguments provided by the system event and convert them in to parameters.

class UserActivatedEvent extends \RainLab\Notify\Classes\EventBase
{
    /**
     * @var array Local conditions supported by this event.
        */
    public $conditions = [
        \RainLab\User\NotifyRules\UserAttributeCondition::class
    ];

    /**
     * Returns information about this event, including name and description.
     */
    public function eventDetails()
    {
        return [
            'name'        => 'Activated',
            'description' => 'A user is activated',
            'group'       => 'user'
        ];
    }

    /**
     * Defines the usable parameters provided by this class.
     */
    public function defineParams()
    {
        return [
            'name' => [
                'title' => 'Name',
                'label' => 'Name of the user',
            ],
            // ...
        ];
    }

    public static function makeParamsFromEvent(array $args, $eventName = null)
    {
        return [
            'user' => array_get($args, 0)
        ];
    }
}

Creating Action classes

Action classes define the final step in a notification and subsequently perform the notification itself. Some examples might be sending and email or writing to the database.

class SendMailTemplateAction extends \RainLab\Notify\Classes\ActionBase
{
    /**
     * Returns information about this event, including name and description.
     */
    public function actionDetails()
    {
        return [
            'name'        => 'Compose a mail message',
            'description' => 'Send a message to a recipient',
            'icon'        => 'icon-envelope'
        ];
    }

    /**
     * Field configuration for the action.
     */
    public function defineFormFields()
    {
        return 'fields.yaml';
    }

    public function getText()
    {
        $template = $this->host->template_name;

        return 'Send a message using '.$template;
    }

    /**
     * Triggers this action.
     * @param array $params
        * @return void
        */
    public function triggerAction($params)
    {
        $email = '[email protected]';
        $template = $this->host->template_name;

        Mail::sendTo($email, $template, $params);
    }
}

A form fields definition file is used to provide form fields when the action is established. These values are accessed from condition using the host model via the $this->host property.

# ===================================
#  Field Definitions
# ===================================

fields:

    template_name:
        label: Template name
        type: text

An action may choose to provide no form fields by simply returning false from the defineFormFields method.

public function defineFormFields()
{
    return false;
}

Creating Condition classes

A condition class should specify how it should appear in the user interface, providing a name, title and summary text. It also must declare an isTrue method for evaluating whether the condition is true or not.

class MyCondition extends \RainLab\Notify\Classes\ConditionBase
{
    /**
     * Return either ConditionBase::TYPE_ANY or ConditionBase::TYPE_LOCAL
     */
    public function getConditionType()
    {
        // If the condition should appear for all events
        return ConditionBase::TYPE_ANY;

        // If the condition should appear only for some events
        return ConditionBase::TYPE_LOCAL;
    }

    /**
     * Field configuration for the condition.
     */
    public function defineFormFields()
    {
        return 'fields.yaml';
    }

    public function getName()
    {
        return 'My condition is checked';
    }

    public function getTitle()
    {
        return 'My condition';
    }

    public function getText()
    {
        $value = $this->host->mycondition;

        return 'My condition <span class="operator">is</span> '.$value;
    }

    /**
     * Checks whether the condition is TRUE for specified parameters
     * @param array $params
        * @return bool
        */
    public function isTrue(&$params)
    {
        return true;
    }
}

A form fields definition file is used to provide form fields when the condition is established. These values are accessed from condition using the host model via the $this->host property.

# ===================================
#  Field Definitions
# ===================================

fields:

    mycondition:
        label: My condition
        type: dropdown
        options:
            true: True
            false: False

Model attribute condition classes

Model attribute conditions are designed specially for applying conditions to sets of model attributes.

class UserAttributeCondition extends \RainLab\Notify\Classes\ModelAttributesConditionBase
{
    protected $modelClass = \RainLab\User\Models\User::class;

    public function getGroupingTitle()
    {
        return 'User attribute';
    }

    public function getTitle()
    {
        return 'User attribute';
    }

    /**
     * Checks whether the condition is TRUE for specified parameters
     * @param array $params Specifies a list of parameters as an associative array.
        * @return bool
        */
    public function isTrue(&$params)
    {
        $hostObj = $this->host;

        $attribute = $hostObj->subcondition;

        if (!$user = array_get($params, 'user')) {
            throw new ApplicationException('Error evaluating the user attribute condition: the user object is not found in the condition parameters.');
        }

        return parent::evalIsTrue($user);
    }
}

An attributes definition file is used to specify which attributes should be included in the condition.

# ===================================
#  Condition Attribute Definitions
# ===================================

attributes:

    name:
        label: Name

    email:
        label: Email address

    country:
        label: Country
        type: relation
        relation:
            model: RainLab\Location\Models\Country
            label: Name
            nameFrom: name
            keyFrom: id

Save to database action

There is a dedicated table in the database for storing events and their parameters. This table is accessed using the RainLab\Notify\Models\Notification model and can be referenced as a relation from your own models. In this example the MyProject model contains its own notification channel called notifications.

class MyProject extends Model
{
    // ...

    public $morphMany = [
        'my_notifications' => [
            \RainLab\Notify\Models\Notification::class,
            'name' => 'notifiable'
        ]
    ];
}

This channel should be registered with the RainLab\Notify\NotifyRules\SaveDatabaseAction so it appears as a related object when selecting the action.

SaveDatabaseAction::extend(function ($action) {
    $action->addTableDefinition([
        'label'    => 'Project activity',
        'class'    => MyProject::class,
        'relation' => 'my_notifications',
        'param'    => 'project'
    ]);
});

The label is shown as the related object, the class references the model class, the relation refers to the relation name. The param defines the parameter name, passed to the triggering event.

So essentially if you pass a project to the event parameters, or if project is a global parameter, a notification model is created with the parameters stored in the data attribute. Equivalent to the following code:

$myproject->my_notifications()->create([
    // ...
    'data' => $params
]);

Dynamically adding conditions to events

Events can be extended to include new local conditions. Simply add the condition class to the event $conditions array property.

UserActivatedEvent::extend(function($event) {
    $event->conditions[] = \RainLab\UserPlus\NotifyRules\UserLocationAttributeCondition::class;
});

notify-plugin's People

Contributors

bennothommo avatar blazorazem avatar daftspunk avatar frthjf avatar jumbophp avatar leocantthinkofaname avatar luketowers avatar mahony0 avatar petehalverson avatar prhost avatar samgeorges avatar sikhub avatar webmaxx avatar zhiweiwu0425 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notify-plugin's Issues

ExecutionContextCondition not evaluating 'theme' and 'context' conditions?

Hi sorry if I'm mistaken about this, but it seems like ExecutionContextCondition is not evaluating either the theme or context options.

Here is a copy of the current isTrue function for that:

    public function isTrue(&$params)
    {
        $hostObj = $this->host;
        $attribute = $hostObj->subcondition;

        $conditionValue = $hostObj->value;
        $conditionValue = trim(mb_strtolower($conditionValue));

        if ($attribute == 'locale') {
            return array_get($params, 'appLocale') == $conditionValue;
        } else if ($attribute === 'environment') {
            return $conditionValue === \App::environment();
        }

        return false;
    }

I see the context variables isBackend and isConsole are made available in Notifier.php, which could probably be used for evaluating the context at least...

Or if I missed something please let me know!

EDIT: I do have a patched version of this condition that is working for me if you are open to a PR

[Feature Request] October Backend channel

I see that the goal of this plugin is to streamline the way notifications are delivered which is great. In addition to doing that, I think there would be a benefit to adding a Backend channel out of the box when this plugin is installed. Basically a way by which a user can set up a way by which notifications are delivered to other backend users based on events occurring and this can show up in the top right as a bell icon with numbered badges showing the number of notifications.

Can't "Compose an email message" when "A user is registered"

Hello,

I wanted to use notify to compose an email message when a user is registered. when when I choose the "compose an email message action" I get an error :

"htmlspecialchars() expects parameter 1 to be string, array given" on line 580 of /vendor/laravel/framework/src/Illuminate/Support/helpers.php

With october 471 with all modules up to dates.

Regards,

Alex

I need more examples

Hello.

Have you got any more examples of how to use RainLab.Notify, please?

I'm interested in creating my own notifications, but you have an example for users only. I am confused and don`t understand anything.

Where can I see examples of how to write notifications from scratch using your plugin?

QUESTION - Help with Event::fire and sending email

In my plugin on boot()

I have added:

    public function boot()
    {
        \RainLab\Notify\Classes\Notifier::bindEvents([
            'rainlab.user.register' => \RainLab\User\NotifyRules\UserRegisteredEvent::class
        ]);
        \RainLab\Notify\Classes\Notifier::instance()->registerCallback(function ($manager) {
            $manager->registerGlobalParams([
                'user' => Auth::getUser()
            ]);
        });
    }

in my component, I have added

Event::fire('rainlab.user.register', [$user, $data]);

But nothings happens,

How I can debug where could be the problem?

Undefined class constant 'CATEGORY_NOTIFICATIONS'

I just saw this plugin in my backend as "recommendation" so i clicked on it and voila: my backend does not work anymore and throws this error:
Undefined class constant 'CATEGORY_NOTIFICATIONS'

public function registerSettings()
{
    return [
        'notifications' => [
            'label'       => 'rainlab.notify::lang.notifications.menu_label',
            'description' => 'rainlab.notify::lang.notifications.menu_description',
            'category'    => SettingsManager::CATEGORY_NOTIFICATIONS,
            'icon'        => 'icon-bullhorn',
            'url'         => Backend::url('rainlab/notify/notifications'),
            'permissions' => ['rainlab.notify.manage_notifications'],
            'order'       => 600
        ],
    ];

Error on new notification rule creation

Hello!
I've just installed the notify plugin and I tried to create a new notification rule.

On actions, if I choose the "Store in database" option I get an "Invalid argument supplied for foreach()" on line 89 of /plugins/rainlab/notify/notifyrules/SaveDatabaseAction.php error.

If I choose the "Compose a mail message", everything work as expected.

Plugin interface not showing up in the settings area

Hi Guys,

I recently installed the Notify Plugin and it says that the plugin will be located in Settings > Notifications. However, I do not see any menu item related to notify in my settings panel (neither on the top bar). I checked my the folder structure, and the notify plugin does exist within the Plugins > RainLab > Notify but I cannot locate in my setting panel.

Am I missing something ? Please Help

How set host type in SaveDatabaseAction

Hello,

I don't know if this is a channel for this, but I'm having a problem with the SaveDatabaseAction action, it has a field called type that it takes from the host and saves, but always saves null, I would like to define this type through my event. It would be possible? the only place I had access to the host was in the construct of the event, but even so it is not affected.

Validation error when saving a notification with action with validation

Hello,

When there is an Action with rules, the action is saved when clicking on save in its modal, but when saving the complete notification, it presents a validation error of the action rules again, as if it had not been saved or going through POST along with the saving of the notification.

In v1 this does not happen. The error is giving in v3.

It's also showing an error in action.js, I don't know if it's related, when saving and closing the action modal, before saving the notification, it accuses this error in the console:

Screenshot 2022-09-16 at 17 27 51

https://github.com/rainlab/notify-plugin/blob/master/formwidgets/actionbuilder/assets/js/actions.js#L85-L87

Creates duplicates every time you view settings

Every time I click on Notification Rules in backend Settings, it creates another record for "Send welcome email to user". Even clicking on one of the rules and deleting it doesn't help because the moment the screen goes back to the list another record is generated.

Action class with repeater type field

Hello,
How can make an action class with a repeater type field?

"Unexpected type of array when attempting to save attribute "repeater_field", try adding it to the $jsonable property." on line 751 of /site/vendor/october/rain/src/Database/Model.php

Conditions not saving the value field

The ConditionBuilder formwidget changes the $_POST superglobal in L351 and L356 but due to this change in https://github.com/octobercms/library, the $_POST superglobal can't be changed directly anymore. So, the value field is not saved. This could be changed here too in order to fix it:

Insted of:

$_POST['condition_data'] = $cache;

Now should be:

$requestData = Request::all();
array_set($requestData, 'condition_data' , $cache);
Request::merge($requestData);        

Manage timing of notifications

I would be good if there was an option to manage the timing of notifications/actions. For instance, sending a reminder 1 month after a user signed up etc.

MIT License?

I'm assuming this is an MIT license like most of the RainLab plugins. Please confirm.

I cannot save notification, i get "The mail template field is required"

Hello,

When i try to save notification rule from 'User' -> 'Is activated' -> Compose a mail message, i choose to send notification to all administrators, i save the rule, and when i save the notification, i get error "The mail template field is required",

When i return to the "Compose a mail message" form, i don't find the data stored.

Hello.

ActionBuilder widget does not remember state of previous actions

When creating a list of actions, it seems that once a new action is added to the list, the previous actions do not remember their selected state. I can't determine yet if this is just in terms of generating the title, or if it is indeed forgetting the state altogether.

Example

1st Action added.

Screenshot_2020-07-13 New Notification rule myStockz

2nd Action added. (note that the first action doesn't have the text anymore, which in this instance is because the field you enter for that action is now null).

Screenshot_2020-07-13 New Notification rule myStockz(1)

3rd Action added. (note that the second action's text has changed)

Screenshot_2020-07-13 New Notification rule myStockz(2)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.