register( new My_Module() ); * } ); * * Backward compatibility rule: every method here has a working default, and new * methods are only ever ADDED with a default. An older plugin running on a newer * core must keep working unchanged. */ abstract class Module { // ─── Identity (required) ────────────────────────────────────────────────── /** Short unique key, e.g. "split". Used in URLs, option names and the activity log. */ abstract public function id(): string; /** Display name, e.g. "Patchwork Split". */ abstract public function name(): string; abstract public function version(): string; /** Main plugin file (__FILE__ of the plugin). */ abstract public function plugin_file(): string; // ─── Identity (optional) ────────────────────────────────────────────────── /** One-line description for the dashboard card. */ public function description(): string { return ''; } /** Dashicon class or a URL to an SVG/PNG icon. */ public function icon(): string { return 'dashicons-admin-generic'; } /** Capability needed to see and change this module's settings. */ public function settings_capability(): string { return 'manage_options'; } /** * Shared features this module uses: * - 'orders_column' → order_badges() is shown in the shared Patchwork column * - 'order_panel' → order_panel() is shown in the shared Patchwork order box */ public function supports(): array { return []; } /** Docs / support links shown on the module page: [ [ 'label' => '', 'url' => '' ], … ] */ public function links(): array { return []; } // ─── Settings ───────────────────────────────────────────────────────────── /** Sections: [ 'section_id' => 'Title', … ]. */ public function settings_sections(): array { return []; } /** * Fields, each: * [ * 'id' => 'option_name', // stored with update_option() * 'section' => 'section_id', * 'label' => 'Label', * 'type' => 'text|number|lines|select|checkbox|custom', * 'default' => '', * 'options' => [ 'value' => 'Label' ], // select * 'description' => '', * 'render' => callable( array $field ), // custom * 'save' => callable( array $field ), // custom — read $_POST yourself * ] */ public function settings_fields(): array { return []; } // ─── Dashboard ──────────────────────────────────────────────────────────── /** * The card on the Patchwork overview: * [ * 'stats' => [ [ 'label' => '', 'value' => '' ], … ], * 'notice' => '', // short status line, optional * 'tone' => 'ok|warning', * ] */ public function dashboard_card(): array { return []; } // ─── Orders (WooCommerce) ───────────────────────────────────────────────── /** * Badges for one row of the orders list (needs 'orders_column' in supports()): * [ [ 'label' => '', 'url' => '', 'title' => '', 'tone' => 'info|success|warning|danger|neutral' ], … ] */ public function order_badges( \WC_Order $order ): array { return []; } /** * HTML for this module's section of the Patchwork box on the order screen * (needs 'order_panel' in supports()). Return '' to show nothing. */ public function order_panel( \WC_Order $order ): string { return ''; } // ─── Licensing (Freemius) ───────────────────────────────────────────────── /** * The module's Freemius instance (from its fs_dynamic_init()), or null while * the module isn't connected to Freemius yet. * * @return \Freemius|null */ public function freemius() { return null; } /** * Is a Pro licence active (or the Pro build in use, for Freemius)? * On a development site, define( 'PATCHWORK_DEV_PRO', true ) in wp-config.php * to test Pro code before Freemius is connected. Pro code isn't in the free * build at all, so the constant does nothing there. */ public function is_pro(): bool { if ( defined( 'PATCHWORK_DEV_PRO' ) && PATCHWORK_DEV_PRO ) return true; $fs = $this->freemius(); return $fs && method_exists( $fs, 'can_use_premium_code' ) && $fs->can_use_premium_code(); } /** True when Pro is only on because of PATCHWORK_DEV_PRO (no real licence). */ public function is_dev_pro(): bool { if ( ! defined( 'PATCHWORK_DEV_PRO' ) || ! PATCHWORK_DEV_PRO ) return false; $fs = $this->freemius(); return ! ( $fs && method_exists( $fs, 'can_use_premium_code' ) && $fs->can_use_premium_code() ); } /** Where to buy / upgrade. Empty hides the button. */ public function upgrade_url(): string { $fs = $this->freemius(); return ( $fs && method_exists( $fs, 'get_upgrade_url' ) ) ? (string) $fs->get_upgrade_url() : ''; } /** Where to manage the licence / account. Empty hides the link. */ public function account_url(): string { $fs = $this->freemius(); return ( $fs && method_exists( $fs, 'get_account_url' ) && $fs->is_registered() ) ? (string) $fs->get_account_url() : ''; } /** * Plain-language list of what Pro adds, shown on the Licence tab of the free * version. Text only — Pro code itself isn't in the free build. */ public function pro_features(): array { return []; } // ─── Helpers ────────────────────────────────────────────────────────────── /** URL of this module's page in the Patchwork dashboard. */ final public function admin_url( string $tab = '' ): string { $args = [ 'page' => 'patchwork-' . $this->id() ]; if ( $tab ) $args['tab'] = $tab; return add_query_arg( $args, admin_url( 'admin.php' ) ); } /** Record an entry in the shared activity log. */ final public function log( string $action, string $message, string $object_type = '', int $object_id = 0, array $data = [] ): void { Activity::log( $this->id(), $action, $message, $object_type, $object_id, $data ); } }