Categorie archief: PHP

Nederlandse vertaling WooCommerce 2.0.5

In WooCommerce versie 2.0.5 kunnen de vertalingen voor de WordPress beheerdersomgeving ingeladen vanuit een eigen bestand. Op die manier hoeven niet alle WooCommerce vertalingen altijd geladen te worden. In de WooCommerce ‘load_plugin_textdomain’ function is duidelijk te zien hoe dit is opgezet.

Om de vertalingen zo efficiënt mogelijk op te zetten moeten er 2 .PO of .POT bestanden aangemaakt worden. Normaliter scande ik altijd met behulp van Poedit naar alle vertaalbare teksten binnen een plugin. Echter kan ik met Poedit niet onderscheid maken tussen vertalingen binnen de admin omgeving.

Er zijn meerdere gebruikers van Poedit die wel graag van een dergelijke functionaliteit gebruik willen maken. De ontwikkelaar van Poedit geeft echter in een ticket het volgende aan:

 The scanning feature of Poedit is intended for basic, simple uses. If you have more demanding needs, they you should write a proper makefile and call xgettext in it to create a POT file from your sources; then use Poedit just to translate PO catalogs and update them from that POT file.

At this time, I don’t want additional complications in this part of Poedit. Maybe later, when all the other, more serious, problems are fixed.

Ik ben me daarom gaan verdiepen in de werking van xgettext:

Uiteindelijk heb ik de volgende twee commando’s geschreven waarmee we de WooCommerce vertalingen kunnen opdelen in verschillende bestanden.

WooCommerce admin

find ./admin -iname "*.php" -type f | xgettext \
--from-code=UTF-8 \
--keyword=__ \
--keyword=_e \
--keyword=_n:1,2 \
--keyword=_x:1,2c \
--keyword=_ex:1,2c \
--keyword=_nx:1,2,4c \
--default-domain=woocommerce \
--language=PHP \
--copyright-holder="Remco Tolsma" \
--package-name=WooCommerce \
--package-version=2.0.5 \
--msgid-bugs-address="Remco Tolsma <info@remcotolsma.nl>" \
--files-from=- \
--output=woocommerce-admin.pot

WooCommerce

find ./ -iname "*.php" -type f | xgettext \
--from-code=UTF-8 \
--keyword=__ \
--keyword=_e \
--keyword=_n:1,2 \
--keyword=_x:1,2c \
--keyword=_ex:1,2c \
--keyword=_nx:1,2,4c \
--default-domain=woocommerce \
--language=PHP \
--copyright-holder="Remco Tolsma" \
--package-name=WooCommerce \
--package-version=2.0.5 \
--msgid-bugs-address="Remco Tolsma <info@remcotolsma.nl>" \
--files-from=- \
--exclude-file=woocommerce-admin.pot \
--output=woocommerce.pot

Bovenstaande commando’s bestaan uit  2 delen, een ‘find’ commando waarmee alle PHP-bestanden binnen de plugin worden gevonden. En een ‘xgettext’ commando die alle vertalingen binnen deze bestanden op zoekt. Hier worden de WordPress vertaalfuncties als ‘keyword’ meegegeven.

Mocht je overigens bij het uitvoeren van het tweede commando een “Segmentation” fout krijgen dan moet je misschien je gettext pakket updaten. Ik kreeg op mijn Mac deze fout met gettext versie 0.18.1. Na het updaten van gettext via MacPorts naar versie 0.18.2 was dit probleem opgelost:

sudo port selfupdate
sudo port upgrade outdated

Vervolgens hebben we de gegenereerde .POT-bestanden toegevoegd aan onze GlotPress installatie.

Omgeving Aantal teksten
WooCommerce 1391
WooCommerce admin 1112

Waar in WooCommerce 1.6.6 nog 2058 teksten in één bestand stonden en altijd geladen werden is dit met deze wijziging bijna gehalveerd. Deze verbetering hebben we ook verwerkt in de “WooCommerce (nl)” plugin. Met behulp van een Makefile kunnen we eenvoudig de 2 .POT-bestanden aanmaken:

WOOCOMMERCE_DIR=../woocommerce/

