Passed
Push — master ( f6b156...dcf7e9 )
by Andreas
32:47 queued 15:02
created

org_openpsa_widgets_contact   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 419
Duplicated Lines 0 %

Test Coverage

Coverage 34.78%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 208
c 3
b 0
f 0
dl 0
loc 419
ccs 72
cts 207
cp 0.3478
rs 2.4
wmc 75

11 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 21 3
A add_head_elements() 0 7 2
A __construct() 0 14 3
C show() 0 60 10
B read_object() 0 29 10
B _show_groups() 0 32 8
A _render_name() 0 23 6
A get_image() 0 13 5
A _show_phone_number() 0 4 2
A show_inline() 0 24 3
F show_address_card() 0 86 23

How to fix   Complexity   

Complex Class

Complex classes like org_openpsa_widgets_contact often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use org_openpsa_widgets_contact, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Class for rendering person records
4
 *
5
 * Uses the hCard microformat for output.
6
 *
7
 * @author Henri Bergius, http://bergie.iki.fi
8
 * @copyright Nemein Oy, http://www.nemein.com
9
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
10
 * @link http://www.microformats.org/wiki/hcard hCard microformat documentation
11
 * @package org.openpsa.widgets
12
 */
13
14
use midcom\datamanager\storage\blobs;
15
16
/**
17
 * @package org.openpsa.widgets
18
 */
