Categories
WooCommerce

WooCommerce Porterage Option

How to force shipping for specific category, add porterage option, and display the lead time?

add_filter( 'woocommerce_checkout_fields', 'conldess_add_fields' );
add_action( 'woocommerce_cart_calculate_fees', 'condless_add_checkout_fee' );
add_filter( 'woocommerce_package_rates', 'condless_set_shipping_methods', 10, 2 );
add_action( 'woocommerce_review_order_before_submit', 'condless_add_lead_time' );
add_filter( 'woocommerce_email_order_meta_fields', 'condless_add_email_order_meta_fields', 10, 3 );
add_action( 'woocommerce_thankyou', 'condless_display_order_meta' );
add_action( 'woocommerce_view_order', 'condless_display_order_meta' );
add_action( 'woocommerce_admin_order_data_after_billing_address', 'condless_admin_display_order_meta' );

function conldess_add_fields( $fields ) {
	$fields['billing']['billing_floor'] = [
		'label'		=> __( 'Floor', 'woocommerce' ),
		'required'	=> true,
		'priority'	=> $fields['billing']['billing_address_1']['priority'] + 2,
		'type'		=> 'number',
		'class'		=> [ 'update_totals_on_change' ],
		'custom_attributes'	=> [ 'min' => 0 ],
	];
	$fields['billing']['billing_elevator'] = [
		'label'		=> __( 'Elevator', 'woocommerce' ),
		'required'	=> true,
		'priority'	=> $fields['billing']['billing_floor']['priority'] + 2,
		'type'		=> 'select',
		'options'	=> [ 'no' => 'no', 'yes' => 'yes' ],
		'default'	=> 'no',
		'class'		=> [ 'update_totals_on_change' ],
	];
 	$fields['billing']['billing_porterage'] = [
		'label'		=> __( 'Porterage', 'woocommerce' ),
		'required'	=> true,
		'priority'	=> $fields['billing']['billing_elevator']['priority'] + 2,
		'type'		=> 'select',
		'options'	=> [ 'no' => 'without', 'yes' => 'include' ],
		'default'	=> 'no',
		'class'		=> [ 'update_totals_on_change' ],
	];
	?>
		<script type="text/javascript">
		jQuery( function( $ ) {
			$( '#billing_floor' ).on( 'keyup change', function() {
				$( 'body' ).trigger( 'update_checkout' );
			} );
		} );
		</script>
	<?php
	return $fields;
}

function condless_add_checkout_fee() {
	$elevator_fee = 10;
	$price_per_floor = 20;
	if ( ! ( is_admin() && ! wp_doing_ajax() ) && false === strpos( WC()->session->get( 'chosen_shipping_methods' )[0], 'local_pickup' ) ) {
		if ( isset( $_POST['post_data'] ) ) {
			parse_str( $_POST['post_data'], $post_data );
		} else {
			$post_data = $_POST;
		}
		if ( ! empty( $post_data['billing_floor'] ) && ! empty( $post_data['billing_elevator'] ) && ! empty ( $post_data['billing_porterage'] ) && 'yes' === $post_data['billing_porterage'] ) {
			WC()->cart->add_fee( __( 'Porterage', 'woocommerce' ), 'yes' === $post_data['billing_elevator'] ? $elevator_fee : $price_per_floor * $post_data['billing_floor'], true );
		}
	}
}

function condless_set_shipping_methods( $rates, $package ) {
	$pickup_only_cat = [ 'general' ];
	$cart_cat = [];
	foreach ( wc()->cart->get_cart() as $cart_item ) {
		$cart_cat = array_merge( $cart_cat, wp_get_post_terms( $cart_item['product_id'], 'product_cat', [ 'fields' => 'names' ] ) );
	}
	if ( array_intersect( $cart_cat, $pickup_only_cat ) ) {
		foreach ( $rates as $rate_id => $rate ) {
			if ( 'local_pickup' !== $rate->method_id ) {
				unset( $rates[ $rate_id ] );
			}
		}
	}
	return $rates;
}

function condless_add_lead_time() {
	$cat_14 = [ 'general' ];
	$lead_time = '1 business day';
	$cart_cat = [];
	foreach ( wc()->cart->get_cart() as $cart_item ) {
		$cart_cat = array_merge( $cart_cat, wp_get_post_terms( $cart_item['product_id'], 'product_cat', [ 'fields' => 'names' ] ) );
	}
	if ( array_intersect( $cart_cat, $cat_14 ) ) {
		$lead_time = '14 business days';
	} ?>
		<table class="woocommerce-table shop_table gift_info">
			<tbody>
				<tr>
					<th><?php _e( 'Lead time', 'woocommerce' ); ?></th>
					<td><?php echo $lead_time; ?></td>
				</tr>
			</tbody>
		</table>
<?php }

