# desktop-mode/1.1.6/includes/desktop-files/class-openstation-file.php

OpenStation: Desktop Windows, Dock &amp; Virtual Desktops for WP Admin, version 1.1.6. 152 lines.

- Page: https://pluginprobe.com/plugins/desktop-mode/1.1.6/code/includes/desktop-files/class-openstation-file.php
- Raw: https://pluginprobe.com/plugins/desktop-mode/1.1.6/raw/includes/desktop-files/class-openstation-file.php
- Modified: 2026-08-28T08:28:50+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/desktop-mode/1.1.6/code/includes/desktop-files/class-openstation-file.php#L10-L20`.

```php
<?php
/**
 * OpenStation — `OpenStation_File` abstract base class.
 *
 * Every "file" that a user can place on the OpenStation wallpaper
 * (a post, a user, an attachment, a term, a comment, a folder, a
 * bookmark — or anything a third-party plugin teaches the system
 * about) is represented by a subclass of this base. The subclass
 * adapts a WordPress entity (or any opaque reference string) to the
 * shape the desktop UI expects: a title, an icon, a preview, and a
 * capability gate.
 *
 * Files do NOT know how to open themselves. Opening is a separate
 * concern delegated to the file-opener registry (Phase 1) so the
 * same `post` file can be opened by Gutenberg, the Classic Editor,
 * or any plugin's editor of choice — the user's per-type
 * association decides which.
 *
 * @package OpenStation
 */

defined( 'ABSPATH' ) || exit;

/**
 * Adapts a WordPress entity to the desktop "file" surface.
 *
 * Subclass contract: implement {@see ::title()} and the static
 * {@see ::type()} slug; override {@see ::icon()}, {@see ::preview_url()},
 * {@see ::can_read()}, or {@see ::serialize()} when the defaults
 * don't fit.
 */
abstract class OpenStation_File {

	/**
	 * Opaque reference identifying the underlying entity.
	 *
	 * For most types this is a numeric id (post id, user id,
	 * attachment id, term id, comment id). For `bookmark` it's the
	 * URL itself. For `folder` it's the row id of the folder.
	 *
	 * Stored as a string because the placements table stores it as
	 * a varchar — type coercion happens in the subclass.
	 *
	 * @var string
	 */
	protected $ref = '';

	/**
	 * @param string|int $ref Entity reference.
	 */
	public function __construct( $ref = '' ) {
		$this->ref = (string) $ref;
	}

	/**
	 * The file-type slug, e.g. `'post'`, `'user'`, `'folder'`.
	 *
	 * @return string
	 */
	abstract public static function type(): string;

	/**
	 * Human-readable title displayed under the tile.
	 *
	 * @return string
	 */
	abstract public function title(): string;

	/**
	 * Reference accessor — read-only on purpose.
	 *
	 * @return string
	 */
	public function ref(): string {
		return $this->ref;
	}

	/**
	 * Dashicon class (or `data:` URI) rendered on the tile. Default
	 * is the generic media glyph; subclasses should override.
	 *
	 * @return string
	 */
	public function icon(): string {
		return 'dashicons-media-default';
	}

	/**
	 * Optional preview-image URL (e.g. featured image, avatar,
	 * attachment thumbnail). Empty string when the tile should
	 * render the icon instead.
	 *
	 * @return string
	 */
	public function preview_url(): string {
		return '';
	}

	/**
	 * Whether `$user_id` can see / open this file. Defaults to
	 * `true`; subclasses tighten the gate. For shared folders this
	 * is consulted PER PLACEMENT at snapshot time so a folder
	 * shared with role `editor` cannot expose individual files the
	 * viewer lacks the cap to read.
	 *
	 * @param int $user_id Viewer.
	 * @return bool
	 */
	public function can_read( int $user_id ): bool {
		return true;
	}

	/**
	 * Whether the underlying entity still exists. The renderer uses
	 * this to flag dead references with a placeholder tile rather
	 * than rendering nothing (so the user can right-click → remove).
	 *
	 * @return bool
	 */
	public function exists(): bool {
		return '' !== $this->ref;
	}

	/**
	 * Shape sent to JS. Subclasses extend by overriding and
	 * `array_merge`'ing on top of `parent::serialize()`.
	 *
	 * @return array
	 */
	public function serialize(): array {
		$shape = array(
			'type'       => static::type(),
			'ref'        => $this->ref,
			'title'      => openstation_plain_text_title( $this->title() ),
			'icon'       => $this->icon(),
			'previewUrl' => $this->preview_url(),
			'exists'     => $this->exists(),
		);

		/**
		 * Filters the serialized shape of a desktop file before it
		 * crosses the wire. Last-mile mutation point — plugins use
		 * this to attach badges, override labels, or splice in
		 * custom render hints without subclassing.
		 *
		 * @param array             $shape The serialized file shape.
		 * @param OpenStation_File $file  The file being serialized.
		 */
		return apply_filters( 'openstation_file_serialize', $shape, $this );
	}
}

```
