Test Setup Failed
Push — release_2_0 ( 5d1ce5...f8cc4d )
by Tomasz
07:42
created

IdPlist   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
eloc 171
c 1
b 0
f 0
dl 0
loc 309
rs 8.64

6 Methods

Rating   Name   Duplication   Size   Complexity  
A orderIdentityProviders() 0 16 3
F listAllIdentityProviders() 0 84 16
F listIdentityProvidersWithProfiles() 0 116 17
A geoDistance() 0 6 1
B getIdpDistance() 0 22 7
A setCurrentLocation() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like IdPlist 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 IdPlist, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * *****************************************************************************
4
 * Contributions to this work were made on behalf of the GÉANT project, a 
5
 * project that has received funding from the European Union’s Framework 
6
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
7
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
8
 * 691567 (GN4-1) and No. 731122 (GN4-2).
9
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
10
 * of the copyright in all material which was developed by a member of the GÉANT
11
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
12
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
13
 * UK as a branch of GÉANT Vereniging.
14
 * 
15
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
16
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
17
 *
18
 * License: see the web/copyright.inc.php file in the file structure or
19
 *          <base_url>/copyright.php after deploying the software
20
 */
21
22
23
namespace core;
24
25
class IdPlist extends common\Entity {
26
    /**
27
     * Order active identity providers according to their distance and name
28
     * @param string $country         the country from which to list IdPs
29
     * @param array  $currentLocation current location
30
     * @return array $IdPs -  list of arrays ('id', 'name');
31
     */
32
    public static function orderIdentityProviders($country, $currentLocation) {
33
        $idps = self::listAllIdentityProviders(1, $country);
34
        $here = self::setCurrentLocation($currentLocation);
35
        $idpTitle = [];
36
        $resultSet = [];
37
        foreach ($idps as $idp) {
38
            $idpTitle[$idp['entityID']] = $idp['title'];
39
            $d = self::getIdpDistance($idp, $here);
40
            $resultSet[$idp['entityID']] = $d . " " . $idp['title'];
41
        }
42
        asort($resultSet);
43
        $outarray = [];
44
        foreach (array_keys($resultSet) as $r) {
45
            $outarray[] = ['idp' => $r, 'title' => $idpTitle[$r]];
46
        }
47
        return($outarray);
48
    }
49
    
50
    
51
    /**
52
     * Lists all identity providers in the database
53
     * adding information required by DiscoJuice.
54
     * 
55
     * @param int    $activeOnly if set to non-zero will cause listing of only those institutions which have some valid profiles defined.
56
     * @param string $country    if set, only list IdPs in a specific country
57
     * @return array the list of identity providers
58
     *
59
     */
60
    public static function listAllIdentityProviders($activeOnly = 0, $country = "") {
61
        common\Entity::intoThePotatoes();
62
        $handle = DBConnection::handle("INST");
63
        $handle->exec("SET SESSION group_concat_max_len=10000");
64
        $query = "SELECT distinct institution.inst_id AS inst_id, institution.country AS country,
65
                     group_concat(concat_ws('===',institution_option.option_name,LEFT(institution_option.option_value,200), institution_option.option_lang) separator '---') AS options
66
                     FROM institution ";
67
        if ($activeOnly == 1) {
68
            $query .= "JOIN v_active_inst ON institution.inst_id = v_active_inst.inst_id ";
69
        }
70
        $query .= "JOIN institution_option ON institution.inst_id = institution_option.institution_id ";
71
        $query .= "WHERE (institution_option.option_name = 'general:instname' 
72
                          OR institution_option.option_name = 'general:geo_coordinates'
73
                          OR institution_option.option_name = 'general:logo_file') ";
74
75
        $query .= ($country != "" ? "AND institution.country = ? " : "");
76
77
        $query .= "GROUP BY institution.inst_id ORDER BY inst_id";
78
79
        $allIDPs = ($country != "" ? $handle->exec($query, "s", $country) : $handle->exec($query));
80
        $returnarray = [];
81
        // SELECTs never return a booleans, always an object
82
        $i=0;
83
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
84
            $i++;
85
            $institutionOptions = explode('---', $queryResult->options);
86
            $oneInstitutionResult = [];
87
            $geo = [];
88
            $names = [];
89
90
            $oneInstitutionResult['entityID'] = $queryResult->inst_id;
91
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
92
            foreach ($institutionOptions as $institutionOption) {
93
                $opt = explode('===', $institutionOption);
94
                switch ($opt[0]) {
95
                    case 'general:logo_file':
96
                        $oneInstitutionResult['icon'] = $queryResult->inst_id;
97
                        break;
98
                    case 'general:geo_coordinates':
99
                        $at1 = json_decode($opt[1], true);
100
                        $geo[] = $at1;
101
                        break;
102
                    case 'general:instname':
103
                        $names[] = [
104
                            'lang' => $opt[2],
105
                            'value' => $opt[1]
106
                        ];
107
                        break;
108
                    default:
109
                        break;
110
                }
111
            }
112
113
            $name = _("Unnamed Entity");
114
            if (count($names) != 0) {
115
                $langObject = new \core\common\Language();
116
                $name = $langObject->getLocalisedValue($names);
117
            }
118
            $oneInstitutionResult['title'] = $name;
119
            $keywords = [];
120
            foreach ($names as $keyword) {
121
                $value = $keyword['value'];
122
                $keywords[$keyword['lang']] = $keyword['value'];
123
                $keywords[$keyword['lang'].'_7'] =
124
                        iconv('UTF-8', 'ASCII//TRANSLIT', $value);
125
            }
126
            
127
            if (isset(CONFIG_CONFASSISTANT['USE_KEYWORDS']) && CONFIG_CONFASSISTANT['USE_KEYWORDS']) {
128
                $keywords_final = array_unique($keywords);
129
130
                if (!empty($keywords_final)) {
131
                    $oneInstitutionResult['keywords'] = [];
132
                    foreach (array_keys($keywords_final) as $key) {
133
                    $oneInstitutionResult['keywords'][] = [$keywords_final[$key]];
134
                    }
135
                }
136
            }
137
            if (count($geo) > 0) {
138
                $oneInstitutionResult['geo'] = $geo;
139
            }
140
            $returnarray[] = $oneInstitutionResult;
141
        }