function condless_add_email_order_meta_fields( $fields, $sent_to_admin, $order_obj ) {
	$fields['billing_floor'] = [
		'label'	=> __( 'Floor', 'woocommerce' ),
		'value'	=> get_post_meta( $order_obj->get_order_number(), '_billing_floor', true ),
	];
	$fields['billing_elevator'] = [
		'label'	=> __( 'Elevator', 'woocommerce' ),
		'value'	=> 'yes' === get_post_meta( $order_obj->get_order_number(), '_billing_elevator', true ) ? 'yes' : 'no',
	];
	$fields['billing_porterage'] = [
		'label'	=> __( 'Porterage', 'woocommerce' ),
		'value'	=> 'yes' === get_post_meta( $order_obj->get_order_number(), '_billing_porterage', true ) ? 'include' : 'without',
	];
	return $fields;
}

function condless_admin_display_order_meta( $order ) {
	echo '<strong>' . __( 'Floor', 'woocommerce' ) . '</strong> ' . get_post_meta( $order->get_id(), '_billing_floor', true ) . '. <strong>' . __( 'Elevator', 'woocommerce' ) . '</strong> ' . ( 'yes' === get_post_meta( $order->get_id(), '_billing__levator', true ) ? 'yes' : 'no' ) . '. <strong>' . __( 'Porterage', 'woocommerce' ) . '</strong> ' . ( 'yes' === get_post_meta( $order->get_id(), '_billing_porterage', true ) ? 'include' : 'without' );
}

function condless_display_order_meta( $order_id ) {
	echo '<strong>' . __( 'Floor', 'woocommerce' ) . '</strong> ' . get_post_meta( $order_id, '_billing_floor', true ) . '. <strong>' . __( 'Elevator', 'woocommerce' ) . '</strong> ' . ( 'yes' === get_post_meta( $order_id, '_billing__levator', true ) ? 'yes' : 'no' ) . '. <strong>' . __( 'Porterage', 'woocommerce' ) . '</strong> ' . ( 'yes' === get_post_meta( $order_id, '_billing_porterage', true ) ? 'include' : 'without' );
}
Categories
WooCommerce

Unit Price for WooCommerce

Categories
WooCommerce

WooCommerce Sell Media

And The University of the People.

Sell access to digital media (content pages, video courses, images, music):

  • Follow the guide
  • Add in the product’s “Purchase note” field link to the page the media embedded in
  • Enable content protection with the plugin
  • In case the media is stored in the website’s media folder prevent direct access to it, disable directory listing and hotlinking with the plugin
  • Limit connection to only one device with the plugin
  • Activate Two-factor authentication

As you probably have noticed- distance learning occupies a growing part of the way of learning, the one that has taken it one step further, is the University of the People, which does not have a physical campus and offers a full online degree without tuition.

Categories
WooCommerce

WooCommerce Security

And the Chamber of Secrets.

Improve system security at the WordPress level:

  • Secure the server
  • Grant folder 755 and files 644 permission
  • Update frequently
  • Install few plugins as possible
  • Report any suspicious activity in your account to your hosting provider
  • Use the build-in tool “Site Health”
  • Remove unused themes
  • Install SSL certificate
  • Make use of an Editor user and use Admin only in need
  • Disable built-in dashbaord theme editor- add the directive to wp-config.php file:
define( 'DISALLOW_FILE_EDIT', true );
  • Disable directory browsing, prevent access to important files and directories, and XSS attacks protection- add the directives to the .htaccess file:
Options -Indexes

<FilesMatch "^.*(xmlrpc.php|error_log|wp-config\.php|php.ini|\.[hH][tT][aApP].*)$">
Order deny,allow
Deny from all
</FilesMatch>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
# Comment the following directive if multisite
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] 
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>

