diff --git a/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/MiningSettingsDAOBean.java b/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/MiningSettingsDAOBean.java
new file mode 100644
index 0000000..57047b9
--- /dev/null
+++ b/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/MiningSettingsDAOBean.java
@@ -0,0 +1,83 @@
+package com.deepclone.lw.beans.game.resources;
+
+
+import java.sql.Types;
+
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.deepclone.lw.interfaces.game.resources.MiningSettingsDAO;
+import com.deepclone.lw.utils.StoredProc;
+
+
+
+/**
+ * Data access component for mining settings
+ * 
+ * <p>
+ * This component implements the methods which allow mining settings to be updated by calling the
+ * appropriate stored procedures.
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+class MiningSettingsDAOBean
+		implements MiningSettingsDAO
+{
+	/** <code>emp.mset_update_start(INT)</code> stored procedure */
+	private StoredProc pStartEmpireUpdate;
+
+	/** <code>emp.mset_update_set(TEXT,INT)</code> stored procedure */
+	private StoredProc pSetMiningPriority;
+
+	/** <code>emp.mset_update_apply()</code> stored procedure */
+	private StoredProc pApplyUpdate;
+
+
+	/**
+	 * Dependency injector that sets the data source
+	 * 
+	 * @param dataSource
+	 *            the data source
+	 */
+	@Autowired( required = true )
+	public void setDataSource( DataSource dataSource )
+	{
+		this.pStartEmpireUpdate = new StoredProc( dataSource , "emp" , "mset_update_start" );
+		this.pStartEmpireUpdate.addParameter( "_empire" , Types.INTEGER );
+		this.pStartEmpireUpdate.addOutput( "_success" , Types.BOOLEAN );
+
+		this.pSetMiningPriority = new StoredProc( dataSource , "emp" , "mset_update_set" );
+		this.pSetMiningPriority.addParameter( "_resource" , Types.VARCHAR );
+		this.pSetMiningPriority.addParameter( "_priority" , Types.INTEGER );
+		this.pSetMiningPriority.addOutput( "_success" , Types.BOOLEAN );
+
+		this.pApplyUpdate = new StoredProc( dataSource , "emp" , "mset_update_apply" );
+		this.pApplyUpdate.addOutput( "_success" , Types.BOOLEAN );
+	}
+
+
+	/* Documented in interface */
+	@Override
+	public boolean startUpdate( int empireId )
+	{
+		return (Boolean) this.pStartEmpireUpdate.execute( empireId ).get( "_success" );
+	}
+
+
+	/* Documented in interface */
+	@Override
+	public boolean setNewPriority( String resource , int priority )
+	{
+		return (Boolean) this.pSetMiningPriority.execute( resource , priority ).get( "_success" );
+	}
+
+
+	/* Documented in interface */
+	@Override
+	public boolean applyUpdate( )
+	{
+		return (Boolean) this.pApplyUpdate.execute( ).get( "_success" );
+	}
+
+}
diff --git a/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/ResourcesControllerBean.java b/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/ResourcesControllerBean.java
new file mode 100644
index 0000000..a2b8040
--- /dev/null
+++ b/legacyworlds-server-beans-resources/src/main/java/com/deepclone/lw/beans/game/resources/ResourcesControllerBean.java
@@ -0,0 +1,68 @@
+package com.deepclone.lw.beans.game.resources;
+
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.deepclone.lw.interfaces.game.resources.MiningSettingsDAO;
+import com.deepclone.lw.interfaces.game.resources.ResourcesController;
+
+
+
+/**
+ * Resources controller component
+ * 
+ * <p>
+ * This component implements all game actions that affect resources or resource-related information,
+ * such as mining priorities.
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+@Transactional
+class ResourcesControllerBean
+		implements ResourcesController
+{
+
+	/** The mining settings access interface */
+	private MiningSettingsDAO settings;
+
+
+	/**
+	 * Dependency injector that sets the mining settings access interface
+	 * 
+	 * @param settings
+	 *            the mining settings access interface
+	 */
+	@Autowired( required = true )
+	public void setMiningSettingsDAO( MiningSettingsDAO settings )
+	{
+		this.settings = settings;
+	}
+
+
+	/**
+	 * Update an empire's mining settings
+	 * 
+	 * <p>
+	 * Start a mining settings update for the specified empire, then set each resource-specific
+	 * priority, then apply the update. Exit whenever something goes wrong.
+	 */
+	@Override
+	public void updateEmpireSettings( int empireId , Map< String , Integer > settings )
+	{
+		if ( !this.settings.startUpdate( empireId ) ) {
+			return;
+		}
+
+		for ( Map.Entry< String , Integer > entry : settings.entrySet( ) ) {
+			if ( ! this.settings.setNewPriority( entry.getKey( ) , entry.getValue( ) ) ) {
+				return;
+			}
+		}
+
+		this.settings.applyUpdate( );
+	}
+
+}
diff --git a/legacyworlds-server-beans-resources/src/main/resources/configuration/game/resources.xml b/legacyworlds-server-beans-resources/src/main/resources/configuration/game/resources.xml
index 2c36ed9..12debf7 100644
--- a/legacyworlds-server-beans-resources/src/main/resources/configuration/game/resources.xml
+++ b/legacyworlds-server-beans-resources/src/main/resources/configuration/game/resources.xml
@@ -3,6 +3,8 @@
 	xsi:schemaLocation="
                         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 
+	<bean id="miningSettingsDAO" class="com.deepclone.lw.beans.game.resources.MiningSettingsDAOBean" />
+	<bean id="resourcesController" class="com.deepclone.lw.beans.game.resources.ResourcesControllerBean" />
 	<bean id="resourcesInformationDAO" class="com.deepclone.lw.beans.game.resources.ResourcesInformationDAOBean" />
 
 </beans>
diff --git a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/UpdateEmpireMiningSettingsCommandDelegateBean.java b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/UpdateEmpireMiningSettingsCommandDelegateBean.java
new file mode 100644
index 0000000..ea0b021
--- /dev/null
+++ b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/UpdateEmpireMiningSettingsCommandDelegateBean.java
@@ -0,0 +1,74 @@
+package com.deepclone.lw.beans.user.player.game;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.deepclone.lw.beans.user.abst.AutowiredCommandDelegate;
+import com.deepclone.lw.beans.user.abst.SessionCommandHandler;
+import com.deepclone.lw.beans.user.player.GameSubTypeBean;
+import com.deepclone.lw.cmd.player.UpdateEmpireMiningSettingsCommand;
+import com.deepclone.lw.interfaces.game.resources.ResourcesController;
+import com.deepclone.lw.interfaces.session.ServerSession;
+import com.deepclone.lw.session.Command;
+import com.deepclone.lw.session.CommandResponse;
+import com.deepclone.lw.session.NullResponse;
+
+
+
+/**
+ * Command handler for empire mining settings updates
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+class UpdateEmpireMiningSettingsCommandDelegateBean
+		implements AutowiredCommandDelegate
+{
+
+	/** The resources controller */
+	private ResourcesController resourcesController;
+
+
+	/**
+	 * Dependency injector that sets the resources controller
+	 * 
+	 * @param resourcesController
+	 *            the resources controller
+	 */
+	@Autowired( required = true )
+	public void setResourcesController( ResourcesController resourcesController )
+	{
+		this.resourcesController = resourcesController;
+	}
+
+
+	/** This class handles {@link UpdateEmpireMiningSettingsCommand} instances */
+	@Override
+	public Class< ? extends Command > getType( )
+	{
+		return UpdateEmpireMiningSettingsCommand.class;
+	}
+
+
+	/** This class is enabled for the {@link GameSubTypeBean} session type */
+	@Override
+	public Class< ? extends SessionCommandHandler > getCommandHandler( )
+	{
+		return GameSubTypeBean.class;
+	}
+
+
+	/**
+	 * If the account is not in vacation mode, update mining settings. Always return a
+	 * {@link NullResponse}.
+	 */
+	@Override
+	public CommandResponse execute( ServerSession session , Command command )
+	{
+		if ( !session.get( "vacation" , Boolean.class ) ) {
+			int empireId = session.get( "empireId" , Integer.class );
+			this.resourcesController.updateEmpireSettings( empireId ,
+					( (UpdateEmpireMiningSettingsCommand) command ).getSettings( ) );
+		}
+		return new NullResponse( );
+	}
+}
diff --git a/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml b/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml
index 37a91ea..203a345 100644
--- a/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml
+++ b/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml
@@ -45,6 +45,7 @@
 	<!-- Game: empire -->
 	<bean class="com.deepclone.lw.beans.user.player.game.OverviewCommandDelegateBean" />
 	<bean class="com.deepclone.lw.beans.user.player.game.ImplementTechCommandDelegateBean" />
