Completed
Pull Request — master (#88)
by Jason
02:47
created

Locator_Controller   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 211
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 46.15%

Importance

Changes 28
Bugs 1 Features 3
Metric Value
wmc 25
c 28
b 1
f 3
lcom 1
cbo 12
dl 0
loc 211
ccs 18
cts 39
cp 0.4615
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
F init() 0 79 10
A index() 0 8 1
A xml() 0 8 1
C Items() 0 33 7
B LocationSearch() 0 47 6
1
<?php
2
3
class Locator extends Page
4
{
5
    private static $db = array(
1 ignored issue
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
6
        'AutoGeocode' => 'Boolean',
7
        'ModalWindow' => 'Boolean',
8
        'Unit' => 'Enum("m,km","m")',
9
    );
10
11
    private static $many_many = array(
1 ignored issue
show
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
12
        'Categories' => 'LocationCategory',
13
    );
14
15
    private static $defaults = array(
1 ignored issue
show
Unused Code introduced by
The property $defaults is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
16
        'AutoGeocode' => true,
17
    );
18
19 1
    private static $singular_name = 'Locator';
1 ignored issue
show
Unused Code introduced by
The property $singular_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
20
    private static $plural_name = 'Locators';
1 ignored issue
show
Unused Code introduced by
The property $plural_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
21 1
    private static $description = 'Find locations on a map';
1 ignored issue
show
Unused Code introduced by
The property $description is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
22
23
    /**
24 1
     * @return FieldList
25 1
     */
26 1
    public function getCMSFields()
27
    {
28
        $fields = parent::getCMSFields();
29 1
30 1
        // Settings
31
        $fields->addFieldsToTab('Root.Settings', array(
32
            HeaderField::create('DisplayOptions', 'Display Options', 3),
33 1
            OptionsetField::create('Unit', 'Unit of measure', array('m' => 'Miles', 'km' => 'Kilometers')),
34 1
            CheckboxField::create('AutoGeocode', 'Auto Geocode - Automatically filter map results based on user location')
35 1
                ->setDescription('Note: if any locations are set as featured, the auto geocode is automatically disabled.'),
36 1
            CheckboxField::create('ModalWindow', 'Modal Window - Show Map results in a modal window'),
37 1
        ));
38 1
39 1
        // Filter categories
40
        $config = GridFieldConfig_RelationEditor::create();
41 1
        if (class_exists('GridFieldAddExistingSearchButton')) {
42
            $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
43 1
            $config->addComponent(new GridFieldAddExistingSearchButton());
44
        }
45
        $categories = $this->Categories();
46 3
        $categoriesField = GridField::create('Categories', 'Categories', $categories, $config)
47
            ->setDescription('only show locations from the selected category');
48 3
49
            // Filter
50 3
            $fields->addFieldsToTab('Root.Filter', array(
51 3
                HeaderField::create('CategoryOptionsHeader', 'Location Filtering', 3),
52 3
                $categoriesField,
53 3
            ));
54
55
        $this->extend('updateCMSFields', $fields);
56 1
57
        return $fields;
58 1
    }
59
60
    /**
61 1
     * @param array $filter
62
     * @param array $filterAny
63 1
     * @param array $exclude
64
     * @param null $filterByCallback
65
     * @return ArrayList
66
     */
67
    public static function locations(
68
        $filter = array(),
69
        $filterAny = array(),
70
        $exclude = array(),
71
        $filterByCallback = null
72
    ) {
73
        $locationsList = ArrayList::create();
74
75
        $locations = Location::get()->filter($filter);
76
77
        if (!empty($filterAny)) {
78
            $locations = $locations->filterAny($filterAny);
79
        }
80
        if (!empty($exclude)) {
81
            $locations = $locations->exclude($exclude);
82
        }
83
84
        if ($filterByCallback !== null && is_callable($filterByCallback)) {
85
            $locations = $locations->filterByCallback($filterByCallback);
86
        }
87
88
        if ($locations->exists()) {
89
            $locationsList->merge($locations);
90
        }
91
92
        return $locationsList;
93
    }
94
95
    /**
96
     * @return DataList
97
     */
98
    public static function getAllCategories()
99
    {
100
        return LocationCategory::get();
101
    }
102
103
    /**
104
     * @param null $id
105
     * @return bool
106
     */
107
    public static function getPageCategories($id = null)
108
    {
109
        if ($id) {
110
            if ($locator = self::get()->byID($id)) {
111
                return $locator->Categories();
112
            }
113
114
            return false;
115
        }
116
117
        return false;
118
    }
119
120
121
}
122
123
class Locator_Controller extends Page_Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
124
{
125
    // allowed actions
126
    private static $allowed_actions = array(
1 ignored issue
show
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
127
        'xml',
128
    );
129
130
    // Set Requirements based on input from CMS
131
    public function init()
132
    {
133
        parent::init();
134
135
        $themeDir = SSViewer::get_theme_folder();
136
137
        // google maps api key
138
        $key = Config::inst()->get('GoogleGeocoding', 'google_api_key');
139
140
        $locations = $this->Items($this->request);
141
142
        Requirements::javascript('framework/thirdparty/jquery/jquery.js');
143
        if ($locations) {
144
            Requirements::javascript('http://maps.google.com/maps/api/js?key='.$key);
145
            Requirements::javascript('locator/thirdparty/handlebars/handlebars-v1.3.0.js');
146
            Requirements::javascript('locator/thirdparty/jquery-store-locator/js/jquery.storelocator.js');
147
        }
148
149
        Requirements::css('locator/css/map.css');
150
151
        $featured = ($locations->filter(array('Featured' => 1))->count() > 0) ?
152
            'featuredLocations: true' :
153
            'featuredLocations: false';
154
155
        // map config based on user input in Settings tab
156
        // AutoGeocode or Full Map
157
        $load = ($this->data()->AutoGeocode) ?
158
            'autoGeocode: true, fullMapStart: false,' :
159
            'autoGeocode: false, fullMapStart: true, storeLimit: 1000, maxDistance: true,';
160
161
        $base = Director::baseFolder();
162
        $themePath = $base.'/'.$themeDir;
163
164
        $listTemplatePath = (file_exists($themePath.'/templates/location-list-description.html')) ?
165
            $themeDir.'/templates/location-list-description.html' :
166
            'locator/templates/location-list-description.html';
167
        $infowindowTemplatePath = (file_exists($themePath.'/templates/infowindow-description.html')) ?
168 1
            $themeDir.'/templates/infowindow-description.html' :
169
            'locator/templates/infowindow-description.html';
170 1
171 1
        // in page or modal
172 1
        $modal = ($this->data()->ModalWindow) ? 'modalWindow: true' : 'modalWindow: false';
173 1
174
        $kilometer = ($this->data()->Unit == 'km') ? 'lengthUnit: "km"' : 'lengthUnit: "m"';
175 1
176 1
        // pass GET variables to xml action
177 1
        $vars = $this->request->getVars();
178 1
        unset($vars['url']);
179
        $url = '';
180 1
        if (count($vars)) {
181
            $url .= '?'.http_build_query($vars);
182 1
        }
183
        $link = $this->Link().'xml.xml'.$url;
184 1
185
        // init map
186
        if ($locations) {
187
            Requirements::customScript("
188
                $(function($) {
189
                    $('#map-container').storeLocator({
190
                        ".$load."
191
                        dataLocation: '".$link."',
192 1
                        listTemplatePath: '".$listTemplatePath."',
193
                        infowindowTemplatePath: '".$infowindowTemplatePath."',
194 1
                        originMarker: true,
195 1
                        ".$modal.',
196 1
                        '.$featured.",
197
                        slideMap: false,
198 1
                        zoomLevel: 0,
199
                        noForm: true,
200
                        formID: 'Form_LocationSearch',
201 1
                        inputID: 'Form_LocationSearch_Address',
202
                        categoryID: 'Form_LocationSearch_CategoryID',
203
                        distanceAlert: -1,
204
                        ".$kilometer.'
205
                    });
206
                });
207
            ');
208
        }
209
    }
210
211
    /**
212
     * @param SS_HTTPRequest $request
213
     *
214
     * @return ViewableData_Customised
215
     */
216
    public function index(SS_HTTPRequest $request)
217
    {
218
        $locations = $this->Items($request);
219
220
        return $this->customise(array(
221
            'Locations' => $locations,
222
        ));
223
    }
224
225
    /**
226
     * Return a XML feed of all locations marked "show in locator"
227
     *
228
     * @param SS_HTTPRequest $request
229
     * @return HTMLText
230
     */
231
    public function xml(SS_HTTPRequest $request)
232
    {
233
        $locations = $this->Items($request);
234
235
        return $this->customise(array(
236
            'Locations' => $locations,
237
        ))->renderWith('LocationXML');
238
    }
239
240
    /**
241
     * @param array $searchCriteria
242
     *
243
     * @return mixed
244
     */
245
    public function Items($searchCriteria = array())
246
    {
247
        $request = ($this->request) ? $this->request : $this->parentController->getRequest();
248
        if (empty($searchCriteria)) {
249
            $searchCriteria = $request->requestVars();
0 ignored issues
show
Unused Code introduced by
$searchCriteria is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
250
        }
251
252
        $filter = array();
253
        $filterAny = array();
254
        $exclude = array();
255
256
        // only show locations marked as ShowInLocator
257
        $filter['ShowInLocator'] = 1;
258
259
        // search across all address related fields
260
        $address = ($request->getVar('Address')) ? $request->getVar('Address') : false;
261
        if ($address) {
262
            $filterAny['Address:PartialMatch'] = $address;
263
            $filterAny['Suburb:PartialMatch'] = $address;
264
            $filterAny['State:PartialMatch'] = $address;
265
            $filterAny['Postcode:PartialMatch'] = $address;
266
            $filterAny['Country:PartialMatch'] = $address;
267
        }
268
269
        $category = ($request->getVar('CategoryID')) ? $request->getVar('CategoryID') : false;
270
        if ($category) {
271
            $filter['CategoryID'] = $category;
272
        }
273
274
        $locations = Locator::locations($filter, $filterAny, $exclude);
275
276
        return $locations;
277
    }
278
279
    /**
280
     * LocationSearch form.
281
     *
282
     * Search form for locations, updates map and results list via AJAX
283
     *
284
     * @return Form
285
     */
286
    public function LocationSearch()
287
    {
288
        $fields = FieldList::create(
289
            $address = TextField::create('Address', '')
290
                ->setAttribute('placeholder', 'address or zip code')
291
        );
292
293
        $filterCategories = Locator::getPageCategories($this->ID);
294
        $allCategories = Locator::getAllCategories();
295
296
        if ($allCategories->Count() > 0) {
297
            $categories = ArrayList::create();
298
            if ($filterCategories->Count() > 0) {
0 ignored issues
show
Bug introduced by
The method Count cannot be called on $filterCategories (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
299
                if ($filterCategories->Count() != 1) {
0 ignored issues
show
Bug introduced by
The method Count cannot be called on $filterCategories (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
300
                    $categories = $filterCategories;
301
                }
302
            } else {
303
                $categories = $allCategories;
304
            }
305
306
            if ($categories->count() > 0) {
307
                $fields->push(
308
                    DropdownField::create(
309
                        'CategoryID',
310
                        '',
311
                        $categories->map()
312
                    )->setEmptyString('All Categories'));
313
            }
314
        }
315
316
        $actions = FieldList::create(
317
            FormAction::create('index', 'Search')
318
        );
319
320
        if (class_exists('BootstrapForm')) {
321
            $form = BootstrapForm::create($this, 'LocationSearch', $fields, $actions);
322
        } else {
323
            $form = Form::create($this, 'LocationSearch', $fields, $actions);
324
        }
325
326
        return $form
327
            ->setFormMethod('GET')
328
            ->setFormAction($this->Link())
329
            ->disableSecurityToken()
330
            ->loadDataFrom($this->request->getVars())
331
        ;
332
    }
333
}
334