Passed
Push — master ( 40aec1...d8787f )
by Andreas
11:30
created

org_openpsa_notifications::notify()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 43
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.4227

Importance

Changes 0
Metric Value
cc 6
eloc 22
c 0
b 0
f 0
nc 6
nop 3
dl 0
loc 43
ccs 17
cts 22
cp 0.7727
crap 6.4227
rs 8.9457
1
<?php
2
/**
3
 * @package org.openpsa.notifications
4
 * @author Henri Bergius, http://bergie.iki.fi
5
 * @copyright Nemein Oy, http://www.nemein.com
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use midcom\datamanager\datamanager;
10
use midcom\datamanager\schemadb;
11
use Doctrine\ORM\Query\Expr\Join;
12
13
/**
14
 * Class for notifying users of different events. Usage is reasonably straightforward:
15
 *
16
 * <code>
17
 * // Populate the message
18
 * $message = [];
19
 *
20
 * // Add content for long notification formats (email and RSS)
21
 * $message['title'] = 'Something has happened';
22
 * $message['content'] = 'Somebody did something...';
23
 *
24
 * // Add content for short notification formats (XMPP, SMS, Growl)
25
 * $message['abstract'] = 'Somebody did something';
26
 *
27
 * // Send it
28
 * org_openpsa_notifications::notify('net.example.component:some_action', $recipient_guid, $message);
29
 * </code>
30
 *
31
 * If you want users to take some action related to the message, it is a good idea to include
32
 * URLs in the message.
33
 *
34
 * @package org.openpsa.notifications
35
 */
36
class org_openpsa_notifications
37
{
38
    use midcom_baseclasses_components_base;
39
40
    /**
41
     * Sends a notice to a selected person
42
     *
43
     * @param string $component_action Key of the event in format component:event
44
     * @param string $recipient GUID of the receiving person
45
     * @param array $message Notification message in array format
46
     */
47 8
    public static function notify(string $component_action, string $recipient, array $message)
48
    {
49
        // Parse action to component and action
50 8
        $action_parts = explode(':', $component_action);
51 8
        if (count($action_parts) != 2) {
52
            return false;
53
        }
54
55 8
        [$component, $action] = $action_parts;
56
57
        // TODO: Should we sudo here to ensure getting correct prefs regardless of ACLs?
58
        try {
59 8
            $recipient = midcom_db_person::get_cached($recipient);
60
        } catch (midcom_error) {
61
            return false;
62
        }
63
64
        // Find in which ways to notify the user
65 8
        $notification_type = self::_merge_notification_preferences($component, $action, $recipient);
66 8
        if ($notification_type == 'none') {
67
            // User doesn't wish to be notified
68
            return true;
69
        }
70
71
        // Add the action to the message
72 8
        $message['action'] = $component_action;
73
74
        // Figure out notification rendering handler
75 8
        $class_name = 'org_openpsa_notifications_notifier_' . $notification_type;
76 8
        if (!class_exists($class_name)) {
77
            return false;
78
        }
79 8
        $notifier = new $class_name();
80
81
        // Send the type requested by user
82 8
        debug_add("Notifying {$recipient->guid} with type {$notification_type}");
83
84 8
        $ret = $notifier->send($recipient, $message);
85 8
        if ($ret) {
86 1
            $l10n = midcom::get()->i18n->get_l10n('org.openpsa.notifications');
87 1
            midcom::get()->uimessages->add($l10n->get('org.openpsa.notifications'), sprintf($l10n->get('notification sent to %s'), $recipient->name));
88
        }
89 8
        return $ret;
90
    }
91
92
    /**
93
     * Find out how a person prefers to get the event notification
94
     *
95
     * @return string option supported by user
96
     */
97 8
    private static function _merge_notification_preferences(string $component, string $action, midcom_db_person $recipient)
98
    {
99 8
        $preference = 'none';
100
101
        // If user has preference for this message, we use that
102 8
        $personal_preferences = $recipient->list_parameters('org.openpsa.notifications');
103 8
        if (   !empty($personal_preferences)
104 8
            && array_key_exists("{$component}:{$action}", $personal_preferences)) {
105
            return $personal_preferences[$action];
106
        }
107
108
        // Seek possible preferences for this action from user's groups
109 8
        $qb = new midgard_query_builder('midgard_parameter');
110 8
        $qb->get_doctrine()
111 8
            ->leftJoin('midgard_group', 'g', Join::WITH, 'g.guid = c.parentguid')
112 8
            ->leftJoin('midgard_member', 'm', Join::WITH, 'm.gid = g.id')
113 8
            ->where('m.uid = :uid')
114 8
            ->setParameter('uid', $recipient->id);
115
116 8
        $qb->add_constraint('domain', '=', 'org.openpsa.notifications');
117 8
        $qb->add_constraint('name', '=', "{$component}:{$action}");
118 8
        $group_preferences = $qb->execute();
119
120 8
        if (!empty($group_preferences)) {
121
            return $group_preferences[0]->value;
122
        }
123
124
        // Fall back to component defaults
125 8
        $customdata = midcom::get()->componentloader->get_all_manifest_customdata('org.openpsa.notifications');
126 8
        if (!empty($customdata[$component][$action]['default'])) {
127 8
            $preference = $customdata[$component][$action]['default'];
128
        }
129
130 8
        return $preference;
131
    }
132
133 3
    public function load_datamanager() : datamanager
134
    {
135 3
        $schema = [
136 3
            'description' => 'notifications',
137 3
            'fields'      => []
138 3
        ];
139 3
        $notifiers = $this->_list_notifiers();
140
141
        // Load actions of various components
142 3
        $customdata = midcom::get()->componentloader->get_all_manifest_customdata('org.openpsa.notifications');
143
144 3
        foreach ($customdata as $component => $actions) {
145 3
            $i = 0;
146 3
            $total = count($actions);
147 3
            foreach ($actions as $action => $settings) {
148 3
                $action_key = "{$component}:{$action}";
149 3
                $field_config = [
150 3
                    'title'   => $this->_i18n->get_string("action {$action}", $component),
151 3
                    'storage' => [
152 3
                        'location' => 'configuration',
153 3
                        'domain'   => 'org.openpsa.notifications',
154 3
                        'name'     => $action_key,
155 3
                    ],
156 3
                    'type'    => 'select',
157 3
                    'widget'  => 'radiocheckselect',
158 3
                    'type_config' => [
159 3
                        'options' => $notifiers,
160 3
                    ],
161 3
                ];
162 3
                if (!empty($settings['default'])) {
163 3
                    $field_config['default'] = $settings['default'];
164
                }
165 3
                if ($i == 0) {
166 3
                    $field_config['start_fieldset'] = [
167 3
                        'title' => $this->_i18n->get_string($component, $component),
168 3
                        'css_group' => 'area',
169 3
                    ];
170
                }
171 3
                if (++$i == $total) {
172 3
                    $field_config['end_fieldset'] = '';
173
                }
174 3
                $schema['fields'][str_replace([':', '.'], '_', $action_key)] = $field_config;
175
            }
176
        }
177 3
        return new datamanager(new schemadb(['default' => $schema]));
178
    }
179
180 3
    private function _list_notifiers() : array
181
    {
182
        // TODO: Figure out which notifiers are possible
183 3
        return [
184 3
            ''         => $this->_l10n->get('inherit'),
185 3
            'none'     => $this->_l10n->get('none'),
186 3
            'email'    => $this->_l10n->get('email'),
187 3
        ];
188
    }
189
}
190