diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3505d089b0846f2e2f6f9e2bbde5a73c0237cc37..d501ec22e222cd1f782e842a4470c0a268ab91ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+## [1.4.0] - 2019-09-26
+### Added
+- SlimPlugin - abstract class with only most important plugin elements
+- AbstractPlugin - docs and cleaning
+- Activateable - interface to tag plugin to hook into activation hook
+- Deactivateable - interface to tag plugin to hook into deactivation hook
+- Conditional - interface to tag classes that should be instantiated/hooked only in given state
+### Changed
+- WordpressFilterStorage - store plugin using WordPress filter system
+- target blank in default plugin links
+### Fixed
+- Fixed assets and plugin url issues
+
 ## [1.3.4] - 2019-09-26
 ### Fixed
 - Gitlab.ci
diff --git a/src/BuildDirector/LegacyBuildDirector.php b/src/BuildDirector/LegacyBuildDirector.php
index 90e01620403535ff1d204cb9222cbc4be5e97177..5357a35d3b241a981ee5d69789d53e3d486e71de 100644
--- a/src/BuildDirector/LegacyBuildDirector.php
+++ b/src/BuildDirector/LegacyBuildDirector.php
@@ -34,4 +34,4 @@ class LegacyBuildDirector {
 	public function get_plugin() {
 		return $this->builder->get_plugin();
 	}
-}
\ No newline at end of file
+}
diff --git a/src/Builder/AbstractBuilder.php b/src/Builder/AbstractBuilder.php
index 592c969e1c31e68243ade2e929314424866be3dc..e42fa5bec31e6326fe7333cdeada10eb003eab73 100644
--- a/src/Builder/AbstractBuilder.php
+++ b/src/Builder/AbstractBuilder.php
@@ -61,4 +61,4 @@ abstract class AbstractBuilder {
 	 */
 	public function set_helper( $helper ) {
 	}
-}
\ No newline at end of file
+}
diff --git a/src/Builder/InfoActivationBuilder.php b/src/Builder/InfoActivationBuilder.php
index 7b646f31a378cfcdd28f263ad33d644b8d50e9b6..10d9f32dd74abd2cda486404c4e870eeb7f8cf99 100644
--- a/src/Builder/InfoActivationBuilder.php
+++ b/src/Builder/InfoActivationBuilder.php
@@ -79,4 +79,4 @@ class InfoActivationBuilder extends AbstractBuilder
     {
         return $this->plugin;
     }
-}
\ No newline at end of file
+}
diff --git a/src/Builder/InfoBuilder.php b/src/Builder/InfoBuilder.php
index d4d44c8b83e7e27310775556ff10560eb89cc446..1adb8f6c30d5b477c71ba1e3198bbcb517ec5c49 100644
--- a/src/Builder/InfoBuilder.php
+++ b/src/Builder/InfoBuilder.php
@@ -55,4 +55,4 @@ class InfoBuilder extends AbstractBuilder {
 	public function get_plugin() {
 		return $this->plugin;
 	}
-}
\ No newline at end of file
+}
diff --git a/src/Plugin/AbstractPlugin.php b/src/Plugin/AbstractPlugin.php
index e3809c43d4024d87600b1ea26f5843802e6cf730..20ee0261d5285ac285155f25bde6e8204cdf85bc 100644
--- a/src/Plugin/AbstractPlugin.php
+++ b/src/Plugin/AbstractPlugin.php
@@ -3,31 +3,50 @@
 namespace WPDesk\PluginBuilder\Plugin;
 
 /**
- * Base plugin class for WP Desk plugins.
+ * Base plugin with most basic functionalities used by every WPDesk plugin.
  *
- * *************************************************************
- * * Important! This class should be not modified!             *
- * * This class is loaded at startup from first loaded plugin! *
- * *************************************************************
  *
- * @author Grzegorz, Dyszczo
+ * Known issues:
  *
+ * The class name is too generic but can't be changed as it would introduce a major incompatibility for most of the plugins.
+ * The $plugin_url, $docs_url and most other fields should be removed as they only litter the place but for compatibility reasons we can't do it right now.
+ * Hook methods should be moved to external classes but for compatibility reasons we can't do it right now.
  */
