diff --git a/includes/class-activator.php b/includes/class-activator.php new file mode 100644 index 00000000..bb7b215f --- /dev/null +++ b/includes/class-activator.php @@ -0,0 +1,159 @@ + WP_FEATURE_NOTIFICATION_PLUGIN_VERSION, + 'max_lifespan' => 1000 * 60 * 60 * 24 * 31 * 6, // 6 months + 'delete_on_dismiss' => false, + ); + } + + /** + * Create or Update the WP_Notifications options. + * + * @return void + */ + public static function update_options() { + + self::init_options(); + + $options = get_option( 'wp_notifications_options' ); + + if ( false !== $options ) { + + // Update the plugin options but add the new options automatically + if ( isset( $options['version'] ) ) { + unset( $options['version'] ); + } + + // Merge previous options, preserve the previously modified options as default. + $new_options = array_merge( self::$default_options, $options ); + + update_option( 'wp_notifications_options', $new_options ); + } else { + // If the plugin options are missing, initialize the plugin with the default options. + $new_options = array_merge( self::$default_options ); + + add_option( 'wp_notifications_options', $new_options ); + } + } + + /** + * Install the WP Notifications plugin. + * + * Create the plugin's database tables and options + * + * @return void + */ + public static function install() { + global $wpdb; + + // Engage multisite if in the middle of turning it on from network.php. + $is_multisite = is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ); + + if ( $is_multisite ) { + // Get all blogs in the network and uninstall the plugin on each one. + $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ); + + // Loop over the individual sites and create tables for each. + foreach ( $blog_ids as $blog_id ) { + switch_to_blog( $blog_id ); + + self::create_tables(); + + restore_current_blog(); + } + } + + // Always create the main site database tables and options. + self::create_tables(); + } + + /** + * Activate the WP Notifications plugin. + * + * @return void + */ + public static function activate() { + self::install(); + set_transient( 'wp_notifications_activation', true ); + } + + /** + * Create the WP Notifications tables and options. + * + * @return void + */ + public static function create_tables() { + $db_version = get_option( 'wp_notifications_db_version' ); + + if ( ! $db_version ) { + self::create_tables_v1(); + update_option( 'wp_notifications_db_version', WP_FEATURE_NOTIFICATION_DB_VERSION ); + } + + // If the options do not exist then create them + self::update_options(); + } + + /** + * Create v1 WP Notifications tables and options. + * + * @return void + */ + public static function create_tables_v1() { + global $wpdb; + + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + require_once WP_FEATURE_NOTIFICATION_PLUGIN_DIR . '/includes/database/class-schema.php'; + + $tables = $wpdb->get_results( 'SHOW TABLES' ); + + // Create the messages table + if ( ! in_array( $wpdb->prefix . 'notifications_messages', $tables, true ) ) { + $messages_sql = Database\Schema::messages_table_v1(); + dbDelta( $messages_sql ); + } + + // Create the subscriptions table + if ( ! in_array( $wpdb->prefix . 'notifications_subscriptions', $tables, true ) ) { + $subscriptions_sql = Database\Schema::subscriptions_table_v1(); + dbDelta( $subscriptions_sql ); + } + + // Create the queue table + if ( ! in_array( $wpdb->prefix . 'notifications_queue', $tables, true ) ) { + $queue_sql = Database\Schema::queue_table_v1(); + dbDelta( $queue_sql ); + } + } +} diff --git a/includes/class-uninstaller.php b/includes/class-uninstaller.php new file mode 100644 index 00000000..3ff22538 --- /dev/null +++ b/includes/class-uninstaller.php @@ -0,0 +1,87 @@ +get_col( "SELECT blog_id FROM $wpdb->blogs" ); + + foreach ( $blog_ids as $blog_id ) { + switch_to_blog( $blog_id ); + + self::drop_tables(); + self::delete_options(); + + restore_current_blog(); + } + } + + // Always remove the main site database tables and options. + self::drop_tables(); + self::delete_options(); + } + + /** + * Drop the WP Notifications database tables. + * + * @return void + */ + public static function drop_tables() { + global $wpdb; + + $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'notifications_messages' ); + $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'notifications_subscriptions' ); + $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'notifications_queue' ); + } + + /** + * Delete the WP Notifications options. + * + * @return void + */ + public static function delete_options() { + delete_option( 'wp_notifications_db_version' ); + delete_option( 'wp_notifications_options' ); + } +} diff --git a/includes/database/class-schema.php b/includes/database/class-schema.php new file mode 100644 index 00000000..e5be1ca2 --- /dev/null +++ b/includes/database/class-schema.php @@ -0,0 +1,66 @@ +get_charset_collate(); + + return 'CREATE TABLE `' . $wpdb->prefix . "notifications_messages` ( + `id` BIGINT(20) NOT NULL, + `channel_name` VARCHAR(50) NOT NULL, + `channel_title` TINYTEXT NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(), + `expires_at` DATETIME NULL, + `severity` VARCHAR(16) NULL, + `title` TINYTEXT NULL, + `message` TINYTEXT NULL, + `meta` TEXT NULL, + PRIMARY KEY (`id`), + KEY `channel_name` (`channel_name`) + ) $charset_collate;\n"; + } + + + public static function subscriptions_table_v1() { + global $wpdb; + + $charset_collate = $wpdb->get_charset_collate(); + + return 'CREATE TABLE `' . $wpdb->prefix . "notifications_subscriptions` ( + `user_id` BIGINT(20) NOT NULL, + `channel_name` VARCHAR(50) NOT NULL, + `snoozed_until` DATETIME NULL, + KEY `user_id` (`user_id`), + KEY `channel_name` (`channel_name`) + ) $charset_collate;\n"; + } + + public static function queue_table_v1() { + global $wpdb; + + $charset_collate = $wpdb->get_charset_collate(); + + return 'CREATE TABLE `' . $wpdb->prefix . "notifications_queue` ( + `message_id` BIGINT(20) NOT NULL, + `user_id` BIGINT(20) NOT NULL, + `dismissed_at` DATETIME NULL, + `displayed_at` DATETIME NULL, + KEY `message_id` (`message_id`), + KEY `user_id` (`user_id`) + ) $charset_collate;\n"; + } +} diff --git a/tests/phpunit/includes/bootstrap.php b/tests/phpunit/includes/bootstrap.php index efeaa6d6..2b333058 100644 --- a/tests/phpunit/includes/bootstrap.php +++ b/tests/phpunit/includes/bootstrap.php @@ -39,3 +39,4 @@ function handle_wp_setup_failure( $message ) { remove_filter( 'wp_die_handler', 'handle_wp_setup_failure' ); require dirname( __FILE__ ) . '/class-testcase.php'; +require dirname( __FILE__ ) . '/class-db-testcase.php'; diff --git a/tests/phpunit/includes/class-db-testcase.php b/tests/phpunit/includes/class-db-testcase.php new file mode 100644 index 00000000..52b23a20 --- /dev/null +++ b/tests/phpunit/includes/class-db-testcase.php @@ -0,0 +1,32 @@ +prefix . $table_name; + + $actual = $wpdb->get_var( + $wpdb->prepare( + 'SHOW TABLES LIKE %s', + $wpdb->esc_like( $wpdb->prefix . $table_name ) + ) + ); + + return $expected === $actual; + + } + +} diff --git a/tests/phpunit/includes/class-testcase.php b/tests/phpunit/includes/class-testcase.php index 71bcbd5c..91caba4f 100644 --- a/tests/phpunit/includes/class-testcase.php +++ b/tests/phpunit/includes/class-testcase.php @@ -6,4 +6,22 @@ class TestCase extends PHPUnit_Adapter_TestCase { + /** + * Asserts that the contents of two un-keyed, single arrays are the same, without accounting for the order of elements. + * + * Copied from WP_UnitTestCase_Base. + * + * @param array $expected Expected array. + * @param array $actual Array to check. + * @param string $message Optional. Message to display when the assertion fails. + */ + public function assertSameSets( $expected, $actual, $message = '' ) { + $this->assertIsArray( $expected, $message . ' Expected value must be an array.' ); + $this->assertIsArray( $actual, $message . ' Value under test is not an array.' ); + + sort( $expected ); + sort( $actual ); + $this->assertSame( $expected, $actual, $message ); + } + } diff --git a/tests/phpunit/tests/test-activator.php b/tests/phpunit/tests/test-activator.php new file mode 100644 index 00000000..85662e88 --- /dev/null +++ b/tests/phpunit/tests/test-activator.php @@ -0,0 +1,45 @@ +assertFalse( $this->table_exists( 'notifications_messages' ) ); + } + + public function test_it_should_initially_not_have_subscriptions_table() { + $this->assertFalse( $this->table_exists( 'notifications_subscriptions' ) ); + } + + public function test_it_should_initially_not_have_queue_table() { + $this->assertFalse( $this->table_exists( 'notifications_queue' ) ); + } + + // Installation procedure tests. + + /** + * Test to ensure the uninstall procedure drops the correct tables. + */ + public function test_it_should_create_tables() { + Notifications\Activator::create_tables_v1(); + + $this->assertTrue( $this->table_exists( 'notifications_messages' ) ); + $this->assertTrue( $this->table_exists( 'notifications_subscriptions' ) ); + $this->assertTrue( $this->table_exists( 'notifications_queue' ) ); + } +} diff --git a/tests/phpunit/tests/test-channel-registry.php b/tests/phpunit/tests/test-channel-registry.php index 9bc71d21..a0848651 100644 --- a/tests/phpunit/tests/test-channel-registry.php +++ b/tests/phpunit/tests/test-channel-registry.php @@ -1,12 +1,12 @@ assertTrue( $this->table_exists( 'notifications_messages' ) ); + } + + public function test_it_should_initially_have_subscriptions_table() { + $this->assertTrue( $this->table_exists( 'notifications_subscriptions' ) ); + } + + public function test_it_should_initially_have_queue_table() { + $this->assertTrue( $this->table_exists( 'notifications_queue' ) ); + } + + /** + * Test to ensure the uninstall procedure drops the correct tables. + */ + public function test_it_should_drops_tables() { + Notifications\Uninstaller::uninstall(); + + $this->assertFalse( $this->table_exists( 'notifications_messages' ) ); + $this->assertFalse( $this->table_exists( 'notifications_subscriptions' ) ); + $this->assertFalse( $this->table_exists( 'notifications_queue' ) ); + } +} diff --git a/wp-feature-notifications.php b/wp-feature-notifications.php index 52ac7813..e70e42c1 100644 --- a/wp-feature-notifications.php +++ b/wp-feature-notifications.php @@ -19,10 +19,16 @@ use WP\Notifications\REST; +define( 'WP_FEATURE_NOTIFICATION_LOG_PREFIX', 'WP_NOTIFICATIONS: ' ); + if ( ! defined( 'WP_FEATURE_NOTIFICATION_PLUGIN_VERSION' ) ) { define( 'WP_FEATURE_NOTIFICATION_PLUGIN_VERSION', '0.0.1' ); } +if ( ! defined( 'WP_FEATURE_NOTIFICATION_DB_VERSION' ) ) { + define( 'WP_FEATURE_NOTIFICATION_DB_VERSION', '1' ); +} + if ( ! defined( 'WP_FEATURE_NOTIFICATION_PLUGIN_DIR' ) ) { define( 'WP_FEATURE_NOTIFICATION_PLUGIN_DIR', dirname( __FILE__ ) ); } @@ -56,3 +62,33 @@ require_once WP_FEATURE_NOTIFICATION_PLUGIN_DIR . '/includes/restapi/class-notification-controller.php'; new REST\Notification_Controller(); + +/** + * Activation hook function of the WP Notification plugin. + * + * @return void + * + * @package wp-feature-notifications + */ +function activation_hook() { + require_once WP_FEATURE_NOTIFICATION_PLUGIN_DIR . '/includes/class-activator.php'; + Activator::activate(); +} + +register_activation_hook( __FILE__, '\WP\Notifications\activation_hook' ); + +/** + * Uninstall hook function of the WP Notification plugin. + * + * @return void + * + * @package wp-feature-notifications + */ +function uninstall_hook() { + require_once WP_FEATURE_NOTIFICATION_PLUGIN_DIR . '/includes/class-uninstaller.php'; + Uninstaller::uninstall(); +} + +register_uninstall_hook( __FILE__, '\WP\Notifications\uninstall_hook' ); + +new REST\Notification_Controller();