# Make POT files
extract:
	cd $(WOOCOMMERCE_DIR) && \
	find ./admin -iname "*.php" -type f | xgettext \
	--from-code=UTF-8 \
	--keyword=__ \
	--keyword=_e \
	--keyword=_n:1,2 \
	--keyword=_x:1,2c \
	--keyword=_ex:1,2c \
	--keyword=_nx:1,2,4c \
	--default-domain=woocommerce \
	--language=PHP \
	--copyright-holder="Remco Tolsma" \
	--package-name=WooCommerce \
	--package-version=2.0.5 \
	--msgid-bugs-address="Remco Tolsma <info@remcotolsma.nl>" \
	--files-from=- \
	--output=$(CURDIR)/languages/woocommerce/woocommerce-admin.pot \

	cd $(WOOCOMMERCE_DIR) && \
	find ./ -iname "*.php" -type f | xgettext \
	--from-code=UTF-8 \
	--keyword=__ \
	--keyword=_e \
	--keyword=_n:1,2 \
	--keyword=_x:1,2c \
	--keyword=_ex:1,2c \
	--keyword=_nx:1,2,4c \
	--default-domain=woocommerce \
	--language=PHP \
	--copyright-holder="Remco Tolsma" \
	--package-name=WooCommerce \
	--package-version=2.0.5 \
	--msgid-bugs-address="Remco Tolsma <info@remcotolsma.nl>" \
	--files-from=- \
	--exclude-file=$(CURDIR)/languages/woocommerce/woocommerce-admin.pot \
	--output=$(CURDIR)/languages/woocommerce/woocommerce.pot

W3 Total Cache mobile check

Gebruikers van de “W3 Total Cache” plugin weten dat je per “User Agent Groups”, ook wel browser groepen, verschillende pagina caches kunt hanteren. Dit is erg handig voor als je WordPress website voor mobiele apparaten anders is opgebouwd dan voor desktop browsers. Met de wp_is_mobile() functie kun je dan eenvoudig je WordPress thema aanpassen voor mobiele appareten.

W3 Total Cache mobile User Agent Groups

We liepen echter tegen problemen dat de wp_is_mobile() functie check niet overeenkwam met de standaard W3 Total Cache “User Agent Groups”. Hierdoor werd soms op desktop browsers toch de mobiele versie van de website weergegeven. Daarom zijn we opzoek gegaan naar een manier om de W3 Total Cache check te gebruiken.

Na het doorzoeken van de W3 Total Cache code kwamen uit op de volgende classes en functies:

Aan de hand hiervan hebben we de volgende help functie kunnen schrijven:

if ( ! function_exists( 'is_mobile' ) ) { 
	function is_mobile() {
		$is_mobile = false;

		if ( class_exists( 'W3_Mobile' ) ) {
			$w3_mobile = new W3_Mobile();

			$group = $w3_mobile->get_group();

			$is_mobile = $group !== false;
		}

		return $is_mobile;
	}
}

 

WooCommerce teksten wijzigen

In het bericht “WooCommerce ‘Toevoegen aan winkelwagen’ tekst wijzigen” schreef ik al hoe je een specifieke WooCommerce tekst kon wijzigen. Helaas zijn met behulp van deze oplossing niet alle WooCommerce teksten te wijzigen. Toch komt het wel eens voor dat ook andere teksten gewijzigd moet worden. Binnen bepaalde webwinkels is “Bestellen” bijvoorbeeld een betere vertaling voor  “Checkout”  in plaats van “Afrekenen”.

Met behulp van de volgende code kunnen alle WooCommerce teksten eenvoudig aangepast worden.

/**
 * Translate WooCommerce text
 *
 * @link http://codex.wordpress.org/Plugin_API/Filter_Reference/gettext
 */
function prefix_translate_woocommerce( $translated_text, $text, $domain ) {
	if ( $domain == 'woocommerce' ) {
		switch ( $text ) {
			case 'Checkout &rarr;' :
				$translated_text = 'Bestellen';
				break;
			case 'Add to Cart' :
			case 'Add to cart' :
				$translated_text = 'Bestellen';
				break;
		}
	}

	return $translated_text;
}

add_filter( 'gettext', 'prefix_translate_woocommerce', 20, 3 );

WordPress update naar 3.5 fatal error

Bij de WordPress update van 3.4.2 naar WordPress 3.5 liepen we bij een aantal websites tegen de volgende foutmelding aan:

HTTP-fout 500 (Internal Server Error): Er is een onverwachte voorwaarde gevonden toen de server het verzoek wilde uitvoeren.

Via de Apache fouten logboek zagen we de volgende foutmelding voorbij komen:

Website 1

