PluginProbe
Flexible Map / trunk
Flexible Map vtrunk
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4a 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.13.0 1.14.0 1.15.0 1.16.0 1.17.0 1.17.1 1.18.0 1.19.0 1.2.0 All 52 releases
wp-flexible-map / includes / class.FlxMapPlugin.php

class.FlxMapPlugin.php in Flexible Map trunk, at includes/class.FlxMapPlugin.php

680 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * class for managing the plugin
9 */
10 class FlxMapPlugin {
11 protected $locale; // locale of current website
12 protected $locales = array(); // list of locales enqueued for localisation of maps
13 protected $mapTypes = array(); // custom Google Maps map types to be loaded with maps, keyed by mapTypeId
14
15 /**
16 * static method for getting the instance of this singleton object
17 *
18 * @return FlxMapPlugin
19 */
20 public static function getInstance() {
21 static $instance = null;
22
23 if (is_null($instance)) {
24 $instance = new self();
25 }
26
27 return $instance;
28 }
29
30 /**
31 * hide the constructor
32 */
33 private function __construct() {
34 }
35
36 /**
37 * hook into WordPress actions and filters
38 */
39 public function addHooks() {
40 add_action('init', array($this, 'init'));
41
42 if (is_admin()) {
43 // kick off the admin handling
44 require FLXMAP_PLUGIN_ROOT . 'includes/class.FlxMapAdmin.php';
45 new FlxMapAdmin();
46 }
47 else {
48 // non-admin actions and filters for this plugin
49 add_action('wp_enqueue_scripts', array($this, 'enqueueScripts'));
50 add_action('wp_footer', array($this, 'justInTimeLocalisation'));
51
52 // custom actions and filters for this plugin
53 add_filter('flexmap_getmap', array($this, 'getMap'), 10, 1);
54 }
55
56 if (!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
57 // add shortcodes
58 add_shortcode(FLXMAP_PLUGIN_TAG_MAP, array($this, 'shortcodeMap'));
59 }
60 }
61
62 /**
63 * initialise the plugin, called on init action
64 */
65 public function init() {
66 // start off required locales with this website's WP locale
67 $this->locale = get_locale();
68 }
69
70 /**
71 * set the reqired locales so that appropriate scripts will be loaded
72 * @param array $locales a list of locale names
73 */
74 public function setLocales($locales) {
75 foreach ($locales as $locale) {
76 $this->enqueueLocale($locale);
77 }
78 }
79
80 /**
81 * register and enqueue any scripts and styles we require
82 */
83 public function enqueueScripts() {
84 $options = get_option(FLXMAP_PLUGIN_OPTIONS, array());
85
86 if (empty($options['noAPI'])) {
87 $args = array('v' => 'quarterly');
88 if (!empty($options['apiKey'])) {
89 $args['key'] = $options['apiKey'];
90 }
91 $args = apply_filters('flexmap_google_maps_api_args', $args);
92
93 $apiURL = apply_filters('flexmap_google_maps_api_url', add_query_arg($args, 'https://maps.google.com/maps/api/js'));
94 if (!empty($apiURL)) {
95 wp_register_script('google-maps', $apiURL, false, null, true);
96 }
97 }
98
99 $min = SCRIPT_DEBUG ? '' : '.min';
100 $ver = SCRIPT_DEBUG ? time() : FLXMAP_PLUGIN_VERSION;
101 wp_register_script('flxmap', plugins_url("static/js/flexible-map$min.js", FLXMAP_PLUGIN_FILE), array(), $ver, true);
102
103 // theme writers: you can remove this stylesheet by calling wp_dequeue_script('flxmap');
104 wp_enqueue_style('flxmap', plugins_url('static/css/styles.css', FLXMAP_PLUGIN_FILE), false, $ver);
105 }
106
107 /**
108 * enqueue an i18n script
109 * @param string $locale
110 */
111 protected function enqueueLocale($locale) {
112 $locale = strtr($locale, '_', '-');
113 $this->locales[$locale] = 1;
114 }
115
116 /**
117 * load any enqueued locales and map types as localisations on the main script
118 */
119 public function justInTimeLocalisation() {
120 $localise = array();
121
122 if (!empty($this->locales)) {
123 require_once FLXMAP_PLUGIN_ROOT . 'includes/class.FlxMapLocalisation.php';
124 $localisation = new FlxMapLocalisation();
125 $i18n = $localisation->getLocalisations($this->locales);
126 if (!empty($i18n)) {
127 $localise['i18n'] = $i18n;
128 }
129 }
130
131 if (!empty($this->mapTypes)) {
132 $localise['mapTypes'] = $this->mapTypes;
133 }
134
135 if (!empty($localise)) {
136 wp_localize_script('flxmap', 'flxmap', $localise);
137 }
138 }
139
140 /**
141 * handle shortcode for map display
142 *
143 * @param array shortcode attributes as supplied by the WP shortcode API
144 * @return string output to substitute for the shortcode
145 */
146 public function shortcodeMap($attrs) {
147 return $this->getMap($attrs);
148 }
149
150 /**
151 * get HTML and script for map
152 *
153 * @param array shortcode attributes as supplied by the WP shortcode API
154 * @return string HTML and script for the map
155 */
156 public function getMap($attrs) {
157 $html = '';
158
159 // allow plugins / themes to change the shortcode attributes used
160 $attrs = apply_filters('flexmap_shortcode_attrs', $attrs);
161
162 // allow plugins / themes to register custom Google Maps map types
163 $this->mapTypes = apply_filters('flexmap_custom_map_types', $this->mapTypes, $attrs);
164
165 if (!empty($attrs['src']) || !empty($attrs['center']) || !empty($attrs['address'])) {
166 if (empty($attrs['id'])) {
167 $ID = uniqid();
168 $divID = 'flxmap-' . $ID;
169 }
170 else {
171 $ID = $attrs['id'];
172 $divID = esc_attr($ID);
173 }
174 $varID = 'flxmap_' . preg_replace('/[^a-z0-9_$]/i', '_', $ID);
175
176 // build the inline styles for the div
177 $styles = array();
178 $styles['width'] = isset($attrs['width']) ? self::getUnits($attrs['width']) : '400px';
179 $styles['height'] = isset($attrs['height']) ? self::getUnits($attrs['height']) : '400px';
180 $styles = apply_filters('flexmap_shortcode_styles', $styles, $attrs);
181 if (empty($styles)) {
182 $inlinestyles = '';
183 }
184 else {
185 $inlinestyles = 'style="';
186 foreach ($styles as $style => $value) {
187 $inlinestyles .= $style . ':' . $value . ';';
188 }
189 $inlinestyles .= '"';
190 }
191
192 // test for any conditions that show directions (thus requiring the directions div)
193 $directions = false;
194 $divDirectionsID = $divDirectionsID = '';
195 if (isset($attrs['directions']) && !self::isNo($attrs['directions'])) {
196 $directions = true;
197 if (!self::isYes($attrs['directions'])) {
198 $divDirectionsID = esc_js($attrs['directions']);
199 }
200 }
201 if (isset($attrs['showdirections']) && self::isYes($attrs['showdirections'])) {
202 $directions = true;
203 }
204 if (isset($attrs['directionsfrom'])) {
205 $directions = true;
206 }
207
208 // build the directions div, if required
209 $divDirections = '';
210 if ($directions && empty($divDirectionsID)) {
211 $divDirectionsID = "$divID-dir";
212 $divDirections = "\n<div id='$divDirectionsID' class='flxmap-directions'></div>";
213 }
214
215 $html = <<<HTML
216 <div id="$divID" class='flxmap-container' data-flxmap='$varID' $inlinestyles></div>$divDirections
217
218 HTML;
219
220 $script = " var f = new FlexibleMap();\n";
221
222 if (isset($attrs['hidemaptype']) && self::isYes($attrs['hidemaptype'])) {
223 $script .= " f.mapTypeControl = false;\n";
224 }
225
226 if (isset($attrs['hidescale']) && self::isNo($attrs['hidescale'])) {
227 $script .= " f.scaleControl = true;\n";
228 }
229
230 if (isset($attrs['hidepanning']) && self::isNo($attrs['hidepanning'])) {
231 $script .= " f.panControl = true;\n";
232 }
233
234 if (isset($attrs['hidezooming']) && self::isYes($attrs['hidezooming'])) {
235 $script .= " f.zoomControl = false;\n";
236 }
237
238 if (isset($attrs['hidefullscreen']) && self::isYes($attrs['hidefullscreen'])) {
239 $script .= " f.fullscreen = false;\n";
240 }
241
242 if (!empty($attrs['zoomstyle'])) {
243 $text = esc_js($attrs['zoomstyle']);
244 $script .= " f.zoomControlStyle = \"$text\";\n";
245 }
246
247 if (isset($attrs['hidestreetview']) && self::isNo($attrs['hidestreetview'])) {
248 $script .= " f.streetViewControl = true;\n";
249 }
250
251 if (isset($attrs['showinfo']) && self::isNo($attrs['showinfo'])) {
252 $script .= " f.markerShowInfo = false;\n";
253 }
254
255 if (isset($attrs['gesturehandling']) && preg_match('/cooperative|greedy|none|auto/i', $attrs['gesturehandling'])) {
256 $script .= sprintf(" f.gestureHandling = '%s';\n", strtolower(trim($attrs['gesturehandling'])));
257 }
258 else {
259 if (isset($attrs['scrollwheel']) && self::isYes($attrs['scrollwheel'])) {
260 $script .= " f.scrollwheel = true;\n";
261 }
262
263 if (isset($attrs['draggable']) && self::isNo($attrs['draggable'])) {
264 $script .= " f.draggable = false;\n";
265 }
266
267 if (isset($attrs['dblclickzoom']) && self::isNo($attrs['dblclickzoom'])) {
268 $script .= " f.dblclickZoom = false;\n";
269 }
270 }
271
272 if (isset($attrs['directions'])) {
273 if (self::isNo($attrs['directions'])) {
274 $script .= " f.markerDirections = false;\n";
275 }
276 else {
277 $script .= " f.markerDirections = true;\n";
278 }
279 }
280
281 if ($directions) {
282 $script .= " f.markerDirectionsDiv = \"$divDirectionsID\";\n";
283 }
284
285 if (isset($attrs['showdirections']) && self::isYes($attrs['showdirections'])) {
286 $script .= " f.markerDirectionsShow = true;\n";
287 }
288
289 if (isset($attrs['directionsfrom'])) {
290 $text = esc_js($attrs['directionsfrom']);
291 $script .= " f.markerDirectionsDefault = \"$text\";\n";
292 }
293
294 if (isset($attrs['directions']) && self::isNo($attrs['directions'])) {
295 $script .= " f.markerDirectionsInfo = false;\n";
296 }
297
298 if (isset($attrs['markeranimation']) && self::isMarkerAnimation($attrs['markeranimation'])) {
299 $script .= sprintf(" f.markerAnimation = '%s';\n", strtolower($attrs['markeranimation']));
300 }
301
302 if (isset($attrs['dirdraggable']) && self::isYes($attrs['dirdraggable'])) {
303 $script .= " f.dirDraggable = true;\n";
304 }
305
306 if (isset($attrs['dirnomarkers']) && self::isYes($attrs['dirnomarkers'])) {
307 $script .= " f.dirSuppressMarkers = true;\n";
308 }
309
310 if (isset($attrs['dirshowsteps']) && self::isNo($attrs['dirshowsteps'])) {
311 $script .= " f.dirShowSteps = false;\n";
312 }
313
314 if (isset($attrs['dirshowssearch']) && self::isNo($attrs['dirshowssearch'])) {
315 $script .= " f.dirShowSearch = false;\n";
316 }
317
318 if (isset($attrs['dirtravelmode']) && in_array(strtolower($attrs['dirtravelmode']), array('bicycling', 'driving', 'transit', 'walking'))) {
319 $dirTravelMode = strtolower($attrs['dirtravelmode']);
320 $script .= " f.dirTravelMode = \"$dirTravelMode\";\n";
321 }
322
323 if (isset($attrs['dirunitsystem']) && in_array(strtolower($attrs['dirunitsystem']), array('imperial', 'metric'))) {
324 $dirUnitSystem = strtolower($attrs['dirunitsystem']);
325 $script .= " f.dirUnitSystem = \"$dirUnitSystem\";\n";
326 }
327
328 if (isset($attrs['maptype'])) {
329 $text = esc_js($attrs['maptype']);
330 $script .= " f.mapTypeId = \"$text\";\n";
331 }
332
333 if (isset($attrs['maptypes'])) {
334 $text = esc_js($attrs['maptypes']);
335 $script .= " f.mapTypeIds = \"$text\";\n";
336 }
337
338 if (isset($attrs['region'])) {
339 $text = esc_js($attrs['region']);
340 $script .= " f.region = \"$text\";\n";
341 }
342
343 if (isset($attrs['locale'])) {
344 $locale = esc_js(str_replace('_', '-', $attrs['locale']));
345 $script .= " f.setlocale(\"$locale\");\n";
346 $this->enqueueLocale($attrs['locale']);
347 }
348 else if ($this->locale !== '' || $this->locale !== 'en-US') {
349 $locale = esc_js(str_replace('_', '-', $this->locale));
350 $script .= " f.setlocale(\"$locale\");\n";
351 $this->enqueueLocale($locale);
352 }
353
354 // if have address but not coordinates, attempt to retrieve coordinates for address
355 if (empty($attrs['center']) && !empty($attrs['address'])) {
356 $region = empty($attrs['region']) ? '' : $attrs['region'];
357 $center = self::getAddressCoordinates($attrs['address'], $region);
358 if ($center) {
359 $attrs['center'] = implode(',', $center);
360 }
361 }
362
363 // add map based on coordinates, with optional marker coordinates -- but not if KML source file is set
364 if (isset($attrs['center']) && self::isCoordinates($attrs['center']) && empty($attrs['src'])) {
365 $marker = esc_js(self::getCoordinates($attrs['center']));
366 if (isset($attrs['marker']) && self::isCoordinates($attrs['marker'])) {
367 $marker = esc_js(self::getCoordinates($attrs['marker']));
368 }
369
370 if (isset($attrs['zoom'])) {
371 $script .= ' f.zoom = ' . preg_replace('/\D/', '', $attrs['zoom']) . ";\n";
372 }
373
374 if (!empty($attrs['title'])) {
375 $script .= " f.markerTitle = \"{$this->unhtml($attrs['title'])}\";\n";
376 }
377
378 if (!empty($attrs['description'])) {
379 $script .= " f.markerDescription = \"{$this->unhtml($attrs['description'])}\";\n";
380 }
381
382 if (!empty($attrs['html'])) {
383 $text = wp_json_encode(wp_kses_post($attrs['html']));
384 $script .= " f.markerHTML = $text;\n";
385 }
386
387 if (!empty($attrs['address'])) {
388 $script .= " f.markerAddress = \"{$this->unhtml($attrs['address'])}\";\n";
389 }
390
391 if (!empty($attrs['link'])) {
392 $link = esc_js(esc_url($attrs['link']));
393 $script .= " f.markerLink = \"$link\";\n";
394 }
395
396 if (!empty($attrs['linktarget'])) {
397 $text = esc_js($attrs['linktarget']);
398 $script .= " f.markerLinkTarget = \"$text\";\n";
399 }
400
401 if (!empty($attrs['linktext'])) {
402 $script .= " f.markerLinkText = \"{$this->unhtml($attrs['linktext'])}\";\n";
403 }
404
405 if (!empty($attrs['icon'])) {
406 $icon = esc_js($attrs['icon']);
407 $script .= " f.markerIcon = \"$icon\";\n";
408 }
409
410 $script .= " f.showMarker(\"$divID\", [{$attrs['center']}], [{$marker}]);\n";
411 }
412
413 // add map based on address query
414 else if (isset($attrs['address'])) {
415 if (isset($attrs['zoom'])) {
416 $script .= ' f.zoom = ' . preg_replace('/\D/', '', $attrs['zoom']) . ";\n";
417 }
418
419 if (!empty($attrs['title'])) {
420 $script .= " f.markerTitle = \"{$this->unhtml($attrs['title'])}\";\n";
421 }
422
423 if (!empty($attrs['description'])) {
424 $script .= " f.markerDescription = \"{$this->unhtml($attrs['description'])}\";\n";
425 }
426
427 if (!empty($attrs['html'])) {
428 $text = wp_json_encode(wp_kses_post($attrs['html']));
429 $script .= " f.markerHTML = \"$text\";\n";
430 }
431
432 if (!empty($attrs['link'])) {
433 $link = esc_js($attrs['link']);
434 $script .= " f.markerLink = \"$link\";\n";
435 }
436
437 if (!empty($attrs['icon'])) {
438 $icon = esc_js($attrs['icon']);
439 $script .= " f.markerIcon = \"$icon\";\n";
440 }
441
442 $script .= " f.showAddress(\"$divID\", \"{$this->unhtml($attrs['address'])}\");\n";
443 }
444
445 // add map based on KML file
446 else if (isset($attrs['src'])) {
447 if (isset($attrs['targetfix']) && self::isNo($attrs['targetfix'])) {
448 $script .= " f.targetFix = false;\n";
449 }
450
451 if (isset($attrs['kmlcache']) && preg_match('/^(?:none|\d+\s*minutes?|\d+\s*hours?|\d+\s*days?)$/', $attrs['kmlcache'])) {
452 $script .= " f.kmlcache = \"{$attrs['kmlcache']}\";\n";
453 }
454
455 if (isset($attrs['center']) && self::isCoordinates($attrs['center'])) {
456 $script .= sprintf(" f.kmlCentre = [%s];\n", esc_js(self::getCoordinates($attrs['center'])));
457 }
458
459 $kmlfile = esc_js($attrs['src']);
460 $script .= " f.showKML(\"$divID\", \"$kmlfile\"";
461
462 if (isset($attrs['zoom'])) {
463 $script .= ', ' . preg_replace('/\D/', '', $attrs['zoom']);
464 }
465
466 $script .= ");\n";
467 }
468
469 // allow others to change the generated script
470 $script = apply_filters('flexmap_shortcode_script', $script, $attrs);
471
472 if ((defined('DOING_AJAX') && DOING_AJAX) || (isset($attrs['isajax']) && self::isYes($attrs['isajax']))) {
473 // ensure that the required scripts are on the page already
474 if (!wp_script_is('flxmap', 'done')) {
475 wp_print_scripts('flxmap');
476 }
477
478 // wrap it up for AJAX load, no event trigger
479 $html .= <<<HTML
480 <script data-noptimize="1">
481 /* <![CDATA[ */
482 var $varID = (function() {
483 $script return f;
484 })();
485 /* ]]> */
486 </script>
487
488 HTML;
489 }
490 else {
491 // wrap it up for standard page load, with "content ready" trigger
492 $html .= <<<HTML
493 <script data-noptimize="1">
494 /* <![CDATA[ */
495 (function(w, fn) {
496 if (w.addEventListener) w.addEventListener("DOMContentLoaded", fn, false);
497 else if (w.attachEvent) w.attachEvent("onload", fn);
498 })(window, function() {
499 $script window.$varID = f;
500 });
501 /* ]]> */
502 </script>
503
504 HTML;
505 }
506 }
507
508 // allow others to change the generated html
509 $html = apply_filters('flexmap_shortcode_html', $html, $attrs);
510
511 // enqueue scripts
512 $options = get_option(FLXMAP_PLUGIN_OPTIONS, array());
513 if (empty($options['noAPI'])) {
514 wp_enqueue_script('google-maps');
515 }
516 wp_enqueue_script('flxmap');
517 if ($this->locale !== '' && $this->locale !== 'en_US') {
518 $this->enqueueLocale($this->locale);
519 }
520
521 return $html;
522 }
523
524 /**
525 * get valid CSS units from string, or default to 400px if invalid
526 * @param string $units
527 * @return string
528 */
529 protected static function getUnits($units) {
530 $units = trim($units);
531
532 // check for valid CSS units
533 if (!preg_match('/^auto$|^[+-]?[0-9]+\\.?(?:[0-9]+)?(?:px|em|ex|ch|%|in|cm|mm|pt|pc|rem|vh|vw|vmin|vmax)$/', $units)) {
534 // not valid, so check to see if it's only digits
535 if (preg_match('/\D/', $units)) {
536 // not digits, so set to default
537 $units = '400px';
538 }
539 else {
540 // found only digits, so append px
541 $units .= 'px';
542 }
543 }
544
545 return $units;
546 }
547
548 /**
549 * get coordinate for given address
550 * @link https://developers.google.com/maps/documentation/geocoding/intro
551 * @param string $address
552 * @param string $region
553 * @return array|false
554 */
555 protected static function getAddressCoordinates($address, $region) {
556 // only if we have an API key for server requests
557 $options = get_option(FLXMAP_PLUGIN_OPTIONS, array());
558 if (empty($options['apiServerKey'])) {
559 return false;
560 }
561
562 // try to get a cached answer first
563 $cacheKey = 'flxmap_' . md5("$address|$region");
564 $coords = get_transient($cacheKey);
565
566 if ($coords === false) {
567 // build Google Maps geocoding query
568 $args = array(
569 'address' => urlencode($address),
570 'key' => $options['apiServerKey'],
571 );
572 if (!empty($region)) {
573 $args['region'] = urlencode($region);
574 }
575 $url = add_query_arg($args, 'https://maps.googleapis.com/maps/api/geocode/json');
576
577 try {
578 // fetch coordinates
579 $response = wp_remote_get($url);
580
581 if (is_wp_error($response)) {
582 throw new Exception('http error = ' . $response->get_error_message());
583 }
584
585 $result = json_decode($response['body']);
586 if (!$result) {
587 throw new Exception("error decoding JSON\n" . $response['body']);
588 }
589
590 if ($result->status !== 'OK') {
591 if (!empty($result->error_message)) {
592 throw new Exception(sprintf('error retrieving address: %s; %s', $result->status, $result->error_message));
593 }
594 throw new Exception(sprintf('error retrieving address: %s', $result->status));
595 }
596
597 // success, return array with latitude and longitude
598 $location = $result->results[0]->geometry->location;
599 $coords = array($location->lat, $location->lng);
600
601 // save coordinates to prevent unnecessary requery
602 set_transient($cacheKey, $coords, MONTH_IN_SECONDS);
603 }
604 catch (Exception $e) {
605 $coords = "address: $address; " . $e->getMessage();
606 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
607 error_log(__METHOD__ . ': ' . $coords);
608
609 // save error to prevent unnecessary requery
610 set_transient($cacheKey, $coords, WEEK_IN_SECONDS);
611 }
612 }
613
614 // handle failure to map address to coordinates by returning false
615 if (!is_array($coords)) {
616 $coords = false;
617 }
618
619 return $coords;
620 }
621
622 /**
623 * test string to see if contents equate to yes/true
624 * @param string $text
625 * @return boolean
626 */
627 public static function isYes($text) {
628 return preg_match('/^(?:y|yes|true|1)$/i', $text);
629 }
630
631 /**
632 * test string to see if contents equate to no/false
633 * @param string $text
634 * @return boolean
635 */
636 public static function isNo($text) {
637 return preg_match('/^(?:n|no|false|0)$/i', $text);
638 }
639
640 /**
641 * test string to see if contents are map coordinates (latitude,longitude)
642 * @param string $text
643 * @return boolean
644 */
645 public static function isCoordinates($text) {
646 // TODO: handle degrees minutes seconds, degrees minutes.decimal, NSEW
647 return preg_match('/^-?[0-9]+(?:\.[0-9]+)\s*,\s*-?[0-9]+(?:\.[0-9]+)$/', $text);
648 }
649
650 /**
651 * test string to see if contents equate to a marker animation constant
652 * @param string $text
653 * @return boolean
654 */
655 public static function isMarkerAnimation($text) {
656 return preg_match('/^(?:drop|bounce|none)$/i', $text);
657 }
658
659 /**
660 * return standardised coordinates from text
661 * NB: assumes text passes isCoordinates() above
662 * @param string $text
663 * @return boolean
664 */
665 protected static function getCoordinates($text) {
666 // TODO: handle degrees minutes seconds, degrees minutes.decimal, NSEW
667 return str_replace(' ', '', $text);
668 }
669
670 /**
671 * decode HTML-encoded text and encode for JavaScript string
672 * @param string $text
673 * @return string
674 */
675 protected static function unhtml($text) {
676 return esc_js(html_entity_decode($text, ENT_QUOTES, get_option('blog_charset')));
677 }
678
679 }
680