Files
wp-fedistream/includes/Installer.php

463 lines
14 KiB
PHP
Raw Normal View History

<?php
/**
* Plugin installer class.
*
* @package WP_FediStream
*/
namespace WP_FediStream;
use WP_FediStream\Roles\Capabilities;
use WP_FediStream\Taxonomies\Genre;
use WP_FediStream\Taxonomies\Mood;
use WP_FediStream\Taxonomies\License;
// Prevent direct file access.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles plugin activation, deactivation, and uninstallation.
*/
class Installer {
/**
* Plugin activation.
*
* @return void
*/
public static function activate(): void {
self::create_tables();
self::create_directories();
self::set_default_options();
self::schedule_events();
// Install roles and capabilities.
Capabilities::install();
// Store installed version.
update_option( 'wp_fedistream_version', WP_FEDISTREAM_VERSION );
// Flush rewrite rules for custom post types.
flush_rewrite_rules();
// Install default taxonomy terms (after rewrite rules flush).
// Schedule for next page load since taxonomies need to be registered first.
update_option( 'wp_fedistream_install_defaults', 1 );
}
/**
* Plugin deactivation.
*
* @return void
*/
public static function deactivate(): void {
self::unschedule_events();
// Flush rewrite rules.
flush_rewrite_rules();
}
/**
* Plugin uninstallation.
*
* @return void
*/
public static function uninstall(): void {
// Only run if uninstall is explicitly requested.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
return;
}
self::delete_tables();
self::delete_options();
self::delete_user_meta();
self::delete_transients();
self::delete_posts();
self::delete_terms();
// Remove roles and capabilities.
Capabilities::uninstall();
}
/**
* Delete all plugin posts.
*
* @return void
*/
private static function delete_posts(): void {
$post_types = array(
'fedistream_artist',
'fedistream_album',
'fedistream_track',
'fedistream_playlist',
);
foreach ( $post_types as $post_type ) {
$posts = get_posts(
array(
'post_type' => $post_type,
'posts_per_page' => -1,
'post_status' => 'any',
'fields' => 'ids',
)
);
foreach ( $posts as $post_id ) {
wp_delete_post( $post_id, true );
}
}
}
/**
* Delete all plugin taxonomy terms.
*
* @return void
*/
private static function delete_terms(): void {
$taxonomies = array(
'fedistream_genre',
'fedistream_mood',
'fedistream_license',
);
foreach ( $taxonomies as $taxonomy ) {
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'fields' => 'ids',
)
);
if ( ! is_wp_error( $terms ) ) {
foreach ( $terms as $term_id ) {
wp_delete_term( $term_id, $taxonomy );
}
}
}
}
/**
* Install default taxonomy terms.
*
* Called on first load after activation when taxonomies are registered.
*
* @return void
*/
public static function install_defaults(): void {
if ( ! get_option( 'wp_fedistream_install_defaults' ) ) {
return;
}
// Install default genres.
Genre::install_defaults();
// Install default moods.
Mood::install_defaults();
// Install default licenses.
License::install_defaults();
// Clear the flag.
delete_option( 'wp_fedistream_install_defaults' );
}
/**
* Create custom database tables.
*
* @return void
*/
private static function create_tables(): void {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// Track plays table.
$table_plays = $wpdb->prefix . 'fedistream_plays';
$sql_plays = "CREATE TABLE $table_plays (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
track_id bigint(20) unsigned NOT NULL,
user_id bigint(20) unsigned DEFAULT NULL,
remote_actor varchar(255) DEFAULT NULL,
played_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
play_duration int(11) unsigned DEFAULT 0,
PRIMARY KEY (id),
KEY track_id (track_id),
KEY user_id (user_id),
KEY played_at (played_at)
) $charset_collate;";
// Playlist tracks table (many-to-many relationship).
$table_playlist_tracks = $wpdb->prefix . 'fedistream_playlist_tracks';
$sql_playlist_tracks = "CREATE TABLE $table_playlist_tracks (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
playlist_id bigint(20) unsigned NOT NULL,
track_id bigint(20) unsigned NOT NULL,
position int(11) unsigned NOT NULL DEFAULT 0,
added_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY playlist_track (playlist_id, track_id),
KEY playlist_id (playlist_id),
KEY track_id (track_id),
KEY position (position)
) $charset_collate;";
// ActivityPub followers table.
$table_followers = $wpdb->prefix . 'fedistream_followers';
$sql_followers = "CREATE TABLE $table_followers (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
artist_id bigint(20) unsigned NOT NULL,
follower_uri varchar(2083) NOT NULL,
follower_name varchar(255) DEFAULT NULL,
follower_icon varchar(2083) DEFAULT NULL,
inbox varchar(2083) DEFAULT NULL,
shared_inbox varchar(2083) DEFAULT NULL,
activity_data longtext DEFAULT NULL,
followed_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY artist_follower (artist_id, follower_uri(191)),
KEY artist_id (artist_id),
KEY followed_at (followed_at)
) $charset_collate;";
// WooCommerce purchases table.
$table_purchases = $wpdb->prefix . 'fedistream_purchases';
$sql_purchases = "CREATE TABLE $table_purchases (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
content_type varchar(50) NOT NULL,
content_id bigint(20) unsigned NOT NULL,
order_id bigint(20) unsigned NOT NULL,
purchased_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY user_content (user_id, content_type, content_id),
KEY user_id (user_id),
KEY content_type (content_type),
KEY content_id (content_id),
KEY order_id (order_id)
) $charset_collate;";
// User favorites table.
$table_favorites = $wpdb->prefix . 'fedistream_favorites';
$sql_favorites = "CREATE TABLE $table_favorites (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
content_type varchar(50) NOT NULL,
content_id bigint(20) unsigned NOT NULL,
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY user_content (user_id, content_type, content_id),
KEY user_id (user_id),
KEY content_type (content_type),
KEY content_id (content_id),
KEY created_at (created_at)
) $charset_collate;";
// User follows table (local follows, not ActivityPub).
$table_user_follows = $wpdb->prefix . 'fedistream_user_follows';
$sql_user_follows = "CREATE TABLE $table_user_follows (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
artist_id bigint(20) unsigned NOT NULL,
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY user_artist (user_id, artist_id),
KEY user_id (user_id),
KEY artist_id (artist_id),
KEY created_at (created_at)
) $charset_collate;";
// Listening history table.
$table_history = $wpdb->prefix . 'fedistream_listening_history';
$sql_history = "CREATE TABLE $table_history (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
track_id bigint(20) unsigned NOT NULL,
played_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY track_id (track_id),
KEY played_at (played_at),
KEY user_played (user_id, played_at)
) $charset_collate;";
// Notifications table.
$table_notifications = $wpdb->prefix . 'fedistream_notifications';
$sql_notifications = "CREATE TABLE $table_notifications (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
type varchar(50) NOT NULL,
title varchar(255) NOT NULL,
message text NOT NULL,
data longtext DEFAULT NULL,
is_read tinyint(1) unsigned NOT NULL DEFAULT 0,
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
read_at datetime DEFAULT NULL,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY type (type),
KEY is_read (is_read),
KEY created_at (created_at),
KEY user_unread (user_id, is_read)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql_plays );
dbDelta( $sql_playlist_tracks );
dbDelta( $sql_followers );
dbDelta( $sql_purchases );
dbDelta( $sql_favorites );
dbDelta( $sql_user_follows );
dbDelta( $sql_history );
dbDelta( $sql_notifications );
}
/**
* Create required directories.
*
* @return void
*/
private static function create_directories(): void {
$directories = array(
WP_FEDISTREAM_PATH . 'cache/twig',
);
foreach ( $directories as $directory ) {
if ( ! file_exists( $directory ) ) {
wp_mkdir_p( $directory );
}
}
// Create .htaccess to protect cache directory.
$htaccess = WP_FEDISTREAM_PATH . 'cache/.htaccess';
if ( ! file_exists( $htaccess ) ) {
file_put_contents( $htaccess, 'Deny from all' );
}
}
/**
* Set default plugin options.
*
* @return void
*/
private static function set_default_options(): void {
$defaults = array(
'wp_fedistream_enable_activitypub' => 1,
'wp_fedistream_enable_woocommerce' => 0,
'wp_fedistream_audio_formats' => array( 'mp3', 'wav', 'flac', 'ogg' ),
'wp_fedistream_max_upload_size' => 50, // MB
'wp_fedistream_default_license' => 'all-rights-reserved',
);
foreach ( $defaults as $option => $value ) {
if ( false === get_option( $option ) ) {
add_option( $option, $value );
}
}
}
/**
* Schedule cron events.
*
* @return void
*/
private static function schedule_events(): void {
if ( ! wp_next_scheduled( 'wp_fedistream_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'wp_fedistream_daily_cleanup' );
}
}
/**
* Unschedule cron events.
*
* @return void
*/
private static function unschedule_events(): void {
$timestamp = wp_next_scheduled( 'wp_fedistream_daily_cleanup' );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'wp_fedistream_daily_cleanup' );
}
}
/**
* Delete custom database tables.
*
* @return void
*/
private static function delete_tables(): void {
global $wpdb;
$tables = array(
$wpdb->prefix . 'fedistream_plays',
$wpdb->prefix . 'fedistream_playlist_tracks',
$wpdb->prefix . 'fedistream_followers',
$wpdb->prefix . 'fedistream_purchases',
$wpdb->prefix . 'fedistream_reactions',
$wpdb->prefix . 'fedistream_favorites',
$wpdb->prefix . 'fedistream_user_follows',
$wpdb->prefix . 'fedistream_listening_history',
$wpdb->prefix . 'fedistream_notifications',
);
foreach ( $tables as $table ) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "DROP TABLE IF EXISTS $table" );
}
}
/**
* Delete plugin options.
*
* @return void
*/
private static function delete_options(): void {
global $wpdb;
// Delete all options starting with wp_fedistream_.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->options WHERE option_name LIKE %s",
'wp_fedistream_%'
)
);
}
/**
* Delete user meta.
*
* @return void
*/
private static function delete_user_meta(): void {
global $wpdb;
// Delete all user meta starting with wp_fedistream_.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->usermeta WHERE meta_key LIKE %s",
'wp_fedistream_%'
)
);
}
/**
* Delete transients.
*
* @return void
*/
private static function delete_transients(): void {
global $wpdb;
// Delete all transients starting with wp_fedistream_.
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $wpdb->options WHERE option_name LIKE %s OR option_name LIKE %s",
'_transient_wp_fedistream_%',
'_transient_timeout_wp_fedistream_%'
)
);
}
}