Software Directory
Developed by Simone Fioravanti
Plugin Description
Software Directory is a ClassicPress plugin for building directories of plugins and themes. It provides software entries, category-like kinds, frontend templates, GitHub release downloads, and WP-CLI commands for importing repository READMEs.
- Public custom post type
xsxsd, shown as Software in the administration area, with the permalink basesoftware. - Support for titles, full content, authors, excerpts, custom fields, and featured images.
- Hierarchical taxonomy
xsxsd-kind, shown as Kinds, with the rewrite basekind. - A Software Details meta box for the software slug and GitHub repository URL.
- Paginated kind archives using the main ClassicPress query, with responsive cards containing images, linked titles, excerpts, visible fields, and Download links.
- Responsive single pages with full content, featured images, and a sidebar containing downloads, visible fields, and linked kinds.
- Local download URLs redirecting to ZIP assets from the latest GitHub release.
- Software entries and kind terms available to ClassicPress navigation menus; kinds explicitly enable
show_in_nav_menus. - WP-CLI commands to insert entries and refresh their content from GitHub.
- ClassicPress 2.0 or later.
- PHP 8.2 or later, including the PHP runtime used by WP-CLI.
- Outbound HTTPS access to
api.github.comfor release lookups and README imports. - WP-CLI for the optional CLI commands.
- PHP DOM/libxml support (
DOMDocument) for CLI imports and updates, used to remove images from converted HTML. - Working permalink rewriting for the documented frontend paths and download endpoint.
No bundled Markdown library or Composer installation is required. CLI imports reuse an available CommonMark or Parsedown class, otherwise they render Markdown through GitHub’s API.
- Copy the plugin into
wp-content/plugins/software-directory/. - Activate Software Directory in the ClassicPress Plugins screen.
- Open Settings → Permalinks and save the settings to regenerate rewrite rules. Repeat this after installing changes to the plugin’s URL rules; the plugin does not flush rewrite rules automatically.
- Create kinds and software entries under Software.
Create terms such as plugin and theme in Software → Kinds. These terms are not created automatically.
For each software entry, set a title, content, optional excerpt and featured image, and assign one or more kinds. Publish the entry to make its download endpoint eligible for use. Featured image controls also depend on the active theme’s thumbnail support.
The Software Details meta box stores these post meta keys:
| Key | Label | Purpose | Displayed by default |
|---|---|---|---|
slug |
slug | Optional software identifier, sanitized with sanitize_key
|
No |
git-repo |
GitHub repo | Repository URL, sanitized with sanitize_url
|
Yes |
Use a repository URL such as https://github.com/xxsimoxx/githeme. The slug custom field is separate from the post’s permalink slug and is not used to build download URLs.
The meta box save handler requires a valid nonce and the update_plugins capability. Frontend templates omit fields whose display setting is disabled, whose values are missing or non-scalar, or whose URL values fail URL escaping. URL fields use their descriptive labels as link text when available.
Use Appearance → Menus to add software entries and kind archives to an existing theme menu. The plugin does not create a menu or register a menu location.
To authenticate GitHub REST API requests and use the available authenticated rate limits, define an optional token in wp-config.php, before ClassicPress is loaded:
define('GITHUB_API_TOKEN', 'your-github-token');
Keep the token secure: do not commit it to version control or expose it in public files. A token is not required for ordinary plugin operation. Undefined, empty, non-string, or malformed token values leave requests unauthenticated.
The same header preparation is used for release lookups, CLI repository validation, README retrieval, and the GitHub Markdown rendering fallback. A valid token is sent only as Authorization: Bearer <token> to the GitHub REST API; it is not added to URLs or asset redirects. Existing request headers and the native HTTP client’s User-Agent are preserved. Token configuration does not invalidate cached release results or change the CLI’s public-repository validation.
The plugin provides:
| File | Purpose |
|---|---|
templates/taxonomy-xsxsd-kind.php |
Kind archive, term heading and description, main loop, cards, and pagination |
templates/single-xsxsd.php |
Single software page, full content, and optional metadata sidebar |
templates/custom-fields.php |
Shared custom field rendering |
templates/download.php |
Shared repository validation and Download link markup |
Both page templates call the theme’s header and footer. Archive thumbnails request medium_large; single thumbnails request large, using native thumbnail rendering and its responsive attributes.
For kind archives, the plugin searches the active child/parent theme using locate_template() in this order:
-
taxonomy-xsxsd-kind-{term-slug}.php. -
taxonomy-xsxsd-kind.php. -
taxonomy.php. - The plugin’s
templates/taxonomy-xsxsd-kind.phpfallback.
For single software pages, the order is:
- A valid custom template explicitly assigned to the post, if present in the theme.
-
single-xsxsd-{post-slug}.php. -
single-xsxsd.php. - The plugin’s
templates/single-xsxsd.phpfallback.
For both slug-specific lookups, a URL-decoded slug is tried before the stored slug when they differ. Generic theme templates such as single.php, singular.php, archive.php, and index.php do not replace these plugin fallbacks. The shared partials are included directly from the plugin and do not have a separate theme lookup.
software_directory_show_single_thumbnail receives the default boolean true and the current post ID. The plugin renders its single-page featured image only if the filter returns boolean true and the post has a thumbnail.
For a theme that already displays the image, add this to the theme or child theme:
add_filter('software_directory_show_single_thumbnail', function ($show, $post_id) {
return false;
}, 10, 2);
This affects only the image generated by the plugin’s single template; archive images and images rendered by the theme are unaffected.
Published software entries have a local endpoint:
/software/{post-slug}/latest.zip
/software/githeme/latest.zip
The endpoint reads git-repo and accepts HTTPS repository URLs on github.com with an owner and repository name. A trailing slash and a .git suffix are accepted. Additional path segments, credentials, query strings, and fragments are not accepted.
On a cache miss, it calls GET https://api.github.com/repos/{owner}/{repo}/releases/latest, using GITHUB_API_TOKEN when configured with a valid value, or without authentication otherwise. It uses uploaded release assets ending in .zip, not GitHub’s automatically generated source archives. Drafts and prereleases are rejected.
ZIP selection follows these rules:
- Prefer one asset whose name matches
{repository-name}.zip, case-insensitively. - Otherwise, accept a ZIP only when exactly one exists.
- Reject ambiguous selections rather than choosing an arbitrary asset.
The asset’s browser_download_url must be an HTTPS GitHub release download URL under the expected owner and repository. The endpoint issues an HTTP 302 redirect; ClassicPress does not download, proxy, or serve the ZIP itself.
Results, including errors, are stored in a repository-specific transient for 120 minutes. Missing software, invalid repositories, and absent releases or ZIPs return errors; ambiguous ZIP choices return HTTP 409, while upstream failures generally return HTTP 502.
Download buttons appear when git-repo passes local URL validation. Rendering a button does not call GitHub or guarantee that a downloadable release exists. On password-protected single pages, the metadata sidebar is withheld until the password requirement is satisfied.
Commands are registered only when WP-CLI is available. Run them against a ClassicPress installation where Software Directory is active.
wp software-directory insert <kind-slug> <repository-url>
wp software-directory insert theme https://github.com/xxsimoxx/githeme
The command:
- Requires an existing
xsxsd-kindterm with the supplied slug; it does not create terms. - Validates that the GitHub repository is public and retrieves the root
README.mdfrom its default branch. - Converts Markdown to HTML, sanitizes it, removes images, and retains image alternative text where present. Normal links are preserved.
- Creates a published
xsxsdpost with a title and permalink slug derived from the repository name. - Assigns the requested kind and saves the canonical repository URL in
git-repo. - Rejects repositories already associated with a software entry, including entries in other statuses or the trash. Comparison normalizes case, HTTP/HTTPS, a trailing slash, and a
.gitsuffix. - Reports the created post ID and permalink through
WP_CLI::success().
CLI repository URLs may use HTTP or HTTPS; stored canonical URLs use HTTPS and lowercase owner/repository names. Imports do not populate the separate slug custom field, excerpt, or featured image.
wp software-directory update <post-id>
wp software-directory update 693
The ID must identify an existing xsxsd post. The command reads its git-repo value and uses the same repository validation, README retrieval, Markdown conversion, sanitization, and image removal as insert.
Update replaces the post’s entire content with HTML generated from the remote README. Local content edits are overwritten. It submits only post_content for updating and preserves the title, slug, excerpt, status, taxonomy assignments, custom fields, and featured image.
If the generated content is identical to the stored content, no update is performed. A missing README, HTTP failure, or conversion failure prevents the update. Both changed and unchanged outcomes report the post ID through WP_CLI::success(); errors use WP-CLI error messages.
Frontend classes use the software-directory- prefix. Common selectors include .software-directory-archive, .software-directory-single, .software-directory-card, .software-directory-fields, and .software-directory-download.
| Stylesheet | Handle | Loaded on |
|---|---|---|
assets/css/archive.css |
software-directory-archive |
xsxsd-kind archives only |
assets/css/single.css |
software-directory-single |
Single xsxsd pages only |
assets/css/download.css |
software-directory-download |
Both contexts, as a shared dependency |
Archive cards use one, two, or three columns according to viewport width. Single pages use a wider content column and a sidebar on desktop, and one column on smaller screens. Styles inherit theme fonts and colors and provide visible keyboard focus indicators.
Add scoped overrides in a child theme stylesheet. Asset loading depends on the queried archive or post type, so it also applies when the theme supplies a template override.
xxsimoxxSoftwareDirectorySoftwareDirectory::get_custom_fields() exposes field definitions through the filter xsxsd-details. This spelling is the implemented hook name. Definitions include name, display, render, sanitize, and escape. For example, to display the existing slug field:
add_filter('xsxsd-details', function ($fields) {
$fields['slug']['display'] = true;
return $fields;
});
The field editor, saving logic, and frontend partial consume these definitions; release downloads and CLI commands specifically use git-repo.
- The plugin is intended for public GitHub repositories; CLI validation rejects private repositories even when a token is configured. Other Git hosts are not supported.
- GitHub API rate limits and network availability can prevent downloads or README imports. API redirects are not followed; use the repository’s current URL.
- Download links require a suitable uploaded ZIP asset on the latest release. There is no fallback to source archives, tags, or prereleases.
- CLI imports require the exact root filename
README.mdon the default branch and a base64 file response from the Contents API. Unsupported responses are rejected. - Markdown output depends on the available renderer: CommonMark, then Parsedown, then GitHub’s Markdown API. No parser is bundled.
- README content is sanitized and imported without images; images are not downloaded or assigned as featured images.
- There is no automatic README synchronization. Run
updateexplicitly to refresh an entry. - The plugin supplies kind archives, but does not register a general software post-type archive.