[Fri Jan 18 09:14:18.400710 2013] [:error] [pid 17051] [client *.*.*.*:*] PHP Warning:  Missing argument 1 for get_post(), called in /public_html/wp-includes/link-template.php on line 1125 and defined in /public_html/wp-includes/post.php on line 380

[Fri Jan 18 09:17:25.937590 2013] [:error] [pid 17268] [client *.*.*.*:*] PHP Warning:  Division by zero in /public_html/wp-includes/functions.php on line 3237

[Fri Jan 18 09:17:26.019925 2013] [:error] [pid 17144] [client *.*.*.*:*] PHP Fatal error:  Call to undefined function get_current_blog_id() in /public_html/wp-includes/user.php on line 671, referer: http://domainname.tld/wp-admin/update-core.php?action=do-core-upgrade

Website 2

[Fri Jan 18 09:44:52.166372 2013] [:error] [pid 18936] [client *.*.*.*:*] PHP Warning:  Division by zero in /public_html/wp-includes/functions.php on line 3237

[Fri Jan 18 09:44:52.190437 2013] [:error] [pid 18936] [client *.*.*.*:*] PHP Fatal error:  Call to a member function register_handler() on a non-object in /public_html/wp-includes/media.php on line 944

Waardoor deze problemen werden veroorzaakt was een groot raadsel. Ook na een handmatige update van WordPress was het probleem niet opgelost. Uiteindelijk kwamen we tot de ontdekking dat er een caching techniek (XCache) actief was op de hosting omgeving.

Hierdoor werd na de WordPress 3.5 update op de hosting omgeving nog steeds gebruik gemaakt van PHP bestanden/functies uit WordPress 3.4.2. Uiteindelijk hebben we het probleem kunnen oplossen door tijdelijke XCache uit te schakelen.

Het uitschakelen van XCache kan met behulp van de volgende regels in het .htaccess bestand:

php_flag xcache.cacher Off
php_flag xcache.size 0
php_flag xcache.stat Off

Mocht je zelf ook een keer tegen onverklaarbare foutmeldingen aanlopen na een WordPress update dan is het wellicht goed om eens na te gaan welke caching technieken actief zijn.

PHP code debuggen met ChromePHP of FirePHP

Debuggen in PHP wordt vaak gedaan met behulp van de var_dump() en print_r() functies. In veel gevallen werkt dit prima, maar het heeft als nadeel dat de debug output vaak tussen de overige output staat. Gelukkig zijn er handige tools waarmee dit probleem verholpen kan worden. Zo kun je met behulp van FirePHP of ChromePHP debug informatie weergeven in een balk van je webbrowser.

WordPress menu limiet

Binnen WordPress kunnen menu’s eenvoudig aangemaakt en beheert worden. Bij grotere menu’s kunnen er echter problemen ontstaan. Menu’s worden soms niet meer goed opgeslagen of het aantal menu items is beperkt. Binnen het WordPress forum zijn hier verschillende topics over te vinden:

Dit probleem wordt veroorzaakt door de Suhosin module van PHP. Deze module beschermt servers tegen onveilig gebruik van PHP. Dit doet Suhosin onder andere door verschillende beperkingen te activeren.

Eén van de beperking is het aantal ‘post’ en ‘request’ variabelen. WordPress werkt met deze variabelen om menu’s op te slaan. Een minder handige constructie omdat je tegen limieten van bijvoorbeeld Suhosin kunt aanlopen.

Gelukkig kunnen de limieten in veel gevallen verhoogd worden. Vaak moet dit uitgevoerd worden door de hosting partij. Als je echter zelf een server beheert dan kun je dit ook eenvoudig zelf doen. Ik wil graag dat deze limieten per directory in een .htacces bestand geconfigureerd kunnen worden.

Hiervoor heb ik de volgende wijziging doorgevoerd in het “suhosin.ini” configuratie bestand:

nano /etc/php5/apache2/conf.d/suhosin.ini

Vervolgens heb ik de volgende regel gewijzigd van:

suhosin.perdir = "0"

naar:

suhosin.perdir = "pr"

De ‘p’ en ‘r’ karakters geven aan dat ‘post’ en de ‘request’ configuratie instellingen per map zijn in te stellen. Dit onderdeel van Suhosin is niet goed gedocumenteerd, maar in de broncode van de Suhosin bibliotheek is de werking hiervan eenvoudig te raadplegen.

Vervolgens kunnen we het volgende opnemen in het .htaccess bestand om groter menu’s te kunnen beheren binnen WordPress:

