diff --git a/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini b/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
index efaf6119e140079e832d41cf02964faec7f5a562..c8e4c1df0956fb5a2546d0fd74dfa88d670022d2 100644
--- a/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
+++ b/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
@@ -67,3 +67,5 @@ OPENTOOLS_CHECK_CREDENTIALS_ERROR="Konnte Zugangsdaten nicht überprüfen. Bitte
 OPENTOOLS_XMLMANIFEST_ERROR="Konnte die XML-Manifest-Datei der Erweiterung nicht laden (%s)"
 OPENTOOLS_UPDATESCRIPT_ERROR="Konnte die Aktualisierungsinformationen nicht laden (%s)"
 OPENTOOLS_COMMERCIAL_UPDATES_J25="Automatische Aktualisierungen von kommerziellen Erweiterungen sind in Joomla 2.5 leider nicht möglich (erste ab Joomla 3.x). Bitte informieren Sie sich auf der Homepage des Entwicklers über mögliche Aktualisierungen und installieren Sie diese manuell."
+
+OTSHIPMENT_RULES_SCOPING_UNKNOWN="Unbekannte Scoping-Funktion 'evaluate_for_%s' in der Regel '%s'"
diff --git a/language/de-DE/de-DE.plg_vmshipment_rules_shipping_advanced.ini b/language/de-DE/de-DE.plg_vmshipment_rules_shipping_advanced.ini
index dd1ecaf6fe6c1b60c67b6fc10a0ae3a5102a1f07..5aa0e04c95eb7e3cb5931322d7ee0d286935170f 100644
--- a/language/de-DE/de-DE.plg_vmshipment_rules_shipping_advanced.ini
+++ b/language/de-DE/de-DE.plg_vmshipment_rules_shipping_advanced.ini
@@ -57,3 +57,5 @@ OPENTOOLS_CHECK_CREDENTIALS_ERROR="Konnte Zugangsdaten nicht überprüfen. Bitte
 OPENTOOLS_XMLMANIFEST_ERROR="Konnte die XML-Manifest-Datei der Erweiterung nicht laden (%s)"
 OPENTOOLS_UPDATESCRIPT_ERROR="Konnte die Aktualisierungsinformationen nicht laden (%s)"
 OPENTOOLS_COMMERCIAL_UPDATES_J25="Automatische Aktualisierungen von kommerziellen Erweiterungen sind in Joomla 2.5 leider nicht möglich (erste ab Joomla 3.x). Bitte informieren Sie sich auf der Homepage des Entwicklers über mögliche Aktualisierungen und installieren Sie diese manuell."
+
+OTSHIPMENT_RULES_SCOPING_UNKNOWN="Unbekannte Scoping-Funktion 'evaluate_for_%s' in der Regel '%s'"
diff --git a/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini b/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
index b5a23fff790a74818b29c60029badbde8e90808f..ba9aaa25d63c585e67e10830ad355f7d8aabf1d6 100755
--- a/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
+++ b/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
@@ -61,6 +61,7 @@ OTSHIPMENT_RULES_EVALUATE_LISTFUNCTION_UNKNOWN="Unknown list function '%s' encou
 OTSHIPMENT_RULES_NOSHIPPING_MESSAGE="%s"
 
 OTSHIPMENT_RULES_UNKNOWN_TYPE="Unknown rule type '%s' encountered for rule '%s'"
+OTSHIPMENT_RULES_SCOPING_UNKNOWN="Unknown scoping function 'evaluate_for_%s' encountered in rule '%s'"
 
 
 VMSHIPMENT_RULES_CUSTOMFUNCTIONS_NOARRAY="Definition of custom functions (returned by a vmshipmentrules plugin) is not a proper array. Ignoring."