RewriteCond %{HTTP_USER_AGENT} (libwww-perl|wget|python|nikto|curl|scan|java|winhttp|clshttp|loader) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (;|<|>|'|"|\)|\(|%0A|%0D|%22|%27|%28|%3C|%3E|%00).*(libwww-perl|wget|python|nikto|curl|scan|java|winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner) [NC,OR]
RewriteCond %{THE_REQUEST} \?\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} \/\*\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} etc/passwd [NC,OR]
RewriteCond %{THE_REQUEST} cgi-bin [NC,OR]
RewriteCond %{THE_REQUEST} (%0A|%0D) [NC,OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC,OR]
RewriteCond %{QUERY_STRING} \=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [NC,OR]
RewriteCond %{QUERY_STRING} (\.\./|\.\.) [OR]
RewriteCond %{QUERY_STRING} ftp\: [NC,OR]
RewriteCond %{QUERY_STRING} http\: [NC,OR] 
RewriteCond %{QUERY_STRING} https\: [NC,OR]
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)/self/(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)cPath=http://(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*iframe.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^i]*i)+frame.*(>|%3E) [NC,OR] 
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
RewriteCond %{QUERY_STRING} base64_(en|de)code[^(]*\([^)]*\) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} ^.*(\[|\]|\(|\)|<|>).* [NC,OR]
RewriteCond %{QUERY_STRING} (NULL|OUTFILE|LOAD_FILE) [OR]
RewriteCond %{QUERY_STRING} (\./|\../|\.../)+(motd|etc|bin) [NC,OR]
RewriteCond %{QUERY_STRING} (localhost|loopback|127\.0\.0\.1) [NC,OR]
RewriteCond %{QUERY_STRING} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{QUERY_STRING} concat[^\(]*\( [NC,OR]
RewriteCond %{QUERY_STRING} union([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} union([^a]*a)+ll([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} (;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode) [NC,OR]
RewriteCond %{QUERY_STRING} (sp_executesql) [NC]
RewriteRule ^(.*)$ - [F,L]
  • Disable PHP execution in the uploads directory- create inside a .htaccess file with the content:
<Files "*.php">
Order Deny,Allow
Deny from All
</Files>
  • Hide the WordPress version and login errors- add to functions.php file in the child theme:
add_filter( 'the_generator', '__return_false' );
add_filter( 'login_errors', create_function( '$a', "return 'Invalid Input';" ) );
  • Scan frequently the code in the WordPress directory and database for malwares detection and delete unused items, create full backup before each modification, consider reinstalling WordPress (core files, themes and plugins) from the Dashboard. Make use of the recently modified files command:
find ./ -type f -mtime -15

Remove the Mobile Spam Popup malware- delete the wp-tmp.php, wp-vcd.php, wp-feed.php files from wp-includes directory and delete the code which is creating them from the themes’ functions.php.

Remove the Japanese Keyword Hack malware- clean the malicious code from .htaccess, wp-config.php, sitemap.xml files and uploads directory.

As you probably have noticed- many WordPress malwares were not created to shut down the website, but rather to use its infrastructure and create clients traffic to purchase products on a third-party site, usually advertising prohibited health and wellness products.

Categories
WooCommerce

WooCommerce REST API

And the Copyrights.

Creating sign-up form for some WooCommerce website (source website) in another website (destination website): in the source website create application passwords, in the destination website create sign-up form with the plugin and use the code (update the source website address and the access details):

add_action( 'wpcf7_posted_data', 'condless_create_remote_user' );
function condless_create_remote_user( $posted_data ) {
	$api_response = wp_remote_post( 'https://example.com/wp-json/wp/v2/users/', [
		'headers' => [ 'Authorization' => 'Basic ' . base64_encode( 'LOGIN:PASSWORD' ) ],
		'body' => [
			'username'	=> $posted_data['your-name'],
			'email' 	=> $posted_data['your-email'],
			'password'	=> $posted_data['your-password'],
		]
	] );
	$body = json_decode( $api_response['body'] );
	if ( 'OK' === wp_remote_retrieve_response_message( $api_response ) ) {
		// Do something with $body
	}
}

As you probably have noticed- the largest Copyright controversy was agains Google for having distributed a new implementation of Java embedded in the Android operating system, the controversy reached the Supreme Court of the United States.

Categories
WooCommerce

WooCommerce Product Edit Permission

And the Persian Mathematician.

Removal of the option to edit some product fields for specific user role (update the fields and the user role):

if ( ! current_user_can( 'manage_options' ) ) {
	add_action( 'init', 'condless_remove_features' );
	add_action( 'add_meta_boxes_product', 'condless_remove_metaboxes_edit_product', 9999 );
	add_filter( 'woocommerce_product_data_tabs', 'condless_remove_tabs' );
}

function condless_remove_features() {
	remove_post_type_support( 'product', 'editor' );
}

function condless_remove_metaboxes_edit_product() {
	remove_meta_box( 'postexcerpt', 'product', 'normal' );
	remove_meta_box( 'postimagediv', 'product', 'side' );
	remove_meta_box( 'woocommerce-product-images', 'product', 'side' );
}

function condless_remove_tabs( $tabs ) {
	unset( $tabs['advanced'] );
	return( $tabs );
}

As you probably have noticed- the source of the numbering system is India, the West was exposed to the method only in the 11th century, and mainly through the writings of the Persian Mathematician al-Khwarizmi, which the term “algorithm” was named after him.

Categories
WooCommerce Extensions

Cities Shipping Zones for WooCommerce

Which uses shop owners make with the customer city?

  • Set shipping rates per cities
  • Sales stats by city (Dashbaord => WooCommerce => Reports => Sales by city)
  • Display cities shipping calculator with the shortcode [csz_cities]
  • Sell certain products per city (with plugin that limits by state)
  • Ship certain products per city (associating the products to a class without price in shipping method which doesn’t have based price)
  • Apply taxes per city (use the city code in the state field in the tax settings)
  • Enable payment / shipping methods per city (with conditional payment / shipping plugin)
  • Shipping for cities by the day of the week (with order delivery date plugin that has shipping methods based option)
  • Display the cities in external checkout page (retreive the states via WooCommerce REST API)
Options

What is the State Autofill option?
When enabled, the state will be auto filled by the selected city for countries that have states in their cities list. You may set the countries the option will be applied on with the csz_populate_state filter.

What is the Filters option?
When enabled, a state filter will be displayed for the store country in the checkout and the shipping calculator. the state will not be inserted into the order unless the ‘State Autofill’ option is enabled.

Bulk Edit Tool

What is the required format?
Semicolon (;) between each city. For example: Wien; Rust; Steinbrunn

How to modify cities in list to the required format?
Replace [\r\n]+ (regex) with ; in Notepad++.

How to enter all the locations within certain distance from a city with distances list?
Enter exclamation mark (!) followed by the city code, if necessary add minium and maximum distance seperated by semicolon (;).

Integrations

Why in the integrated software the city appear twice or the city code is present?
You will have to remove the sending of the state field (billing/shipping, orders/customers) via its plugin’s code for the countries you apply the plugin on.

Why in my integrated software the city field is empty or the payment failed?
You will have to modify its way of retreiving the city field (billing/shipping) from the order for the countries you apply the plugin on- from: $_POST['billing_city'] into $order->get_billing_city(). As alternative, add to the woocommerce_checkout_create_order action a command that copies $order->get_billing_city() into $_POST['billing_city'].

Why the state code and not the state name appears in the order details in the first order email confirmation and in the my account section while using the State Autofill option on countries with WooCommerce built-in states list?
You may use the woocommerce_formatted_address_replacements filter to fix it.

How to prevent the loading of the cities in WCFM plugin pages?

add_filter( 'csz_enable_cities', 'csz_disable_cities_wcfm' );
add_filter( 'woocommerce_states', 'csz_load_states_wcfm', 9999 );

function csz_disable_cities_wcfm( $cities_enabled ) {
	return isset( $_GET['store-setup'] ) && 'yes' === $_GET['store-setup'] || false !== strpos( $_SERVER['REQUEST_URI'], 'store-manager' ) ? false : $cities_enabled;
}

function csz_load_states_wcfm( $states ) {
	if ( isset( $_GET['store-setup'] ) && 'yes' === $_GET['store-setup'] || false !== strpos( $_SERVER['REQUEST_URI'], 'store-manager' ) ) {
		foreach ( get_option( 'wc_csz_countries_codes' ) as $country_code ) {
			include( WP_PLUGIN_DIR . '/cities-shipping-zones-for-woocommerce/i18n/cities/' . $country_code . '.php' );
			if ( $country_states ) {
				$states[ $country_code ] = $country_states;
			}
		}
	}
	return $states;
}

How to integrate with Food Online Premium plugin?
Prevent the customer state from beeing loaded in config_default_customer_address function and add to line 836 in class-fdoe-del.php file (update the mismatch cities names):

if ( is_plugin_active( 'cities-shipping-zones-for-woocommerce/cities-shipping-zones-for-woocommerce.php' ) && in_array( $country, get_option( 'wc_csz_countries_codes' ) ) ) {
	$cities = [
		'Talin' => 'Tallinn',
	];
	if ( isset( $cities[ $_POST['city'] ] ) ) {
		$_POST['city'] = $cities[ $_POST['city'] ];
	}
	$state = array_search( _x( wc_clean( $_POST['city'] ), "{$country}-cities", 'cities-shipping-zones-for-woocommerce' ), WC()->countries->get_states( $country ) );
	$_POST['city'] = '';
}
Cities List

Why some locations are absent?
The list of most of the countries contains the municipalities, so villages within those municipalities will not be present.

How to modify cities names?
Update countries codes, and cities codes and names (not compatible with the Filters option):

add_filter( 'csz_cities', 'csz_modify_cities' );
function csz_modify_cities( $states ) {
	$states['AT']['AT90001'] = 'Vienna';
	return $states;
}

How to remove some cities from the list?
Update country code and cities codes:

add_filter( 'csz_cities', 'csz_remove_cities' );
function csz_remove_cities( $states ) {
	$country_code = 'AT';
	$unnecessary_cities = [ 'AT10101', 'AT10201', 'AT10301' ];
	foreach ( $unnecessary_cities as $city ) {
		unset( $states[ $country_code ][ $city ] );
	}
	return $states;
}

How to keep only specific cities in the list?
Update country code and cities codes:

add_filter( 'csz_cities', 'csz_set_cities' );
function csz_set_cities( $states ) {
	$country = 'AT';
	$cities = [ 'AT10101', 'AT10201', 'AT10301' ];
	$new_states = [];
	foreach ( $cities as $city ) {
		if ( isset( $states[ $country ][ $city ] ) ) {
			$new_states[ $city ] = $states[ $country ][ $city ];
		}
	}
	$states[ $country ] = $new_states;
	return $states;
}

How to remove from the list all the cities of certain states?
Update states codes:

add_filter( 'csz_states', 'csz_remove_states_cities' );
function csz_remove_states_cities( $states ) {
	$unnecessary_states = [ 'EE001', 'EE004', 'EE006' ];
	foreach ( $unnecessary_states as $state_code ) {
		if ( isset( $states[ $state_code ] ) ) {
			unset( $states[ $state_code ] );
		}
	}
	return $states;
}

How to create shipping zones for quarters inside a city?
Create shipping zone with that city and restrict to the relevant postcodes, or (not compatible with the State Autofill / Filters options and 3-party integrations) split the city into areas (update country code and city code and areas names, if the store is located inside this city update the store location in WooCommerce settings):

add_filter( 'csz_cities', 'csz_split_cities' );
function csz_split_cities( $states ) {
	$country_code = 'AT';
	$city_code = 'AT90001';
	$sub_cities = [ __( 'Northern', 'woocommerce' ), __( 'Southern', 'woocommerce' ) ];
	$count = 101;
	foreach ( $sub_cities as $sub_city ) {
		$states[ $country_code ][ $city_code . $count ] = $states[ $country_code ][ $city_code ] . ' - ' . $sub_city;
		$count++;
	}
	unset( $states[ $country_code ][ $city_code ] );
	return $states;
}

How to let the customer to select a city for the order which is not displayed in the list?
Temporary don’t apply the plugin on any country and remove all the saved states values of your current customers from those countries to prevent loading the state in the checkout city field:

add_filter( 'csz_enable_custom_city', '__return_true' );
Shortcode

How to display the shipping calculator shortocde message permanently?
Not compatible with multinational stores:

add_filter( 'wp_footer', 'csz_custom_shipping_message' );
function csz_custom_shipping_message() {
	?>
		<script type="text/javascript">
		jQuery( function( $ ) {
			$( document ).on( 'zone_matched', function( event, response ) {
				$( '#shipping_state-description' ).remove();
				if ( ! $( '#shipping_message' ).length ) {
					$( '#shipping_state_field' ).append( '<span id="shipping_message">response</span>' );
				}
				$( '#shipping_message' ).text( response );
				$( '#shipping_message' ).css( { 'color':'black', 'background-color':'yellow' } );
			} );
		} );
		</script>
	<?php
}
Distance Fee

How to apply distance fee?
Given a (domestic) city’s distances list, it will be possible to add distance based fee for the shipping rate or product price (for example- to sell moving services).

Why the tax is not calculated on the distance fee?
Enter in the shipping method cost greater than zero.

Where the configured product distance fee will be displayed?
The product the fee will be added to its price and will be displayed in the subtotal of the mini cart widget.

How I shall organize the distances list for some city?
Use as index the locations codes or names (as they appear in the dashboard and update $key_format), verify to set distance to the city itself, the distances should be greater than 0.

WooCommerce REST API

Use the class:

add_filter( 'woocommerce_rest_api_get_rest_namespaces', 'wc_states_api' );
function wc_states_api( $controllers ) {
	$controllers['wc/v3']['data-states'] = 'WC_REST_Data_States_Controller';
	return $controllers;
}

class WC_REST_Data_States_Controller extends WC_REST_Data_Controller {
	protected $namespace = 'wc/v3';
	protected $rest_base = 'data/states';
	function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<state>[\w-]+)/zone', [
			'methods'		=> 'GET',
			'callback'		=> [ $this, 'get_zone' ],
			'args'			=> [ 'state' => [ 'description' => __( 'State / County', 'woocommerce' ), 'type' => 'string' ] ],
			'permission_callback'	=> [ $this, 'get_items_permissions_check' ],
		] );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<state>[\w-]+)/state', [
			'methods'		=> 'GET',
			'callback'		=> [ $this, 'get_state' ],
			'args'			=> [ 'state' => [ 'description' => __( 'State / County', 'woocommerce' ), 'type' => 'string' ] ],
			'permission_callback'	=> [ $this, 'get_items_permissions_check' ],
		] );
	}
	function get_zone( $data ) {
		return [ 'id' => wc_get_shipping_zone( [ 'destination' => [ 'country' => substr( $data['state'], 0, 2 ), 'state' => $data['state'], 'postcode' => '' ] ] )->get_zone_id() ];
	}
	function get_state( $data ) {
		foreach ( get_option( 'wc_csz_countries_codes' ) as $country_code ) {
			$country_cities = '';
			$country_states = '';
			include( WP_PLUGIN_DIR . '/cities-shipping-zones-for-woocommerce/i18n/cities/' . $country_code . '.php' );
			if ( isset( $country_states ) ) {
				foreach ( $country_states as $state_code => $state_name ) {
					if ( isset( $country_cities[ $state_code ][ $data['state'] ] ) ) {
						return $country_states[ $state_code ];
					}
				}
			}
		}
		return false;
	}
}