# BEGIN PHP
<IfModule mod_php5.c>
	php_value suhosin.post.max_vars 2048
	php_value suhosin.request.max_vars 2048
</IfModule>
# END PHP

Meer informatie:

WooCommerce ‘Toevoegen aan winkelwagen’ tekst wijzigen

Gebruikers van WooCommerce weten dat de knop waarmee bezoekers een product kunnen toevoegen in het Engels standaard de tekst ‘Add to cart‘ bevat. Als we dit vertalen naar het Nederlands wordt dit ‘Toevoegen aan winkelwagen‘. Sommige mensen zijn geneigd om dit vertalen naar een kortere variant, bijvoorbeeld ‘Toevoegen’. Ik vind echter dat je geen informatie moet weglaten binnen een vertaling. Daarom hanteren we in de ‘WooCommerce (nl)‘ plugin de volledige vertaling.

Doordat de Nederlandse volledig vertaling echter vrij lang is in vergelijking met de Engelse tekst komt dit echter niet altijd mooi uit binnen WordPress/WooCommerce thema’s.

Daarom ging ik op zoek naar een methode waarmee we de tekst eenvoudig kunnen aanpassen. Gelukkig past WooCommerce een filter (‘add_to_cart_text’) toe op de betreffende tekst waarmee we de tekst eenvoudig kunnen aanpassen. Met onderstaand voorbeeld is daardoor de tekst te wijzigen naar een korte variant, bijvoorbeeld: ‘Add’.

function prefix_add_to_cart_text( $text ) {
	$text = __( 'Add', 'text_domain' );

	return $text;
}

add_filter( 'add_to_cart_text', 'prefix_add_to_cart_text' );

Bovenstaande code kan toegevoegd worden aan het WordPress functies thema bestand (functions.php). Vaak kan de code zonder problemen aan het eind van dit bestand toegevoegd worden. Als je niet werkt met een maatwerk thema dan kan het overigens handig zijn om deze toevoeging binnen een child thema of plugin te definiëren. Op die manier kun je zonder problemen je thema blijven bijwerken.

Mocht je zelf ook tegen vergelijkbare problemen aanlopen en op zoek zijn naar een oplossing dan kun je altijd even contact met me opnemen.

WordPress gebruikers niet in auteur dropdown

Bij veel geavanceerdere WordPress websites zijn de standaard WordPress gebruikersrollen en bijbehorende mogelijkheden (capabilities) niet meer voldoende. Gelukkig zijn deze gebruikersrollen eenvoudig met allerlei plugins te beheren en uit te breiden. Zo kan men bijvoorbeeld met behulp van de Members plugin dit alles via eenvoudige gebruikersinterface beheren.

Toch blijken er ook nog wel een aantal nadelen te kleven aan het inzetten van maatwerk gebruikersrollen. Zo loop ik regelmatig tegen het probleem aan dat gebruikers met maatwerk gebruikersrollen niet zichtbaar zijn de auteur dropdown. Hierdoor hebben eindbgeruikers niet de mogelijkheid om een gebruiker met maatwerk rol als auteur toe te wijzen aan een WordPress bericht.

Dit probleem wordt veroorzaakt doordat binnen de auteur meta box gebruik gemaakt wordt van de wp_dropdown_users() functie. De wp_dropdown_user() functie maakt op zijn beurt weer gebruik van get_users() en dus WP_User_Query. Met behulp van deze query klasse kunnen gebruikers opgevraagd. Met de ‘who’ parameter kan er geselecteerd worden op alle gebruikers of enkel auteurs.

Binnen de auteur meta box worden alleen de auteurs weergegeven. De implementatie van WP_User_Query vraagt de auteurs echter op aan de hand van het gebruikersniveau (level). WordPress gebruikersniveaus is een techniek die werd gebruik in WordPress versies voor 2.0. Tegenwoordig wordt bij het aanmaken van maatwerk gebruikersrollen deze gebruikersniveaus niet meer meegenomen.

In veel gevallen is het echterwel  verstandig om ook de gebruikersniveaus te koppelen aan maatwerk gebruikersrollen. Dit kan eenvoudig gerealiseerd worden door met behulp van de Members plugin respectievelijk de volgende mogelijkheden (capabilities) toe te voegen:

  • level_0
  • level_1
  • level_2
  • level_3
  • level_4
  • level_5
  • level_6
  • level_7
  • level_8
  • level_9
  • level_10