+	<bean class="com.deepclone.lw.beans.user.player.game.UpdateEmpireMiningSettingsCommandDelegateBean" />
 	<bean class="com.deepclone.lw.beans.user.player.game.GetNewPlanetCommandDelegateBean" />
 
 	<!-- Game: planet list -->
diff --git a/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-mining.sql b/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-mining.sql
index 9c4545f..d8b4f04 100644
--- a/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-mining.sql
+++ b/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-mining.sql
@@ -29,15 +29,17 @@ BEGIN
 
 	CREATE TEMPORARY TABLE mset_update(
 		empire_id			INT ,
-		resource_name_id	INT ,
+		resource_name		TEXT ,
 		empmset_weight		INT
 	) ON COMMIT DROP;
 
 	INSERT INTO mset_update
-		SELECT _mset.empire_id , _mset.resource_name_id , 2
+		SELECT _mset.empire_id , _str.name , 2
 			FROM emp.empires _empire
 				INNER JOIN emp.mining_settings _mset
 					ON _empire.name_id = _mset.empire_id
+				INNER JOIN defs.strings _str
+					ON _str.id = _mset.resource_name_id
 			WHERE _empire.name_id = _empire_id
 			FOR SHARE OF _empire
 			FOR UPDATE OF _mset;
@@ -81,7 +83,7 @@ BEGIN
 	CREATE TEMPORARY TABLE mset_update(
 		empire_id			INT ,
 		planet_id			INT ,
-		resource_name_id	INT ,
+		resource_name		TEXT ,
 		empmset_weight		INT
 	) ON COMMIT DROP;
 	
@@ -106,8 +108,10 @@ BEGIN
 		FOR UPDATE;
 
 	INSERT INTO mset_update
-		SELECT _empire_id , _planet_id , resource_name_id , 2
+		SELECT _empire_id , _planet_id , _str.name , 2
 			FROM verse.resource_providers
+				INNER JOIN defs.strings _str
+					ON _str.id = resource_name_id
 			WHERE planet_id = _planet_id;
 
 	RETURN TRUE;
@@ -131,14 +135,14 @@ GRANT EXECUTE
  * must be called after emp.mset_update_start() has initialised the table.
  *
  * Parameters:
- *		_resource_id		The resource's identifier
+ *		_resource			The resource's text identifier
  *		_weight				The setting's new value
  *
  * Returns:
  *		?					True if the resource exists, false otherwise.
  */
-DROP FUNCTION IF EXISTS emp.mset_update_set( INT , INT );
-CREATE FUNCTION emp.mset_update_set( _resource_id INT , _weight INT )
+DROP FUNCTION IF EXISTS emp.mset_update_set( TEXT , INT );
+CREATE FUNCTION emp.mset_update_set( _resource TEXT , _weight INT )
 		RETURNS BOOLEAN
 		STRICT VOLATILE
 		SECURITY DEFINER
@@ -147,7 +151,7 @@ BEGIN
 
 	UPDATE mset_update
 		SET empmset_weight = _weight
-		WHERE resource_name_id = _resource_id;
+		WHERE resource_name = _resource;
 	RETURN FOUND;
 
 END;
@@ -155,10 +159,10 @@ $mset_update_set$ LANGUAGE PLPGSQL;
 
 
 REVOKE EXECUTE
-	ON FUNCTION emp.mset_update_set( INT , INT )
+	ON FUNCTION emp.mset_update_set( TEXT , INT )
 	FROM PUBLIC;
 GRANT EXECUTE
-	ON FUNCTION emp.mset_update_set( INT , INT )
+	ON FUNCTION emp.mset_update_set( TEXT , INT )
 	TO :dbuser;
 
 
@@ -201,8 +205,10 @@ BEGIN
 					empire_id , planet_id , resource_name_id ,
 					emppmset_weight
 			) SELECT empire_id , planet_id ,
-					resource_name_id ,	empmset_weight
-				FROM mset_update;
+					_str.id , empmset_weight
+				FROM mset_update
+					INNER JOIN defs.strings _str
+						ON _str.name = resource_name;
 
 	EXCEPTION
 
@@ -211,8 +217,10 @@ BEGIN
 			UPDATE emp.mining_settings _settings
 				SET empmset_weight = _update.empmset_weight
 				FROM mset_update _update
+					INNER JOIN defs.strings _str
+						ON _str.name = _update.resource_name
 				WHERE _update.empire_id = _settings.empire_id
-					AND _update.resource_name_id = _settings.resource_name_id;
+					AND _str.id = _settings.resource_name_id;
 	END;
 	RETURN TRUE;
 