19
class org_openpsa_widgets_contact extends midcom_baseclasses_components_purecode
20
{
21
    /**
22
     * Do we have our contact data ?
23
     */
24
    private $_data_read_ok = false;
25
26
    /**
27
     * Contact information of the person being displayed
28
     */
29
    public $contact_details = [
30
        'guid' => '',
31
        'id' => '',
32
        'firstname' => '',
33
        'lastname' => ''
34
    ];
35
36
    /**
37
     * Optional URI to person details
38
     *
39
     * @var string
40
     */
41
    public $link;
42
43
    /**
44
     * Optional HTML to be placed into the card
45
     *
46
     * @var string
47
     */
48
    public $extra_html;
49
50
    /**
51
     * Optional HTML to be placed into the card (before any other output in the DIV)
52
     *
53
     * @var string
54
     */
55
    public $prefix_html;
56
57
    /**
58
     * Whether to show person's groups in a list
59
     *
60
     * @var boolean
61
     */
62
    public $show_groups = true;
63
64
    /**
65
     * Whether to generate links to the groups using NAP
66
     *
67
     * @var boolean
68
     */
69
    var $link_contacts = true;
70
71
    /**
72
     * Default org.openpsa.contacts URL to be used for linking to groups. Will be autoprobed if not supplied.
73
     *
74
     * @var string
75
     */
76
    private static $_contacts_url;
77
78
    private $person;
79
80
    /**
81
     * Initializes the class and stores the selected person to be shown
82
     */
83 8
    public function __construct(midcom_db_person $person = null)
84
    {
85 8
        parent::__construct();
86
87 8
        if (null === self::$_contacts_url) {
0 ignored issues
show
introduced by
The condition null === self::_contacts_url is always false.
Loading history...
88 1
            $siteconfig = org_openpsa_core_siteconfig::get_instance();
89 1
            self::$_contacts_url = $siteconfig->get_node_full_url('org.openpsa.contacts');
90
        }
91
92
        // Read properties of provided person object
93
        // TODO: Handle groups as well
94 8
        if ($person) {
95 7
            $this->person = $person;
96 7
            $this->_data_read_ok = $this->read_object($person);
97
        }
98 8
    }
99
100 1
    public static function add_head_elements()
101
    {
102 1
        static $added = false;
103 1
        if (!$added) {
104 1
            midcom::get()->head->add_stylesheet(MIDCOM_STATIC_URL . "/org.openpsa.widgets/hcard.css");
105 1
            midcom::get()->head->add_stylesheet(MIDCOM_STATIC_URL . '/stock-icons/font-awesome-4.7.0/css/font-awesome.min.css');
106 1
            $added = true;
107
        }
108 1
    }
109
110
    /**
111
     * Retrieve an object, uses in-request caching
112
     *
113
     * @param mixed $src GUID of object (ids work but are discouraged)
114
     */
115 10
    public static function get($src) : self
116
    {
117 10
        static $cache = [];
118
119 10
        if (isset($cache[$src])) {
120 4
            return $cache[$src];
121
        }
122
123
        try {
124 7
            $person = midcom_db_person::get_cached($src);
125 1
        } catch (midcom_error $e) {
126 1
            $widget = new self();
127 1
            $cache[$src] = $widget;
128 1
            return $widget;
129
        }
130
131 6
        $widget = new self($person);
132
133 6
        $cache[$person->guid] = $widget;
134 6
        $cache[$person->id] = $cache[$person->guid];
135 6
        return $cache[$person->guid];
136
    }
137
138
    /**
139
     * Read properties of a person object and populate local fields accordingly
140
     */
141 7
    private function read_object(midcom_db_person $person)
142
    {
143
        // Database identifiers
144 7
        $this->contact_details['guid'] = $person->guid;
145 7
        $this->contact_details['id'] = $person->id;
146
147 7
        if ($person->guid == "") {
148
            $this->contact_details['lastname'] = $this->_l10n->get('no person');
149 7
        } elseif ($person->firstname == '' && $person->lastname == '') {
150 7
            $this->contact_details['lastname'] = "Person #{$person->id}";
151
        } else {
152
            $this->contact_details['firstname'] = $person->firstname;
153
            $this->contact_details['lastname'] = $person->lastname;
154
        }
155
156 7
        foreach (['handphone', 'workphone', 'homephone', 'email', 'homepage'] as $property) {
157 7
            if ($person->$property) {
158
                $this->contact_details[$property] = $person->$property;
159
            }
160
        }
161
162 7
        if (   $this->_config->get('jabber_enable_presence')
163 7
            && $person->get_parameter('org.openpsa.jabber', 'jid')) {
164
            $this->contact_details['jid'] = $person->get_parameter('org.openpsa.jabber', 'jid');
165
        }
166
167 7
        if (   $this->_config->get('skype_enable_presence')
168 7
            && $person->get_parameter('org.openpsa.skype', 'name')) {
169
            $this->contact_details['skype'] = $person->get_parameter('org.openpsa.skype', 'name');
170
        }
171 7
    }
172
173
    private function _render_name(string $fallback_image) : string
174
    {
175
        $name = "<span class=\"given-name\">{$this->contact_details['firstname']}</span> <span class=\"family-name\">{$this->contact_details['lastname']}</span>";
176
177
        $url = false;
178
179
        if ($this->link) {
180
            $url = $this->link;
181
        } elseif ($this->link_contacts && !empty($this->contact_details['guid'])) {
182
            if (!self::$_contacts_url) {
183
                $this->link_contacts = false;
184
            } else {
185
                $url = self::$_contacts_url . 'person/' . $this->contact_details['guid'] . '/';
186
            }
187
        }
188
189
        $name = $this->get_image('thumbnail', $fallback_image) . $name;
190
191
        if ($url) {
192
            $name = '<a href="' . $url . '">' . $name . '</a>';
193
        }
194
195
        return $name;
196
    }
197
198 1
    public function get_image(string $type, string $fallback) : string
199
    {
200 1
        $attachments = blobs::get_attachments($this->person, 'photo');
201 1
        if (!empty($attachments[$type])) {
202
            return '<img class="photo" src="' . midcom_db_attachment::get_url($attachments[$type]) . '">';
203
        }
204 1
        if (   $this->_config->get('gravatar_enable')
205 1
            && !empty($this->contact_details['email'])) {
206
            $size = ($type == 'view') ? 500 : 32;
207
            $gravatar_url = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->contact_details['email']))) . "?s=" . $size . '&d=identicon';
208
            return "<img src=\"{$gravatar_url}\" class=\"photo\" />\n";
209
        }
210 1
        return '<i class="fa fa-' . $fallback . '"></i>';
211
    }
212
213
    /**
214
     * Show selected person object inline. Outputs hCard XHTML.
215
     */
216 9
    public function show_inline() : string
217
    {
218 9
        if (!$this->_data_read_ok) {
219 9
            return '';
220
        }
221
        self::add_head_elements();
222
        $inline_string = '';
223
224
        // Start the vCard
225
        $inline_string .= "<span class=\"vcard\">";
226
227
        if (!empty($this->contact_details['guid'])) {
228
            // Identifier
229
            $inline_string .= "<span class=\"uid\" style=\"display: none;\">{$this->contact_details['guid']}</span>";
230
        }
231
232
        // The name sequence
233
        $inline_string .= "<span class=\"n\">";
234
        $inline_string .= $this->_render_name('address-card-o');
235
        $inline_string .= "</span>";
236
237
        $inline_string .= "</span>";
238
239
        return $inline_string;
240
    }
241
242
    /**
243
     * Show the selected person. Outputs hCard XHTML.
244
     */
245 2
    public function show()
