You've already forked wp-fedistream
- Implement license management using magdev/wc-licensed-product-client - Reorganize settings page into License, Default Settings, Integrations tabs - Add license validation and activation via AJAX - Frontend features require valid license (admin works always) - Update translations with German (de_CH) for license strings Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
470 lines
15 KiB
PHP
470 lines
15 KiB
PHP
<?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',
|
|
// License management options.
|
|
'wp_fedistream_license_key' => '',
|
|
'wp_fedistream_license_server_url' => '',
|
|
'wp_fedistream_license_server_secret' => '',
|
|
'wp_fedistream_license_status' => 'unchecked',
|
|
'wp_fedistream_license_data' => array(),
|
|
'wp_fedistream_license_last_check' => 0,
|
|
);
|
|
|
|
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_%'
|
|
)
|
|
);
|
|
}
|
|
}
|