Dit zorgt ervoor dat zodra er gebruikers met een maatwerk gebruikersrol worden aangemaakt het gebruikersniveau niet op 0 blijft staan. Doordat deze niet op 0 blijft staan zullen deze gebruikers automatisch ook in de auteur dropdown weergegeven worden. Indien je al veel gebruikers met gebruikersniveau 0 in je systeem hebt staan dan kun je deze eenvoudig bijwerken met behulp van de volgende query:

UPDATE 
	wp_usermeta
SET 
	meta_value = 4
WHERE
	meta_key = 'wp_user_level'
		AND
	meta_value = 0
		AND
	user_id IN (
		SELECT 
			user_id 
		FROM (
			SELECT
				DISTINCT user_id 
			FROM 
				wp_usermeta
			WHERE 
				meta_key = 'wp_capabilities'
					AND
				meta_value LIKE '%company_author%'
		) AS temporary_table
	);

Met behulp van bovenstaande query upgrade ik gebruikers met de gebruikersrol ‘company_author’ en gebruikersniveau 0 naar gebruikersniveau 4.

WordPress eenvoudig in debug mode

Elke WordPress ontwikkelaar moet wel eens snel een probleem oplossen op een website die al live staat. Om problemen op te lossen is het vaak handig om te werken in debug mode. In een live omgeving is dit echter vaak niet een goed idee, omdat bezoekers dan onnodige meldingen te zien krijgen. Dit is echter eenvoudig te omzeilen door het volgende in het wp-config.php bestand te zetten:

define( 'WP_DEBUG', filter_input( INPUT_GET, 'debug', FILTER_VALIDATE_BOOLEAN) );

Vervolgens kun je eenvoudig de WordPress website in debug mode zetten door deze als volgt op te vragen:

  • http://domeinnaam.tld/?debug=true

Ook de volgende varianten zullen functioneren dankzij de krachtige FILTER_VALIDATE_BOOLEAN validatie filter.

  • http://domeinnaam.tld/?debug=1
  • http://domeinnaam.tld/?debug=on
  • http://domeinnaam.tld/?debug=yes

Het is waarschijnlijk wel verstandig om deze functie na het oplossen van het probleem weer te verwijderen en debug mode gewoon op ‘false’ te zetten.

Gravity Forms conditionele omleiding

Gravity Forms is een erg krachtige formulieren plugin voor WordPress. Onlangs hebben we deze plugin gebruikt voor het opzetten van een keuzerondjes vragen test. De bezoeker moest echter afhankelijk van aantal gekozen A, B en C antwoorden doorgestuurd worden naar een specifieke pagina. Standaard kan dit niet binnen Gravity Forms geconfigureerd worden, maar met behulp van een eenvoudig filter functie kan dit wel gerealiseerd worden:

function prefix_test_confirmation($confirmation, $form, $lead, $ajax) {
	if($form['id'] == '1') {
		foreach($form['fields'] as $field) {
			$values[] = $lead[$field['id']];
		}

		$result = array_count_values($values);
		arsort($result);
		$result = key($result);

		switch($result) {
			case 'A':
				$confirmation = array('redirect' => 'http://domeinnaam.nl/test/a-antwoorden/');
				break;
			case 'B':
				$confirmation = array('redirect' => 'http://domeinnaam.nl/test/b-antwoorden/');
				break;
			case 'C':
				$confirmation = array('redirect' => 'http://domeinnaam.nl/test/c-antwoorden/');
				break;
		}
	}

	return $confirmation;
}

add_filter('gform_confirmation', 'prefix_test_confirmation', 10, 4);

Bovenstaande functie telt hoeveel dezelfde antwoorden er zijn gegevens. Dit resulteert in een data array waarbij per antwoord staat hoe vaak deze is gegeven. Vervolgens wordt met behulp van de volgende regels code het meest gegeven antwoord bepaalt:

$result = array_count_values($values);
arsort($result);
$result = key($result);

FatBat geeft deze oplossing op de PHP documentatie website:

Needed to get the index of the max/highest value in an assoc array.
max() only returned the value, no index, so I did this instead.

reset($x);   // optional. 
arsort($x); 
$key_of_max = key($x);   // returns the index.

Mocht je ook een test / enquete willen opzetten in WordPress dan kan Gravity Forms een zeer handige oplossing zijn. Mocht je hier meer informatie over willen dan kun je altijd vrijblijvende contact opnemen met Pronamic.