diff --git a/library/rules_shipping_framework.php b/library/rules_shipping_framework.php
index 3c89c16218870dd25b21bbedb06c81e17fb60666..942edc522b153c3f01df735420ef70a8a0d7f54a 100644
--- a/library/rules_shipping_framework.php
+++ b/library/rules_shipping_framework.php
@@ -55,7 +55,8 @@ class RulesShippingFramework {
 	// Store the parsed and possibly evaluated rules for each method (method ID is used as key)
 	protected $rules = array();
 	protected $match = array();
-	var $custom_functions = array ();
+	protected $custom_functions = array ();
+	protected $available_scopings = array();
 	
 	function __construct() {
 // 		$this->registerCallback('addCustomCartValues',	array($this, 'addCustomCartValues'));
@@ -79,44 +80,69 @@ class RulesShippingFramework {
 		$this->callbacks[$callback] = $func;
 	}
 	
-	public function __($string) {
-		$args = func_get_args();
+	/**
+	 * Register all possible scopings to the framework in the form
+	 *    array("skus" => "products" , "products" => "products")
+	 * This registers functions evaluate_for_skus and evaluate_for_products,
+	 * which both filter products (they are identical).
+	 */
+	 public function registerScopings($scopings) {
+		$this->available_scopings = $scopings;
+	}
+	
+	/**
+	 * Get the list of all scopings the framework implementation claims to have
+	 * implemented.
+	 */
+	public function getScopings() {
+		return $this->available_scopings;
+	}
+	
+	public function readableString($string) {
 		switch ($string) {
 			case "OTSHIPMENT_RULES_CUSTOMFUNCTIONS_ALREADY_DEFINED":
-					$args[0]=""; break;
+					return "Custom function %s already defined. Ignoring this definition and using previous one.";
 			case "OTSHIPMENT_RULES_CUSTOMFUNCTIONS_NOARRAY":
-					$args[0]=""; break;
+					return "Definition of custom functions (returned by a plugin) is not a proper array. Ignoring.";
 			case "OTSHIPMENT_RULES_EVALUATE_ASSIGNMENT_TOPLEVEL":
-					$args[0]="Assignments are not allowed inside expressions (rule given was '%s')"; break;
+					return "Assignments are not allowed inside expressions (rule given was '%s')";
 			case "OTSHIPMENT_RULES_EVALUATE_LISTFUNCTION_ARGS":
-					$args[0]="List function '%s' requires all arguments to be lists. (Full rule: '%s')"; break;
+					return "List function '%s' requires all arguments to be lists. (Full rule: '%s')";
 			case "OTSHIPMENT_RULES_EVALUATE_LISTFUNCTION_CONTAIN_ARGS":
-					$args[0]="List function '%s' requires the first argument to be lists. (Full rule: '%s')"; break;
+					return "List function '%s' requires the first argument to be lists. (Full rule: '%s')";
 			case "OTSHIPMENT_RULES_EVALUATE_LISTFUNCTION_UNKNOWN":
-					$args[0]="Unknown list function '%s' encountered. (Full rule: '%s')"; break;
+					return "Unknown list function '%s' encountered. (Full rule: '%s')";
 			case "OTSHIPMENT_RULES_EVALUATE_SYNTAXERROR":
-					$args[0]="Syntax error during evaluation, RPN is not well formed! (Full rule: '%s')"; break;
+					return "Syntax error during evaluation, RPN is not well formed! (Full rule: '%s')";
 			case "OTSHIPMENT_RULES_EVALUATE_UNKNOWN_ERROR":
-					$args[0]="Unknown error occurred during evaluation of rule '%s'."; break;
+					return "Unknown error occurred during evaluation of rule '%s'.";
 			case "OTSHIPMENT_RULES_EVALUATE_UNKNOWN_FUNCTION":
-					$args[0]="Unknown function '%s' encountered during evaluation of rule '%s'."; break;
+					return "Unknown function '%s' encountered during evaluation of rule '%s'.";
 			case "OTSHIPMENT_RULES_EVALUATE_UNKNOWN_VALUE":
-					$args[0]="Evaluation yields unknown value while evaluating rule part '%s'."; break;
+					return "Evaluation yields unknown value while evaluating rule part '%s'.";
 			case "OTSHIPMENT_RULES_NOSHIPPING_MESSAGE":
-					$args[0]=""; break;
+					return "%s";
 			case "OTSHIPMENT_RULES_PARSE_FUNCTION_NOT_CLOSED":
-					$args[0]="Error during parsing expression '%s': A function call was not closed properly!"; break;
+					return "Error during parsing expression '%s': A function call was not closed properly!";
 			case "OTSHIPMENT_RULES_PARSE_MISSING_PAREN":
-					$args[0]="Error during parsing expression '%s': Opening parenthesis cannot be found!"; break;
+					return "Error during parsing expression '%s': Opening parenthesis cannot be found!";
 			case "OTSHIPMENT_RULES_PARSE_PAREN_NOT_CLOSED":
-					$args[0]="Error during parsing expression '%s': A parenthesis was not closed properly!"; break;
+					return "Error during parsing expression '%s': A parenthesis was not closed properly!";
 			case "OTSHIPMENT_RULES_UNKNOWN_OPERATOR":
-					$args[0]="Unknown operator '%s' in shipment rule '%s'"; break;
+					return "Unknown operator '%s' in shipment rule '%s'";
 			case "OTSHIPMENT_RULES_UNKNOWN_TYPE":
-					$args[0]=""; break;
+					return "Unknown rule type '%s' encountered for rule '%s'";
+			case "OTSHIPMENT_RULES_SCOPING_UNKNOWN":
+					return "Unknown scoping function 'evaluate_for_%s' encountered in rule '%s'";
 			case "OTSHIPMENT_RULES_UNKNOWN_VARIABLE":
-					$args[0]="Unknown variable '%s' in rule '%s'"; break;
+					return "Unknown variable '%s' in rule '%s'";
+			default:
+					return $string;
 		}
+	}
+	
+	public function __($string) {
+		$args = func_get_args();
 
 		if (isset($this->callbacks["translate"])) {
 			return call_user_func_array($this->callbacks["translate"], $args);
@@ -261,6 +287,14 @@ class RulesShippingFramework {
 		return array();
 	}
 	
+	protected function getDebugVariables ($cart, $products, $method) {
+		
+		return array(
+			'debug_cart'=> print_r($cart,1),
+			'debug_products' => print_r($products, 1),
+		);
+	}
+	
 	/** 
 	 * Extract information about non-numerical zip codes (UK and Canada) from the postal code
 	 */
@@ -323,7 +357,8 @@ class RulesShippingFramework {
 			$this->getOrderAddress ($cart, $method),
 			// Add Total/Min/Max weight and dimension variables:
 			$this->getOrderWeights ($cart, $products, $method),
-			$this->getOrderDimensions ($cart, $products, $method)
+			$this->getOrderDimensions ($cart, $products, $method),
+			$this->getDebugVariables ($cart, $products, $method)
 		);
 		// Let child classes update the $cartvals array, or add new variables
 		$this->addCustomCartValues($cart, $products, $method, $cartvals);
@@ -759,24 +794,31 @@ class ShippingRule {
 		}
 	}
 	
+	protected function normalizeScoping($scoping) {
+		$scopings = $this->framework->getScopings();
+// $this->framework->warning("<pre>normalizing Scoping $scoping. Registered scopings are: ".print_r($scopings,1)."</pre>");
+		if (isset($scopings[$scoping])) {
+			return $scopings[$scoping];
+		} else {
+			return false;
+		}
+	}
+	
 	/** Evaluate the given expression $expr only for the products that match the filter given by the scoping 
 	 * function and the corresponding conditions */
 	protected function evaluateScoping($expr, $scoping, $conditionvals, $vals, $products, $cartvals_callback) {
 		if (count($conditionvals)<1)
 			return $this->evaluateTerm($expr, $vals, $products, $cartvals_callback);
-
-		// TODO: Make this more general!
-		$filterkeys = array( 
-			"evaluate_for_categories" =>    'categories',
-			"evaluate_for_products" =>      'skus',
-			"evaluate_for_skus" =>          'skus',
-			"evaluate_for_vendors" =>       'vendors',
-			"evaluate_for_manufacturers" => 'manufacturers',
-		);
 		
-		$conditions = array();
-		if (isset($filterkeys[$scoping])) 
-			$conditions[$filterkeys[$scoping]] = $conditionvals;
+// $this->framework->warning("<pre>evaluating scoping $scoping of expression ".print_r($expr,1)." with conditions ".print_r($conditionvals,1)."</pre>");
+		// Normalize aliases (e.g. 'skus' and 'products' usually indicate the same scoping
+		$normalizedScoping = $this->normalizeScoping($scoping);
+		if (!$normalizedScoping) {
+			$this->framework->warning('OTSHIPMENT_RULES_SCOPING_UNKNOWN', $scoping, $this->rulestring);
+			return false;
+		} else {
+			$conditions = array($normalizedScoping => $conditionvals);
+		}
 
 		// Pass the conditions to the parent plugin class to filter the current list of products:
 		$filteredproducts = $this->framework->filterProducts($products, $conditions);
@@ -873,8 +915,11 @@ class ShippingRule {
 			return $varname;
 		} elseif ($varname=='values') {
 			return $vals;
-		} elseif ($varname=='values_debug') {
-			return print_r($vals,1);
+		} elseif ($varname=='values_debug' || $varname='debug_values') {
+			$tmpvals = $vals;
+			unset($tmpvals['debug_cart']);
+			unset($tmpvals['debug_products']);
+			return print_r($tmpvals,1);
 		} else {
 			$this->framework->warning('OTSHIPMENT_RULES_EVALUATE_UNKNOWN_VALUE', $expr, $this->rulestring);
 			return null;
@@ -885,8 +930,7 @@ class ShippingRule {
 		// The scoping functions need to be handled differently, because they first need to adjust the cart variables to the filtered product list
 		// before evaluating its first argument. So even though parsing the rules handles scoping functions like any other function, their 
 		// evaluation is fundamentally different and is special-cased here:
-		$scoping_functions = array("evaluate_for_categories", "evaluate_for_products", "evaluate_for_skus", "evaluate_for_vendors", "evaluate_for_manufacturers");
-		$is_scoping = is_array($expr) && ($expr[0]=="FUNCTION") && (count($expr)>1) && in_array($expr[1], $scoping_functions);
+		$is_scoping = is_array($expr) && ($expr[0]=="FUNCTION") && (count($expr)>1) && (substr($expr[1], 0, 13)==="evaluate_for_");
 
 		if (is_null($expr)) {
 			return $expr;
@@ -901,14 +945,14 @@ class ShippingRule {
 			}
 		} elseif ($is_scoping) {
 			$op = array_shift($expr); // ignore the "FUNCTION"
-			$func = array_shift($expr); // The scoping function name
+			$scope = substr(array_shift($expr), 13); // The scoping function name with "evaluate_for_" cut off
 			$expression = array_shift($expr); // The expression to be evaluated
 			// the remaining $expr list now contains the conditions. Evaluate them one by one:
 			$conditions = array();
 			foreach ($expr as $e) {
 				$conditions[] = $this->evaluateTerm($e, $vals, $products, $cartvals_callback);
 			}
-			return $this->evaluateScoping ($expression, $func, $conditions, $vals, $products, $cartvals_callback);
+			return $this->evaluateScoping ($expression, $scope, $conditions, $vals, $products, $cartvals_callback);
 			
 		} elseif (is_array($expr)) {
 			// Operator
diff --git a/rules_shipping_framework_joomla.php b/rules_shipping_framework_joomla.php
index 6862d3b8e131a77e89f183d3cec236f10886f4ba..11eb008025f3ae29f047660744c103ac4f80c4db 100644
--- a/rules_shipping_framework_joomla.php
+++ b/rules_shipping_framework_joomla.php
@@ -18,10 +18,11 @@ if (!class_exists( 'RulesShippingFramework' ))
 // $test=new asdfasdsf();
 class RulesShippingFrameworkJoomla extends RulesShippingFramework {
 	/* Constructor: Register the available scopings */
-	function _construct() {
-		parent::_construct();
+	function __construct() {
+		parent::__construct();
 		$this->registerScopings(array(
 			"categories"    => 'categories',
+			"subcategories" => 'subcategories',
 			"products"      => 'products',
 			"skus"          => 'products',
 			"vendors"       => 'vendors',
@@ -344,11 +345,32 @@ class RulesShippingFrameworkJoomla extends RulesShippingFramework {
 	*/
 	public function filterProducts($products, $filter_conditions) {
 		$result = array();
+		
+		// For the subcategories scoping we need all subcategories of the conditions:
+		$subcategories = array();
+		if (isset($filter_conditions['subcategories']) && !empty($filter_conditions['subcategories'])) {
+			$catmodel = VmModel::getModel('category');
+			foreach ($filter_conditions['subcategories'] as $catid) {
+				$subcategories[] = $catid;
+				// Get all child categories from the category model
+				$categories = $catmodel->getCategoryTree($catid,0,/*onlyPublished=*/false);
+				foreach ($categories as $subcat) {
+					$subcategories[] = $subcat->virtuemart_category_id;
+				}
+			}
+			$subcategories = array_unique($subcategories);
+JFactory::getApplication()->enqueueMessage("<pre>Subcategories: ".print_r($subcategories,1)."</pre>", 'error');
+			
+		}
+		
+
 		foreach ($products as $p) {
 			if (!empty($filter_conditions['products']) && !in_array($p->product_sku, $filter_conditions['products']))
 				continue;
 			if (!empty($filter_conditions['categories']) && count(array_intersect($filter_conditions['categories'], $p->categories))==0)
 				continue;
+			if (!empty($filter_conditions['subcategories']) && count(array_intersect($subcategories, $p->categories))==0)
+				continue;
 			if (!empty($filter_conditions['manufacturers']) && count(array_intersect($filter_conditions['manufacturers'], $p->virtuemart_manufacturer_id))==0)
 				continue;
 			if (!empty($filter_conditions['vendors']) && !in_array($p->virtuemart_vendor_id, $filter_conditions['vendors']))