142
        common\Entity::outOfThePotatoes();
143
        return $returnarray;
144
    }
145
146
    /**
147
     * outputs a full list of IdPs containing the fllowing data:
148
     * institution_is, institution name in all available languages,
149
     * list of production profiles.
150
     * For eache profile the profile identifier, profile name in all languages
151
     * and redirect values (empty rediret value means that no redirect has been
152
     * set).
153
     * 
154
     * @return array of identity providers with attriutes
155
     */
156
    public function listIdentityProvidersWithProfiles() {
157
        $handle = DBConnection::handle("INST");
158
        $handle->exec("SET SESSION group_concat_max_len=10000");
159
        $idpQuery = "SELECT distinct institution.inst_id AS inst_id,
160
            institution.country AS country,
161
            group_concat(concat_ws('===',institution_option.option_name,
162
                LEFT(institution_option.option_value,200),
163
                institution_option.option_lang) separator '---') AS options
164
            FROM institution
165
            JOIN v_active_inst ON institution.inst_id = v_active_inst.inst_id 
166
            JOIN institution_option ON institution.inst_id = institution_option.institution_id
167
            WHERE (institution_option.option_name = 'general:instname'
168
                OR institution_option.option_name = 'general:geo_coordinates'
169
                OR institution_option.option_name = 'general:logo_file')
170
            GROUP BY institution.inst_id";
171
        
172
        $allIDPs = $handle->exec($idpQuery);
173
        $idpArray = [];
174
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) {
175
            $institutionOptions = explode('---', $queryResult->options);
176
            $oneInstitutionResult = [];
177
            $geo = [];
178
            $names = [];
179
            $oneInstitutionResult['country'] = strtoupper($queryResult->country);
180
            $oneInstitutionResult['entityID'] = (int)$queryResult->inst_id;
181
            
182
            foreach ($institutionOptions as $institutionOption) {
183
                $opt = explode('===', $institutionOption);
184
                switch ($opt[0]) {
185
                    case 'general:logo_file':
186
                        $oneInstitutionResult['icon'] = $queryResult->inst_id;
187
                        break;
188
                    case 'general:geo_coordinates':
189
                        $at1 = json_decode($opt[1], true);
190
                        $geo[] = $at1;
191
                        break;
192
                    case 'general:instname':
193
                        $names[] = [
194
                            'lang' => $opt[2],
195
                            'value' => $opt[1]
196
                        ];
197
                        break;
198
                    default:
199
                        break;
200
                }
201
            }
202
            $oneInstitutionResult['names'] = $names;
203
            if (count($geo) > 0) {
204
                $geoArray=[];
205
                foreach ($geo as $coords) {
206
                    $geoArray[] = ['lon' => (float)$coords['lon'],
207
                        'lat' => (float)$coords['lat']];
208
                }
209
                $oneInstitutionResult['geo'] = $geoArray;
210
            }              
211
            $idpArray[$queryResult->inst_id] = $oneInstitutionResult;
212
        }
