Skip to content

Commit

Permalink
Merge pull request #1 from cbratschi/master
Browse files Browse the repository at this point in the history
Private Media 1.3 Version
  • Loading branch information
froger-me authored Nov 28, 2024
2 parents 7e4f89c + 79f33f2 commit 9371915
Show file tree
Hide file tree
Showing 30 changed files with 12,330 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module.exports = {
env: {
browser: true,
es6: true,
node: true
},
globals: {
window: true,
document: true,
$: true,
jQuery: true
},
extends: [
'eslint:recommended'
],
'parserOptions': {
ecmaVersion: 2022,
sourceType: 'module',
requireConfigFile: false
},
'rules': {
'indent': [
'error',
4,
{
'SwitchCase': 1
}
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single',
{
'allowTemplateLiterals': true
}
],
'semi': [
'error',
'always'
],
'no-console': 'off',
'no-unused-vars': [ 'error', { 'argsIgnorePattern': '^_' }]
}
};
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"remove-tabs-on-save.ignoreFileExtensions": [
"first-time",
"*.go",
"*"
],
"githubPullRequests.ignoredPullRequestBranches": [
"master"
]
}
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Private Media #

**Contributors:** [frogerme](https://profiles.wordpress.org/frogerme/), [cbratschi](https://profiles.wordpress.org/cbratschi/)
**Tags:** media, uploads, private
**Requires at least:** 4.9.8
**Tested up to:** 6.4.3
**Requires PHP:** 8.0
**Stable tag:** trunk
**License:** GPLv2 or later
**License URI:** http://www.gnu.org/licenses/gpl-2.0.html

Add access restrictions to specific items of the WordPress Media Library.

## Description ##

Ever wanted to make your media truely private? Make sure images, videos and other files are only accessible to chosen roles, or cannot be hotlinked, with permissions specifically set per item.

This plugin adds the following major features to WordPress:

* **Media Privacy:** Lock access to items in the Media Library by preventing hotlinks only or by limiting access to files to selected user roles.
* **User-friendly forbidden handler:** Images set to private do not break on the frontend. Instead, they are replaced by a simple access denied SVG picture - the forbidden handler can be replaced using the filter hooks `pvtmed_forbidden_response_content` (`apply_filters( 'pvtmed_forbidden_response_content', $forbidden_response_content, $file );`) and `pvtmed_forbidden_mimetype` (`apply_filters( 'pvtmed_forbidden_mimetype', 'image/svg+xml' );`).
* **Customizable for more granularity:** Restricted media will be checked for autorization - plugin developers can hook into the `pvtmed_is_authorized` filter (`apply_filters( 'pvtmed_is_authorized', $authorized, $attachment_id );`) to apply more complex conditions for authorization.
* **Optimized private media delivery:** Files with access restriction are served using streams without loading the file entirely in memory before delivery, and WordPress files are loaded as lightly as possible for an optimised server memory usage.
* **Fallbacks:** Restricted files are kept in an alternate `wp-content/pvtmed-uploads` folder (or equivalent if `WP_CONTENT_DIR` is not the default) ; fallbacks are in place to make sure:
* moving a media to private does not break previously embedded media (javascript dynamic fallback with notice on post edit screen - Classic Editor only).
* deactivating the plugin does not break previously embedded media (database update).
* deleting the plugin does not break previously embedded media (database update).

A [Must Use Plugin](https://codex.wordpress.org/Must_Use_Plugins) `pvtmed-endpoint-optimizer.php` is installed automatically to make sure WordPress is loaded as lightly as possible when requesting restricted media items. Developers can safely edit it to enable their plugin to execute during such request if necessary.

The media privacy policy is set per media item - therefore, this plugin is not a replacement for general image hotlink prevention plugins, but is ideal for anyone looking for preventing direct links to files depending on specific conditions.

## Installation ##

This section describes how to install the plugin and get it working.

1. Upload the plugin files to the `/wp-content/plugins/private-media` directory, or install the plugin through the WordPress plugins screen directly.
2. Activate the plugin through the 'Plugins' screen in WordPress
3. Edit Media Privacy Settings on media items in the Media Library

## Changelog ##

### 1.11 ###

* Fixed _load_textdomain_just_in_time warning.

### 1.10 ###

* PDF preview image workaround.

### 1.9 ###

* Removes all attachment files on delete.

### 1.8 ###

* fixed manage_media_columns optional parameter

### 1.7 ###

* Duplicator Pro: reset .htaccess file on cloned site

### 1.6 ###

* Added pvtmed_is_attachment_authorized filter: check if active user is authorized.

### 1.5 ###

* Fix: added readme.txt back again

### 1.4 ###

* Added pvtmed_load_frontend filter: opt-out of loading main.js on all pages.
* Added pvtmed_hotlink_feature filter: opt-out of hotlinking feature.
* Added pvtmed_has_permissions filter: implement own permission system.
* Added pvtmed_add_permissions filter: add custom permissions.
* Added pvtmed_edit_settings filter: customize media settings.
* Added pvtmed_edit_roles filter: modify displayed roles in media edit screen.
* Added "Always Private" checkbox.
* Keep files private even without any permissions.
* Can be used for instance by filter passed permissions.
* Added lock icon to media grid view for protected files.
* Added Private Media column to media list view.
* New APIs:
* $private = apply_filters('pvtmed_is_attachment_private', null, $attachmend_id);
* $permissions = apply_filters('pvtmed_get_attachment_permissions', null, $attachmend_id);
* do_action('pvtmed_set_attachment_permissions', $attachment_id, [ 'admin' => '1' ]);
* Make private with admin access.
* do_action('pvtmed_set_attachment_permissions', $attachment_id);
* Make public.
* URL decode file query parameter.
* Support PDF preview images.

### 1.3 ###

* fixed crash in status_header() call
* added build system
* check if TinyMCE is available

### 1.2 ###

* Add `pvtmed_private_upload_url` and `pvtmed_htaccess_rules` filters (see [this support request for details]())

### 1.1 ###

* Bump version - supported by WordPress 5.0 (post edit screen fallbacks only in Classic Editor)

### 1.0 ###

* First version
98 changes: 98 additions & 0 deletions assets/css/admin/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#pvtmed_save_roles {
float: left;
}

.compat-field-pvtmed th label span {
position: relative;
font-weight: 700;
color: #666;
margin: 24px 0 8px;
}

.compat-item .compat-field-pvtmed .label {
float: left;
min-width: auto;
}

.compat-field-pvtmed .setting span,
.compat-field-pvtmed .field {
float: none;
}

.compat-field-pvtmed .setting input[type=checkbox] {
float:right;
}

.compat-field-pvtmed .setting input[type=checkbox] {
margin-top: 1px;
}

.compat-field-pvtmed .field {
float: none;
width: 100%;
}

@-webkit-keyframes pvtmed-highlight {
from {
border-color: #7c7c7c;
}
to {
border-color: #ddd;
}
}
@-moz-keyframes pvtmed-highlight {
from {
border-color: #7c7c7c;
}
to {
border-color: #ddd;
}
}
@-o-keyframes pvtmed-highlight {
from {
border-color: #7c7c7c;
}
to {
border-color: #ddd;
}
}
@keyframes pvtmed-highlight {
from {
border-color: #7c7c7c;
}
to {
border-color: #ddd;
}
}

.pvtmed-highlight {
-webkit-animation: pvtmed-highlight 1s infinite;
-moz-animation: pvtmed-highlight 1s infinite;
-o-animation: pvtmed-highlight 1s infinite;
animation: pvtmed-highlight 1s infinite;
}

/* media grid icon */

.pvtmed-attachment .thumbnail .centered {
position: relative !important;
transform: none !important;

display: flex;
justify-content: center;
align-items: center;
}

.pvtmed-attachment .thumbnail .centered img {
display: block;
transform: none !important;
position: static !important;
}

.pvtmed-attachment .thumbnail .centered span {
position: absolute;
width: 10px;
height: 10px;
bottom: 15px;
left: 3px;
}
1 change: 1 addition & 0 deletions assets/css/admin/main.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions assets/data/htaccess.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## BEGIN Private Media DO NOT EDIT ##
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^(.*)$ [SITE_URL]/pvtmed/$1 [QSA,L]
</IfModule>
## END Private Media ##
89 changes: 89 additions & 0 deletions assets/data/pvtmed-endpoint-optimizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* Run as little as possible of the WordPress core with PRivate Media actions and filters.
* Effect:
* - keep only a selection of plugins (@see $pvtmed_always_active_plugins below)
* - prevent inclusion of themes functions.php (parent and child)
* - remove all core actions and filters that haven't been fired yet
*
* Place this file in a wp-content/mu-plugin folder (after editing if needed) and it will be loaded automatically.
* Use the @see global $pvtmed_doing_private_media_api_request in the plugins you kept active for optimization purposes.
*/

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}

