Changeset 3255254
- Timestamp:
- 03/13/2025 10:04:09 AM (12 months ago)
- Location:
- extendago-wp-connection/trunk
- Files:
-
- 8 edited
-
extendago-wp-connection.php (modified) (1 diff)
-
includes/admin/class-extendago-wp-connection-admin.php (modified) (9 diffs)
-
includes/api/class-arture-web-api.php (modified) (2 diffs)
-
includes/api/class-extendago-web-api-functions.php (modified) (2 diffs)
-
includes/api/class-extendago-web-api.php (modified) (1 diff)
-
includes/cronjob/class-extendago-cronjob-functions.php (modified) (11 diffs)
-
includes/woocommerce/class-extendago-woocommerce-functions.php (modified) (5 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
extendago-wp-connection/trunk/extendago-wp-connection.php
r3226185 r3255254 4 4 Plugin URI: https://extendago-connect.com/ 5 5 Description: The Wordpress plugin for connecting Woocommerce with Extenda GO / Wallmob. You can manage your products inside Extenda GO or make your webshop as leading foor product manangement. You Stock changes will be two-way binding. 6 Version: 1.6. 06 Version: 1.6.1 7 7 Requires Plugins: woocommerce 8 8 Author: Arture B.V. -
extendago-wp-connection/trunk/includes/admin/class-extendago-wp-connection-admin.php
r3226185 r3255254 110 110 add_settings_field('extendago_update_images', esc_html(__('Product images', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_update_images_element' ), 'extendago_import_options', 'import_section'); 111 111 add_settings_field('extendago_new_product_status', esc_html(__('Product status', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_new_product_status_element' ), 'extendago_import_options', 'import_section'); 112 add_settings_field('extendago_ Off_images_product_status', esc_html(__('Product status by Off image', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_Off_images_product_status_element' ), 'extendago_import_options', 'import_section');112 add_settings_field('extendago_no_images_product_status', esc_html(__('Product status by no image', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_no_images_product_status_element' ), 'extendago_import_options', 'import_section'); 113 113 add_settings_field('extendago_excerpt', esc_html(__('Short description', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_excerpt_element' ), 'extendago_import_options', 'import_section'); 114 114 add_settings_field('extendago_hide_featured_image_from_gallery', esc_html(__('Featured image', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_hide_featured_image_from_gallery_element' ), 'extendago_import_options', 'import_section'); … … 125 125 else{ 126 126 add_settings_field('extendago_export_sale_price', esc_html(__('Export sale price', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_export_sale_price_element' ), 'extendago_import_options', 'import_section'); 127 add_settings_field('extendago_use_barcode_as_sku', esc_html(__('Export barcode as SKU', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_use_barcode_as_sku_element' ), 'extendago_import_options', 'import_section'); 128 // add_settings_field('extendago_use_sku_as_supplier_sku', esc_html(__('Export SKU as Supplier SKU', 'extendago-wp-connection-plugin')), array( $this, 'display_extendago_use_sku_as_supplier_sku_element' ), 'extendago_import_options', 'import_section'); 127 129 } 128 130 … … 169 171 170 172 register_setting('extendago_import_options', 'extendago_export_sale_price'); 173 register_setting('extendago_import_options', 'extendago_use_barcode_as_sku'); 174 // register_setting('extendago_import_options', 'extendago_use_sku_as_supplier_sku'); 171 175 register_setting('extendago_import_options', 'extendago_import_shop_products'); 172 176 register_setting('extendago_import_options', 'extendago_shop_group_ids'); … … 175 179 register_setting('extendago_import_options', 'extendago_update_images'); 176 180 register_setting('extendago_import_options', 'extendago_new_product_status'); 177 register_setting('extendago_import_options', 'extendago_ Off_images_product_status');181 register_setting('extendago_import_options', 'extendago_no_images_product_status'); 178 182 register_setting('extendago_import_options', 'extendago_excerpt'); 179 183 register_setting('extendago_import_options', 'extendago_hide_featured_image_from_gallery'); … … 466 470 } 467 471 472 public function display_extendago_use_barcode_as_sku_element(){ 473 ?> 474 <label class="checkbox-inline"> 475 <input name="extendago_use_barcode_as_sku" id="extendago_use_barcode_as_sku" value="1" <?php checked( '1', get_option( 'extendago_use_barcode_as_sku' ) ); ?> data-toggle="toggle" data-on="On" data-off="Off" data-onstyle="success" type="checkbox"> Off / On 476 </label> 477 <div class="info"> 478 <?php echo __('Off: Woocommerce SKU = Extenda SKU', 'extendago-wp-connection'); ?> 479 </br> 480 <?php echo __('On: Woocommerce GTIN/EAN = Extenda SKU', 'extendago-wp-connection'); ?> 481 </div> 482 <?php 483 } 484 485 public function display_extendago_use_sku_as_supplier_sku_element(){ 486 ?> 487 <label class="checkbox-inline"> 488 <input name="extendago_use_sku_as_supplier_sku" id="extendago_use_sku_as_supplier_sku" value="1" <?php checked( '1', get_option( 'extendago_use_sku_as_supplier_sku' ) ); ?> data-toggle="toggle" data-on="On" data-off="Off" data-onstyle="success" type="checkbox"> Off / On 489 </label> 490 <div class="info"> 491 <?php echo __('Off: Woocommerce have no supplier SKU field!', 'extendago-wp-connection'); ?> 492 </br> 493 <?php echo __('On: Woocommerce SKU = Extenda Supplier SKU', 'extendago-wp-connection'); ?> 494 </div> 495 <?php 496 } 468 497 469 498 public function display_extendago_import_shop_products_element(){ … … 536 565 } 537 566 538 public function display_extendago_ Off_images_product_status_element() {539 ?> 540 <label class="checkbox-inline"> 541 <input name="extendago_ Off_images_product_status" id="extendago_Off_images_product_status" value="1" <?php checked( '1', get_option( 'extendago_Off_images_product_status' ) ); ?> data-toggle="toggle" data-on="Live" data-off="Draft" data-onstyle="success" type="checkbox"> <?php echo __('Draft/Published', 'extendago-wp-connection-plugin'); ?>567 public function display_extendago_no_images_product_status_element() { 568 ?> 569 <label class="checkbox-inline"> 570 <input name="extendago_no_images_product_status" id="extendago_no_images_product_status" value="1" <?php checked( '1', get_option( 'extendago_no_images_product_status' ) ); ?> data-toggle="toggle" data-on="Live" data-off="Draft" data-onstyle="success" type="checkbox"> <?php echo __('Draft/Published', 'extendago-wp-connection-plugin'); ?> 542 571 </label> 543 572 <div class="info"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span><?php echo __('The product status of a new product in Woocommerce when the product has no image inside Extenda GO.', 'extendago-wp-connection'); ?></div> … … 976 1005 // Check if connection available 977 1006 if ( $this->active_connection ) { 978 979 global $woocommerce;980 981 1007 $extendago_click_and_collect = get_option( 'extendago_click_and_collect' ); 982 1008 ?> … … 1019 1045 // Check if connection available 1020 1046 if ( $this->active_connection ) { 1021 1022 global $woocommerce;1023 1024 1047 $extendago_cash_on_delivery = get_option( 'extendago_cash_on_delivery' ); 1025 1048 ?> … … 1028 1051 <?php 1029 1052 $payment_gateways = WC()->payment_gateways->get_available_payment_gateways(); 1030 foreach ( $payment_gateways as $gateway_id => $gateway ) { 1031 echo '<option value="' . esc_attr( $gateway_id ) . '">' . esc_html( $gateway->get_title() ) . '</option>'; 1032 } 1033 ?> 1053 foreach ( $payment_gateways as $gateway_id => $gateway ): ?> 1054 <option value="<?php echo esc_attr( $gateway_id ); ?>" <?php echo ( isset( $extendago_cash_on_delivery ) && $extendago_cash_on_delivery == $gateway_id ) ? 'selected=selected' : ''; ?>><?php echo esc_html( $gateway->get_title() ); ?></option> 1055 <?php endforeach; ?> 1034 1056 </select> 1035 1057 -
extendago-wp-connection/trunk/includes/api/class-arture-web-api.php
r3019318 r3255254 5 5 6 6 public function __construct() { 7 $this -> arture_api_url = "https:// www.arture.nl/wp-json/arture_plugins/v1";7 $this -> arture_api_url = "https://extendago-connect.com/wp-json/arture_extendago/v1"; 8 8 } 9 9 … … 24 24 'Accept: application/json', 25 25 'Content-Type: application/json', 26 'X-API-KEY: i7TOIho967phuL&I%^P(LGIGK' 26 27 ); 27 28 -
extendago-wp-connection/trunk/includes/api/class-extendago-web-api-functions.php
r3212240 r3255254 540 540 541 541 $extendago_masterdata = $this->extendago_masterdata; 542 $parent_term_id = '0'; 543 foreach( $product_category_hierarchy as $product_category_name ) { 544 545 $product_category_index = array_search($product_category_name, array_column($extendago_masterdata['ProductCategories'], 'name')); 546 $product_category = array_values($extendago_masterdata['ProductCategories'])[$product_category_index]; 547 $category_term = term_exists(htmlentities(ucfirst($product_category_name)), 'product_cat', $parent_term_id); 548 if (isset($category_term) && !empty($category_term)) { 549 $parent_term_id = $category_term['term_id']; 550 } 551 else { 552 553 $args = array( 554 'parent' => $parent_term_id 555 ); 556 $category_term = wp_insert_term(ucfirst($product_category_name), 'product_cat', $args); 557 // Check for WP ERROR 542 $parent_term_id = 0; // Standaard root categorie ID 543 544 // Controleer of 'ProductCategories' een geldige array is 545 if (!isset($extendago_masterdata['ProductCategories']) || !is_array($extendago_masterdata['ProductCategories'])) { 546 return []; // Stoppen en lege array retourneren als de data ongeldig is 547 } 548 549 foreach ($product_category_hierarchy as $product_category_name) { 550 // Controleer of 'name' bestaat in de array voordat je array_column() gebruikt 551 $product_categories = $extendago_masterdata['ProductCategories']; 552 if (empty($product_categories) || !is_array($product_categories)) { 553 continue; // Als de lijst leeg of ongeldig is, ga verder met de volgende iteratie 554 } 555 556 $product_category_index = array_search($product_category_name, array_column($product_categories, 'name')); 557 558 // Controleer of de categorie-index geldig is 559 if ($product_category_index === false) { 560 continue; // Sla over als de categorie niet wordt gevonden 561 } 562 563 $product_category = $product_categories[$product_category_index]; 564 565 // Controleer of de term al bestaat 566 $category_term = term_exists(ucwords(htmlentities($product_category_name)), 'product_cat', $parent_term_id); 567 568 if (is_array($category_term) && isset($category_term['term_id'])) { 569 $parent_term_id = $category_term['term_id']; // Bestaande categorie, ga verder 570 } else { 571 // Voeg de term toe als deze niet bestaat 572 $args = ['parent' => $parent_term_id]; 573 $category_term = wp_insert_term(ucwords($product_category_name), 'product_cat', $args); 574 558 575 if (is_wp_error($category_term)) { 559 560 if( isset($category_term->error_data['term_exists']) ){ 576 if (isset($category_term->error_data['term_exists'])) { 561 577 $parent_term_id = $category_term->error_data['term_exists']; 562 578 } … … 568 584 $output[] = (int) $parent_term_id; 569 585 570 if( isset($product_category['image']) && !empty($product_category['image']) ){571 572 $attach_id = $this->upload_external_file( $product_category['image']);573 574 update_term_meta( $parent_term_id, 'thumbnail_id', absint( $attach_id ) );575 } 576 577 update_term_meta( $parent_term_id, 'extendago_id', $product_category['id']);586 // Voeg een afbeelding toe als die bestaat 587 if (!empty($product_category['image'])) { 588 $attach_id = $this->upload_external_file($product_category['image']); 589 update_term_meta($parent_term_id, 'thumbnail_id', absint($attach_id)); 590 } 591 592 // Sla de Extendago ID op 593 update_term_meta($parent_term_id, 'extendago_id', $product_category['id']); 578 594 } 579 595 -
extendago-wp-connection/trunk/includes/api/class-extendago-web-api.php
r3176970 r3255254 282 282 while ($HasMore) { 283 283 $HasMore = false; 284 $Results = $this->CurlRequest("/promotion_campaigns", "GET", array("limit" => $Limit, "offset" => $Offset, "filters" => json_encode(array(array('property' =>'date_to', 'comparator' => '>', 'value' => time()))))); 284 $Results = $this->CurlRequest("/promotion_campaigns", "GET", array( 285 "limit" => $Limit, 286 "offset" => $Offset, 287 "filters" => json_encode(array( 288 array( 289 'property' => 'date_to', 290 'comparator' => '>', 291 'value' => time(), 292 'logical' => 'OR', 293 'set' => 'date_to' 294 ), 295 array( 296 'property' => 'date_to', 297 'comparator' => 'IS', 298 'value' => NULL, 299 'logical' => 'OR', 300 'set' => 'date_to' 301 ), 302 array( 303 'property' => 'enabled', 304 'comparator' => '=', 305 'value' => '1', 306 ) 307 )) 308 )); 285 309 foreach ($Results as $Result) { 286 310 if (isset($Result['id'])) { -
extendago-wp-connection/trunk/includes/cronjob/class-extendago-cronjob-functions.php
r3212240 r3255254 282 282 283 283 $extendago_costs_field = get_option('extendago_costs_field'); 284 $extendago_use_barcode_as_sku = get_option( 'extendago_use_barcode_as_sku' ); 284 285 285 286 $Extendago = new Extendago_Web_Api(); … … 317 318 $product_data['sku'] = $product->get_sku(); 318 319 $product_data['vat_rate_id'] = $vat_rate_id; 320 321 // 7-02-2025 | Fallback on 322 if( isset($extendago_use_barcode_as_sku) && $extendago_use_barcode_as_sku == '1' ) { 323 $global_unique_id = get_post_meta($product_data['id'], '_global_unique_id', true); 324 if (!empty($global_unique_id)) { 325 $product_data['sku'] = $global_unique_id; 326 } 327 } 319 328 320 329 $woocommerce_prices_include_tax = get_option('woocommerce_prices_include_tax'); … … 411 420 'vat_rate_id' => $vat_rate_id, 412 421 ); 422 423 // 7-02-2025 | Fallback on GTIN field 424 if( isset($extendago_use_barcode_as_sku) && $extendago_use_barcode_as_sku == '1' ) { 425 $variable_global_unique_id = get_post_meta($product_variation_id, '_global_unique_id', true); 426 if (!empty($variable_global_unique_id)) { 427 $variant_data['sku'] = $variable_global_unique_id; 428 } 429 } 413 430 414 431 // Costs of goods … … 944 961 } 945 962 963 $original_wc_product_id = $product['id']; 946 964 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'GET'); 947 965 if( empty($ProductResponse) || isset($ProductResponse['error']) ){ … … 959 977 if( is_null($ProductResponse) ){ 960 978 unset($ExtendagoProductJSON['image']); 961 $this->logging->log_file_write('E RROR| Product ' . $product['id'] . ' image is not available or too large');979 $this->logging->log_file_write('Error | Product ' . $product['id'] . ' image is not available or too large'); 962 980 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'PUT', $ExtendagoProductJSON, false, true); 963 981 } … … 967 985 if( is_null($ProductResponse) ){ 968 986 unset($ExtendagoProductJSON['image']); 969 $this->logging->log_file_write('E RROR| Product ' . $product['id'] . ' image is not available or too large');987 $this->logging->log_file_write('Error | Product ' . $product['id'] . ' image is not available or too large'); 970 988 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'PUT', $ExtendagoProductJSON, false, true); 989 } 990 991 if( !isset($ProductResponse['error']) ){ 992 $$this->logging->log_file_write('Action | Product updated: ' .$original_wc_product_id. ' / ' .$product['sku']. ' | PUT 1'); 971 993 } 972 994 } … … 975 997 if( is_null($ProductResponse) ){ 976 998 unset($ExtendagoProductJSON['image']); 977 $this->logging->log_file_write('E RROR| Product ' . $product['id'] . ' image is not available or too large');999 $this->logging->log_file_write('Error | Product ' . $product['id'] . ' image is not available or too large'); 978 1000 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'PUT', $ExtendagoProductJSON, false, true); 979 1001 } 1002 1003 if( !isset($ProductResponse['error']) ){ 1004 $this->logging->log_file_write('Action | Product updated: ' .$original_wc_product_id. ' / ' .$product['sku']. ' | PUT 2'); 1005 } 980 1006 } 981 1007 else{ 1008 1009 $product_data = json_decode($ExtendagoProductJSON['product_data'], true); 1010 if (isset($product_data['0'])) { 1011 foreach ($product_data as $productData) { 1012 1013 // Loop door alle product data heen = combinatie van stokc/prijs/regio/locatie 1014 foreach ($ProductResponse['product_data'] as $existingProductData) { 1015 if ( 1016 isset($existingProductData['product_id'], $existingProductData['price_region_id']) && 1017 $existingProductData['product_id'] == $productData['product_id'] && 1018 $existingProductData['price_region_id'] == $productData['price_region_id'] 1019 ) { 1020 $product_data['0']['id'] = $existingProductData['id']; 1021 } 1022 } 1023 } 1024 $ExtendagoProductJSON['product_data'] = json_encode($product_data); 1025 } 1026 1027 if( isset($ExtendagoProductJSON['product_variants']) ) { 1028 $product_variants = json_decode($ExtendagoProductJSON['product_variants'], true); 1029 foreach ($product_variants as &$item) { 1030 if (isset($item['product_data'])) { 1031 foreach ($item['product_data'] as &$productData) { 1032 $productData['id'] = $this->findProductDataId($ProductResponse, $productData['product_variant_id'], $productData['price_region_id']); 1033 } 1034 } 1035 } 1036 $ExtendagoProductJSON['product_variants'] = json_encode($product_variants); 1037 } 1038 982 1039 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'PATCH', $ExtendagoProductJSON, false, true); 983 1040 if( is_null($ProductResponse) ){ 984 1041 unset($ExtendagoProductJSON['image']); 985 $this->logging->log_file_write('E RROR| Product ' . $product['id'] . ' image is not available or too large');1042 $this->logging->log_file_write('Error | Product ' . $product['id'] . ' image is not available or too large'); 986 1043 $ProductResponse = $Extendago->CurlRequest('/products/'.$product['id'], 'PATCH', $ExtendagoProductJSON, false, true); 987 1044 } … … 990 1047 991 1048 if( empty($ProductResponse) || isset($ProductResponse['error']) ){ 992 $this->logging->log_file_write('E XPORT |' . $ProductResponse['error']);1049 $this->logging->log_file_write('Error | Export productResponse 1: ' . $ProductResponse['error']); 993 1050 } 994 1051 … … 1009 1066 1010 1067 if (empty($ProductResponse) || isset($ProductResponse['error'])) { 1011 $this->logging->log_file_write('EXPORT | ' . $ProductResponse['error']);1068 $this->logging->log_file_write('EXPORT | ProductResponse 2: ' . $ProductResponse['error']); 1012 1069 } 1013 1070 } 1014 1071 } 1015 1072 else{ 1016 $this->logging->log_file_write('E XPORT |' . $ProductResponse['error']);1073 $this->logging->log_file_write('Error | Export productResponse 3: ' . $ProductResponse['error']); 1017 1074 } 1018 1075 } … … 1044 1101 if( $extendago_size_id != $extenda_product_variant_data['id'] ) { 1045 1102 update_post_meta($found_variation_id, 'extendago_size_id', $extenda_product_variant_data['id']); 1046 $this->logging->log_file_write(' EXPORT | Product variation ID ' . $found_variation_id . ' has new Extendago ID: ' . $extenda_product_variant_data['id']);1103 $this->logging->log_file_write('Notice | Export product variation ID ' . $found_variation_id . ' has new Extendago ID: ' . $extenda_product_variant_data['id']); 1047 1104 } 1048 1105 } … … 1231 1288 $this->delete_old_export_files(); 1232 1289 } 1290 } 1291 1292 public function findProductDataId($data, $productVariantId, $priceRegionId) { 1293 foreach ($data['product_variants'] as $variant) { 1294 if (isset($variant['product_data'])) { 1295 foreach ($variant['product_data'] as $productData) { 1296 if ( 1297 isset($productData['product_variant_id'], $productData['price_region_id']) && 1298 $productData['product_variant_id'] == $productVariantId && 1299 $productData['price_region_id'] == $priceRegionId 1300 ) { 1301 return $productData['id']; // Retourneer de bijbehorende 'id' 1302 } 1303 } 1304 } 1305 } 1306 return null; // Retourneer null als geen match is gevonden 1233 1307 } 1234 1308 -
extendago-wp-connection/trunk/includes/woocommerce/class-extendago-woocommerce-functions.php
r3226185 r3255254 401 401 402 402 $extendago_costs_field = get_option('extendago_costs_field'); 403 $extendago_use_barcode_as_sku = get_option( 'extendago_use_barcode_as_sku' ); 403 404 404 405 $product = wc_get_product($product_id); … … 439 440 $product_data['sku'] = $product->get_sku(); 440 441 $product_data['vat_rate_id'] = $vat_rate_id; 442 443 // 7-02-2025 | Fallback on 444 if( isset($extendago_use_barcode_as_sku) && $extendago_use_barcode_as_sku == '1' ) { 445 $global_unique_id = get_post_meta($product_data['id'], '_global_unique_id', true); 446 if (!empty($global_unique_id)) { 447 $product_data['sku'] = $global_unique_id; 448 } 449 } 441 450 442 451 // Costs of goods … … 568 577 ); 569 578 579 // 7-02-2025 | Fallback on GTIN field 580 if( isset($extendago_use_barcode_as_sku) && $extendago_use_barcode_as_sku == '1' ) { 581 $variable_global_unique_id = get_post_meta($product_variation_id, '_global_unique_id', true); 582 if (!empty($variable_global_unique_id)) { 583 $variant_data['sku'] = $variable_global_unique_id; 584 } 585 } 586 570 587 // Costs of goods 571 588 if ( isset($extendago_costs_field) && !empty($extendago_costs_field) ) { … … 780 797 781 798 // Check ones more if order is paid and completed 782 if( $order->has_status( 'processing' ) || $order->has_status( 'completed' ) ){ 783 $process_order = true; 784 $order_is_paid = true; 785 } 786 elseif( isset($extendago_click_and_collect) && !empty($extendago_click_and_collect) ) { 799 if( isset($extendago_click_and_collect) && !empty($extendago_click_and_collect) ) { 787 800 $shipping_lines = $order->get_items( 'shipping' ); 788 801 foreach( $shipping_lines as $shipping_line ) { … … 805 818 } 806 819 } 807 808 // Check for update 809 if( isset($extendago_order_id) && !empty($extendago_order_id)){ 810 $process_order = true; 811 $order_is_paid = false; 812 if( $order->has_status( 'processing' ) || $order->has_status( 'completed' ) ) { 813 $order_is_paid = true; 814 } 815 } 816 elseif( $refund ){ 820 elseif( $order->has_status( 'processing' ) || $order->has_status( 'completed' ) ){ 817 821 $process_order = true; 818 822 $order_is_paid = true; -
extendago-wp-connection/trunk/readme.txt
r3226185 r3255254 5 5 Requires at least: 6.0 6 6 Tested up to: 6.7.1 7 Stable tag: 1. 5.87 Stable tag: 1.6.1 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 29 29 8. That's all. 30 30 31 == Changelog == 31 == Changelog = 32 33 = 1.6.1 = 34 * Small bugfix for showing correct selected COD setting 35 * Updated promotion campagnes logic for discounts 36 * Some small improvements and optimalisations 32 37 33 38 = 1.6.0 =
Note: See TracChangeset
for help on using the changeset viewer.