PHPackages                             texxasrulez/plugin\_manager - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. texxasrulez/plugin\_manager

ActiveRoundcube-plugin[Utility &amp; Helpers](/categories/utility)

texxasrulez/plugin\_manager
===========================

Roundcube Plugin Manager: lists installed plugins, shows local vs latest versions, and highlights updates.

1.6.0(1mo ago)1278↓13.3%1GPL-3.0PHPPHP &gt;=7.4

Since Aug 19Pushed 1mo agoCompare

[ Source](https://github.com/texxasrulez/plugin_manager)[ Packagist](https://packagist.org/packages/texxasrulez/plugin_manager)[ Docs](https://github.com/texxasrulez/plugin_manager)[ RSS](/packages/texxasrulez-plugin-manager/feed)WikiDiscussions main Synced today

READMEChangelog (10)Dependencies (2)Versions (25)Used By (0)

Plugin Manager for Roundcube
============================

[](#plugin-manager-for-roundcube)

[![Downloads](https://camo.githubusercontent.com/680809998725e9b6076ba6dab3b6173dcbc3c94ec71e8d83edb0ea62df9bd0b6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f646f776e6c6f6164732f74657878617372756c657a2f706c7567696e5f6d616e616765722f746f74616c3f7374796c653d706c6173746963266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465266c6162656c3d446f776e6c6f616473266c6162656c436f6c6f723d6171756126636f6c6f723d626c7565)](https://camo.githubusercontent.com/680809998725e9b6076ba6dab3b6173dcbc3c94ec71e8d83edb0ea62df9bd0b6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f646f776e6c6f6164732f74657878617372756c657a2f706c7567696e5f6d616e616765722f746f74616c3f7374796c653d706c6173746963266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465266c6162656c3d446f776e6c6f616473266c6162656c436f6c6f723d6171756126636f6c6f723d626c7565)[![Packagist Downloads](https://camo.githubusercontent.com/02d2ced5c5b137d4f954b7081abed0f2bff6edd765daffa203867696cc75f119/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d7061636b6167697374266c6f676f436f6c6f723d7768697465266c6162656c3d446f776e6c6f616473266c6162656c436f6c6f723d626c756526636f6c6f723d676f6c64)](https://packagist.org/packages/texxasrulez/plugin_manager)[![Packagist Version](https://camo.githubusercontent.com/4988ecfba1903786d3999427d6422a7b823faf37b9cbaf009882903ae36d89a0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d7061636b6167697374266c6f676f436f6c6f723d7768697465266c6162656c3d56657273696f6e266c6162656c436f6c6f723d626c756526636f6c6f723d6c696d65677265656e)](https://packagist.org/packages/texxasrulez/plugin_manager)[![Github License](https://camo.githubusercontent.com/3732bbe03cac4be035b5eca28e7a68a008509db7a587e99525c2921818a29d73/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d676974687562266c6162656c3d4c6963656e7365266c6162656c436f6c6f723d626c756526636f6c6f723d636f72616c)](https://github.com/texxasrulez/plugin_manager/LICENSE)[![GitHub Stars](https://camo.githubusercontent.com/086de87bc94c1d4a3895fa82ea535c22146e0596517688128436d9399d32e790/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d676974687562266c6162656c3d5374617273266c6162656c436f6c6f723d626c756526636f6c6f723d64656570736b79626c7565)](https://github.com/texxasrulez/plugin_manager/stargazers)[![GitHub Issues](https://camo.githubusercontent.com/bf876ac6e7e927ae338ddace8c72e67c37eaffc3d64317480522e23c6d621c8b/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d676974687562266c6162656c3d497373756573266c6162656c436f6c6f723d626c756526636f6c6f723d61717561)](https://github.com/texxasrulez/plugin_manager/issues)[![GitHub Contributors](https://camo.githubusercontent.com/11a145cacae601d70a93e366256dfd3d743080e734a7fee72735848110dde599/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465266c6162656c3d436f6e7472696275746f7273266c6162656c436f6c6f723d626c756526636f6c6f723d6f7263686964)](https://github.com/texxasrulez/plugin_manager/graphs/contributors)[![GitHub Forks](https://camo.githubusercontent.com/d4259e0f65b988bfbcf99e7dafb8dec5eacc2b794bb9e2f2c525dd0b61a8dc52/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f74657878617372756c657a2f706c7567696e5f6d616e616765723f7374796c653d706c6173746963266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465266c6162656c3d466f726b73266c6162656c436f6c6f723d626c756526636f6c6f723d6461726b6f72616e6765)](https://github.com/texxasrulez/plugin_manager/forks)[![Donate Paypal](https://camo.githubusercontent.com/02124075ee7ea4c192ef867cb30577b0b43ba74ea42b8dbe20b56ef518d94cdb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50617970616c2d4d6f6e65795f506c65617365212d626c75652e7376673f7374796c653d706c6173746963266c6162656c436f6c6f723d626c756526636f6c6f723d666f72657374677265656e266c6f676f3d70617970616c)](https://www.paypal.me/texxasrulez)

Lists installed plugins, shows local vs latest versions, and highlights **Update available**. Works with Larry, Elastic, and custom skins.

Versioning
----------

[](#versioning)

- `plugin_manager` now keeps its own canonical version in `plugin_manager::PLUGIN_VERSION` inside `plugin_manager.php`.
- `plugin_manager::info()` exposes the plugin metadata array used for self-identification.
- Development builds should use a `+dev` suffix such as `1.0.0+dev`.
- Release builds should use a clean tagged version such as `1.0.0`.

For a release bump:

1. Update `plugin_manager::PLUGIN_VERSION` in `plugin_manager.php` or run `sh scripts/bump-version.sh 1.0.0`.
2. Update `CHANGELOG.md`.
3. Create the matching release tag after verification.

Features
--------

[](#features)

- Discovers installed plugins
- Shows local version (from composer.json, explicit version constants/tags, or best-effort)
- Checks online (Packagist / GitHub releases, falls back to tags)
- Bold “Update available”
- Post-update advisory scan for upgrade/migration signals such as `migrations/`, `sql/`, `UPGRADE.md`, and composer metadata hints
- Optional allowlisted Plugin Manager-owned post-update hook files, disabled by default and scoped per plugin
- Manual admin-only `Run maintenance` action for plugins with an eligible allowlisted hook
- Per-plugin maintenance status badges and a recent maintenance activity panel
- One-click **(check now)** per row to bypass cache
- Diagnostics panel for connectivity
- `sources.map.php` to resolve outliers or mark plugins as **bundled**
- Scroll-friendly UI for large lists

Install
=======

[](#install)

A) Composer (recommended)
-------------------------

[](#a-composer-recommended)

1. In your Roundcube root (the folder with `composer.json`), run:

    ```
    composer require texxasrulez/plugin_manager
    ```

    This uses `roundcube/plugin-installer` to place the plugin in `plugins/plugin_manager`.
2. Enable the plugin in Roundcube config (e.g. `config/config.inc.php`):

    ```
    $config['plugins'][] = 'plugin_manager';
    ```
3. Clear caches.

B) Manual
---------

[](#b-manual)

1. Copy the `plugin_manager/` folder into `roundcube/plugins/`.
2. Enable in config: ```
    $config['plugins'][] = 'plugin_manager';
    ```
3. Clear caches.

Optional
--------

[](#optional)

- To raise GitHub API limits, set in `plugins/plugin_manager/config.inc.php` (or main config):

    ```
    $config['pm_github_token'] = 'ghp_xxxxx';
    ```
- To default remote checks on/off:

    ```
    $config['pm_remote_checks'] = true; // or false
    ```

Local version notes
-------------------

[](#local-version-notes)

On startup, Plugin Manager bootstraps `data/installed_versions.json` and `data/version.json` if they are missing.

For most modern plugins, the local installed version can be detected immediately from local plugin files such as `composer.json` or explicit PHP version constants.

Some older or legacy plugins do not publish a reliable local version inside the installed files. For those plugins, Plugin Manager may show a best-effort local value until the plugin is updated through the Plugin Manager UI or the upstream plugin adopts explicit local version metadata.

Post-update advisory and hooks
------------------------------

[](#post-update-advisory-and-hooks)

After a successful plugin update, Plugin Manager now performs a read-only inspection of the updated plugin directory. It looks for migration or upgrade signals such as:

- `sql/`, `db/`, `database/`, `migrations/`, `updates/`, `upgrade/`
- `UPGRADE`, `UPGRADING`, `INSTALL`, `CHANGELOG`, `README`, `UPGRADE.md`, `INSTALL.md`
- `composer.json` signals such as install/update-related `scripts`, `extra`, or `suggest` metadata

This inspection is advisory only. It does not run SQL, shell commands, composer scripts, or arbitrary plugin code.

If signals are found, Plugin Manager shows an advisory flash message after the normal update success message.

### Allowlisted post-update hooks

[](#allowlisted-post-update-hooks)

Plugin Manager can also run a strictly allowlisted internal PHP hook file after a successful install or update. This is deny-by-default: nothing runs unless you explicitly configure it in `pm_post_update_hooks`.

The executable maintenance code now lives inside `plugin_manager/webhooks/`, not inside the target plugin directory. That means:

- third-party plugins do not need to ship their own maintenance hook files
- administrators do not need to patch vendor plugins just to add maintenance support
- Plugin Manager remains the only codebase that owns runnable maintenance hook files
- target plugin directories are still inspected for advisory signals and used as data inputs, but not as hook sources

Important safety rules:

- only hooks listed by the administrator in config are considered
- only `type = 'internal'` is supported
- the configured hook path must resolve under `plugin_manager/webhooks//`
- plugin metadata does not authorize hook execution
- manual maintenance appears only for plugins with a valid allowlisted hook that explicitly supports manual execution
- hook preflight validation is reused for status badges, automatic runs, and manual runs
- no shell commands, SQL execution, or composer script execution are performed by Plugin Manager

#### Where the hook file lives

[](#where-the-hook-file-lives)

The hook file lives under a dedicated directory inside Plugin Manager:

```
plugin_manager/
└── webhooks/
    └── archive/
        └── post-update.php

```

The config path is relative to the per-plugin webhook folder, not to the Roundcube root and not to the target plugin directory. For the example above:

```
'path' => 'post-update.php',
```

Conceptually:

1. create `plugin_manager/webhooks//`
2. put a hook file in that folder
3. allowlist that file for the matching plugin in `pm_post_update_hooks`

Example config shape:

```
$config['pm_post_update_hooks'] = [
    'archive' => [
        'enabled' => true,
        'type' => 'internal',
        'path' => 'post-update.php',
        'label' => 'Run archive maintenance',
        'timeout' => 10,
        'args' => [],
        'run_on' => ['update', 'manual'],
        'allow_manual_run' => true,
        'require_confirmation' => true,
    ],
];
```

In that example:

- the plugin key is `archive`
- the executable code is `plugin_manager/webhooks/archive/post-update.php`
- Plugin Manager will only consider that hook file for the `archive` plugin
- no file inside `plugins/archive/` is used as executable hook code
- the plugin directory is still passed in the context array so the hook can inspect or update plugin-owned files when appropriate

#### Step-by-step setup

[](#step-by-step-setup)

1. Decide which plugin needs maintenance support.
2. Create a directory under `plugin_manager/webhooks/` that matches the plugin directory name.
3. Put a dedicated hook file in that folder, such as `post-update.php`.
4. Keep that hook file narrow and plugin-specific.
5. Add an allowlist entry under the plugin’s exact directory name in `pm_post_update_hooks`.
6. Set `path` to the file path relative to `plugin_manager/webhooks//`.
7. Decide which events may run it with `run_on`.
8. Set `require_confirmation` if you want automatic update-time execution suppressed until an admin explicitly runs it.
9. If you want manual execution available at all, include `manual` in `run_on` and leave `allow_manual_run` enabled.
10. Set `show_manual_link` to `true` only if you want the row-level UI action visible even when the hook can already auto-run.

Plugin Manager now ships a growing set of example webhook files under `webhooks/` for popular plugins. These examples are meant to save admins time and provide a safe starting point for common plugin maintenance reviews.

Example:

```
$config['pm_post_update_hooks'] = [
    'archive' => [
        'enabled' => true,
        'type' => 'internal',
        'path' => 'post-update.php',
        'label' => 'Run archive maintenance',
        'run_on' => ['update', 'manual'],
        'allow_manual_run' => true,
        'show_manual_link' => false,
        'require_confirmation' => true,
    ],
];
```

In that example:

- the plugin directory name is `archive`
- Plugin Manager will only consider hooks for `archive` if that exact key exists
- the executable maintenance code is `plugin_manager/webhooks/archive/post-update.php`
- the hook will not auto-run on install because `install` is not listed in `run_on`
- the hook will not auto-run on update because `require_confirmation` is `true`
- the admin can still use the row-level `Run maintenance` action because confirmation is required

Field notes:

- `enabled`: master switch for this plugin hook
- `type`: currently only `internal`
- `path`: relative path under `plugin_manager/webhooks//`
- `timeout`: parsed and logged, but not enforced in the current phase
- `run_on`: supported values are `install`, `update`, and `manual`
- `allow_manual_run`: if omitted it is treated as enabled, but the hook must still allow the `manual` event
- `show_manual_link`: if `true`, show the row-level manual action even when the hook could auto-run; default behavior keeps it hidden unless confirmation is required
- `require_confirmation`: if `true`, the hook is not auto-run after update; the manual action satisfies that confirmation requirement

#### What Plugin Manager validates before it will run a hook

[](#what-plugin-manager-validates-before-it-will-run-a-hook)

For every automatic or manual maintenance attempt, Plugin Manager performs a preflight check. The hook is refused unless all of the following are true:

- the current user is allowed to perform update/maintenance actions
- the request token is valid
- the plugin name is syntactically valid
- the plugin has an exact allowlist entry in `pm_post_update_hooks`
- the hook config is enabled
- `type` is exactly `internal`
- the current event is allowed by `run_on`
- manual runs are allowed when the event is `manual`
- the configured `path` is syntactically safe
- the resolved hook file exists under `plugin_manager/webhooks//`
- the hook file is readable, is a regular file, is not a symlink, and remains inside that webhook directory

If any of those checks fail:

- the hook is not run
- the plugin may show a `Blocked` maintenance badge
- the admin gets a warning when a run was attempted
- the refusal is recorded in the maintenance audit trail
- update ZIP archives are also sanity-checked before extraction so obviously unsafe archive paths are rejected early

#### How to write an internal hook file

[](#how-to-write-an-internal-hook-file)

An internal hook file should be a narrow PHP file inside `plugin_manager/webhooks//`. It should return a callable that accepts a plain context array and returns a normalized result array. Keep the file explicit and easy to review.

Recommended practices for hook files:

- keep each hook file dedicated to one plugin or one narrow maintenance task
- use the provided `$context` array rather than inferring event state indirectly
- return concise admin-facing messages
- treat missing prerequisites as a clean failure and report them in `message` or `details`
- keep plugin-specific filesystem writes scoped to the selected plugin directory unless there is a strong reason not to

Avoid in hook files:

- printing output directly
- calling `exit` or `die`
- returning scalars or non-array data
- assuming Plugin Manager will execute shell commands for you
- assuming Plugin Manager will run Composer for you
- assuming Plugin Manager will execute SQL files for you automatically

### Maintenance status and preflight

[](#maintenance-status-and-preflight)

Each plugin row can show a compact maintenance badge:

- `Available`: a valid allowlisted manual maintenance hook is ready to run
- `Blocked`: a hook is configured, but preflight validation found a problem
- `Ran recently`: a maintenance run for that plugin was recently recorded in the audit log

Preflight validation checks:

- exact plugin allowlist match
- hook config exists and is enabled
- supported hook type
- `run_on` allows the current event
- `allow_manual_run` allows manual execution
- hook path is syntactically valid
- hook file is present under `plugin_manager/webhooks//`
- hook file remains inside that directory after resolution
- hook file is readable, is a regular file, and is not a symlink

Blocked hooks stay blocked until the underlying configuration or file problem is fixed.

If `require_confirmation` is `true`, Plugin Manager will show a message that the hook is available but was not run automatically.

### Manual Run maintenance action

[](#manual-run-maintenance-action)

If a plugin has an eligible allowlisted hook with `run_on` containing `manual`, Plugin Manager can show a row-level `Run maintenance` action for administrators.

By default, the row link is shown only when manual confirmation is required. If you want the manual link visible for an otherwise auto-running hook, set `show_manual_link = true` in that hook config.

This action:

- is admin-only
- requires the normal request token
- asks for confirmation before running
- uses the same allowlisted internal hook-file validation and execution path as automatic post-update hooks
- returns to the main Plugin Manager page with normal flash messages

It is not a generic script runner. Plugin Manager still does not execute shell commands, composer scripts, or arbitrary plugin-discovered code.

### Maintenance audit trail

[](#maintenance-audit-trail)

Plugin Manager keeps a lightweight bounded audit trail of recent maintenance activity in its local data directory. Entries include:

- timestamp
- plugin
- event (`install`, `update`, or `manual`)
- result (`success`, `warning`, `failure`, `skipped`, or `refused`)
- short message

The Plugin Manager page shows a small read-only `Recent maintenance activity` panel for administrators. It appears below the plugin list and can be hidden entirely with the `Hide recent maintenance activity` checkbox. That visibility preference is stored in the browser so admins can keep it collapsed if they prefer. By default, the audit trail is enabled and retains up to 100 recent entries.

### Internal hook file contract

[](#internal-hook-file-contract)

An internal maintenance hook file is a PHP file owned by `plugin_manager`. Plugin Manager loads it from `plugin_manager/webhooks//...`, expects the file to return a callable, and then calls that callable with the maintenance context.

Example file:

```