-abstract class AbstractPlugin implements \WPDesk_Translable {
+abstract class AbstractPlugin extends SlimPlugin {
 
-	/** @var \WPDesk_Plugin_Info */
+	/**
+	 * Most info about plugin internals.
+	 *
+	 * @var \WPDesk_Plugin_Info
+	 */
 	protected $plugin_info;
 
-	/** @var string */
+	/**
+	 * Unique string for this plugin in [a-z_]+ format.
+	 *
+	 * @var string
+	 */
 	protected $plugin_namespace;
 
-	/** @var string */
+	/**
+	 * Absolute URL to the plugin dir.
+	 *
+	 * @var string
+	 */
 	protected $plugin_url;
 
-	/** @var string */
+	/**
+	 * Absolute URL to the plugin docs.
+	 *
+	 * @var string
+	 */
 	protected $docs_url;
 
-	/** @var string */
+	/**
+	 * Absolute URL to the plugin settings url.
+	 *
+	 * @var string
+	 */
 	protected $settings_url;
 
 	/**
@@ -45,34 +64,35 @@ abstract class AbstractPlugin implements \WPDesk_Translable {
 	public function __construct( $plugin_info ) {
 		$this->plugin_info      = $plugin_info;
 		$this->plugin_namespace = strtolower( $plugin_info->get_plugin_dir() );
-	}
-
-	public function init() {
+		$this->plugin_url       = $this->plugin_info->get_plugin_url();
 		$this->init_base_variables();
-		$this->hooks();
 	}
 
+	/**
+	 * Initialize internal state of the plugin.
+	 *
+	 * @return void
+	 * @deprecated Just use __construct to initialize plugin internal state.
+	 *
+	 */
 	public function init_base_variables() {
-		$this->plugin_url = plugin_dir_url( $this->plugin_info->get_plugin_dir() );
 	}
 
 	/**
+	 * Initializes plugin external state.
+	 *
+	 * The plugin internal state is initialized in the constructor and the plugin should be internally consistent after creation.
+	 * The external state includes hooks execution, communication with other plugins, integration with WC etc.
+	 *
 	 * @return void
 	 */
-	protected function hooks() {
-		add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
-
-		add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] );
-
-		add_action( 'plugins_loaded', [ $this, 'load_plugin_text_domain' ] );
-		add_filter( 'plugin_action_links_' . plugin_basename( $this->get_plugin_file_path() ), [
-			$this,
-			'links_filter'
-		] );
-
+	public function init() {
+		$this->hooks();
 	}
 
 	/**
+	 * Returns absolute path to the plugin dir.
+	 *
 	 * @return string
 	 */
 	public function get_plugin_file_path() {
@@ -80,82 +100,118 @@ abstract class AbstractPlugin implements \WPDesk_Translable {
 	}
 
 	/**
-	 * @return $this
+	 * Returns plugin text domain.
+	 *
+	 * @return string
 	 */
-	public function get_plugin() {
-		return $this;
+	public function get_text_domain() {
+		return $this->plugin_info->get_text_domain();
 	}
 
 	/**
-	 * @return void
+	 * Returns unique string for plugin in [a-z_]+ format. Can be used as plugin id in various places like plugin slug etc.
+	 *
+	 * @return string
 	 */
-	public function load_plugin_text_domain() {
-		load_plugin_textdomain( $this->get_text_domain(), false, $this->get_namespace() . '/lang/' );
+	public function get_namespace() {
+		return $this->plugin_namespace;
 	}
 
 	/**
+	 * Returns plugin absolute URL.
+	 *
 	 * @return string
 	 */
-	public function get_text_domain() {
-		return $this->plugin_info->get_text_domain();
+	public function get_plugin_url() {
+		return esc_url( trailingslashit( $this->plugin_url ) );
 	}
 
 	/**
+	 * Returns plugin absolute URL to dir with front end assets.
+	 *
 	 * @return string
 	 */
-	public function get_namespace() {
-		return $this->plugin_namespace;
-	}
-
 	public function get_plugin_assets_url() {
 		return esc_url( trailingslashit( $this->get_plugin_url() . 'assets' ) );
 	}
 
 	/**
+	 * @return $this
+	 * @deprecated For backward compatibility.
 	 *
-	 * @return string
 	 */
-	public function get_plugin_url() {
-		return esc_url( trailingslashit( $this->plugin_url ) );
+	public function get_plugin() {
+		return $this;
+	}
+
+	/**
+	 * Integrate with WordPress and with other plugins using action/filter system.
+	 *
+	 * @return void
+	 */
+	protected function hooks() {
+		add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
+		add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] );
+		add_action( 'plugins_loaded', [ $this, 'load_plugin_text_domain' ] );
+		add_filter( 'plugin_action_links_' . plugin_basename( $this->get_plugin_file_path() ), [
+			$this,
+			'links_filter'
+		] );
+	}
+	/**
+	 * Initialize plugin test domain. This is a hook function. Do not execute directly.
+	 *
+	 * @return void
+	 */
+	public function load_plugin_text_domain() {
+		load_plugin_textdomain( $this->get_text_domain(), false, $this->get_namespace() . '/lang/' );
 	}
 
+	/**
+	 * Append JS scripts in the WordPress admin panel. This is a hook function. Do not execute directly.
+	 *
+	 * @return void
+	 */
 	public function admin_enqueue_scripts() {
 	}
 
+	/**
+	 * Append JS scripts in WordPress. This is a hook function. Do not execute directly.
+	 *
+	 * @return void
+	 */
 	public function wp_enqueue_scripts() {
 	}
 
 	/**
-	 * action_links function.
-	 *
-	 * @access public
+	 * Initialize plugin admin links. This is a hook function. Do not execute directly.
 	 *
-	 * @param mixed $links
+	 * @param string[] $links
 	 *
-	 * @return array
+	 * @return string[]
 	 */
 	public function links_filter( $links ) {
 		$support_link = get_locale() === 'pl_PL' ? 'https://www.wpdesk.pl/support/' : 'https://www.wpdesk.net/support';
 
-		if( $this->support_url ) {
+		if ( $this->support_url ) {
 			$support_link = $this->support_url;
 		}
 
 		$plugin_links = [
-			'<a href="' . $support_link . '">' . __( 'Support', $this->get_text_domain() ) . '</a>',
+			'<a target="_blank" href="' . $support_link . '">' . __( 'Support', $this->get_text_domain() ) . '</a>',
 		];
 		$links        = array_merge( $plugin_links, $links );
 
 		if ( $this->docs_url ) {
 			$plugin_links = [
-				'<a href="' . $this->docs_url . '">' . __( 'Docs', $this->get_text_domain() ) . '</a>',
+				'<a target="_blank" href="' . $this->docs_url . '">' . __( 'Docs', $this->get_text_domain() ) . '</a>',
 			];
 			$links        = array_merge( $plugin_links, $links );
 		}
 
 		if ( $this->settings_url ) {
 			$plugin_links = [
-				'<a href="' . $this->settings_url . '">' . __( 'Settings', $this->get_text_domain() ) . '</a>',
+				'<a target="_blank" href="' . $this->settings_url . '">' . __( 'Settings', $this->get_text_domain() ) . '</a>',
 			];
 			$links        = array_merge( $plugin_links, $links );
 		}
diff --git a/src/Plugin/Activateable.php b/src/Plugin/Activateable.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e084717f11cdf08ad4b27b3a12433d64f6123e5
--- /dev/null
+++ b/src/Plugin/Activateable.php
@@ -0,0 +1,21 @@
+<?php
+
+
+namespace WPDesk\PluginBuilder\Plugin;
+
+/**
+ * Tag the plugin with this ingterface to hook it to the WordPress activation hook.
+ *
+ * Note: works from plugin flow ^2.2.
+ *
+ * @package WPDesk\PluginBuilder\Plugin
+ */
+interface Activateable {
+
+	/**
+	 * Plugin activated in WordPress. Do not execute directly.
+	 *
+	 * @return void
+	 */
+	public function activate();
+}
diff --git a/src/Plugin/ActivationAware.php b/src/Plugin/ActivationAware.php
index f9cdac6938e424db972b4110875d352a7c567fb7..6fa0c798e3ec47db325e7d9cd81db9ea54778ae2 100644
--- a/src/Plugin/ActivationAware.php
+++ b/src/Plugin/ActivationAware.php
@@ -3,7 +3,7 @@
 namespace WPDesk\PluginBuilder\Plugin;
 
 /**
- * It means that this class is should know about subscription activation
+ * It means that this class is should know about SUBSCRIPTION activation
  *
  * @package WPDesk\PluginBuilder\Plugin
  */
diff --git a/src/Plugin/Conditional.php b/src/Plugin/Conditional.php
new file mode 100644
index 0000000000000000000000000000000000000000..c30e6daf87ecc3c347f4ad0a280cc7c8543c2a64
--- /dev/null
+++ b/src/Plugin/Conditional.php
@@ -0,0 +1,20 @@
+<?php
+
+
+namespace WPDesk\PluginBuilder\Plugin;
+
+/**
+ * Something that can be instantiated/hooked conditionally.
+ *
+ * @see https://github.com/mwpd/basic-scaffold/blob/master/src/Infrastructure/Conditional.php by Alain Schlesser
+ *
+ * @package WPDesk\PluginBuilder\Plugin
+ */
+interface Conditional {
+	/**
+	 * Check whether the conditional object is currently needed.
+	 *
+	 * @return bool Whether the conditional object is needed.
+	 */
+	public static function is_needed();
+}
diff --git a/src/Plugin/Deactivateable.php b/src/Plugin/Deactivateable.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d7f5786e0be9c01cb60e04bd05c3d876c5ac77b
--- /dev/null
+++ b/src/Plugin/Deactivateable.php
@@ -0,0 +1,21 @@
+<?php
+
+
+namespace WPDesk\PluginBuilder\Plugin;
+
+/**
+ * Tag the plugin with this ingterface to hook it to the WordPress deactivation hook.
+ *
+ * Note: works from plugin flow ^2.2.
+ *
+ * @package WPDesk\PluginBuilder\Plugin
+ */
+interface Deactivateable {
+
+	/**
+	 * Plugin deactivate in WordPress. Do not execute directly.
+	 *
+	 * @return void
+	 */
+	public function deactivate();
+}
diff --git a/src/Plugin/HookableParent.php b/src/Plugin/HookableParent.php
index 5aca3784d46ede75c9938c0248f5fd0c8c9b605f..667060567026a630c5a4bcb8554d4b02f3943517 100644
--- a/src/Plugin/HookableParent.php
+++ b/src/Plugin/HookableParent.php
@@ -45,9 +45,15 @@ trait HookableParent {
 	protected function hooks_on_hookable_objects() {
 		/** @var Hookable $hookable_object $hookable_object */
 		foreach ( $this->hookable_objects as $hookable_object ) {
-			$hookable_object->hooks();
+			if ($hookable_object instanceof Conditional) {
+				if ($hookable_object::is_needed()) {
+					$hookable_object->hooks();
+				}
+			} else {
+				$hookable_object->hooks();
+			}
 		}
 	}
 
 
-}
\ No newline at end of file
+}
diff --git a/src/Plugin/PluginAccess.php b/src/Plugin/PluginAccess.php
index 96e0ada648f0d5edd01eedeb16abb5b34ec50558..ebb9d3fd0a4f5ef10e90eab7b6843c027b77bff7 100644
--- a/src/Plugin/PluginAccess.php
+++ b/src/Plugin/PluginAccess.php
@@ -31,4 +31,4 @@ trait PluginAccess {
 		return $this->plugin;
 	}
 
-}
\ No newline at end of file
+}
diff --git a/src/Plugin/SlimPlugin.php b/src/Plugin/SlimPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf95f3d7dc131367c5197aa669c7fe31851a1ac6
--- /dev/null
+++ b/src/Plugin/SlimPlugin.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Plugin;
+
+/**
+ * Most clean plugin class with only most important details.
+ */
+abstract class SlimPlugin implements \WPDesk_Translatable {
+
+	/**
+	 * Initializes plugin external state.
+	 *
+	 * The plugin internal state is initialized in the constructor and the plugin should be internally consistent after creation.
+	 * The external state includes hooks execution, communication with other plugins, integration with WC etc.
+	 *
+	 * @return void
+	 */
+	abstract public function init();
+}
+
diff --git a/src/Storage/StorageFactory.php b/src/Storage/StorageFactory.php
index 8a1422f53e033dc1fc3b1da9cf1dee71a302f23a..89f5f56f2d2c6ad16e85b828af21061f0f811033 100644
--- a/src/Storage/StorageFactory.php
+++ b/src/Storage/StorageFactory.php
@@ -8,7 +8,7 @@ class StorageFactory {
 	 * @return PluginStorage
 	 */
 	public function create_storage() {
-		return new StaticStorage();
+		return new WordpressFilterStorage();
 	}
 }
 
diff --git a/src/Storage/WordpressFilterStorage.php b/src/Storage/WordpressFilterStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8c564abd1de999128ffb2824d826241a3869d33
--- /dev/null
+++ b/src/Storage/WordpressFilterStorage.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Storage;
+
+use WPDesk\PluginBuilder\Plugin\AbstractPlugin;
+
+/**
+ * Can store plugin instances in WordPress filter system.
+ *
+ * @package WPDesk\PluginBuilder\Storage
+ */
+class WordpressFilterStorage implements PluginStorage {
+	const STORAGE_FILTER_NAME = 'wpdesk_plugin_instances';
+
+	/**
+	 * @param string $class
+	 * @param AbstractPlugin $object
+	 */
+	public function add_to_storage( $class, $object ) {
+		add_filter( self::STORAGE_FILTER_NAME, function ( $plugins ) use ( $class, $object ) {
+			if ( isset( $plugins[ $class ] ) ) {
+				throw new Exception\ClassAlreadyExists( "Class {$class} already exists" );
+			}
+			$plugins[ $class ] = $object;
+
+			return $plugins;
+		} );
+
+	}
+
+	/**
+	 * @param string $class
+	 *
+	 * @return AbstractPlugin
+	 */
+	public function get_from_storage( $class ) {
+		$plugins = apply_filters( self::STORAGE_FILTER_NAME, [] );
+		if ( isset( $plugins[ $class ] ) ) {
+			return $plugins[ $class ];
+		}
+		throw new Exception\ClassNotExists( "Class {$class} not exists in storage" );
+	}
+}
+