How to retrieve the shipping zone a city belong to (for the countries the plugin apply on)?

/wp-json/wc/v3/data/states/<city_code>/zone

How to retrieve the state name a city belong to (for the countries the plugin apply on)?

/wp-json/wc/v3/data/states/<city_code>/state
Uninstall Plugin / Countries / Locations
  • WooCommerce Shipping Zones settings: Remove the locations (you may use the Bulk Edit tool)
  • Plugin Settings: Remove the countries
  • WooCommerce Settings: Update the store location
  • Users: convert/erase the state field values of users that were created/updated while the plugin was applied on the countries they belong to
  • Plugins: Deactivate and delete the plugin
Categories
WooCommerce

WooCommerce RTL

And the Philosopher’s Stone.

Fix WordPress Gutenberg built-in code block (CSS):

pre.wp-block-code {
	text-align: left;
	direction: ltr;
}

Fix Owl Carousel based slider elements (CSS):

.owl-carousel,
.bx-wrapper { direction: ltr; }
.owl-carousel .owl-item { direction: rtl; }

Fix Chosen Drop based dropdown elements (CSS):

.chosen-container .chosen-drop { left: 9999px; }

Create Twenty Twenty child theme- create the path wp-content/themes/twentytwenty-child, and create inside the files style.css and style-rtl.css with the content:

/*
 Theme Name:   twentytwenty Child
 Template:     twentytwenty
*/

And the file functions.php with the content:

<?php
add_action( 'wp_enqueue_scripts', 'condless_theme_enqueue_styles' );
function condless_theme_enqueue_styles() {
        if ( is_rtl() ) {
                wp_enqueue_style( 'parent-style-rtl', get_template_directory_uri() . '/style-rtl.css' );
        } else {
                wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
        }
}

Fix its fonts:


body {
	font-family: "Inter var", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, sans-serif;
}

Create Storefront child theme- create the path wp-content/themes/storefront-child, and create inside the files style.css and style-rtl.css with the content:

/*
Theme Name: Storefront Child
Template: storefront
*/

And the file functions.php with the content:

<?php
add_action( 'wp_enqueue_scripts', 'condless_theme_enqueue_styles', 9999 );
function condless_theme_enqueue_styles() {
	if ( is_rtl() ) {
        	wp_dequeue_style( 'storefront-child-style' );
		wp_enqueue_style( 'child-style-rtl', get_stylesheet_directory_uri() . '/style-rtl.css', 'storefront-style' );
	}
}

As you probably have noticed- most of the Middle Eastern languages are written from right to left, it is suggested that as stone was the main material used, it being easier to chisel right to left. With ink, suggestions continue, moving left to right prevented smudging.