diff --git a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/010-mset-update-start.sql b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/010-mset-update-start.sql
index b73c7bc..8584f82 100644
--- a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/010-mset-update-start.sql
+++ b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/010-mset-update-start.sql
@@ -23,7 +23,7 @@ BEGIN;
 	SELECT ok( NOT emp.mset_update_start( _get_bad_emp_name( ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT ) - Temporary table exists despite bad empire identifier' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	
 	SELECT diag_test_name( 'emp.mset_update_start( INT ) - Return value on valid empire identifier' );
diff --git a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/015-mset-update-start-planet.sql b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/015-mset-update-start-planet.sql
index 816508a..31536c8 100644
--- a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/015-mset-update-start-planet.sql
+++ b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/015-mset-update-start-planet.sql
@@ -28,31 +28,31 @@ BEGIN;
 	SELECT ok( NOT emp.mset_update_start( _get_bad_emp_name( ) , _get_map_name( 'testPlanet1' ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Temporary table exists despite bad empire identifier' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Return value on bad planet identifier' );
 	SELECT ok( NOT emp.mset_update_start( _get_emp_name( 'testEmp1' ) , _get_bad_map_name( ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Temporary table exists despite bad planet identifier' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Return value on unowned planet identifier' );
 	SELECT ok( NOT emp.mset_update_start( _get_emp_name( 'testEmp1' ) , _get_map_name( 'testPlanet2' ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Temporary table exists despite unowned planet identifier' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Return value on unowned planet identifier' );
 	SELECT ok( NOT emp.mset_update_start( _get_emp_name( 'testEmp1' ) , _get_map_name( 'testPlanet2' ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Temporary table exists despite unowned planet identifier' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Return value on planet with no resource providers' );
 	SELECT ok( NOT emp.mset_update_start( _get_emp_name( 'testEmp1' ) , _get_map_name( 'testPlanet3' ) ) );
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Temporary table exists despite planet with no resource providers' );
 	SELECT has_table( 'mset_update' );
-	DROP TABLE mset_update;
+	DROP TABLE IF EXISTS mset_update;
 
 	
 	SELECT diag_test_name( 'emp.mset_update_start( INT , INT ) - Return value on valid identifiers' );
diff --git a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/020-mset-update-set.sql b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/020-mset-update-set.sql
index 7515bfb..5009bb6 100644
--- a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/020-mset-update-set.sql
+++ b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/020-mset-update-set.sql
@@ -4,34 +4,34 @@
 BEGIN;
 	CREATE TEMPORARY TABLE mset_update(
 		empire_id			INT ,
-		resource_name_id	INT ,
+		resource_name		TEXT ,
 		empmset_weight		INT
 	) ON COMMIT DROP;
-	INSERT INTO mset_update VALUES ( 1 , 1 , 0 ) , ( 1 , 2 , 0 );
+	INSERT INTO mset_update VALUES ( 1 , 'a' , 0 ) , ( 1 , 'b' , 0 );
 	
 	/***** TESTS BEGIN HERE *****/
 	SELECT plan( 7 );
 	
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Valid update' );
-	SELECT ok( emp.mset_update_set( 1 , 1 ) );
+	SELECT ok( emp.mset_update_set( 'a' , 1 ) );
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Valid update results (1/2)' );
-	SELECT is( empmset_weight , 1 ) FROM mset_update WHERE resource_name_id = 1;
+	SELECT is( empmset_weight , 1 ) FROM mset_update WHERE resource_name = 'a';
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Valid update results (2/2)' );
-	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name_id = 2;
+	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name = 'b';
 	DELETE FROM mset_update;
 
-	INSERT INTO mset_update VALUES ( 1 , 1 , 0 ) , ( 1 , 2 , 0 );
+	INSERT INTO mset_update VALUES ( 1 , 'a' , 0 ) , ( 1 , 'b' , 0 );
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Update on unknown resource' );
-	SELECT ok( NOT emp.mset_update_set( 12 , 1 ) );
+	SELECT ok( NOT emp.mset_update_set( 'c' , 1 ) );
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Unknown resource update results (1/2)' );
-	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name_id = 1;
+	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name = 'a';
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Unknown resource update results (2/2)' );
-	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name_id = 2;
+	SELECT is( empmset_weight , 0 ) FROM mset_update WHERE resource_name = 'b';
 	DELETE FROM mset_update;
 
-	INSERT INTO mset_update VALUES ( 1 , 1 , 0 ) , ( 1 , 2 , 0 );
+	INSERT INTO mset_update VALUES ( 1 , 'a' , 0 ) , ( 1 , 'b' , 0 );
 	SELECT diag_test_name( 'emp.mset_update_set( ) - Update with invalid weight' );
-	SELECT ok( emp.mset_update_set( 1 , -1 ) );
+	SELECT ok( emp.mset_update_set( 'a' , -1 ) );
 
 	SELECT * FROM finish( );
 ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/030-mset-update-apply.sql b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/030-mset-update-apply.sql
index 14c8d6d..d9088fa 100644
--- a/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/030-mset-update-apply.sql
+++ b/legacyworlds-server-data/db-structure/tests/admin/040-functions/045-empire-mining/030-mset-update-apply.sql
@@ -2,6 +2,11 @@
  * Test the emp.mset_update_apply() function
  */
 BEGIN;
+	/*
+	 * Create a pair of strings
+	 */
+	INSERT INTO defs.strings (name) VALUES ( 'a' ) , ( 'b' );
+
 	/*
 	 * Remove foreign keys from the empire mining settings table, insert some
 	 * data into it.
@@ -10,15 +15,17 @@ BEGIN;
 		DROP CONSTRAINT fk_empmset_empire ,
 		DROP CONSTRAINT fk_empmset_resource;
 	INSERT INTO emp.mining_settings ( empire_id , resource_name_id )
-		VALUES ( 1 , 1 ) , ( 1 , 2 );
+		SELECT 1 , id
+			FROM defs.strings
+			WHERE name IN ( 'a' , 'b' );
 
 	/* Create the temporary table */
 	CREATE TEMPORARY TABLE mset_update(
 		empire_id			INT ,
-		resource_name_id	INT ,
+		resource_name		TEXT ,
 		empmset_weight		INT
 	) ON COMMIT DROP;
-	INSERT INTO mset_update VALUES ( 1 , 1 , 0 ) , ( 1 , 2 , 4 );
+	INSERT INTO mset_update VALUES ( 1 , 'a' , 0 ) , ( 1 , 'b' , 4 );
 
 	/***** TESTS BEGIN HERE *****/
 	SELECT plan( 8 );
@@ -27,23 +34,31 @@ BEGIN;
 	SELECT ok( emp.mset_update_apply( ) );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Empire update results' );
 	SELECT set_eq(
-		$$ SELECT resource_name_id , empmset_weight FROM emp.mining_settings $$ ,
-		$$ VALUES ( 1 , 0 ) , ( 2 , 4 ) $$
+		$$ SELECT name , empmset_weight
+			FROM emp.mining_settings
+				INNER JOIN defs.strings
+					ON resource_name_id = id $$ ,
+		$$ VALUES ( 'a' , 0 ) , ( 'b' , 4 ) $$
 	);
 
 	/* Reset temporary table and settings */
 	DELETE FROM mset_update;
 	DELETE FROM emp.mining_settings;
 	INSERT INTO emp.mining_settings ( empire_id , resource_name_id )
-		VALUES ( 1 , 1 ) , ( 1 , 2 );
+		SELECT 1 , id
+			FROM defs.strings
+			WHERE name IN ( 'a' , 'b' );
 
-	INSERT INTO mset_update VALUES ( 1 , 1 , -1 ) , ( 1 , 2 , 4 );
+	INSERT INTO mset_update VALUES ( 1 , 'a' , -1 ) , ( 1 , 'b' , 4 );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Applying invalid empire update' );
 	SELECT ok( NOT emp.mset_update_apply( ) );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Invalid empire update results' );
 	SELECT set_eq(
-		$$ SELECT resource_name_id , empmset_weight FROM emp.mining_settings $$ ,
-		$$ VALUES ( 1 , 2 ) , ( 2 , 2 ) $$
+		$$ SELECT name , empmset_weight
+			FROM emp.mining_settings
+				INNER JOIN defs.strings
+					ON resource_name_id = id $$ ,
+		$$ VALUES ( 'a' , 2 ) , ( 'b' , 2 ) $$
 	);
 
 
@@ -55,38 +70,48 @@ BEGIN;
 		DROP CONSTRAINT fk_emppmset_empire ,
 		DROP CONSTRAINT fk_emppmset_resource;
 	INSERT INTO emp.planet_mining_settings ( empire_id , planet_id , resource_name_id )
-		VALUES ( 1 , 1 , 1 ) , ( 1 , 1 , 2 );
+		SELECT 1 , 1 , id
+			FROM defs.strings
+			WHERE name IN ( 'a' , 'b' );
 
 	DROP TABLE mset_update;
 	CREATE TEMPORARY TABLE mset_update(
 		empire_id			INT ,
 		planet_id			INT ,
-		resource_name_id	INT ,
+		resource_name		TEXT ,
 		empmset_weight		INT
 	) ON COMMIT DROP;
-	INSERT INTO mset_update VALUES ( 1 , 1 , 1 , 0 ) , ( 1 , 1 , 2 , 4 );
+	INSERT INTO mset_update VALUES ( 1 , 1 , 'a' , 0 ) , ( 1 , 1 , 'b' , 4 );
 
 	SELECT diag_test_name( 'emp.mset_update_apply() - Applying valid planet update' );
 	SELECT ok( emp.mset_update_apply( ) );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Planet update results' );
 	SELECT set_eq(
-		$$ SELECT resource_name_id , emppmset_weight FROM emp.planet_mining_settings $$ ,
-		$$ VALUES ( 1 , 0 ) , ( 2 , 4 ) $$
+		$$ SELECT name , emppmset_weight
+			FROM emp.planet_mining_settings
+				INNER JOIN defs.strings
+					ON resource_name_id = id $$ ,
+		$$ VALUES ( 'a' , 0 ) , ( 'b' , 4 ) $$
 	);
 
 	/* Reset temporary table and settings */
 	DELETE FROM mset_update;
 	DELETE FROM emp.planet_mining_settings;
 	INSERT INTO emp.planet_mining_settings ( empire_id , planet_id , resource_name_id )
-		VALUES ( 1 , 1 , 1 ) , ( 1 , 1 , 2 );
+		SELECT 1 , 1 , id
+			FROM defs.strings
+			WHERE name IN ( 'a' , 'b' );
 
-	INSERT INTO mset_update VALUES ( 1 , 1 , 1 , -1 ) , ( 1 , 1 , 2 , 4 );
+	INSERT INTO mset_update VALUES ( 1 , 1 , 'a' , -1 ) , ( 1 , 1 , 'b' , 4 );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Applying invalid planet update' );
 	SELECT ok( NOT emp.mset_update_apply( ) );
 	SELECT diag_test_name( 'emp.mset_update_apply() - Invalid planet update results' );
 	SELECT set_eq(
-		$$ SELECT resource_name_id , emppmset_weight FROM emp.planet_mining_settings $$ ,
-		$$ VALUES ( 1 , 2 ) , ( 2 , 2 ) $$
+		$$ SELECT name , emppmset_weight
+			FROM emp.planet_mining_settings
+				INNER JOIN defs.strings
+					ON resource_name_id = id $$ ,
+		$$ VALUES ( 'a' , 2 ) , ( 'b' , 2 ) $$
 	);
 
 	SELECT * FROM finish( );
diff --git a/legacyworlds-server-data/db-structure/tests/user/040-functions/045-empire-mining/020-mset-update-set.sql b/legacyworlds-server-data/db-structure/tests/user/040-functions/045-empire-mining/020-mset-update-set.sql
index cc37514..633ccae 100644
--- a/legacyworlds-server-data/db-structure/tests/user/040-functions/045-empire-mining/020-mset-update-set.sql
+++ b/legacyworlds-server-data/db-structure/tests/user/040-functions/045-empire-mining/020-mset-update-set.sql
@@ -7,7 +7,7 @@ BEGIN;
 	
 	SELECT diag_test_name( 'emp.mset_update_set() - Privileges' );
 	SELECT lives_ok( $$
-			SELECT emp.mset_update_set( 1 , -1 )
+			SELECT emp.mset_update_set( 'a' , -1 )
 		$$ );
 	
 	SELECT * FROM finish( );
diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/MiningSettingsDAO.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/MiningSettingsDAO.java
new file mode 100644
index 0000000..c19656e
--- /dev/null
+++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/MiningSettingsDAO.java
@@ -0,0 +1,60 @@
+package com.deepclone.lw.interfaces.game.resources;
+
+
+/**
+ * Data access object for mining settings
+ * 
+ * <p>
+ * This interface provides methods which call stored procedures that control mining settings for
+ * both empires and planets.
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+public interface MiningSettingsDAO
+{
+
+	/**
+	 * Start an empire settings update
+	 * 
+	 * <p>
+	 * Start an empire settings update by calling the <code>emp.mset_update_start(INT)</code> stored
+	 * procedure.
+	 * 
+	 * @param empireId
+	 *            the identifier of the empire whose settings will be updated
+	 * 
+	 * @return <code>true</code> on success, <code>false</code> on failure
+	 */
+	public boolean startUpdate( int empireId );
+
+
+	/**
+	 * Set the new extraction priority of a resource
+	 * 
+	 * <p>
+	 * Call the <code>emp.mset_update_set(TEXT,INT)</code> stored procedure to set the new
+	 * extraction priority for some type of resources. An mining settings update (empire- or
+	 * planet-wide) must have been initiated before this method is called.
+	 * 
+	 * @param resource
+	 *            the identifier of the resource
+	 * @param priority
+	 *            the new extraction priority for the resource
+	 * 
+	 * @return <code>true</code> on success, <code>false</code> if the resource did not exist.
+	 */
+	public boolean setNewPriority( String resource , int priority );
+
+
+	/**
+	 * Apply the current settings update
+	 * 
+	 * <p>
+	 * This method applies a previously initiated mining settings update by calling the
+	 * <code>emp.mset_update_apply()</code> stored procedure.
+	 * 
+	 * @return <code>true</code> on success, <code>false</code> if one of the priorities was
+	 *         invalid.
+	 */
+	public boolean applyUpdate( );
+}
diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/ResourcesController.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/ResourcesController.java
new file mode 100644
index 0000000..2444da7
--- /dev/null
+++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/resources/ResourcesController.java
@@ -0,0 +1,30 @@
+package com.deepclone.lw.interfaces.game.resources;
+
+
+import java.util.Map;
+
+
+
+/**
+ * Resources control interface
+ * 
+ * <p>
+ * This interface corresponds to the component which controls resources from the game's point of
+ * view. It contains methods that update mining settings.
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+public interface ResourcesController
+{
+
+	/**
+	 * Update an empire's mining settings
+	 * 
+	 * @param empireId
+	 *            the empire's identifier
+	 * @param settings
+	 *            the new settings
+	 */
+	public void updateEmpireSettings( int empireId , Map< String , Integer > settings );
+
+}
diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/MockMiningSettingsDAO.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/MockMiningSettingsDAO.java
new file mode 100644
index 0000000..0b46dce
--- /dev/null
+++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/MockMiningSettingsDAO.java
@@ -0,0 +1,121 @@
+package com.deepclone.lw.beans.game.resources;
+
+
+import com.deepclone.lw.interfaces.game.resources.MiningSettingsDAO;
+
+
+
+/**
+ * Mock mining settings DAO which can be used to simulate failures and trace which methods were
+ * called.
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ * 
+ */
+public class MockMiningSettingsDAO
+		implements MiningSettingsDAO
+{
+	/** The empire identifier with which {@link #startUpdate(int)} was called */
+	private Integer updateEmpire = null;
+
+	/** The amount of calls to {@link #setNewPriority(String, int)} */
+	private int callsToSet = 0;
+
+	/** Whether {@link #applyUpdate()} was called */
+	private boolean applyCalled = false;
+
+	/** Whether {@link #startUpdate(int)} will succeed or fail */
+	private boolean startUpdateSucceeds = true;
+
+	/** Whether applyUpdate will succeed */
+	private boolean applyUpdateSucceeds = true;
+
+	/**
+	 * Amount of calls to {@link #setNewPriority(String, int)} that will succeed, or
+	 * <code>null</code> if they will all succeed.
+	 */
+	private Integer maxSetCalls = null;
+
+
+	/** @return the empire identifier to update */
+	public Integer getUpdateEmpire( )
+	{
+		return this.updateEmpire;
+	}
+
+
+	/** @return the amount of calls to {@link #setNewPriority(String, int)} */
+	public int getCallsToSet( )
+	{
+		return this.callsToSet;
+	}
+
+
+	/** @return <code>true</code> if {@link #applyUpdate()} was called */
+	public boolean wasApplyCalled( )
+	{
+		return this.applyCalled;
+	}
+
+
+	/**
+	 * Determine whether calls to {@link #startUpdate(int)} will succeed
+	 * 
+	 * @param startUpdateSucceeds
+	 *            <code>true</code> if the call is to succeed, <code>false</code> if it is to fail
+	 */
+	public void setStartUpdateSucceeds( boolean startUpdateSucceeds )
+	{
+		this.startUpdateSucceeds = startUpdateSucceeds;
+	}
+
+
+	/**
+	 * Determine whether calls to {@link #applyUpdate()} will succeed
+	 * 
+	 * @param applyUpdateSucceeds
+	 *            <code>true</code> if the call is to succeed, <code>false</code> if it is to fail
+	 */
+	public void setApplyUpdateSucceeds( boolean applyUpdateSucceeds )
+	{
+		this.applyUpdateSucceeds = applyUpdateSucceeds;
+	}
+
+
+	/**
+	 * Set the amount of calls to {@link #setNewPriority(String, int)} that will succeed
+	 * 
+	 * @param maxSetCalls
+	 *            the amount of calls that will succeed, or <code>null</code> if the method must
+	 *            always succeed
+	 */
+	public void setMaxSetCalls( Integer maxSetCalls )
+	{
+		this.maxSetCalls = maxSetCalls;
+	}
+
+
+	@Override
+	public boolean startUpdate( int empireId )
+	{
+		this.updateEmpire = empireId;
+		return this.startUpdateSucceeds;
+	}
+
+
+	@Override
+	public boolean setNewPriority( String resource , int priority )
+	{
+		this.callsToSet++;
+		return ( this.maxSetCalls == null || this.maxSetCalls > this.callsToSet - 1 );
+	}
+
+
+	@Override
+	public boolean applyUpdate( )
+	{
+		this.applyCalled = true;
+		return this.applyUpdateSucceeds;
+	}
+
+}
diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/TestResourcesControllerBean.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/TestResourcesControllerBean.java
new file mode 100644
index 0000000..b836ab9
--- /dev/null
+++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/beans/game/resources/TestResourcesControllerBean.java
@@ -0,0 +1,121 @@
+package com.deepclone.lw.beans.game.resources;
+
+
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.deepclone.lw.interfaces.game.resources.MiningSettingsDAO;
+
+
+
+/**
+ * Tests for {@link ResourcesControllerBean}
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ * 
+ */
+public class TestResourcesControllerBean
+{
+
+	/** Empire identifier used in the tests */
+	private static final Integer EMPIRE_ID = 42;
+
+	/** Mining settings used in the tests */
+	private static final Map< String , Integer > MINING_SETTINGS;
+
+	/**
+	 * Initialise the mining settings map
+	 */
+	static {
+		HashMap< String , Integer > tempMap = new HashMap< String , Integer >( );
+		tempMap.put( "test 1" , 1 );
+		tempMap.put( "test 2" , 2 );
+		tempMap.put( "test 3" , 3 );
+		tempMap.put( "test 4" , 4 );
+		MINING_SETTINGS = Collections.unmodifiableMap( tempMap );
+	}
+
+	/** The mock database access object */
+	private MockMiningSettingsDAO miningSettingsDAO;
+
+	/** The instance to test on */
+	private ResourcesControllerBean ctrl;
+
+
+	/**
+	 * Create the resource controller that will be used in the tests and its various (usually fake)
+	 * dependencies
+	 */
+	@Before
+	public void setUp( )
+	{
+		this.miningSettingsDAO = new MockMiningSettingsDAO( );
+		this.ctrl = new ResourcesControllerBean( );
+		this.ctrl.setMiningSettingsDAO( this.miningSettingsDAO );
+	}
+
+
+	/**
+	 * When calling {@link MiningSettingsDAO#startUpdate(int)} fails, the empire settings update is
+	 * interrupted.
+	 */
+	@Test
+	public void testEmpireStartUpdateFails( )
+	{
+		this.miningSettingsDAO.setStartUpdateSucceeds( false );
+		this.ctrl.updateEmpireSettings( EMPIRE_ID , MINING_SETTINGS );
+		assertEquals( EMPIRE_ID , this.miningSettingsDAO.getUpdateEmpire( ) );
+		assertEquals( 0 , this.miningSettingsDAO.getCallsToSet( ) );
+		assertFalse( this.miningSettingsDAO.wasApplyCalled( ) );
+	}
+
+
+	/**
+	 * When calling {@link MiningSettingsDAO#startUpdate(int)} succeeds but one of the calls to
+	 * {@link MiningSettingsDAO#setNewPriority(String, int)} fails, the update is interrupted.
+	 */
+	@Test
+	public void testSetMiningPriorityFails( )
+	{
+		this.miningSettingsDAO.setMaxSetCalls( 2 );
+		this.ctrl.updateEmpireSettings( EMPIRE_ID , MINING_SETTINGS );
+		assertEquals( EMPIRE_ID , this.miningSettingsDAO.getUpdateEmpire( ) );
+		assertEquals( 3 , this.miningSettingsDAO.getCallsToSet( ) );
+		assertFalse( this.miningSettingsDAO.wasApplyCalled( ) );
+	}
+
+
+	/**
+	 * If both {@link MiningSettingsDAO#startUpdate(int)} and
+	 * {@link MiningSettingsDAO#setNewPriority(String, int)} succeed,
+	 * {@link MiningSettingsDAO#applyUpdate()} is called.
+	 */
+	@Test
+	public void testSettingsSuccess( )
+	{
+		this.ctrl.updateEmpireSettings( EMPIRE_ID , MINING_SETTINGS );
+		assertEquals( EMPIRE_ID , this.miningSettingsDAO.getUpdateEmpire( ) );
+		assertEquals( 4 , this.miningSettingsDAO.getCallsToSet( ) );
+		assertTrue( this.miningSettingsDAO.wasApplyCalled( ) );
+	}
+
+
+	/**
+	 * A failure of {@link MiningSettingsDAO#applyUpdate()} has no influence on the call.
+	 */
+	@Test
+	public void testSettingsApplyFail( )
+	{
+		this.miningSettingsDAO.setApplyUpdateSucceeds( false );
+		this.ctrl.updateEmpireSettings( EMPIRE_ID , MINING_SETTINGS );
+		assertEquals( EMPIRE_ID , this.miningSettingsDAO.getUpdateEmpire( ) );
+		assertEquals( 4 , this.miningSettingsDAO.getCallsToSet( ) );
+		assertTrue( this.miningSettingsDAO.wasApplyCalled( ) );
+	}
+}
diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/UpdateEmpireMiningSettingsCommand.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/UpdateEmpireMiningSettingsCommand.java
new file mode 100644
index 0000000..757417d
--- /dev/null
+++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/UpdateEmpireMiningSettingsCommand.java
@@ -0,0 +1,50 @@
+package com.deepclone.lw.cmd.player;
+
+
+import java.util.Map;
+
+import com.deepclone.lw.session.Command;
+
+
+
+/**
+ * Command that updates empire mining settings
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
+public class UpdateEmpireMiningSettingsCommand
+		extends Command
+{
+
+	/**
+	 * Serialisation version identifier
+	 * 
+	 * <ul>
+	 * <li>Introduced in B6M2
+	 * </ul>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/** The new empire mining settings */
+	private final Map< String , Integer > settings;
+
+
+	/**
+	 * Initialise the command using mining settings
+	 * 
+	 * @param settings
+	 *            a map that associates resource identifiers to priorities
+	 */
+	public UpdateEmpireMiningSettingsCommand( Map< String , Integer > settings )
+	{
+		this.settings = settings;
+	}
+
+
+	/** @return the mining settings to apply */
+	public Map< String , Integer > getSettings( )
+	{
+		return this.settings;
+	}
+
+}
diff --git a/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java b/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java
index 1cc1607..0f15268 100644
--- a/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java
+++ b/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java
@@ -85,6 +85,19 @@ public class PlayerSession
 	}
 
 
+	/**
+	 * Send an {@link UpdateEmpireMiningSettingsCommand} to the server
+	 * 
+	 * @param miningSettings
+	 *            the mining settings
+	 */
+	public void updateEmpireMiningSettings( Map< String , Integer > miningSettings )
+			throws SessionException , SessionServerException , SessionMaintenanceException
+	{
+		this.execute( new UpdateEmpireMiningSettingsCommand( miningSettings ) );
+	}
+
+
 	/* Planet list */
 
 	public ListPlanetsResponse listPlanets( )
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl
index 639de56..28d1686 100644
--- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl
+++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl
@@ -63,60 +63,8 @@
 		</@tab>
 
 		<@tab id="economy" title="Economy">
-		
-			<@listview>
-				<@lv_line headers=true>
-					<@lv_column width=30>&nbsp;</@lv_column>
-					<@lv_column width="x">Resource</@lv_column>
-					<@lv_column width=100 centered=true><@over_time "Income" /></@lv_column>
-					<@lv_column width=100 centered=true><@over_time "Upkeep" /></@lv_column>
-					<@lv_column width=100 centered=true>Reserves</@lv_column>
-					<@lv_column width=100 centered=true>Mining priority</@lv_column>
-				</@lv_line>
-					
-				<#list ov.economy as resource>
-
-					<#if previousCategory?has_content && !resource.category?has_content
-							|| resource.category?has_content && !previousCategory?has_content
-							|| resource.category?has_content && previousCategory?has_content
-								&& resource.category != previousCategory>
-						<@lv_line>
-							<#if resource.category?has_content>
-								<td colspan="5"><strong>${resource.category?xhtml}</strong></td>
-							<#else>
-								<td colspan="5"><hr /></td>
-							</#if>
-						</@lv_line>
-						<#local previousCategory=resource.category>
-					</#if>
-					
-					<@lv_line>
-						<@lv_column>&nbsp;</@lv_column>
-						<@lv_column>${resource.title?xhtml}
-							<div class="auto-hide">${resource.description?xhtml}</div>
-						</@lv_column>
-						<@lv_column centered=true>${resource.income?string(",##0")}</@lv_column>
-						<@lv_column centered=true>${resource.upkeep?string(",##0")}</@lv_column>
-						<@lv_column centered=true>${resource.stockpiled?string(",##0")}</@lv_column>
-						<@lv_column centered=true>
-							<#if resource.miningPriority?has_content>
-								<#switch resource.miningPriority>
-									<#case 0>lowest<#break>
-									<#case 1>low<#break>
-									<#case 2>normal<#break>
-									<#case 3>high<#break>
-									<#case 4>highest<#break>
-								</#switch>
-							<#else>
-								N/A
-							</#if>
-						</@lv_column>
-					</@lv_line>
-
-				</#list>
-
-			</@listview>
-
+			<#include "overview/resources.ftl" />
+			<@overviewResources />
 		</@tab>
 
 		<@tab id="research" title="Research">
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview/resources.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview/resources.ftl
new file mode 100644
index 0000000..7206f92
--- /dev/null
+++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview/resources.ftl
@@ -0,0 +1,64 @@
+<#macro overviewResources>
+<form action="update-mining-settings.action" method="POST"><@listview>
+	<@lv_line headers=true>
+		<@lv_column width=30>&nbsp;</@lv_column>
+		<@lv_column width="x">Resource</@lv_column>
+		<@lv_column width=100 centered=true><@over_time "Income" /></@lv_column>
+		<@lv_column width=100 centered=true><@over_time "Upkeep" /></@lv_column>
+		<@lv_column width=100 centered=true>Reserves</@lv_column>
+		<@lv_column width=100 centered=true>Mining priority</@lv_column>
+	</@lv_line>
+		
+	<#list ov.economy as resource>
+
+		<#if previousCategory?has_content && !resource.category?has_content
+				|| resource.category?has_content && !previousCategory?has_content
+				|| resource.category?has_content && previousCategory?has_content
+					&& resource.category != previousCategory>
+			<@lv_line>
+				<#if resource.category?has_content>
+					<td colspan="5"><strong>${resource.category?xhtml}</strong></td>
+				<#else>
+					<td colspan="5"><hr /></td>
+				</#if>
+			</@lv_line>
+			<#local previousCategory=resource.category>
+		</#if>
+		
+		<@lv_line>
+			<@lv_column>&nbsp;</@lv_column>
+			<@lv_column>${resource.title?xhtml}
+				<div class="auto-hide">${resource.description?xhtml}</div>
+			</@lv_column>
+			<@lv_column centered=true>${resource.income?string(",##0")}</@lv_column>
+			<@lv_column centered=true>${resource.upkeep?string(",##0")}</@lv_column>
+			<@lv_column centered=true>${resource.stockpiled?string(",##0")}</@lv_column>
+			<@lv_column centered=true>
+				<#if resource.miningPriority?has_content>
+					<select name="ems-${resource.identifier?xhtml}" class="input">
+						<option style="padding: 0 5px" value="0" <#if resource.miningPriority = 0>selected="selected"</#if>>lowest</option>
+						<option style="padding: 0 5px" value="1" <#if resource.miningPriority = 1>selected="selected"</#if>>low</option>
+						<option style="padding: 0 5px" value="2" <#if resource.miningPriority = 2>selected="selected"</#if>>normal</option>
+						<option style="padding: 0 5px" value="3" <#if resource.miningPriority = 3>selected="selected"</#if>>high</option>
+						<option style="padding: 0 5px" value="4" <#if resource.miningPriority = 4>selected="selected"</#if>>highest</option>
+					</select>
+				<#else>
+					N/A
+				</#if>
+			</@lv_column>
+		</@lv_line>
+
+	</#list>
+	
+	<@lv_line headers=true>
+		<@lv_column width="x" colspan=6>&nbsp;</@lv_column>
+	</@lv_line>
+	<@lv_line>
+		<@lv_column colspan=4>&nbsp;</@lv_column>
+		<@lv_column right=true colspan=2>
+			<input type="submit" value="Update mining priorities" class="input" style="margin: 15px 0 0 0" />
+		</@lv_column>
+	</@lv_line>
+
+</@listview></form>
+</#macro>
\ No newline at end of file
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl
index 5bf71e8..01aa309 100644
--- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl
+++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl
@@ -63,60 +63,8 @@
 		</@tab>
 		
 		<@tab id="economy" title="Économie">
-		
-			<@listview>
-				<@lv_line headers=true>
-					<@lv_column width=30>&nbsp;</@lv_column>
-					<@lv_column width="x">Ressource</@lv_column>
-					<@lv_column width=100 centered=true><@over_time "Bénéfice" /></@lv_column>
-					<@lv_column width=100 centered=true><@over_time title="Charges" feminin=true pluriel=true /></@lv_column>
-					<@lv_column width=100 centered=true>Réserves</@lv_column>
-					<@lv_column width=100 centered=true>Priorité d'extraction</@lv_column>
-				</@lv_line>
-					
-				<#list ov.economy as resource>
-
-					<#if previousCategory?has_content && !resource.category?has_content
-							|| resource.category?has_content && !previousCategory?has_content
-							|| resource.category?has_content && previousCategory?has_content
-								&& resource.category != previousCategory>
-						<@lv_line>
-							<#if resource.category?has_content>
-								<td colspan="5"><strong>${resource.category?xhtml}</strong></td>
-							<#else>
-								<td colspan="5"><hr /></td>
-							</#if>
-						</@lv_line>
-						<#local previousCategory=resource.category>
-					</#if>
-					
-					<@lv_line>
-						<@lv_column>&nbsp;</@lv_column>
-						<@lv_column>${resource.title?xhtml}
-							<div class="auto-hide">${resource.description?xhtml}</div>
-						</@lv_column>
-						<@lv_column centered=true>${resource.income?string(",##0")}</@lv_column>
-						<@lv_column centered=true>${resource.upkeep?string(",##0")}</@lv_column>
-						<@lv_column centered=true>${resource.stockpiled?string(",##0")}</@lv_column>
-						<@lv_column centered=true>
-							<#if resource.miningPriority?has_content>
-								<#switch resource.miningPriority>
-									<#case 0>très basse<#break>
-									<#case 1>basse<#break>
-									<#case 2>normale<#break>
-									<#case 3>haute<#break>
-									<#case 4>très haute<#break>
-								</#switch>
-							<#else>
-								N/A
-							</#if>
-						</@lv_column>
-					</@lv_line>
-
-				</#list>
-
-			</@listview>
-
+			<#include "overview/resources.ftl" />
+			<@overviewResources />
 		</@tab>
 		
 		<@tab id="research" title="Recherche">
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview/resources.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview/resources.ftl
new file mode 100644
index 0000000..fd0a77f
--- /dev/null
+++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview/resources.ftl
@@ -0,0 +1,64 @@
+<#macro overviewResources>
+<form action="update-mining-settings.action" method="POST"><@listview>
+	<@lv_line headers=true>
+		<@lv_column width=30>&nbsp;</@lv_column>
+		<@lv_column width="x">Ressource</@lv_column>
+		<@lv_column width=100 centered=true><@over_time "Bénéfice" /></@lv_column>
+		<@lv_column width=100 centered=true><@over_time title="Charges" feminin=true pluriel=true /></@lv_column>
+		<@lv_column width=100 centered=true>Réserves</@lv_column>
+		<@lv_column width=100 centered=true>Priorité d'extraction</@lv_column>
+	</@lv_line>
+		
+	<#list ov.economy as resource>
+
+		<#if previousCategory?has_content && !resource.category?has_content
+				|| resource.category?has_content && !previousCategory?has_content
+				|| resource.category?has_content && previousCategory?has_content
+					&& resource.category != previousCategory>
+			<@lv_line>
+				<#if resource.category?has_content>
+					<td colspan="5"><strong>${resource.category?xhtml}</strong></td>
+				<#else>
+					<td colspan="5"><hr /></td>
+				</#if>
+			</@lv_line>
+			<#local previousCategory=resource.category>
+		</#if>
+		
+		<@lv_line>
+			<@lv_column>&nbsp;</@lv_column>
+			<@lv_column>${resource.title?xhtml}
+				<div class="auto-hide">${resource.description?xhtml}</div>
+			</@lv_column>
+			<@lv_column centered=true>${resource.income?string(",##0")}</@lv_column>
+			<@lv_column centered=true>${resource.upkeep?string(",##0")}</@lv_column>
+			<@lv_column centered=true>${resource.stockpiled?string(",##0")}</@lv_column>
+			<@lv_column centered=true>
+				<#if resource.miningPriority?has_content>
+					<select name="ems-${resource.identifier?xhtml}" class="input">
+						<option style="padding: 0 5px" value="0" <#if resource.miningPriority = 0>selected="selected"</#if>>très basse</option>
+						<option style="padding: 0 5px" value="1" <#if resource.miningPriority = 1>selected="selected"</#if>>basse</option>
+						<option style="padding: 0 5px" value="2" <#if resource.miningPriority = 2>selected="selected"</#if>>normale</option>
+						<option style="padding: 0 5px" value="3" <#if resource.miningPriority = 3>selected="selected"</#if>>haute</option>
+						<option style="padding: 0 5px" value="4" <#if resource.miningPriority = 4>selected="selected"</#if>>très haute</option>
+					</select>
+				<#else>
+					N/A
+				</#if>
+			</@lv_column>
+		</@lv_line>
+
+	</#list>
+	
+	<@lv_line headers=true>
+		<@lv_column width="x" colspan=6>&nbsp;</@lv_column>
+	</@lv_line>
+	<@lv_line>
+		<@lv_column colspan=4>&nbsp;</@lv_column>
+		<@lv_column right=true colspan=2>
+			<input type="submit" value="Modifier les priorités" class="input" style="margin: 15px 0 0 0" />
+		</@lv_column>
+	</@lv_line>
+
+</@listview></form>
+</#macro>
\ No newline at end of file
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl
index 3e196b7..10b04f9 100644
--- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl
+++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl
@@ -10,15 +10,15 @@
 </#macro>
 <#macro lv_column width=0 centered=false right=false colspan=0>
 	<#if width?is_string>
-		<th style="text-align: <#if centered>center<#elseif right>right<#else>left</#if>"<#if colspan gt 1>colspan="${colspan}"</#if>>
+		<th style="text-align: <#if centered>center<#elseif right>right<#else>left</#if>"<#if colspan gt 1> colspan="${colspan}"</#if>>
 			<#nested>
 		</th>
 	<#elseif width gt 0>
-		<th style="width: ${width}px; text-align: <#if centered>center<#elseif right>right<#else>left</#if>"<#if colspan gt 1>colspan="${colspan}"</#if>>
+		<th style="width: ${width}px; text-align: <#if centered>center<#elseif right>right<#else>left</#if>"<#if colspan gt 1> colspan="${colspan}"</#if>>
 			<#nested>
 		</th>
 	<#else>
-		<td style="text-align: <#if centered>center<#elseif right>right<#else>left</#if>" <#if colspan gt 1>colspan="${colspan}"</#if>>
+		<td style="text-align: <#if centered>center<#elseif right>right<#else>left</#if>"<#if colspan gt 1> colspan="${colspan}"</#if>>
 			<#nested>
 		</td>
 	</#if>
diff --git a/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java
index ffbbf46..ecc8c1f 100644
--- a/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java
+++ b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java
@@ -1,6 +1,10 @@
 package com.deepclone.lw.web.main.game;
 
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.springframework.stereotype.Controller;
@@ -20,6 +24,15 @@ import com.deepclone.lw.web.csess.PlayerSession;
 
 
 
+/**
+ * Overview page controller
+ * 
+ * <p>
+ * This page controller implements the "Overview" page, as well as the commands it supports
+ * (implement technology, update empire mining settings).
+ * 
+ * @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
+ */
 @Controller
 @SessionRequirement( value = true , redirectTo = "player-session" , subType = "game" )
 @SessionAttributes( "language" )
@@ -27,6 +40,29 @@ public class OverviewPage
 		extends PageControllerBase
 {
 
+	/**
+	 * Main overview display
+	 * 
+	 * <p>
+	 * This method is mapped to the overview page's URL. It will fetch the empire overview from the
+	 * server and display the appropriate page.
+	 * 
+	 * @param request
+	 *            the HTTP request
+	 * @param language
+	 *            the language from the session
+	 * @param model
+	 *            the model
+	 * 
+	 * @return the overview page rendering order
+	 * 
+	 * @throws SessionException
+	 *             if some error occurs on the server
+	 * @throws SessionServerException
+	 *             if the server is unreachable
+	 * @throws SessionMaintenanceException
+	 *             if the game is under maintenance
+	 */
 	@RequestMapping( "/overview" )
 	public String overview( HttpServletRequest request , @ModelAttribute( "language" ) String language , Model model )
 			throws SessionException , SessionServerException , SessionMaintenanceException
@@ -36,6 +72,30 @@ public class OverviewPage
 	}
 
 
+	/**
+	 * "Implement technology" command
+	 * 
+	 * <p>
+	 * This method is mapped to the technology implementation command URL.
+	 * 
+	 * @param request
+	 *            the HTTP request
+	 * @param language
+	 *            the language from the session
+	 * @param model
+	 *            the model
+	 * @param tech
+	 *            the technology identifier
+	 * 
+	 * @return the overview page rendering order
+	 * 
+	 * @throws SessionException
+	 *             if some error occurs on the server
+	 * @throws SessionServerException
+	 *             if the server is unreachable
+	 * @throws SessionMaintenanceException
+	 *             if the game is under maintenance
+	 */
 	@RequestMapping( value = "/implement-{tech}.action" , method = RequestMethod.POST )
 	public String implement( HttpServletRequest request , @ModelAttribute( "language" ) String language , Model model ,
 			@PathVariable String tech )
@@ -52,4 +112,67 @@ public class OverviewPage
 		return this.render( model , "game" , language , "overview" , pSession.implementTechnology( techId ) );
 	}
 
+
+	@RequestMapping( value = "/update-mining-settings.action" , method = RequestMethod.POST )
+	public String updateMiningSettings( HttpServletRequest request , @ModelAttribute( "language" ) String language ,
+			Model model )
+			throws SessionException , SessionServerException , SessionMaintenanceException
+	{
+		Map< String , Integer > miningSettings = this.getMiningSettings( request );
+		if ( miningSettings != null ) {
+			this.getSession( PlayerSession.class , request ).updateEmpireMiningSettings( miningSettings );
+		}
+		return this.redirect( "overview#economy" );
+	}
+
+
+	/**
+	 * Extract mining priorities from the HTTP request
+	 * 
+	 * <p>
+	 * Look for all submitted fields that begin with "ems-" then try to extract their values into a
+	 * map that associates resource identifiers to priorities.
+	 * 
+	 * @param request
+	 *            the HTTP request
+	 * 
+	 * @return the map containing the submitted mining settings, or <code>null</code> if one of the
+	 *         values was incorrect.
+	 */
+	private Map< String , Integer > getMiningSettings( HttpServletRequest request )
+	{
+		Map< String , Object > input = this.getInput( request );
+		Map< String , Integer > miningSettings = new HashMap< String , Integer >( );
+		for ( Entry< String , Object > entry : input.entrySet( ) ) {
+			// Ignore items which are not mining settings
+			String name = entry.getKey( );
+			if ( !name.startsWith( "ems-" ) ) {
+				continue;
+			}
+			name = name.substring( 4 );
+
+			// Get values
+			if ( ! ( entry.getValue( ) instanceof String[] ) ) {
+				continue;
+			}
+			String[] values = (String[]) entry.getValue( );
+			if ( values.length < 1 ) {
+				continue;
+			}
+
+			// Pre-validate them
+			int value;
+			try {
+				value = Integer.parseInt( values[ 0 ] );
+			} catch ( NumberFormatException e ) {
+				value = -1;
+			}
+			if ( value < 0 || value > 4 ) {
+				return null;
+			}
+
+			miningSettings.put( name , value );
+		}
+		return miningSettings;
+	}
 }
diff --git a/legacyworlds/dev-tools/run-database-tests.sh b/legacyworlds/dev-tools/run-database-tests.sh
new file mode 100755
index 0000000..cfaf812
--- /dev/null
+++ b/legacyworlds/dev-tools/run-database-tests.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+[ -z "$3" ] && {
+	echo "Syntax: $0 test-database admin-user test-user [misc]" >&2
+	echo
+	echo "Where misc is:"
+	echo "       --no-create	don't create the DB"
+	echo "       --run-name name	run tests matching '*name*'"
+	echo
+	exit 1
+}
+
+test_db="$1"
+admin_user="$2"
+test_user="$3"
+
+create_db=1
+run_name=""
+while ! [ -z "$4" ]; do
+	if [ "x$4" = "x--no-create" ]; then
+		create_db=0
+	elif [ "x$4" = "x--run-name" ]; then
+		shift
+		run_name="$4"
+	fi
+
+	shift
+done
+
+scriptdir="`dirname $0`"
+tempdir="`mktemp -d`"
+cp -Rap $scriptdir/../../legacyworlds-server-data/db-structure/* $tempdir/
+cat > $tempdir/db-config.txt <<EOF
+admin=$admin_user
+db=$test_db
+user=$test_user
+password=$test_user
+EOF
+
+(
+	cd $tempdir
+
+	if [ $create_db -eq 1 ]; then
+		echo "Creating DB..."
+		psql -vQUIET=1 -vON_ERROR_STOP=1 --file database.sql || exit 1
+		psql -vQUIET=1 -f tests/pgtap.sql $test_db || exit 1
+	fi
+
+	cd tests
+	if [ "x$run_name" = "x" ]; then
+		run_name='*.sql'
+	else
+		run_name='*'"$run_name"'*'
+	fi
+	pg_prove -d $test_db `find admin/ -type f -name "$run_name" | sort` || exit 1
+	pg_prove -U $test_user -d $test_db `find user/ -type f -name "$run_name" | sort` || exit 1
+)
+result=$?
+
+rm -rf $tempdir
+exit $result