Skip to content

Instantly share code, notes, and snippets.

@peterdm
Created March 16, 2012 16:53
Show Gist options
  • Save peterdm/2051081 to your computer and use it in GitHub Desktop.
Save peterdm/2051081 to your computer and use it in GitHub Desktop.

Revisions

  1. peterdm revised this gist Mar 16, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion observable_model.php
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@ abstract public function update($observable, $event, &$data);
    }

    /**
    * Abstract observable class to implement the observer design pattern
    * Abstract observable superclass (observers attach to this)
    *
    * 1) Each ObservableModel subclass maintains a list of Observers listening for specific events.
    *
  2. peterdm created this gist Mar 16, 2012.
    151 changes: 151 additions & 0 deletions notifier.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,151 @@
    <?php
    uses_model('top/user_object');
    uses_model('message/message');
    uses_model('notifications/notification_queue');
    uses_model('notifications/subscription');

    /**
    * Notifier is one of several Observers. Each Observer takes action across a particular channel.
    * Notifier queues messages for delivery to users' mailboxes within the social network.
    *
    * (Other observers: Twitterer, ActivityLogger, Flasher)
    */
    class Notifier extends Observer {

    /*
    * Update - Implementation of the abstract method defined by the Observer class
    */
    public function update($object, $event, &$data) {
    trace('Observer', 'Notifier called with ' . get_class($object) . '/' . $event);

    $subscription = $this->get_subscription($object);
    $data['subscription'] = $subscription;

    if ((!$subscription || $subscription->notification_type_id == 1) && method_exists($this, $event)) {
    return $this->{$event}($object, $data);
    }

    }


    /*
    * Subscriptions are unique to Notifier -- allows users to opt-in/out of various event notifications
    */
    protected function get_subscription($object) {
    if (!isset($this->params['subscription']))
    return null; // no restriction, no worries

    if ($object instanceof Profile)
    $recipient = $object;
    elseif ($object->owner instanceof Profile)
    $recipient = $object->owner;
    else
    return null; // we only store these notification settings for users at this point

    // Check to see what this recipient has chosen for the preference named in the eventmap
    return $recipient->get_subscription_for_text_id($this->params['subscription']);
    }



    /* --------------- CUSTOM NOTIFICATION HANDLERS --------------- */


    /*
    * Construct/queue a message alerting user to the arrival of a new message
    */
    private function new_mail($object, &$data) {
    if ($object instanceof MailboxMessage) {
    $recipient_id = $object->to_object_id;
    $sender_id = $object->message->profile_id;
    $param_1 = $object->id; // the mailboxmessage id

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1);
    }
    }

    /*
    * Construct/queue a message alerting user that another user wants to connect
    */
    private function connection_invite($object, &$data) {
    if ($object instanceof MailboxMessage) {
    $recipient_id = $object->to_object_id;
    $sender_id = $object->message->profile_id;
    $param_1 = $object->id; // the mailboxmessage id

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1);
    }
    }

    /*
    * Construct/queue a message alerting user that another user has invited them to collaborate on a project
    */
    private function project_invite($object, &$data) {
    if ($object instanceof MailboxMessage) {
    $recipient_id = $object->to_object_id;
    $sender_id = $object->message->profile_id;
    $param_1 = $object->id; // the mailboxmessage id

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1);
    }
    }

    /*
    * Construct/queue a message alerting user that someone has applied to their job posting
    */
    private function job_application($object, &$data) {
    if ($object instanceof MailboxMessage) {
    $recipient_id = $object->to_object_id;
    $sender_id = $object->message->profile_id;
    $param_1 = $object->mailbox->object_id; // the cast or crew listing
    $param_2 = $object->id;

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1, $param_2);
    }
    }

    /*
    * Construct/queue a message alerting user another user has commented on an item of theirs
    */
    private function comment($object, &$data) {
    $comment = $data['comment'];

    $recipient_id = $object->profile_id; // the owner of the object being commented on
    $sender_id = $comment->profile_id;
    $param_1 = $comment->id;
    $param_2 = $object->id; // the id of the object being commented on

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1, $param_2);
    }

    /*
    * Construct/queue a message alerting user another user has reviewed an item of theirs
    */
    private function review($object, &$data) {
    $review = $data['review'];

    $recipient_id = $object->profile_id; // the owner of the object being reviewed
    $sender_id = $review->profile_id;
    $param_1 = $review->id;
    $param_2 = $object->id; // the id of the object being reviewed

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1, $param_2);
    }

    /*
    * Construct/queue a message alerting user another user has accepted their invitation
    */
    private function accepted($object, &$data) {
    if ($object instanceof Invite) {
    $invitee = $data['invitee'];
    $invitor = $data['invitor'];

    $recipient_id = $invitor->id; // we're actually notifying the sender of the invite that it was accepted
    $sender_id = $invitee->id;
    $param_1 = $object->id;

    NotificationQueue::AddNotification($this->params['subscription'], $recipient_id, $sender_id, $param_1);

    }
    }
    }
    224 changes: 224 additions & 0 deletions observable_model.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,224 @@
    <?php

    /**
    * Observer class - An Observer receives update messages from ObservableModel instances
    * via the ->notify($event, $data) method
    */
    abstract class Observer {
    protected $params = array();

    public function __construct($params = null) {
    if ($params)
    $this->params = $params;
    }

    abstract public function update($observable, $event, &$data);
    }

    /**
    * Abstract observable class to implement the observer design pattern
    *
    * 1) Each ObservableModel subclass maintains a list of Observers listening for specific events.
    *
    * 2) When an instance of ObservableModel calls it's ->notify($event) method, all Observers
    * registered to listen for this particular event receive a callback to their ->update() method.
    *
    */
    class ObservableModel extends Model {
    /**
    * An array of Observer objects to notify
    *
    * @access private
    * @var array
    */
    var $_observers = array();


    /**
    * Constructor
    */
    function __construct($id = null, $fields = null, $db = null, $row = null, $cacheable = false) {
    $this->_observers = array();

    // Load observers from observers.conf (e.g. ActivityLogger, Notifier, Messenger)
    $config = Config::Get('observers/eventmap');

    /* Eventmap format
    * ---------------
    *
    * <class_name>: (use instanceof to check so we can include superclasses here)
    * - event: <e.g. post_create, accept, ignore, decline>
    * observers:
    * - class: <e.g. ActivityLogger, Notifier, Twitterer>
    * params: {<optional map of any parameters we want to pass>}
    *
    *
    * Sample mapping
    * --------------
    * Project:
    * - event: rate
    * observers:
    * - class: ActivityLogger
    *
    * - event: comment
    * observers:
    * - class: Notifier
    * params: {subscription: itemcomment}
    *
    * - class: ActivityLogger
    *
    * #
    * # This sample config will:
    * # - Trigger a post on the Project owner's "activity log" (wall) whenever
    * # someone rates or comments on their project.
    * #
    * # - Send a message to the Project owner's mailbox with the comment
    * # provided they're opted into this type of alert.
    * #
    */

    // autoload observers
    $conf = Config::Get('observers');
    if ($conf->auto) {
    foreach ($conf->auto->items as $obs)
    uses("observer.$obs");

    foreach ($config->items as $observable_class => $event_observers) {
    if ($this instanceof $observable_class) {
    foreach ($event_observers->items as $event) {
    foreach ($event->observers->items as $item) {
    $params = $item->params;
    $parameters = ($params) ? $params->items : null;

    $instance = new $item->class($parameters);
    $this->attach($instance, $event->event);
    }
    }
    }
    }
    }


    parent::__construct($id, $fields, $db, $row, $cacheable);
    }



    /**
    * Update each attached observer object and return an array of their return values
    *
    * @access public
    * @return array Array of return values from the observers
    */
    function notify($event, $data = null) {
    $return = array();
    $data = ($data) ? $data : array();

    // Iterate through the _observers array
    if (isset($this->_observers[$event])) {
    foreach ($this->_observers[$event] as $observer) {
    $return[] = $observer->update($this, $event, $data);
    }
    }

    return $return;
    }

    /**
    * Attach an observer object
    *
    * @access public
    * @param object $observer An observer object to attach
    * @return void
    */
    function attach(&$observer, $event) {
    trace(get_class($this), 'Attached observer ' . get_class($observer) . ' for event ' . $event);

    // Make sure we haven't already attached this object as an observer
    if (is_object($observer)) {
    $class = get_class($observer);


    if (isset($this->_observers[$event]))
    foreach ($this->_observers[$event] as $check)
    if (is_a($check, $class))
    return;

    if (!isset($this->_observers[$event]))
    $this->_observers[$event] = array();

    $this->_observers[$event][] = $observer;
    }
    }

    /**
    * Detach an observer object
    *
    * @access public
    * @param object $observer An observer object to detach
    * @return boolean True if the observer object was detached
    */
    function detach($observer, $event) {
    // Initialize variables
    $retval = false;

    if (isset($this->_observers[$event])) {
    $key = array_search($observer, $this->_observers[$event]);

    if ($key !== false) {
    unset($this->_observers[$event][$key]);
    $retval = true;
    }
    }

    return $retval;
    }


    /*
    * The following methods are all events raised during the persistance lifecycle of a Model instance.
    *
    * They are being overridden here in ObservableModel to expose these lifecycle events to Observers
    * that want to listen for them.
    */

    protected function pre_create(&$fields) {
    parent::pre_create($fields);
    $this->notify(__FUNCTION__);
    }

    protected function post_create() {
    parent::post_create();
    $this->notify(__FUNCTION__);
    }

    protected function pre_read() {
    parent::pre_read();
    $this->notify(__FUNCTION__);
    }

    protected function post_read() {
    parent::post_read();
    $this->notify(__FUNCTION__);
    }

    protected function pre_update(&$fields) {
    parent::pre_update($fields);
    $this->notify(__FUNCTION__);
    }

    protected function post_update($fields) {
    parent::post_update($fields);
    $this->notify(__FUNCTION__);
    }

    protected function pre_delete() {
    parent::pre_delete();
    $this->notify(__FUNCTION__);
    }

    protected function post_delete() {
    parent::post_delete();
    $this->notify(__FUNCTION__);
    }
    }