Categories
WooCommerce

WooCommerce Restrict Access

And The Little Trump.

Grant access to product pages for visitors only via the website search form:

add_action( 'template_redirect', 'condless_redirect' );
function condless_redirect() {
	if ( ! is_user_logged_in() && is_product() && false === strpos( wp_get_referer(), home_url() . '/?s' ) && 0 === wc()->cart->get_cart_contents_count() ) {
		wp_redirect( home_url() );
	}
}

Restrict submition to 1 form per email- install the pluginplugin and use:

add_filter( 'wpcf7_validate_email*', 'condless_email_validation_filter', 20, 2 );
function condless_email_validation_filter( $result, $tag ) {
	if ( Flamingo_Contact::find( [ 's' => trim( $_POST['your-email'] ) ] ) ) {
		$result->invalidate( $tag, __( 'Sorry, that email address is already used!' ) );
	}
        return $result;
}

Save attachments in folder restricted to logged-in users only- install the plugin, and plugin and create .htaccesss file in the wpcf7-submissions directory that inside the uploads directory with the content:

Options -Indexes
RewriteEngine On
RewriteCond %{HTTP_COOKIE} !^.*wordpress_logged_in.*$ [NC]
RewriteRule ^(.*)$ - [R=403,L]

As you probably have noticed- unlike other WordPress default themes, the Twenty Twenty theme was written based on already existing theme, Chaplin by Anders Noren.