246
    {
247 2
        if (!$this->_data_read_ok) {
248 2
            return false;
249
        }
250
        self::add_head_elements();
251
        // Start the vCard
252
        echo "<div class=\"vcard\" id=\"org_openpsa_widgets_contact-{$this->contact_details['guid']}\">\n";
253
        if ($this->prefix_html) {
254
            echo $this->prefix_html;
255
        }
256
257
        if (!empty($this->contact_details['guid'])) {
258
            // Identifier
259
            echo "<span class=\"uid\" style=\"display: none;\">{$this->contact_details['guid']}</span>";
260
        }
261
262
        // The Name sequence
263
        echo "<div class=\"n\">\n";
264
        echo $this->_render_name('user-o');
265
        echo "</div>\n";
266
267
        // Contact information sequence
268
        echo "<ul class=\"contact_information\">\n";
269
        if ($this->extra_html) {
270
            echo $this->extra_html;
271
        }
272
273
        $this->_show_groups();
274
275
        $this->_show_phone_number('handphone', 'mobile');
276
        $this->_show_phone_number('workphone', 'phone');
277
        $this->_show_phone_number('homephone', 'home');
278
279
        if (!empty($this->contact_details['email'])) {
280
            echo "<li><a title=\"{$this->contact_details['email']}\" href=\"mailto:{$this->contact_details['email']}\"><i class=\"fa fa-envelope-o\"></i>{$this->contact_details['email']}</a></li>\n";
281
        }
282
283
        if (!empty($this->contact_details['skype'])) {
284
            echo "<li>";
285
            echo "<a href=\"skype:{$this->contact_details['skype']}?call\"><i class=\"fa fa-skype\"></i>{$this->contact_details['skype']}</a></li>\n";
286
        }
287
288
        // Instant messaging contact information
289
        if (!empty($this->contact_details['jid'])) {
290
            echo "<li>";
291
            echo "<a href=\"xmpp:{$this->contact_details['jid']}\"";
292
            $edgar_url = $this->_config->get('jabber_edgar_url');
293
            if (!empty($edgar_url)) {
294
                echo " style=\"background-repeat: no-repeat;background-image: url('{$edgar_url}?jid={$this->contact_details['jid']}&type=image');\"";
295
            }
296
            echo "><i class=\"fa fa-comment-o\"></i>{$this->contact_details['jid']}</a></li>\n";
297
        }
298
299
        if (!empty($this->contact_details['homepage'])) {
300
            echo "<li><a title=\"{$this->contact_details['homepage']}\" href=\"{$this->contact_details['homepage']}\"><i class=\"fa fa-globe\"></i>{$this->contact_details['homepage']}</a></li>\n";
301
        }
302
303
        echo "</ul>\n";
304
        echo "</div>\n";
305
    }
306
307
    private function _show_phone_number(string $field, string $type)
308
    {
309
        if (!empty($this->contact_details[$field])) {
310
            echo "<li><a title=\"Dial {$this->contact_details[$field]}\" href=\"tel:{$this->contact_details[$field]}\"><i class=\"fa fa-{$type}\"></i>{$this->contact_details[$field]}</a></li>\n";
311
        }
312
    }
313
314
    private function _show_groups()
315
    {
316
        if (   !$this->show_groups
317
            || empty($this->contact_details['id'])) {
318
            return;
319
        }
320
        $link_contacts = $this->link_contacts && self::$_contacts_url;
321
322
        $mc = org_openpsa_contacts_member_dba::new_collector('uid', $this->contact_details['id']);
323
        $mc->add_constraint('gid.orgOpenpsaObtype', '>=', org_openpsa_contacts_group_dba::ORGANIZATION);
324
325
        foreach ($mc->get_rows(['gid', 'extra']) as $data) {
326
            try {
327
                $group = org_openpsa_contacts_group_dba::get_cached($data['gid']);
328
            } catch (midcom_error $e) {
329
                $e->log();
330
                continue;
331
            }
332
333
            echo "<li>";
334
335
            if ($data['extra']) {
336
                echo "<span class=\"title\">" . htmlspecialchars($data['extra']) . "</span>, ";
337
            }
338
339
            $group_label = htmlspecialchars($group->get_label());
0 ignored issues
show
Bug introduced by
The method get_label() does not exist on midcom_core_dbaobject. It seems like you code against a sub-type of midcom_core_dbaobject such as org_openpsa_calendar_event_dba or org_openpsa_invoices_invoice_dba or net_nemein_tag_tag_dba or org_openpsa_invoices_invoice_item_dba or midcom_db_parameter or org_openpsa_calendar_event_member_dba or org_openpsa_sales_salesproject_offer_dba or midcom_db_topic or net_nemein_tag_link_dba or org_openpsa_invoices_billing_data_dba or org_openpsa_directmarketing_campaign_member_dba or org_openpsa_contacts_group_dba or midcom_db_person or midcom_db_member or org_openpsa_projects_project or org_openpsa_calendar_event_resource_dba or midcom_db_group or org_openpsa_projects_task_dba or org_openpsa_documents_document_dba. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

339
            $group_label = htmlspecialchars($group->/** @scrutinizer ignore-call */ get_label());
Loading history...
340
            if ($link_contacts) {
341
                $group_label = "<a href=\"" . self::$_contacts_url . "group/{$group->guid}/\"><i class=\"fa fa-users\"></i>" . $group_label . '</a>';
342
            }
343
344
            echo "<span class=\"organization-name\">{$group_label}</span>";
345
            echo "</li>\n";
346
        }
347
    }
