Changeset 2460774
- Timestamp:
- 01/22/2021 07:00:54 AM (5 years ago)
- Location:
- everlytic
- Files:
-
- 12 added
- 20 edited
- 1 copied
-
tags/1.3 (copied) (copied from everlytic/trunk)
-
tags/1.3/index.php (modified) (2 diffs)
-
tags/1.3/src/Coupons (added)
-
tags/1.3/src/Coupons/EvCouponFormatter.php (added)
-
tags/1.3/src/Coupons/EvCouponRepository.php (added)
-
tags/1.3/src/Coupons/EvCoupons.php (added)
-
tags/1.3/src/EvPluginDeactivator.php (modified) (1 diff)
-
tags/1.3/src/Events/AbandonedCart/EvCart.php (modified) (2 diffs)
-
tags/1.3/src/Events/AbandonedCart/EvDispatcher.php (modified) (1 diff)
-
tags/1.3/src/EverlyticWoocommerce.php (modified) (7 diffs)
-
tags/1.3/src/Product/EvFeaturedProducts.php (modified) (1 diff)
-
tags/1.3/src/Product/EvProductCategories.php (modified) (1 diff)
-
tags/1.3/src/Product/EvProductSearch.php (modified) (3 diffs)
-
tags/1.3/src/Product/EvProducts.php (modified) (2 diffs)
-
tags/1.3/src/Store/EvStoreDetailsSaver.php (modified) (1 diff)
-
tags/1.3/src/Traits (added)
-
tags/1.3/src/Traits/EvAuthenticationTrait.php (added)
-
trunk/index.php (modified) (2 diffs)
-
trunk/src/Coupons (added)
-
trunk/src/Coupons/EvCouponFormatter.php (added)
-
trunk/src/Coupons/EvCouponRepository.php (added)
-
trunk/src/Coupons/EvCoupons.php (added)
-
trunk/src/EvPluginDeactivator.php (modified) (1 diff)
-
trunk/src/Events/AbandonedCart/EvCart.php (modified) (2 diffs)
-
trunk/src/Events/AbandonedCart/EvDispatcher.php (modified) (1 diff)
-
trunk/src/EverlyticWoocommerce.php (modified) (7 diffs)
-
trunk/src/Product/EvFeaturedProducts.php (modified) (1 diff)
-
trunk/src/Product/EvProductCategories.php (modified) (1 diff)
-
trunk/src/Product/EvProductSearch.php (modified) (3 diffs)
-
trunk/src/Product/EvProducts.php (modified) (2 diffs)
-
trunk/src/Store/EvStoreDetailsSaver.php (modified) (1 diff)
-
trunk/src/Traits (added)
-
trunk/src/Traits/EvAuthenticationTrait.php (added)
Legend:
- Unmodified
- Added
- Removed
-
everlytic/tags/1.3/index.php
r2394349 r2460774 27 27 require_once plugin_dir_path(__FILE__) . 'src/EvConstants.php'; 28 28 require_once plugin_dir_path(__FILE__) . 'src/Events/AbandonedCart/EvCart.php'; 29 require_once plugin_dir_path(__FILE__) . 'src/Coupons/EvCouponRepository.php'; 30 require_once plugin_dir_path(__FILE__) . 'src/Coupons/EvCoupons.php'; 29 31 require_once plugin_dir_path(__FILE__) . 'src/Events/AbandonedCart/EvAbandonedCartHydrator.php'; 30 32 require_once plugin_dir_path(__FILE__) . 'src/EvConstants.php'; … … 95 97 new EvProducts() 96 98 ), 99 new EvCoupons( 100 new EvCouponRepository() 101 ), 97 102 new EvUserSyncer( 98 103 new EvUserRepository(), -
everlytic/tags/1.3/src/EvPluginDeactivator.php
r2394223 r2460774 33 33 $this->databaseDeleter->delete(); 34 34 $this->evProductTrackingDeleter->delete(); 35 $timestamp = wp_next_scheduled('evAbandonedCartCronJob');36 wp_unschedule_event($timestamp, 'evAbandonedCartCronJob');37 35 delete_option('ev_db_version'); 38 36 delete_option('ev_store_hash'); -
everlytic/tags/1.3/src/Events/AbandonedCart/EvCart.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvCart 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 9 * @var EvProducts … … 30 33 public function get($requestData) 31 34 { 32 if ( is_user_logged_in() === false) {33 return new WP_REST_Response( 'User not logged in', 404);35 if ($this->authenticate() === false) { 36 return new WP_REST_Response($this->getError(), 401); 34 37 } 38 35 39 $parameters = $requestData->get_params(); 36 40 $email = $parameters['customer_email']; -
everlytic/tags/1.3/src/Events/AbandonedCart/EvDispatcher.php
r2394223 r2460774 27 27 public function schedule() 28 28 { 29 if (wp_next_scheduled('evAbandonedCartCronJob') === false) {30 wp_schedule_event(time(),'one_minute','evAbandonedCartCronJob');29 if (false === as_next_scheduled_action('ev_abandoned_cart_scheduler')) { 30 as_schedule_recurring_action( time(), 60, 'ev_abandoned_cart_scheduler'); 31 31 } 32 32 } -
everlytic/tags/1.3/src/EverlyticWoocommerce.php
r2407293 r2460774 70 70 */ 71 71 private $cart; 72 73 /** 74 * @var EvCoupons 75 */ 76 private $coupons; 72 77 73 78 /** … … 103 108 * @param EvHTTPClient $evHTTPClient 104 109 * @param EvCart $cart 110 * @param EvCoupons $coupons 105 111 * @param EvUserSyncer $evUserSyncer 112 * @param EvProductCategories $evProductCategories 106 113 * @param EvProductSearch $evProductSearch 107 * @param EvProductCategories $evProductCategories108 114 * @param EvAbandonedCartHydrator $abandonedCartHydrator 109 115 */ … … 120 126 EvHTTPClient $evHTTPClient, 121 127 EvCart $cart, 128 EvCoupons $coupons, 122 129 EvUserSyncer $evUserSyncer, 123 130 EvProductCategories $evProductCategories, … … 136 143 $this->evHTTPClient = $evHTTPClient; 137 144 $this->cart = $cart; 145 $this->coupons = $coupons; 138 146 $this->userSyncer = $evUserSyncer; 139 147 $this->evProductCategories = $evProductCategories; … … 150 158 add_action('init', [$this, 'registerUserSyncScheduler']); 151 159 add_action('ev_user_sync_scheduler', [$this, 'runUserSyncTask']); 160 add_action('init', [$this, 'abandonedCartCronScheduler']); 161 add_action('ev_abandoned_cart_scheduler', [$this->dispatcher, 'dispatcher']); 152 162 add_action('template_redirect', [$this->cartLogger, 'initialize']); 153 163 add_action('template_redirect', [$this->abandonedCartHydrator, 'hydrate']); 154 164 add_action('rest_api_init', [$this, 'registerRoutes']); 155 add_filter('cron_schedules', [$this, 'abandonedCartCronInterval']);156 165 add_action('wp', [$this->dispatcher, 'schedule']); 157 166 add_filter('woocommerce_product_data_store_cpt_get_products_query', [$this->evProductSearch, 'handleEVCustomSearch'], 10, 2 ); 158 add_action('evAbandonedCartCronJob', [$this->dispatcher, 'dispatcher']);159 167 add_action('woocommerce_created_customer', [$this, 'createLastUpdate'], 10, 3 ); 160 168 … … 190 198 * @return mixed 191 199 */ 192 public function abandonedCartCronInterval($schedules) 193 { 194 $schedules['one_minute'] = array( 195 'interval' => 60, 196 'display' => esc_html__('Every One Minute'), 197 ); 198 return $schedules; 200 public function abandonedCartCronScheduler() 201 { 202 if (false === as_next_scheduled_action('ev_abandoned_cart_scheduler')) { 203 as_schedule_recurring_action( time(), 60, 'ev_abandoned_cart_scheduler'); 204 } 199 205 } 200 206 … … 257 263 'methods' => WP_REST_Server::CREATABLE, 258 264 'callback' => [$this->cart, 'get'], 265 'permission_callback' => '__return_true' 266 ] 267 ); 268 269 register_rest_route(self::API_NAMESPACE, '/coupons', [ 270 'methods' => WP_REST_Server::READABLE, 271 'callback' => [$this->coupons, 'get'], 259 272 'permission_callback' => '__return_true' 260 273 ] -
everlytic/tags/1.3/src/Product/EvFeaturedProducts.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 require_once plugin_dir_path( __FILE__ ) . 'Utilities/EvFormatter.php'; 3 4 4 5 class EvFeaturedProducts 5 6 { 7 use EvAuthenticationTrait; 8 9 /** 10 * @return array|WP_REST_Response|null 11 */ 6 12 public function get() 7 13 { 14 if ($this->authenticate() === false) { 15 return new WP_REST_Response($this->getError(), 401); 16 } 17 8 18 $posts = wc_get_products([ 9 'post_type' => 'product',19 'post_type' => 'product', 10 20 'posts_per_page' => 10, 11 21 'post_status' => 'publish', 12 'post__in' => wc_get_featured_product_ids(),22 'post__in' => wc_get_featured_product_ids(), 13 23 ]); 14 24 -
everlytic/tags/1.3/src/Product/EvProductCategories.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvProductCategories 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 * @return array 9 * @return array[]|WP_REST_Response 7 10 */ 8 11 public function get() 9 12 { 13 if ($this->authenticate() === false) { 14 return new WP_REST_Response($this->getError(), 401); 15 } 16 10 17 $args = [ 11 'taxonomy' => 'product_cat',12 'orderby' => 'name',13 'show_count' => 0,14 'pad_counts' => 0,18 'taxonomy' => 'product_cat', 19 'orderby' => 'name', 20 'show_count' => 0, 21 'pad_counts' => 0, 15 22 'hierarchical' => 1, 16 'title_li' => '',17 'hide_empty' => 023 'title_li' => '', 24 'hide_empty' => 0 18 25 ]; 19 26 -
everlytic/tags/1.3/src/Product/EvProductSearch.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvProductSearch 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 9 * @var array … … 8 11 private $argument = []; 9 12 13 /** 14 * @param $requestData 15 * @return array|WP_REST_Response 16 */ 10 17 public function search($requestData) 11 18 { 19 if ($this->authenticate() === false) { 20 return new WP_REST_Response($this->getError(), 401); 21 } 22 12 23 $this->argument = ['status' => 'publish', 'limit' => 30, 'post_type' => 'product']; 13 24 $parameters = json_decode($requestData->get_body()); … … 18 29 $this->setPriceRange($parameters); 19 30 $this->setReturnOrder($parameters); 31 20 32 return EvFormatter::formatProducts(wc_get_products($this->argument), [], false); 21 33 } -
everlytic/tags/1.3/src/Product/EvProducts.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 require_once plugin_dir_path( __FILE__ ) . 'Utilities/EvFormatter.php'; 3 4 4 5 class EvProducts 5 6 { 7 use EvAuthenticationTrait; 8 9 /** 10 * @param $requestData 11 * @return array|WP_REST_Response|null 12 */ 6 13 public function get($requestData) 7 14 { 15 if ($this->authenticate() === false) { 16 return new WP_REST_Response($this->getError(), 401); 17 } 18 8 19 $parameters = $requestData->get_params(); 9 20 $posts = wc_get_products([ … … 35 46 } 36 47 48 /** 49 * @param $ids 50 * @return array|WP_REST_Response|null 51 */ 37 52 public function getByIds($ids) 38 53 { 54 if ($this->authenticate() === false) { 55 return new WP_REST_Response($this->getError(), 401); 56 } 57 39 58 if (empty($ids)) { 40 59 return null; -
everlytic/tags/1.3/src/Store/EvStoreDetailsSaver.php
r2394223 r2460774 17 17 public function save($requestData) 18 18 { 19 if ($this->authenticateStoreSetup() === false) { 20 return new WP_REST_Response('Incorrect store setup', 401); 21 } 22 19 23 $parameters = $requestData->get_params(); 24 20 25 return $this->isInvalid($parameters) 21 26 ? $this->handleFailure($parameters) 22 27 : $this->handleSuccess($parameters); 28 } 29 30 private function authenticateStoreSetup() 31 { 32 if (get_option("ev_install_url") === false && 33 get_option('ev_store_hash') === false && 34 get_option('ev_customer_hash') === false 35 ) { 36 $hasReferer = (is_null($_SERVER["HTTP_REFERER"]) === false && 37 empty($_SERVER["HTTP_REFERER"]) === false) ? parse_url($_SERVER["HTTP_REFERER"])['host'] : false; 38 $hasInstallUrl = (is_null($_REQUEST['ev_install_url']) === false && 39 empty($_REQUEST['ev_install_url']) === false) ? parse_url($_REQUEST['ev_install_url'])['host'] : false; 40 41 return $hasReferer === $hasInstallUrl; 42 } 23 43 } 24 44 -
everlytic/trunk/index.php
r2394349 r2460774 27 27 require_once plugin_dir_path(__FILE__) . 'src/EvConstants.php'; 28 28 require_once plugin_dir_path(__FILE__) . 'src/Events/AbandonedCart/EvCart.php'; 29 require_once plugin_dir_path(__FILE__) . 'src/Coupons/EvCouponRepository.php'; 30 require_once plugin_dir_path(__FILE__) . 'src/Coupons/EvCoupons.php'; 29 31 require_once plugin_dir_path(__FILE__) . 'src/Events/AbandonedCart/EvAbandonedCartHydrator.php'; 30 32 require_once plugin_dir_path(__FILE__) . 'src/EvConstants.php'; … … 95 97 new EvProducts() 96 98 ), 99 new EvCoupons( 100 new EvCouponRepository() 101 ), 97 102 new EvUserSyncer( 98 103 new EvUserRepository(), -
everlytic/trunk/src/EvPluginDeactivator.php
r2394223 r2460774 33 33 $this->databaseDeleter->delete(); 34 34 $this->evProductTrackingDeleter->delete(); 35 $timestamp = wp_next_scheduled('evAbandonedCartCronJob');36 wp_unschedule_event($timestamp, 'evAbandonedCartCronJob');37 35 delete_option('ev_db_version'); 38 36 delete_option('ev_store_hash'); -
everlytic/trunk/src/Events/AbandonedCart/EvCart.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvCart 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 9 * @var EvProducts … … 30 33 public function get($requestData) 31 34 { 32 if ( is_user_logged_in() === false) {33 return new WP_REST_Response( 'User not logged in', 404);35 if ($this->authenticate() === false) { 36 return new WP_REST_Response($this->getError(), 401); 34 37 } 38 35 39 $parameters = $requestData->get_params(); 36 40 $email = $parameters['customer_email']; -
everlytic/trunk/src/Events/AbandonedCart/EvDispatcher.php
r2394223 r2460774 27 27 public function schedule() 28 28 { 29 if (wp_next_scheduled('evAbandonedCartCronJob') === false) {30 wp_schedule_event(time(),'one_minute','evAbandonedCartCronJob');29 if (false === as_next_scheduled_action('ev_abandoned_cart_scheduler')) { 30 as_schedule_recurring_action( time(), 60, 'ev_abandoned_cart_scheduler'); 31 31 } 32 32 } -
everlytic/trunk/src/EverlyticWoocommerce.php
r2407293 r2460774 70 70 */ 71 71 private $cart; 72 73 /** 74 * @var EvCoupons 75 */ 76 private $coupons; 72 77 73 78 /** … … 103 108 * @param EvHTTPClient $evHTTPClient 104 109 * @param EvCart $cart 110 * @param EvCoupons $coupons 105 111 * @param EvUserSyncer $evUserSyncer 112 * @param EvProductCategories $evProductCategories 106 113 * @param EvProductSearch $evProductSearch 107 * @param EvProductCategories $evProductCategories108 114 * @param EvAbandonedCartHydrator $abandonedCartHydrator 109 115 */ … … 120 126 EvHTTPClient $evHTTPClient, 121 127 EvCart $cart, 128 EvCoupons $coupons, 122 129 EvUserSyncer $evUserSyncer, 123 130 EvProductCategories $evProductCategories, … … 136 143 $this->evHTTPClient = $evHTTPClient; 137 144 $this->cart = $cart; 145 $this->coupons = $coupons; 138 146 $this->userSyncer = $evUserSyncer; 139 147 $this->evProductCategories = $evProductCategories; … … 150 158 add_action('init', [$this, 'registerUserSyncScheduler']); 151 159 add_action('ev_user_sync_scheduler', [$this, 'runUserSyncTask']); 160 add_action('init', [$this, 'abandonedCartCronScheduler']); 161 add_action('ev_abandoned_cart_scheduler', [$this->dispatcher, 'dispatcher']); 152 162 add_action('template_redirect', [$this->cartLogger, 'initialize']); 153 163 add_action('template_redirect', [$this->abandonedCartHydrator, 'hydrate']); 154 164 add_action('rest_api_init', [$this, 'registerRoutes']); 155 add_filter('cron_schedules', [$this, 'abandonedCartCronInterval']);156 165 add_action('wp', [$this->dispatcher, 'schedule']); 157 166 add_filter('woocommerce_product_data_store_cpt_get_products_query', [$this->evProductSearch, 'handleEVCustomSearch'], 10, 2 ); 158 add_action('evAbandonedCartCronJob', [$this->dispatcher, 'dispatcher']);159 167 add_action('woocommerce_created_customer', [$this, 'createLastUpdate'], 10, 3 ); 160 168 … … 190 198 * @return mixed 191 199 */ 192 public function abandonedCartCronInterval($schedules) 193 { 194 $schedules['one_minute'] = array( 195 'interval' => 60, 196 'display' => esc_html__('Every One Minute'), 197 ); 198 return $schedules; 200 public function abandonedCartCronScheduler() 201 { 202 if (false === as_next_scheduled_action('ev_abandoned_cart_scheduler')) { 203 as_schedule_recurring_action( time(), 60, 'ev_abandoned_cart_scheduler'); 204 } 199 205 } 200 206 … … 257 263 'methods' => WP_REST_Server::CREATABLE, 258 264 'callback' => [$this->cart, 'get'], 265 'permission_callback' => '__return_true' 266 ] 267 ); 268 269 register_rest_route(self::API_NAMESPACE, '/coupons', [ 270 'methods' => WP_REST_Server::READABLE, 271 'callback' => [$this->coupons, 'get'], 259 272 'permission_callback' => '__return_true' 260 273 ] -
everlytic/trunk/src/Product/EvFeaturedProducts.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 require_once plugin_dir_path( __FILE__ ) . 'Utilities/EvFormatter.php'; 3 4 4 5 class EvFeaturedProducts 5 6 { 7 use EvAuthenticationTrait; 8 9 /** 10 * @return array|WP_REST_Response|null 11 */ 6 12 public function get() 7 13 { 14 if ($this->authenticate() === false) { 15 return new WP_REST_Response($this->getError(), 401); 16 } 17 8 18 $posts = wc_get_products([ 9 'post_type' => 'product',19 'post_type' => 'product', 10 20 'posts_per_page' => 10, 11 21 'post_status' => 'publish', 12 'post__in' => wc_get_featured_product_ids(),22 'post__in' => wc_get_featured_product_ids(), 13 23 ]); 14 24 -
everlytic/trunk/src/Product/EvProductCategories.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvProductCategories 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 * @return array 9 * @return array[]|WP_REST_Response 7 10 */ 8 11 public function get() 9 12 { 13 if ($this->authenticate() === false) { 14 return new WP_REST_Response($this->getError(), 401); 15 } 16 10 17 $args = [ 11 'taxonomy' => 'product_cat',12 'orderby' => 'name',13 'show_count' => 0,14 'pad_counts' => 0,18 'taxonomy' => 'product_cat', 19 'orderby' => 'name', 20 'show_count' => 0, 21 'pad_counts' => 0, 15 22 'hierarchical' => 1, 16 'title_li' => '',17 'hide_empty' => 023 'title_li' => '', 24 'hide_empty' => 0 18 25 ]; 19 26 -
everlytic/trunk/src/Product/EvProductSearch.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 3 4 class EvProductSearch 4 5 { 6 use EvAuthenticationTrait; 7 5 8 /** 6 9 * @var array … … 8 11 private $argument = []; 9 12 13 /** 14 * @param $requestData 15 * @return array|WP_REST_Response 16 */ 10 17 public function search($requestData) 11 18 { 19 if ($this->authenticate() === false) { 20 return new WP_REST_Response($this->getError(), 401); 21 } 22 12 23 $this->argument = ['status' => 'publish', 'limit' => 30, 'post_type' => 'product']; 13 24 $parameters = json_decode($requestData->get_body()); … … 18 29 $this->setPriceRange($parameters); 19 30 $this->setReturnOrder($parameters); 31 20 32 return EvFormatter::formatProducts(wc_get_products($this->argument), [], false); 21 33 } -
everlytic/trunk/src/Product/EvProducts.php
r2394223 r2460774 1 1 <?php 2 require_once plugin_dir_path(__FILE__) . '../Traits/EvAuthenticationTrait.php'; 2 3 require_once plugin_dir_path( __FILE__ ) . 'Utilities/EvFormatter.php'; 3 4 4 5 class EvProducts 5 6 { 7 use EvAuthenticationTrait; 8 9 /** 10 * @param $requestData 11 * @return array|WP_REST_Response|null 12 */ 6 13 public function get($requestData) 7 14 { 15 if ($this->authenticate() === false) { 16 return new WP_REST_Response($this->getError(), 401); 17 } 18 8 19 $parameters = $requestData->get_params(); 9 20 $posts = wc_get_products([ … … 35 46 } 36 47 48 /** 49 * @param $ids 50 * @return array|WP_REST_Response|null 51 */ 37 52 public function getByIds($ids) 38 53 { 54 if ($this->authenticate() === false) { 55 return new WP_REST_Response($this->getError(), 401); 56 } 57 39 58 if (empty($ids)) { 40 59 return null; -
everlytic/trunk/src/Store/EvStoreDetailsSaver.php
r2394223 r2460774 17 17 public function save($requestData) 18 18 { 19 if ($this->authenticateStoreSetup() === false) { 20 return new WP_REST_Response('Incorrect store setup', 401); 21 } 22 19 23 $parameters = $requestData->get_params(); 24 20 25 return $this->isInvalid($parameters) 21 26 ? $this->handleFailure($parameters) 22 27 : $this->handleSuccess($parameters); 28 } 29 30 private function authenticateStoreSetup() 31 { 32 if (get_option("ev_install_url") === false && 33 get_option('ev_store_hash') === false && 34 get_option('ev_customer_hash') === false 35 ) { 36 $hasReferer = (is_null($_SERVER["HTTP_REFERER"]) === false && 37 empty($_SERVER["HTTP_REFERER"]) === false) ? parse_url($_SERVER["HTTP_REFERER"])['host'] : false; 38 $hasInstallUrl = (is_null($_REQUEST['ev_install_url']) === false && 39 empty($_REQUEST['ev_install_url']) === false) ? parse_url($_REQUEST['ev_install_url'])['host'] : false; 40 41 return $hasReferer === $hasInstallUrl; 42 } 23 43 } 24 44
Note: See TracChangeset
for help on using the changeset viewer.