Categories
WordPress Plugins

User Posts Limit

How to make some user unrestricted in rules that applied on certain post type?
Update the user id and the post type:

add_filter( 'upl_query', 'upl_unrestrict_user' );
function upl_unrestrict_user( $args ) {
	$post_type = 'post';
	$user_id = 12;
	if ( $post_type === $args['post_type'] && $user_id === $args['author'] ) {
		$args['post_type'] = 'none';
	}
	return $args;
}

How to make rules that applied on certain post type to limit the total posts in the website?
Update the post type:

add_filter( 'upl_query', 'upl_limit_total_posts' );
function upl_limit_total_posts( $args ) {
	$post_type = 'post';
	if ( $post_type === $args['post_type'] ) {
		$args['author'] = '';
	}
	return $args;
}

How to make rules that applied on certain post type to limit by posts in specific category only?
Update the post type and the category id (make sure users can’t migrate posts into unrestricted posts category):

add_filter( 'upl_query', 'upl_specific_category' );
function upl_specific_category( $args ) {
	$post_type = 'post';
	$category_id = 4;
	if ( $post_type === $args['post_type'] ) {
		$args['cat'] = $category_id;
	}
	return $args;
}

How to make rules that applied on certain post type to limit specific post status?
Update the post type and the post status (make sure users can’t migrate posts into unrestricted post status):

add_filter( 'upl_query', 'upl_specific_post_status' );
function upl_specific_post_status( $args ) {
	$post_type = 'post';
	$post_status = [ 'any' ];
	if ( $post_type === $args['post_type'] ) {
		$args['post_status'] = $post_status;
	}
	return $args;
}

How to modify the cycle for rules that applied on certain post type?
Update the post type and the cycle (keep in mind that the change will not take affect in places that the date is displayed):

add_filter( 'upl_query', 'upl_modify_cycle' );
function upl_modify_cycle( $args ) {
	$post_type = 'post';
	$cycle = '3 days ago';
	if ( $post_type === $args['post_type'] ) {
		$args['date_query']['after'] = $cycle;
	}
	return $args;
}

How to send users an Email to encourage them to upgrade their membership after they try to post after posts limit exceeded?
Attach your code for sending Email to the upl_limit_applied action hook.

How to define different message types for each user?
Use the upl_message and upl_notice filters.

How to add limit with code?
Update the role, type, limit and cycle:

$role = 'author';
$type = 'post';
$limit = '2';
$cycle = 'day';
$formatted_cycle = '1 ' . $cycle . ' ago';
$role_option = get_option( 'upl_user_role' );
$type_option = get_option( 'upl_posts_type' );
$limit_option = get_option( 'upl_num_limit' );
$period_option = get_option( 'upl_period' );
$role_option[] = $role;
$type_option[] = $type;
$limit_option[] = $limit;
$period_option[] = $formatted_cycle;
update_option( 'upl_rules_count', get_option( 'upl_rules_count' ) + 1 );
update_option( 'upl_user_role', $role_option );
update_option( 'upl_posts_type', $type_option );
update_option( 'upl_num_limit', $limit_option );
update_option( 'upl_period', $period_option );