213
        
214
        $profileQuery = "SELECT profile.inst_id AS inst_id,
215
            profile.profile_id,
216
            group_concat(concat_ws('===',profile_option.option_name, 
217
                LEFT(profile_option.option_value, 200),
218
                profile_option.option_lang) separator '---') AS profile_options
219
            FROM profile
220
            JOIN profile_option ON profile.profile_id = profile_option.profile_id
221
            WHERE profile.sufficient_config = 1
222
                AND profile_option.eap_method_id = 0
223
                AND (profile_option.option_name = 'profile:name'
224
                OR (profile_option.option_name = 'device-specific:redirect'
225
                    AND isnull(profile_option.device_id))
226
                OR profile_option.option_name = 'profile:production')        
227
            GROUP BY profile.profile_id ORDER BY inst_id";
228
229
        $allProfiles = $handle->exec($profileQuery);
230
        while ($queryResult = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allProfiles)) {
231
            $productionProfile = false;
232
            $profileOptions = explode('---', $queryResult->profile_options);
233
            $idpId = $queryResult->inst_id;
234
            if (empty($idpArray[$idpId])) {
235
                continue;
236
            }
237
            if (empty($idpArray[$idpId]['profiles'])) {
238
                $idpArray[$idpId]['profiles'] = [];
239
            }
240
            $profileNames = [];
241
            $redirect = '';
242
            foreach ($profileOptions as $profileOption) {
243
                $opt = explode('===', $profileOption);
244
                switch ($opt[0]) {
245
                    case 'profile:production':
246
                        if ($opt[1] = 'on') {
247
                            $productionProfile = true;
248
                        }
249
                        break;
250
                    case 'device-specific:redirect':
251
                            $redirect = $opt[1] . ':' . $queryResult->device_id;
252
                       
253
                        break;
254
                    case 'profile:name': 
255
                        $profileNames[] = [
256
                            'lang' => $opt[2],
257
                            'value' => $opt[1]
258
                        ];
259
                        break;
260
                    default:
261
                        break; 
262
                }
263
            }
264
            if ($productionProfile) {
265
                $idpArray[$idpId]['profiles'][] =
266
                        ['id'=> (int)$queryResult->profile_id,
267
                            'names'=> $profileNames,
268
                            'redirect'=>$redirect];
269
            }
270
        }       
271
        return $idpArray;
272
    }
273
274
    /**
275
     * sets the current location
276
     * 
277
     * @param array $currentLocation the location to set
278
     * @return array
279
     */
280
    private static function setCurrentLocation($currentLocation) {
281
        if (is_null($currentLocation)) {
282
            $currentLocation = ['lat' => "90", 'lon' => "0"];
283
            $userLocation = DeviceLocation::locateDevice();
284
            if ($userLocation['status'] == 'ok') {
285
                $currentLocation = $userLocation['geo'];
286
            }
287
        }
288
        return $currentLocation;
289
    }
290
    
291
    /**
292
     * calculate surface distance from user location to IdP location
293
     * @param array $idp      the IdP in question
294
     * @param array $location user location
295
     * @return string
296
     */
297
    private static function getIdpDistance($idp, $location) {
298
        $dist = 10000;
299
        if (isset($idp['geo'])) {
300
            $G = $idp['geo'];
301
            if (isset($G['lon'])) {
302
                $d1 = self::geoDistance($location, $G);
303
                if ($d1 < $dist) {
304
                    $dist = $d1;
305
                }
306
            } else {
307
                foreach ($G as $g) {
308
                    $d1 = self::geoDistance($location, $g);
309
                    if ($d1 < $dist) {
310
                        $dist = $d1;
311
                    }
312
                }
313
            }
314
        }
315
        if ($dist > 100) {
316
            $dist = 10000;
317
        }
318
        return(sprintf("%06d", $dist));
319
    }
320
    
321
    /**
322
     * Calculate the distance in km between two points given their
323
     * geo coordinates.
324
     * @param array $point1   first point as an 'lat', 'lon' array 
325
     * @param array $profile1 second point as an 'lat', 'lon' array 
326
     * @return float distance in km
327
     */
328
    private static function geoDistance($point1, $profile1) {
329
330
        $distIntermediate = sin(deg2rad($point1['lat'])) * sin(deg2rad($profile1['lat'])) +
331
                cos(deg2rad($point1['lat'])) * cos(deg2rad($profile1['lat'])) * cos(deg2rad($point1['lon'] - $profile1['lon']));
332
        $dist = rad2deg(acos($distIntermediate)) * 60 * 1.1852;
333
        return(round($dist));
334
    }
335
    
336
}