global $pvtmed_doing_private_media_api_request, $pvtmed_always_active_plugins;

if ( ! $pvtmed_always_active_plugins ) {
$pvtmed_always_active_plugins = [
// Edit with your plugin IDs here to keep them active during media access.
// 'my-plugin-slug/my-plugin-file.php',
// 'my-other-plugin-slug/my-other-plugin-file.php',
'private-media/private-media.php'
];
}

$pvtmed_doing_private_media_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], 'pvtmed-uploads' ) );

if ( true === $pvtmed_doing_private_media_api_request ) {
//remove filter hooks
$hooks = [
'registered_taxonomy',
'wp_register_sidebar_widget',
'registered_post_type',
'widgets_init',
'wp_default_scripts',
'option_siteurl',
'option_home',
'option_active_plugins',
'query',
'option_blog_charset',
'plugins_loaded',
'sanitize_comment_cookies',
'template_directory',
'stylesheet_directory',
'set_current_user',
'user_has_cap',
'init',
'option_category_base',
'option_tag_base',
'heartbeat_settings',
'locale',
'wp_loaded',
'query_vars',
'request',
'parse_request',
'shutdown',
];

foreach ( $hooks as $hook ) {
remove_all_filters( $hook );
}

add_filter( 'option_active_plugins', 'pvtmed_unset_plugins', 99, 1 );
add_filter( 'template_directory', 'pvtmed_bypass_themes_functions', 99, 3 );
add_filter( 'stylesheet_directory', 'pvtmed_bypass_themes_functions', 99, 3 );
add_filter( 'enable_loading_advanced_cache_dropin', 'pvtmed_bypass_cache', 99, 1 );
}

function pvtmed_unset_plugins( $plugins ) {
global $pvtmed_always_active_plugins;

foreach ( $plugins as $key => $plugin ) {
if ( ! in_array( $plugin, $pvtmed_always_active_plugins, true ) ) {
unset( $plugins[ $key ] );
}
}

return $plugins;
}

function pvtmed_bypass_cache( $is_cache ) {
return false;
}

function pvtmed_bypass_themes_functions( $template_dir, $template, $theme_root ) {
return dirname( __FILE__ );
}
Binary file added assets/images/forbidden.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/images/forbidden.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 9371915

Please sign in to comment.