How to modify the limits in all the rules that applied on specified user role with code?
Update the role and limit:

for ( $i = 0; $i < get_option( 'upl_rules_count' ); $i++ ) {
	$role = 'subscriber';
	$limit = '3';
	if ( $role === get_option( 'upl_user_role' )[ $i ] ) {
		$limit_option = get_option( 'upl_num_limit' );
		$limit_option[ $i ] = $limit;
		update_option( 'upl_num_limit', $limit_option );
	}
}

How to reset user’s posts counts when purchase some product in WooCommerce (by the end of the day)?
Update the product id:

add_action( 'woocommerce_order_status_completed', 'upl_update_user_cycle' );
add_filter( 'upl_query', 'upl_users_cycle', 10, 2 );
add_action( 'show_user_profile', 'upl_add_cycle_user_data' );
add_action( 'edit_user_profile', 'upl_add_cycle_user_data' );
add_action( 'personal_options_update', 'upl_save_cycle_user_data' );
add_action( 'edit_user_profile_update', 'upl_save_cycle_user_data' );
add_filter( 'manage_users_columns', 'upl_cycle_user_table' );
add_filter( 'manage_users_custom_column', 'upl_cycle_user_table_row', 10, 3 );

function upl_update_user_cycle( $order_id ) {
	$product_id = '683';
	$order = wc_get_order( $order_id );
	$user_id = $order->get_user_id();
	if ( ! user_can( $user_id, 'manage_options' ) ) {
		foreach ( $order->get_items() as $item ) {
			if ( $product_id == $item->get_product_id() ) {
				if ( empty( get_user_meta( $user_id, 'cycle', true ) ) || get_user_meta( $user_id, 'cycle', true ) <= current_time( 'Y-m-d' ) ) {
					update_user_meta( $user_id, 'cycle', date( 'Y-m-d', strtotime( current_time( 'Y-m-d' ) . ' + 1 day' ) ) );
				}
				break;
			}
		}
	}
}

function upl_users_cycle( $args, $i ) {
	if ( ! user_can( $args['author'], 'manage_options' ) ) {
		$rule_role = '';
		$cycle = get_user_meta( $args['author'], 'cycle', true );
		if ( ( empty( $rule_role ) || $rule_role === get_option( 'upl_user_role' )[ $i ] ) && ! empty( $cycle ) && $cycle <= current_time( 'Y-m-d' ) ) {
			$args['date_query']['after'] = date( 'Y-m-d', strtotime( $cycle . ' - 1 day' ) );
		}
	}
	return $args;
}

function upl_add_cycle_user_data( $user ) {
	if ( current_user_can( 'manage_options' ) ) {
		?>
			<h3><?php esc_html_e( 'User Posts Limit', 'user-posts-limit' ); ?></h3>
			<table class="form-table">
				<tr>
					<th><label for="cycle"><?php esc_html_e( 'Cycle', 'user-posts-limit' ); ?></label></th>
					<td><input type="date" min="1970-01-01" name="cycle" value="<?php echo esc_attr( get_user_meta( $user->ID, 'cycle', true ) ); ?>" class="regular-text" /></td>
				</tr>
			</table>
			<br />
		<?php
	}
}

function upl_save_cycle_user_data( $user_id ) {
	if ( current_user_can( 'manage_options' ) ) {
		update_user_meta( $user_id, 'cycle', sanitize_text_field( $_POST['cycle'] ) );
	}
}

function upl_cycle_user_table( $columns ) {
	$columns['cycle'] = __( 'Cycle', 'user-posts-limit' );
	return $columns;
}

function upl_cycle_user_table_row( $row_output, $column_id_attr, $user_id ) {
	if ( 'cycle' === $column_id_attr ) {
		return get_user_meta( $user_id, 'cycle', true );
	}
	return $row_output;
}

How to make the listing rules apply seperately on every listing type in MyListing theme?

add_filter( 'upl_query', 'upl_add_meta_query' );
function upl_add_meta_query( $args ) {
	if ( 'job_listing' === $args['post_type'] && isset( $_POST['listing_type'] ) ) {
		$args['meta_query'] = [
			[
				'key'	=> '_case27_listing_type',
				'value'	=> sanitize_text_field( $_POST['listing_type'] )
			]
		];
	}
	return $args;
}