PluginProbe
Tracking Script Manager / 1.0.5
Tracking Script Manager v1.0.5
trunk 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 All 33 releases
tracking-script-manager / tracking-scripts.php

tracking-scripts.php in Tracking Script Manager 1.0.5, at tracking-scripts.php

517 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Tracking Script Manager
4 * Plugin URI: http://wordpress.org/plugins/tracking-script-manager/
5 * Description: A plugin that allows you to add tracking scripts to your site.
6 * Version: 1.0.5
7 * Author: Red8 Interactive
8 * Author URI: http://red8interactive.com
9 * License: GPL2
10 */
11
12 /*
13 Copyright 2014 Red8 Interactive (email : james@red8interactive.com)
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 2
18 of the License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 */
29
30 if( !class_exists('Tracking_Scripts') ) {
31
32 class Tracking_Scripts {
33
34 function __construct() {
35 self::define_constants();
36 self::load_hooks();
37 }
38
39 /**
40 * Defines plugin constants
41 */
42 public static function define_constants() {
43 define('TRACKING_SCRIPT_PATH', plugins_url( ' ', __FILE__ ) );
44 define('TRACKING_SCRIPT_BASENAME', plugin_basename( __FILE__ ));
45
46 define('WP_TRACKING_SCRIPTS_OPTION_GROUP', 'tracking_scripts_options' );
47 define('WP_HEADER_TRACKING_SCRIPT', 'header_tracking_script_code' );
48 define('WP_FOOTER_TRACKING_SCRIPT', 'footer_tracking_script_code' );
49 define('WP_PAGE_TRACKING_SCRIPT', 'page_tracking_script_code' );
50 define('WP_NEW_HEADER_TRACKING_SCRIPT', 'new_header_tracking_script_code' );
51 define('WP_NEW_FOOTER_TRACKING_SCRIPT', 'new_footer_tracking_script_code' );
52 define('WP_NEW_PAGE_TRACKING_SCRIPT', 'new_page_tracking_script_code' );
53 define('WP_NEW_PAGE_TRACKING_SCRIPT_ID', 'new_page_tracking_script_code_id' );
54 define('WP_NEW_PAGE_TRACKING_SCRIPT_LOCATION', 'new_page_tracking_script_code_location' );
55 define('WP_NEW_PAGE_TRACKING_SCRIPT_GLOBAL', 'new_page_tracking_script_code_global' );
56 define('WP_PAGE_TRACKING_SCRIPT_COUNT', 'page_tracking_script_count');
57 }
58
59 public static function load_hooks() {
60 add_action('wp_head', array(__CLASS__, 'find_header_tracking_codes'));
61
62 add_action('wp_footer', array(__CLASS__, 'find_footer_tracking_codes'));
63
64 add_action('admin_menu', array(__CLASS__, 'tracking_scripts_create_menu'));
65
66 add_action('admin_enqueue_scripts', array(__CLASS__, 'tracking_scripts_admin_scripts'));
67
68 add_action('admin_init', array(__CLASS__, 'initialize_admin_posts'));
69
70 add_action( 'wp_ajax_tracking_scripts_get_posts', array(__CLASS__, 'tracking_scripts_posts_ajax_handler') );
71 add_action( 'wp_ajax_nopriv_tracking_scripts_get_posts', array(__CLASS__, 'tracking_scripts_posts_ajax_handler') );
72
73 add_action( 'wp_ajax_tracking_scripts_get_post_content', array(__CLASS__, 'tracking_scripts_post_content_ajax_handler') );
74 add_action( 'wp_ajax_nopriv_tracking_scripts_get_posts', array(__CLASS__, 'tracking_scripts_posts_ajax_handler') );
75 }
76
77 /*************************************************
78 * Front End
79 **************************************************/
80
81 // Header Tracking Codes
82 public static function find_header_tracking_codes() {
83 $header_scripts = unserialize(get_option(WP_HEADER_TRACKING_SCRIPT));
84
85 if($header_scripts) {
86 foreach($header_scripts as $script) {
87 if($script->active) {
88 echo html_entity_decode(esc_attr($script->script_code), ENT_QUOTES, 'cp1252');
89 }
90 }
91 }
92
93 $page_scripts = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT));
94
95 if($page_scripts) {
96 global $wp_query;
97 $post_id = $wp_query->post->ID;
98 foreach($page_scripts as $script) {
99 if($script->active && $script->location == 'header' && $script->page_id == $post_id) {
100 echo html_entity_decode(esc_attr($script->script_code), ENT_QUOTES, 'cp1252');
101 }
102 }
103 }
104 }
105
106
107 // Footer Tracking Codes
108 public static function find_footer_tracking_codes() {
109 $footer_scripts = unserialize(get_option(WP_FOOTER_TRACKING_SCRIPT));
110
111 if($footer_scripts) {
112 foreach($footer_scripts as $script) {
113 if($script->active) {
114 echo html_entity_decode(esc_attr($script->script_code), ENT_QUOTES, 'cp1252');
115 }
116 }
117 }
118
119 $page_scripts = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT));
120
121 if($page_scripts) {
122 global $wp_query;
123 $post_id = $wp_query->post->ID;
124 foreach($page_scripts as $script) {
125 if($script->active && $script->location == 'footer' && $script->page_id == $post_id) {
126 echo html_entity_decode(esc_attr($script->script_code), ENT_QUOTES, 'cp1252');
127 }
128 }
129 }
130 }
131
132 /*************************************************
133 * Admin Area
134 **************************************************/
135
136 public static function tracking_scripts_create_menu() {
137 add_menu_page('Tracking Scripts Manager', 'Tracking Scripts Manager', 'administrator', __FILE__, array(__CLASS__, 'tracking_options'), '');
138 add_action('admin_init', array(__CLASS__, 'register_tracking_scripts_settings'));
139 }
140
141 public static function register_tracking_scripts_settings() {
142 register_setting( WP_TRACKING_SCRIPTS_OPTION_GROUP, WP_HEADER_TRACKING_SCRIPT, 'esc_textarea' );
143 register_setting( WP_TRACKING_SCRIPTS_OPTION_GROUP, WP_FOOTER_TRACKING_SCRIPT, 'esc_textarea' );
144 }
145
146 public static function tracking_scripts_admin_tabs( $current = 'add_new' ) {
147 $tabs = array('add_new' => 'Add New', 'global' => 'Global', 'pages' => 'Specific Location');
148 echo '<div id="tracking_scripts_admin"><br></div>';
149 echo '<h2 style="font-size: 22px; font-weight: bold; margin: 10px 0 40px;">Tracking Scripts Manager Pro</h2>';
150 echo '<h2 class="nav-tab-wrapper">';
151 foreach($tabs as $tab => $name) {
152 $class = ($tab == $current) ? ' nav-tab-active' : '';
153 echo "<a class='nav-tab$class' href='?page=".TRACKING_SCRIPT_BASENAME."&tab=$tab'>$name</a>";
154 }
155 echo '</h2>';
156 }
157
158 public static function tracking_options() {
159 global $pagenow;
160 $settings = get_option('tracking_scripts_settings');
161
162 if(isset($_GET['tab'])) {
163 self::tracking_scripts_admin_tabs($_GET['tab']);
164 $pagenow = $_GET['tab'];
165 } else {
166 self::tracking_scripts_admin_tabs('add_new');
167 $pagenow = 'add_new';
168 }
169 ?>
170 <div class="wrap tracking_scripts_wrap">
171 <form method="post" action="<?php echo get_admin_url(); ?>admin-post.php">
172 <?php settings_fields(WP_TRACKING_SCRIPTS_OPTION_GROUP); ?>
173 <?php do_settings_sections(WP_TRACKING_SCRIPTS_OPTION_GROUP); ?>
174 <?php if($pagenow == 'add_new') { ?>
175 <?php include_once('templates/tracking-scripts-manager-pro-add-new.php'); ?>
176 <?php } else if($pagenow == 'global') { ?>
177 <?php include_once('templates/tracking-scripts-manager-pro-edit-global.php'); ?>
178 <?php } else { ?>
179 <?php include_once('templates/tracking-scripts-manager-pro-edit-single-post.php'); ?>
180 <?php } ?>
181 </form>
182 </div>
183 <?php
184
185 }
186
187
188 // Admin Scripts
189 public static function tracking_scripts_admin_scripts() {
190 wp_enqueue_script('jquery');
191 wp_enqueue_script('jquery-ui-sortable');
192
193 wp_register_style('tracking-scripts-main', plugins_url('/css/main.css', __FILE__));
194 wp_enqueue_style('tracking-scripts-main');
195
196 wp_enqueue_script( 'tracking_script_js', plugin_dir_url(__FILE__) . '/js/main.js', array(), '', true );
197 wp_localize_script( 'tracking_script_js', 'ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
198 }
199
200
201 // Ajax Functions
202 public static function tracking_scripts_posts_ajax_handler() {
203 $post_type = ($_POST['postType']) ? esc_attr($_POST['postType']) : 'post';
204
205 $args = array(
206 'post_type' => $post_type,
207 'posts_per_page' => -1,
208 'orderby' => 'name',
209 'order' => 'ASC'
210 );
211
212 ob_start();
213
214 $query = new WP_Query($args);
215 echo '<option value="none" id="none">Choose '.ucwords($post_type).'</option>';
216 while($query->have_posts()) : $query->the_post();
217 echo '<option value="'.get_the_ID().'" id="'.get_the_ID().'">'.ucwords(get_the_title()).'</option>';
218 endwhile;
219 wp_reset_postdata();
220
221 echo ob_get_clean();
222 die();
223 }
224
225 public static function tracking_scripts_post_content_ajax_handler() {
226 $current_page_id = ($_POST['postID']) ? esc_attr($_POST['postID']) : null;
227
228 if($current_page_id) {
229 $page_scripts = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT)); $i = 1;
230 $page_scripts_array = array();
231 foreach($page_scripts as $script) {
232 if($script->page_id == $current_page_id) {
233 $page_scripts_array[$script->location][] = $script;
234 }
235 }
236
237 ob_start();
238 ?>
239 <div id="tracking_scripts_<?php echo $current_page_id; ?>" class="tracking_scripts">
240 <?php
241 $page_header_scripts = $page_scripts_array['header'];
242 $page_footer_scripts = $page_scripts_array['footer'];
243 ?>
244 <h3>Header</h3>
245 <?php if($page_header_scripts) { ?>
246 <ul class="tracking_script_list">
247 <?php $i = 1; ?>
248 <?php foreach($page_header_scripts as $script) { ?>
249 <div class="tracking_script">
250 <i class="fa fa-sort" title="Drag to Sort"></i>
251 <p><?php echo $i; ?></p>
252 <div class="script_info">
253 <input type="text" name="page_script_<?php echo $script->script_id; ?>_name" value="<?php echo $script->script_name; ?>" readonly="readonly">
254 <input type="text" name="page_script_<?php echo $script->script_id; ?>_code" value="<?php echo $script->script_code; ?>" readonly="readonly">
255 </div>
256 <i class="active_tracking fa <?php if($script->active === true) { echo 'fa-check-circle'; } else { echo 'fa-circle-o'; } ?>" title="<?php if($script->active === true) { echo 'Deactivate Script'; } else { echo 'Activate Script'; } ?>"></i>
257 <i class="edit_tracking fa fa-edit" title="Edit Script"></i>
258 <i class="delete_tracking fa fa-times" title="Delete Script"></i>
259 <input type="hidden" class="script_order" name="page_script_<?php echo $script->script_id; ?>_order" value="<?php echo $i; ?>">
260 <input type="hidden" class="script_active" name="page_script_<?php echo $script->script_id; ?>_active" value="<?php if($script->active === true) { echo 'true'; } else { echo 'false'; } ?>">
261 <input type="hidden" class="script_exists" name="page_script_<?php echo $script->script_id; ?>_exists" value="true">
262 </div>
263 <?php $i++; ?>
264 <?php } ?>
265 </ul>
266 <?php } ?>
267 <h3>Footer</h3>
268 <?php if($page_footer_scripts) { ?>
269 <ul class="tracking_script_list">
270 <?php foreach($page_footer_scripts as $script) { ?>
271 <div class="tracking_script">
272 <i class="fa fa-sort" title="Drag to Sort"></i>
273 <p><?php echo $i; ?></p>
274 <div class="script_info">
275 <input type="text" name="page_script_<?php echo $script->script_id; ?>_name" value="<?php echo $script->script_name; ?>" readonly="readonly">
276 <input type="text" name="page_script_<?php echo $script->script_id; ?>_code" value="<?php echo $script->script_code; ?>" readonly="readonly">
277 </div>
278 <i class="active_tracking fa <?php if($script->active === true) { echo 'fa-check-circle'; } else { echo 'fa-circle-o'; } ?>" title="<?php if($script->active === true) { echo 'Deactivate Script'; } else { echo 'Activate Script'; } ?>"></i>
279 <i class="edit_tracking fa fa-edit" title="Edit Script"></i>
280 <i class="delete_tracking fa fa-times" title="Delete Script"></i>
281 <input type="hidden" class="script_order" name="page_script_<?php echo $script->script_id; ?>_order" value="<?php echo $i; ?>">
282 <input type="hidden" class="script_active" name="page_script_<?php echo $script->script_id; ?>_active" value="<?php if($script->active === true) { echo 'true'; } else { echo 'false'; } ?>">
283 <input type="hidden" class="script_exists" name="page_script_<?php echo $script->script_id; ?>_exists" value="true">
284 </div>
285 <?php $i++; ?>
286 <?php } ?>
287 </ul>
288 <?php } ?>
289 </div>
290 <?php
291 echo ob_get_clean();
292 die();
293 }
294 }
295
296 // Admin Hooks
297 public static function initialize_admin_posts() {
298 add_action('admin_post_save_new_tracking_codes', array(__CLASS__, 'save_new_tracking_codes')); // If the user is logged in
299 add_action('admin_post_nopriv_save_new_tracking_codes', array(__CLASS__, 'save_new_tracking_codes')); // If the user in not logged in
300
301 add_action('admin_post_update_tracking_codes', array(__CLASS__, 'update_tracking_codes')); // If the user is logged in
302 add_action('admin_post_nopriv_update_tracking_codes', array(__CLASS__, 'update_tracking_codes')); // If the user in not logged in
303
304 add_action('admin_post_update_page_tracking_codes', array(__CLASS__, 'update_page_tracking_codes')); // If the user is logged in
305 add_action('admin_post_nopriv_update_page_tracking_codes', array(__CLASS__, 'update_page_tracking_codes')); // If the user in not logged in
306 }
307
308
309
310 // Save New Tracking Codes
311 public static function save_new_tracking_codes() {
312 $header_scripts = unserialize(get_option(WP_HEADER_TRACKING_SCRIPT));
313 $footer_scripts = unserialize(get_option(WP_FOOTER_TRACKING_SCRIPT));
314 $page_scripts = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT));
315
316 if(!$header_scripts) {
317 $header_scripts = array();
318 }
319
320 if(!$footer_scripts) {
321 $footer_scripts = array();
322 }
323
324 if($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_GLOBAL] && $_POST[WP_NEW_PAGE_TRACKING_SCRIPT_GLOBAL] == 'yes') {
325 $tracking_location = esc_attr($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_LOCATION]);
326
327 if($tracking_location == 'header') {
328 $tracking = new Tracking_Script();
329 $tracking->script_name = sanitize_text_field($_POST['new_page_script_name']);
330 $tracking->script_code = stripslashes(esc_textarea($_POST[WP_NEW_PAGE_TRACKING_SCRIPT]));
331 $tracking->active = true;
332 $tracking->order = count($header_scripts);
333 $tracking->location = 'header';
334 $header_scripts[] = $tracking;
335 update_option(WP_HEADER_TRACKING_SCRIPT, serialize($header_scripts));
336 } else if($tracking_location == 'footer') {
337 $tracking = new Tracking_Script();
338 $tracking->script_name = sanitize_text_field($_POST['new_page_script_name']);
339 $tracking->script_code = stripslashes(esc_textarea($_POST[WP_NEW_PAGE_TRACKING_SCRIPT]));
340 $tracking->active = true;
341 $tracking->order = count($footer_scripts);
342 $tracking->location = 'footer';
343 $footer_scripts[] = $tracking;
344 update_option(WP_FOOTER_TRACKING_SCRIPT, serialize($footer_scripts));
345 }
346 } else {
347 if($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_ID] && $_POST[WP_NEW_PAGE_TRACKING_SCRIPT]) {
348 $tracking_pagecount = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT_COUNT));
349 if(!$tracking_pagecount) {
350 $tracking_pagecount = 1;
351 } else {
352 $tracking_pagecount++;
353 }
354 update_option(WP_PAGE_TRACKING_SCRIPT_COUNT, serialize($tracking_pagecount));
355
356 $tracking = new Tracking_Script();
357 $tracking->script_name = sanitize_text_field($_POST['new_page_script_name']);
358 $tracking->script_code = stripslashes(esc_textarea($_POST[WP_NEW_PAGE_TRACKING_SCRIPT]));
359 $tracking->active = true;
360 $tracking->page_id = esc_attr($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_ID]);
361 $tracking->location = esc_attr($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_LOCATION]);
362 $tracking->script_id = $tracking_pagecount;
363 $page_scripts[] = $tracking;
364 update_option(WP_PAGE_TRACKING_SCRIPT, serialize($page_scripts));
365 }
366 }
367
368 $redirect_url = get_admin_url().'admin.php?page='.TRACKING_SCRIPT_BASENAME;
369 if($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_GLOBAL] == 'yes') {
370 $redirect_url = get_admin_url().'admin.php?page='.TRACKING_SCRIPT_BASENAME.'&tab=global';
371 } else if($_POST[WP_NEW_PAGE_TRACKING_SCRIPT_ID] && $_POST[WP_NEW_PAGE_TRACKING_SCRIPT]) {
372 $redirect_url = get_admin_url().'admin.php?page='.TRACKING_SCRIPT_BASENAME.'&tab=pages';
373 }
374
375 wp_redirect($redirect_url);
376 exit();
377 }
378
379
380 // Update Global Codes
381 public static function update_tracking_codes() {
382 $header_scripts = unserialize(get_option(WP_HEADER_TRACKING_SCRIPT));
383 $footer_scripts = unserialize(get_option(WP_FOOTER_TRACKING_SCRIPT));
384
385 $i = 1;
386 foreach($header_scripts as $script) {
387 if($_POST['header_script_'.$i.'_name']) {
388 $script->script_name = sanitize_text_field($_POST['header_script_'.$i.'_name']);
389 }
390 if($_POST['header_script_'.$i.'_code']) {
391 $script->script_code = stripslashes(esc_textarea($_POST['header_script_'.$i.'_code']));
392 }
393 if($_POST['header_script_'.$i.'_active']) {
394 if($_POST['header_script_'.$i.'_active'] === 'false') {
395 $script->active = false;
396 } else {
397 $script->active = true;
398 }
399 }
400 if($_POST['header_script_'.$i.'_order']) {
401 $order = filter_input(INPUT_POST, 'header_script_'.$i.'_order', FILTER_VALIDATE_INT);
402 if(is_int($order)) {
403 $script->order = $order;
404 }
405 }
406 if($_POST['header_script_'.$i.'_exists']) {
407 if($_POST['header_script_'.$i.'_exists'] === 'false') {
408 unset($header_scripts[$i-1]);
409 }
410 }
411
412 $i++;
413 }
414
415 $i = 1;
416 foreach($footer_scripts as $script) {
417 if($_POST['footer_script_'.$i.'_name']) {
418 $script->script_name = sanitize_text_field($_POST['footer_script_'.$i.'_name']);
419 }
420 if($_POST['footer_script_'.$i.'_code']) {
421 $script->script_code = stripslashes(esc_textarea($_POST['footer_script_'.$i.'_code']));
422 }
423 if($_POST['footer_script_'.$i.'_active']) {
424 if($_POST['footer_script_'.$i.'_active'] === 'false') {
425 $script->active = false;
426 } else {
427 $script->active = true;
428 }
429 }
430 if($_POST['footer_script_'.$i.'_order']) {
431 $order = filter_input(INPUT_POST, 'footer_script_'.$i.'_order', FILTER_VALIDATE_INT);
432 if(is_int($order)) {
433 $script->order = $order;
434 }
435 }
436 if($_POST['footer_script_'.$i.'_exists']) {
437 if($_POST['footer_script_'.$i.'_exists'] === 'false') {
438 unset($footer_scripts[$i-1]);
439 }
440 }
441
442 $i++;
443 }
444
445 usort($header_scripts, array(__CLASS__, 'compare_order'));
446 usort($footer_scripts, array(__CLASS__, 'compare_order'));
447
448
449 update_option(WP_HEADER_TRACKING_SCRIPT, serialize($header_scripts));
450 update_option(WP_FOOTER_TRACKING_SCRIPT, serialize($footer_scripts));
451
452 wp_redirect(get_admin_url().'admin.php?page='.TRACKING_SCRIPT_BASENAME.'&tab=global');
453 exit();
454 }
455
456
457 // Update Page Specific Codes
458 public static function update_page_tracking_codes() {
459 $page_scripts = unserialize(get_option(WP_PAGE_TRACKING_SCRIPT));
460
461 $index = 0;
462 foreach($page_scripts as $script) {
463 $script_id = $script->script_id;
464 if($_POST['page_script_'.$script_id.'_name']) {
465 $script->script_name = sanitize_text_field($_POST['page_script_'.$script_id.'_name']);
466 }
467 if($_POST['page_script_'.$script_id.'_code']) {
468 $script->script_code = stripslashes(esc_textarea($_POST['page_script_'.$script_id.'_code']));
469 }
470 if($_POST['page_script_'.$script_id.'_active']) {
471 if($_POST['page_script_'.$script_id.'_active'] === 'false') {
472 $script->active = false;
473 } else {
474 $script->active = true;
475 }
476 }
477 if($_POST['page_script_'.$script_id.'_order']) {
478 $order = filter_input(INPUT_POST, 'page_script_'.$script_id.'_order', FILTER_VALIDATE_INT);
479 if(is_int($order)) {
480 $script->order = $order;
481 }
482 }
483 if($_POST['page_script_'.$script_id.'_exists']) {
484 if($_POST['page_script_'.$script_id.'_exists'] === 'false') {
485 unset($page_scripts[$index]);
486 }
487 }
488 $index++;
489 }
490
491 usort($page_scripts, array(__CLASS__, 'compare_order'));
492
493 update_option(WP_PAGE_TRACKING_SCRIPT, serialize($page_scripts));
494
495 wp_redirect(get_admin_url().'admin.php?page='.TRACKING_SCRIPT_BASENAME.'&tab=pages');
496 exit();
497 }
498
499 public static function compare_order($a, $b) {
500 return ($a->order < $b->order) ? -1 : 1;
501 }
502 }
503
504 $class['Tracking_Scripts'] = new Tracking_Scripts();
505 }
506
507 class Tracking_Script {
508 public $script_name;
509 public $script_code;
510 public $active;
511 public $order;
512
513 function __construct() {
514
515 }
516 }
517 ?>