348
349
    /**
350
     * Renderer for organization address cards
351
     */
352 1
    public static function show_address_card($customer, array $cards)
353
    {
354 1
        $cards_to_show = [];
355 1
        $multiple_addresses = false;
356 1
        $inherited_cards_only = true;
357 1
        $default_shown = false;
358 1
        $siteconfig = org_openpsa_core_siteconfig::get_instance();
359 1
        $contacts_url = $siteconfig->get_node_full_url('org.openpsa.contacts');
360
361 1
        foreach ($cards as $cardname) {
362 1
            if ($cardname == 'visiting') {
363 1
                if ($customer->street) {
364
                    $default_shown = true;
365
                    $cards_to_show[] = $cardname;
366
                }
367 1
                continue;
368
            }
369
370 1
            $property = $cardname . 'Street';
371
372 1
            if (empty($cards_to_show)) {
373 1
                if ($customer->$property) {
374
                    $inherited_cards_only = false;
375
                    $cards_to_show[] = $cardname;
376 1
                } elseif (!$default_shown && $customer->street) {
377
                    $default_shown = true;
378 1
                    $cards_to_show[] = $cardname;
379
                }
380
            } elseif (    $customer->$property
381
                      || ($customer->street && !$inherited_cards_only && !$default_shown)) {
382
                $inherited_cards_only = false;
383
                $multiple_addresses = true;
384
                $cards_to_show[] = $cardname;
385
            }
386
        }
387
388 1
        if (empty($cards_to_show)) {
389 1
            return;
390
        }
391
392
        $root_group = org_openpsa_contacts_interface::find_root_group();
393
        $parent = $customer->get_parent();
394
        $parent_name = false;
395
        if ($parent->id != $root_group->id) {
396
            $parent_name = $parent->get_label();
397
        }
398
399
        foreach ($cards_to_show as $cardname) {
400
            echo '<div class="vcard">';
401
            if (   $multiple_addresses
402
                || (   $cardname != 'visiting'
403
                    && !$inherited_cards_only)) {
404
                echo '<div style="text-align:center"><em>' . midcom::get()->i18n->get_string($cardname . ' address', 'org.openpsa.contacts') . "</em></div>\n";
405
            }
406
            echo "<strong>\n";
407
            if ($parent_name) {
408
                echo '<a href="' . $contacts_url . 'group/' . $parent->guid . '/">' . $parent_name . "</a><br />\n";
0 ignored issues
show
Bug introduced by
Are you sure $contacts_url of type false|mixed|null can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

408
                echo '<a href="' . /** @scrutinizer ignore-type */ $contacts_url . 'group/' . $parent->guid . '/">' . $parent_name . "</a><br />\n";
Loading history...
409
            }
410
411
            $label = $customer->get_label();
412
413
            if ($cardname != 'visiting') {
414
                $label_property = $cardname . '_label';
415
                $label = $customer->$label_property;
416
            }
417
418
            echo $label . "\n";
419
            echo "</strong>\n";
420
421
            $property_street = 'street';
422
            $property_postcode = 'postcode';
423
            $property_city = 'city';
424
425
            if ($cardname != 'visiting') {
426
                $property_street = $cardname . 'Street';
427
                $property_postcode = $cardname . 'Postcode';
428
                $property_city = $cardname . 'City';
429
            }
430
            if ($customer->$property_street) {
431
                echo "<p>{$customer->$property_street}<br />\n";
432
                echo "{$customer->$property_postcode} {$customer->$property_city}</p>\n";
433
            } elseif ($customer->street) {
434
                echo "<p>{$customer->street}<br />\n";
435
                echo "{$customer->postcode} {$customer->city}</p>\n";
436
            }
437
            echo "</div>\n";
438
        }
439
    }
440
}
441