From 942b78e38d3be8aa86bcc21126469417bca9fdee Mon Sep 17 00:00:00 2001
From: mx5kevin <47847121+mx5kevin@users.noreply.github.com>
Date: Fri, 15 May 2026 04:30:31 +0000
Subject: [PATCH 1/6] Delete plugins/ContentFilter directory
Removed ContentFilter plugin. See issues: https://github.com/EpixZone/EpixNet/issues/17 and https://github.com/EpixZone/EpixNet/issues/10
---
plugins/ContentFilter/ContentFilterPlugin.py | 281 ------------------
plugins/ContentFilter/ContentFilterStorage.py | 165 ----------
.../ContentFilter/Test/TestContentFilter.py | 82 -----
plugins/ContentFilter/Test/conftest.py | 1 -
plugins/ContentFilter/Test/pytest.ini | 5 -
plugins/ContentFilter/__init__.py | 1 -
plugins/ContentFilter/languages/hu.json | 6 -
plugins/ContentFilter/languages/it.json | 6 -
plugins/ContentFilter/languages/jp.json | 6 -
plugins/ContentFilter/languages/pt-br.json | 6 -
plugins/ContentFilter/languages/zh-tw.json | 6 -
plugins/ContentFilter/languages/zh.json | 6 -
plugins/ContentFilter/media/blocklisted.html | 89 ------
plugins/ContentFilter/media/js/EpixFrame.js | 119 --------
plugins/ContentFilter/plugin_info.json | 5 -
15 files changed, 784 deletions(-)
delete mode 100644 plugins/ContentFilter/ContentFilterPlugin.py
delete mode 100644 plugins/ContentFilter/ContentFilterStorage.py
delete mode 100644 plugins/ContentFilter/Test/TestContentFilter.py
delete mode 100644 plugins/ContentFilter/Test/conftest.py
delete mode 100644 plugins/ContentFilter/Test/pytest.ini
delete mode 100644 plugins/ContentFilter/__init__.py
delete mode 100644 plugins/ContentFilter/languages/hu.json
delete mode 100644 plugins/ContentFilter/languages/it.json
delete mode 100644 plugins/ContentFilter/languages/jp.json
delete mode 100644 plugins/ContentFilter/languages/pt-br.json
delete mode 100644 plugins/ContentFilter/languages/zh-tw.json
delete mode 100644 plugins/ContentFilter/languages/zh.json
delete mode 100644 plugins/ContentFilter/media/blocklisted.html
delete mode 100644 plugins/ContentFilter/media/js/EpixFrame.js
delete mode 100644 plugins/ContentFilter/plugin_info.json
diff --git a/plugins/ContentFilter/ContentFilterPlugin.py b/plugins/ContentFilter/ContentFilterPlugin.py
deleted file mode 100644
index 29605414e..000000000
--- a/plugins/ContentFilter/ContentFilterPlugin.py
+++ /dev/null
@@ -1,281 +0,0 @@
-import time
-import re
-import html
-import os
-
-from Plugin import PluginManager
-from Translate import Translate
-from Config import config
-from util.Flag import flag
-
-from .ContentFilterStorage import ContentFilterStorage
-
-
-plugin_dir = os.path.dirname(__file__)
-
-if "_" not in locals():
- _ = Translate(plugin_dir + "/languages/")
-
-
-@PluginManager.registerTo("SiteManager")
-class SiteManagerPlugin(object):
- def load(self, *args, **kwargs):
- global filter_storage
- super(SiteManagerPlugin, self).load(*args, **kwargs)
- filter_storage = ContentFilterStorage(site_manager=self)
-
- def add(self, address, *args, **kwargs):
- should_ignore_block = kwargs.get("ignore_block") or kwargs.get("settings")
- if should_ignore_block:
- block_details = None
- elif filter_storage.isSiteblocked(address):
- block_details = filter_storage.getSiteblockDetails(address)
- else:
- address_hashed = filter_storage.getSiteAddressHashed(address)
- if filter_storage.isSiteblocked(address_hashed):
- block_details = filter_storage.getSiteblockDetails(address_hashed)
- else:
- block_details = None
-
- if block_details:
- raise Exception(f'Site blocked: {html.escape(block_details.get("reason", "unknown reason"))}')
- else:
- return super(SiteManagerPlugin, self).add(address, *args, **kwargs)
-
-
-@PluginManager.registerTo("UiWebsocket")
-class UiWebsocketPlugin(object):
- # Mute
- def cbMuteAdd(self, to, auth_address, cert_user_id, reason):
- filter_storage.file_content["mutes"][auth_address] = {
- "cert_user_id": cert_user_id, "reason": reason, "source": self.site.address, "date_added": time.time()
- }
- filter_storage.save()
- filter_storage.changeDbs(auth_address, "remove")
- self.response(to, "ok")
-
- @flag.no_multiuser
- def actionMuteAdd(self, to, auth_address, cert_user_id, reason):
- self.cmd(
- "prompt",
- [_["Remove all content from %s?"] % html.escape(cert_user_id), reason, _["Mute"]],
- lambda res: self.cbMuteAdd(to, auth_address, cert_user_id, res if res else reason)
- )
-
- @flag.no_multiuser
- def cbMuteRemove(self, to, auth_address):
- del filter_storage.file_content["mutes"][auth_address]
- filter_storage.save()
- filter_storage.changeDbs(auth_address, "load")
- self.response(to, "ok")
-
- @flag.no_multiuser
- def actionMuteRemove(self, to, auth_address):
- if "ADMIN" in self.getPermissions(to):
- self.cbMuteRemove(to, auth_address)
- else:
- cert_user_id = html.escape(filter_storage.file_content["mutes"][auth_address]["cert_user_id"])
- self.cmd(
- "confirm",
- [_["Unmute %s?"] % cert_user_id, _["Unmute"]],
- lambda res: self.cbMuteRemove(to, auth_address)
- )
-
- @flag.admin
- def actionMuteList(self, to):
- self.response(to, filter_storage.file_content["mutes"])
-
- # Siteblock
- @flag.no_multiuser
- @flag.admin
- def actionSiteblockIgnoreAddSite(self, to, site_address):
- if site_address in filter_storage.site_manager.sites:
- return {"error": "Site already added"}
- else:
- if filter_storage.site_manager.need(site_address, ignore_block=True):
- return "ok"
- else:
- return {"error": "Invalid address"}
-
- @flag.no_multiuser
- @flag.admin
- def actionSiteblockAdd(self, to, site_address, reason=None):
- filter_storage.file_content["siteblocks"][site_address] = {"date_added": time.time(), "reason": reason}
- filter_storage.save()
- self.response(to, "ok")
-
- @flag.no_multiuser
- @flag.admin
- def actionSiteblockRemove(self, to, site_address):
- del filter_storage.file_content["siteblocks"][site_address]
- filter_storage.save()
- self.response(to, "ok")
-
- @flag.admin
- def actionSiteblockList(self, to):
- self.response(to, filter_storage.file_content["siteblocks"])
-
- @flag.admin
- def actionSiteblockGet(self, to, site_address):
- if filter_storage.isSiteblocked(site_address):
- res = filter_storage.getSiteblockDetails(site_address)
- else:
- site_address_hashed = filter_storage.getSiteAddressHashed(site_address)
- if filter_storage.isSiteblocked(site_address_hashed):
- res = filter_storage.getSiteblockDetails(site_address_hashed)
- else:
- res = {"error": "Site block not found"}
- self.response(to, res)
-
- # Include
- @flag.no_multiuser
- def actionFilterIncludeAdd(self, to, inner_path, description=None, address=None):
- if address:
- if "ADMIN" not in self.getPermissions(to):
- return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
- site = self.server.sites[address]
- else:
- address = self.site.address
- site = self.site
-
- if "ADMIN" in self.getPermissions(to):
- self.cbFilterIncludeAdd(to, True, address, inner_path, description)
- else:
- content = site.storage.loadJson(inner_path)
- title = _["New shared global content filter: %s (%s sites, %s users)"] % (
- html.escape(inner_path), len(content.get("siteblocks", {})), len(content.get("mutes", {}))
- )
-
- self.cmd(
- "confirm",
- [title, "Add"],
- lambda res: self.cbFilterIncludeAdd(to, res, address, inner_path, description)
- )
-
- def cbFilterIncludeAdd(self, to, res, address, inner_path, description):
- if not res:
- self.response(to, res)
- return False
-
- filter_storage.includeAdd(address, inner_path, description)
- self.response(to, "ok")
-
- @flag.no_multiuser
- def actionFilterIncludeRemove(self, to, inner_path, address=None):
- if address:
- if "ADMIN" not in self.getPermissions(to):
- return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
- else:
- address = self.site.address
-
- key = "%s/%s" % (address, inner_path)
- if key not in filter_storage.file_content["includes"]:
- self.response(to, {"error": "Include not found"})
- filter_storage.includeRemove(address, inner_path)
- self.response(to, "ok")
-
- def actionFilterIncludeList(self, to, all_sites=False, filters=False):
- if all_sites and "ADMIN" not in self.getPermissions(to):
- return self.response(to, {"error": "Forbidden: Only ADMIN sites can list all sites includes"})
-
- back = []
- includes = filter_storage.file_content.get("includes", {}).values()
- for include in includes:
- if not all_sites and include["address"] != self.site.address:
- continue
- if filters:
- include = dict(include) # Don't modify original file_content
- include_site = filter_storage.site_manager.get(include["address"])
- if not include_site:
- continue
- content = include_site.storage.loadJson(include["inner_path"])
- include["mutes"] = content.get("mutes", {})
- include["siteblocks"] = content.get("siteblocks", {})
- back.append(include)
- self.response(to, back)
-
-
-@PluginManager.registerTo("SiteStorage")
-class SiteStoragePlugin(object):
- def updateDbFile(self, inner_path, file=None, cur=None):
- if file is not False: # File deletion always allowed
- # Find user directory names (epix1... addresses or xID names) in file path
- matches = re.findall("/([A-Za-z0-9][A-Za-z0-9.]{2,})/", str(inner_path))
- # Check if any of the adresses are in the mute list
- for auth_address in matches:
- if filter_storage.isMuted(auth_address):
- self.log.debug(f'Mute match: {auth_address}, ignoring {inner_path}')
- return False
-
- return super(SiteStoragePlugin, self).updateDbFile(inner_path, file=file, cur=cur)
-
- def onUpdated(self, inner_path, file=None):
- file_path = f'{self.site.address}/{inner_path}'
- if file_path in filter_storage.file_content['includes']:
- self.log.debug('Filter file updated: {inner_path}')
- filter_storage.includeUpdateAll()
- return super(SiteStoragePlugin, self).onUpdated(inner_path, file=file)
-
-@PluginManager.registerTo("Site")
-class SitePlugin(object):
- def needFile(self, inner_path, update=False, blocking=True, peer=None, priority=0):
- self.log.debug(f'needFile {inner_path}')
- matches = re.findall('/([A-Za-z0-9][A-Za-z0-9.]{2,})/', str(inner_path))
- for auth_address in matches:
- if filter_storage.isMuted(auth_address):
- self.log.info(f'Mute match in Site.needFile: {auth_address}, ignoring {inner_path}')
- return False
- return super(SitePlugin, self).needFile(inner_path, update, blocking, peer, priority)
-
-@PluginManager.registerTo("FileRequest")
-class FileRequestPlugin:
- def actionUpdate(self, params):
- inner_path = params.get('inner_path', '')
- matches = re.findall('/([A-Za-z0-9][A-Za-z0-9.]{2,})/', str(inner_path))
- for auth_address in matches:
- if filter_storage.isMuted(auth_address):
- self.log.info(f'Mute match in FileRequest.actionUpdate: {auth_address}, ignoring {inner_path}')
- self.response({'ok': f'Thanks, file {inner_path} updated!'})
- return False
- return super(FileRequestPlugin, self).actionUpdate(params)
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
- def actionWrapper(self, path, extra_headers=None):
- match = re.match(r"/(?P
[A-Za-z0-9\._-]+)(?P/.*|$)", path)
- if not match:
- return False
- address = match.group("address")
-
- if self.server.site_manager.get(address): # Site already exists
- return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
- if self.isDomain(address):
- address = self.resolveDomain(address)
-
- if address:
- address_hashed = filter_storage.getSiteAddressHashed(address)
- else:
- address_hashed = None
-
- if filter_storage.isSiteblocked(address) or filter_storage.isSiteblocked(address_hashed):
- site = self.server.site_manager.get(config.homepage)
- if not extra_headers:
- extra_headers = {}
-
- script_nonce = self.getScriptNonce()
-
- self.sendHeader(extra_headers=extra_headers, script_nonce=script_nonce)
- return iter([super(UiRequestPlugin, self).renderWrapper(
- site, path, "uimedia/plugins/contentfilter/blocklisted.html?address=" + address,
- "Blacklisted site", extra_headers, show_loadingscreen=False, script_nonce=script_nonce
- )])
- else:
- return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
-
- def actionUiMedia(self, path, *args, **kwargs):
- if path.startswith("/uimedia/plugins/contentfilter/"):
- file_path = path.replace("/uimedia/plugins/contentfilter/", plugin_dir + "/media/")
- return self.actionFile(file_path)
- else:
- return super(UiRequestPlugin, self).actionUiMedia(path)
diff --git a/plugins/ContentFilter/ContentFilterStorage.py b/plugins/ContentFilter/ContentFilterStorage.py
deleted file mode 100644
index 1074aee04..000000000
--- a/plugins/ContentFilter/ContentFilterStorage.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import os
-import json
-import logging
-import collections
-import time
-import hashlib
-from pathlib import Path
-
-from Debug import Debug
-from Plugin import PluginManager
-from Config import config
-from util import helper
-
-
-class ContentFilterStorage(object):
- def __init__(self, site_manager):
- self.log = logging.getLogger("ContentFilterStorage")
- self.file_path = config.config_dir / 'filters.json'
- self.site_manager = site_manager
- self.file_content = self.load()
-
- # Set default values for filters.json
- if not self.file_content:
- self.file_content = {}
-
- # Site blacklist renamed to site blocks
- if "site_blacklist" in self.file_content:
- self.file_content["siteblocks"] = self.file_content["site_blacklist"]
- del self.file_content["site_blacklist"]
-
- for key in ["mutes", "siteblocks", "includes"]:
- if key not in self.file_content:
- self.file_content[key] = {}
-
- self.include_filters = collections.defaultdict(set) # Merged list of mutes and blacklists from all include
- self.includeUpdateAll(update_site_dbs=False)
-
- def load(self):
- # Rename previously used mutes.json -> filters.json
- if (config.config_dir / 'mutes.json').is_file():
- self.log.info("Renaming mutes.json to filters.json...")
- os.rename(config.config_dir / 'mutes.json', self.file_path)
- if self.file_path.is_file():
- try:
- return json.load(self.file_path.open())
- except Exception as err:
- self.log.error("Error loading filters.json: %s" % err)
- return None
- else:
- return None
-
- def includeUpdateAll(self, update_site_dbs=True):
- s = time.time()
- new_include_filters = collections.defaultdict(set)
-
- # Load all include files data into a merged set
- for include_path in self.file_content["includes"]:
- address, inner_path = include_path.split("/", 1)
- try:
- content = self.site_manager.get(address).storage.loadJson(inner_path)
- except Exception as err:
- self.log.warning(
- "Error loading include %s: %s" %
- (include_path, Debug.formatException(err))
- )
- continue
-
- for key, val in content.items():
- if type(val) is not dict:
- continue
-
- new_include_filters[key].update(val.keys())
-
- mutes_added = new_include_filters["mutes"].difference(self.include_filters["mutes"])
- mutes_removed = self.include_filters["mutes"].difference(new_include_filters["mutes"])
-
- self.include_filters = new_include_filters
-
- if update_site_dbs:
- for auth_address in mutes_added:
- self.changeDbs(auth_address, "remove")
-
- for auth_address in mutes_removed:
- if not self.isMuted(auth_address):
- self.changeDbs(auth_address, "load")
-
- num_mutes = len(self.include_filters["mutes"])
- num_siteblocks = len(self.include_filters["siteblocks"])
- self.log.debug(
- "Loaded %s mutes, %s blocked sites from %s includes in %.3fs" %
- (num_mutes, num_siteblocks, len(self.file_content["includes"]), time.time() - s)
- )
-
- def includeAdd(self, address, inner_path, description=None):
- self.file_content["includes"]["%s/%s" % (address, inner_path)] = {
- "date_added": time.time(),
- "address": address,
- "description": description,
- "inner_path": inner_path
- }
- self.includeUpdateAll()
- self.save()
-
- def includeRemove(self, address, inner_path):
- del self.file_content["includes"]["%s/%s" % (address, inner_path)]
- self.includeUpdateAll()
- self.save()
-
- def save(self):
- s = time.time()
- helper.atomicWrite(self.file_path, json.dumps(self.file_content, indent=2, sort_keys=True).encode("utf8"))
- self.log.debug("Saved in %.3fs" % (time.time() - s))
-
- def isMuted(self, auth_address):
- if auth_address in self.file_content["mutes"] or auth_address in self.include_filters["mutes"]:
- return True
- else:
- return False
-
- def getSiteAddressHashed(self, address):
- return "0x" + hashlib.sha256(address.encode("ascii")).hexdigest()
-
- def isSiteblocked(self, address):
- if address in self.file_content["siteblocks"] or address in self.include_filters["siteblocks"]:
- return True
- return False
-
- def getSiteblockDetails(self, address):
- details = self.file_content["siteblocks"].get(address)
- if not details:
- address_sha256 = self.getSiteAddressHashed(address)
- details = self.file_content["siteblocks"].get(address_sha256)
-
- if not details:
- includes = self.file_content.get("includes", {}).values()
- for include in includes:
- include_site = self.site_manager.get(include["address"])
- if not include_site:
- continue
- content = include_site.storage.loadJson(include["inner_path"])
- details = content.get("siteblocks", {}).get(address)
- if details:
- details["include"] = include
- break
-
- return details
-
- # Search and remove or readd files of an user
- def changeDbs(self, auth_address, action):
- self.log.debug("Mute action %s on user %s" % (action, auth_address))
- res = list(self.site_manager.list().values())[0].content_manager.contents.db.execute(
- "SELECT * FROM content LEFT JOIN site USING (site_id) WHERE inner_path LIKE :inner_path",
- {"inner_path": "%%/%s/%%" % auth_address}
- )
- for row in res:
- site = self.site_manager.sites.get(row["address"])
- if not site:
- continue
- dir_inner_path = helper.getDirname(row["inner_path"])
- for file_name in site.storage.walk(Path(dir_inner_path)):
- if action == "remove":
- site.storage.delete(dir_inner_path + file_name)
- else:
- site.storage.onUpdated(dir_inner_path + file_name)
- site.onFileDone(dir_inner_path + file_name)
diff --git a/plugins/ContentFilter/Test/TestContentFilter.py b/plugins/ContentFilter/Test/TestContentFilter.py
deleted file mode 100644
index e1b37b163..000000000
--- a/plugins/ContentFilter/Test/TestContentFilter.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import pytest
-from ContentFilter import ContentFilterPlugin
-from Site import SiteManager
-
-
-@pytest.fixture
-def filter_storage():
- ContentFilterPlugin.filter_storage = ContentFilterPlugin.ContentFilterStorage(SiteManager.site_manager)
- return ContentFilterPlugin.filter_storage
-
-
-@pytest.mark.usefixtures("resetSettings")
-@pytest.mark.usefixtures("resetTempSettings")
-class TestContentFilter:
- def createInclude(self, site):
- site.storage.writeJson("filters.json", {
- "mutes": {"1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C": {}},
- "siteblocks": {site.address: {}}
- })
-
- def testIncludeLoad(self, site, filter_storage):
- self.createInclude(site)
- filter_storage.file_content["includes"]["%s/%s" % (site.address, "filters.json")] = {
- "date_added": 1528295893,
- }
-
- assert not filter_storage.include_filters["mutes"]
- assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
- assert not filter_storage.isSiteblocked(site.address)
- filter_storage.includeUpdateAll(update_site_dbs=False)
- assert len(filter_storage.include_filters["mutes"]) == 1
- assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
- assert filter_storage.isSiteblocked(site.address)
-
- def testIncludeAdd(self, site, filter_storage):
- self.createInclude(site)
- query_num_json = "SELECT COUNT(*) AS num FROM json WHERE directory = 'users/1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C'"
- assert not filter_storage.isSiteblocked(site.address)
- assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
- assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
- # Add include
- filter_storage.includeAdd(site.address, "filters.json")
-
- assert filter_storage.isSiteblocked(site.address)
- assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
- assert site.storage.query(query_num_json).fetchone()["num"] == 0
-
- # Remove include
- filter_storage.includeRemove(site.address, "filters.json")
-
- assert not filter_storage.isSiteblocked(site.address)
- assert not filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
- assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
- def testIncludeChange(self, site, filter_storage):
- self.createInclude(site)
- filter_storage.includeAdd(site.address, "filters.json")
- assert filter_storage.isSiteblocked(site.address)
- assert filter_storage.isMuted("1J6UrZMkarjVg5ax9W4qThir3BFUikbW6C")
-
- # Add new blocked site
- assert not filter_storage.isSiteblocked("1Hello")
-
- filter_content = site.storage.loadJson("filters.json")
- filter_content["siteblocks"]["1Hello"] = {}
- site.storage.writeJson("filters.json", filter_content)
-
- assert filter_storage.isSiteblocked("1Hello")
-
- # Add new muted user
- query_num_json = "SELECT COUNT(*) AS num FROM json WHERE directory = 'users/1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q'"
- assert not filter_storage.isMuted("1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q")
- assert site.storage.query(query_num_json).fetchone()["num"] == 2
-
- filter_content["mutes"]["1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q"] = {}
- site.storage.writeJson("filters.json", filter_content)
-
- assert filter_storage.isMuted("1C5sgvWaSgfaTpV5kjBCnCiKtENNMYo69q")
- assert site.storage.query(query_num_json).fetchone()["num"] == 0
-
-
diff --git a/plugins/ContentFilter/Test/conftest.py b/plugins/ContentFilter/Test/conftest.py
deleted file mode 100644
index 634e66e2e..000000000
--- a/plugins/ContentFilter/Test/conftest.py
+++ /dev/null
@@ -1 +0,0 @@
-from src.Test.conftest import *
diff --git a/plugins/ContentFilter/Test/pytest.ini b/plugins/ContentFilter/Test/pytest.ini
deleted file mode 100644
index d09210d1d..000000000
--- a/plugins/ContentFilter/Test/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-python_files = Test*.py
-addopts = -rsxX -v --durations=6
-markers =
- webtest: mark a test as a webtest.
\ No newline at end of file
diff --git a/plugins/ContentFilter/__init__.py b/plugins/ContentFilter/__init__.py
deleted file mode 100644
index 2cbca8eea..000000000
--- a/plugins/ContentFilter/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import ContentFilterPlugin
diff --git a/plugins/ContentFilter/languages/hu.json b/plugins/ContentFilter/languages/hu.json
deleted file mode 100644
index 9b57e6979..000000000
--- a/plugins/ContentFilter/languages/hu.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "%s tartalmaniak elrejtése?",
- "Mute": "Elnémítás",
- "Unmute %s?": "%s tartalmaniak megjelenítése?",
- "Unmute": "Némítás visszavonása"
-}
diff --git a/plugins/ContentFilter/languages/it.json b/plugins/ContentFilter/languages/it.json
deleted file mode 100644
index 9a2c6761d..000000000
--- a/plugins/ContentFilter/languages/it.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "%s Vuoi nascondere i contenuti di questo utente ?",
- "Mute": "Attiva Silenzia",
- "Unmute %s?": "%s Vuoi mostrare i contenuti di questo utente ?",
- "Unmute": "Disattiva Silenzia"
-}
diff --git a/plugins/ContentFilter/languages/jp.json b/plugins/ContentFilter/languages/jp.json
deleted file mode 100644
index ef586a1a6..000000000
--- a/plugins/ContentFilter/languages/jp.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "%s のコンテンツをすべて隠しますか?",
- "Mute": "ミュート",
- "Unmute %s?": "%s のミュートを解除しますか?",
- "Unmute": "ミュート解除"
-}
diff --git a/plugins/ContentFilter/languages/pt-br.json b/plugins/ContentFilter/languages/pt-br.json
deleted file mode 100644
index 3c6bfbdcf..000000000
--- a/plugins/ContentFilter/languages/pt-br.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "%s Ocultar todo o conteúdo de ?",
- "Mute": "Ativar o Silêncio",
- "Unmute %s?": "%s Você quer mostrar o conteúdo deste usuário ?",
- "Unmute": "Desligar o silêncio"
-}
diff --git a/plugins/ContentFilter/languages/zh-tw.json b/plugins/ContentFilter/languages/zh-tw.json
deleted file mode 100644
index 0995f3a0f..000000000
--- a/plugins/ContentFilter/languages/zh-tw.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "屏蔽 %s 的所有內容?",
- "Mute": "屏蔽",
- "Unmute %s?": "對 %s 解除屏蔽?",
- "Unmute": "解除屏蔽"
-}
diff --git a/plugins/ContentFilter/languages/zh.json b/plugins/ContentFilter/languages/zh.json
deleted file mode 100644
index bf63f1075..000000000
--- a/plugins/ContentFilter/languages/zh.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "Hide all content from %s?": "屏蔽 %s 的所有内容?",
- "Mute": "屏蔽",
- "Unmute %s?": "对 %s 解除屏蔽?",
- "Unmute": "解除屏蔽"
-}
diff --git a/plugins/ContentFilter/media/blocklisted.html b/plugins/ContentFilter/media/blocklisted.html
deleted file mode 100644
index 156dd127e..000000000
--- a/plugins/ContentFilter/media/blocklisted.html
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
Site blocked
-
This site is on your blocklist:
-
-
Too much image
-
on 2015-01-25 12:32:11
-
-
-
-
-
-
-
-
-
diff --git a/plugins/ContentFilter/media/js/EpixFrame.js b/plugins/ContentFilter/media/js/EpixFrame.js
deleted file mode 100644
index 010414c06..000000000
--- a/plugins/ContentFilter/media/js/EpixFrame.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// Version 1.0.0 - Initial release
-// Version 1.1.0 (2017-08-02) - Added cmdp function that returns promise instead of using callback
-// Version 1.2.0 (2017-08-02) - Added Ajax monkey patch to emulate XMLHttpRequest over EpixFrame API
-
-const CMD_INNER_READY = 'innerReady'
-const CMD_RESPONSE = 'response'
-const CMD_WRAPPER_READY = 'wrapperReady'
-const CMD_PING = 'ping'
-const CMD_PONG = 'pong'
-const CMD_WRAPPER_OPENED_WEBSOCKET = 'wrapperOpenedWebsocket'
-const CMD_WRAPPER_CLOSE_WEBSOCKET = 'wrapperClosedWebsocket'
-
-class EpixFrame {
- constructor(url) {
- this.url = url
- this.waiting_cb = {}
- this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
- this.connect()
- this.next_message_id = 1
- this.init()
- }
-
- init() {
- return this
- }
-
- connect() {
- this.target = window.parent
- window.addEventListener('message', e => this.onMessage(e), false)
- this.cmd(CMD_INNER_READY)
- }
-
- onMessage(e) {
- let message = e.data
- let cmd = message.cmd
- if (cmd === CMD_RESPONSE) {
- if (this.waiting_cb[message.to] !== undefined) {
- this.waiting_cb[message.to](message.result)
- }
- else {
- this.log("Websocket callback not found:", message)
- }
- } else if (cmd === CMD_WRAPPER_READY) {
- this.cmd(CMD_INNER_READY)
- } else if (cmd === CMD_PING) {
- this.response(message.id, CMD_PONG)
- } else if (cmd === CMD_WRAPPER_OPENED_WEBSOCKET) {
- this.onOpenWebsocket()
- } else if (cmd === CMD_WRAPPER_CLOSE_WEBSOCKET) {
- this.onCloseWebsocket()
- } else {
- this.onRequest(cmd, message)
- }
- }
-
- onRequest(cmd, message) {
- this.log("Unknown request", message)
- }
-
- response(to, result) {
- this.send({
- cmd: CMD_RESPONSE,
- to: to,
- result: result
- })
- }
-
- cmd(cmd, params={}, cb=null) {
- this.send({
- cmd: cmd,
- params: params
- }, cb)
- }
-
- cmdp(cmd, params={}) {
- return new Promise((resolve, reject) => {
- this.cmd(cmd, params, (res) => {
- if (res && res.error) {
- reject(res.error)
- } else {
- resolve(res)
- }
- })
- })
- }
-
- send(message, cb=null) {
- message.wrapper_nonce = this.wrapper_nonce
- message.id = this.next_message_id
- this.next_message_id++
- this.target.postMessage(message, '*')
- if (cb) {
- this.waiting_cb[message.id] = cb
- }
- }
-
- log(...args) {
- console.log.apply(console, ['[EpixFrame]'].concat(args))
- }
-
- onOpenWebsocket() {
- this.log('Websocket open')
- }
-
- onCloseWebsocket() {
- this.log('Websocket close')
- }
-
- monkeyPatchAjax() {
- var page = this
- XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open
- this.cmd("wrapperGetAjaxKey", [], (res) => { this.ajax_key = res })
- var newOpen = function (method, url, async) {
- url += "?ajax_key=" + page.ajax_key
- return this.realOpen(method, url, async)
- }
- XMLHttpRequest.prototype.open = newOpen
- }
-}
diff --git a/plugins/ContentFilter/plugin_info.json b/plugins/ContentFilter/plugin_info.json
deleted file mode 100644
index f63bc984c..000000000
--- a/plugins/ContentFilter/plugin_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "ContentFilter",
- "description": "Manage site and user block list.",
- "default": "enabled"
-}
\ No newline at end of file
From f511f728c6d96087de6750332c9a3020fdd538e8 Mon Sep 17 00:00:00 2001
From: mx5kevin <47847121+mx5kevin@users.noreply.github.com>
Date: Fri, 15 May 2026 09:24:49 +0000
Subject: [PATCH 2/6] Allow proxy users to add new sites
Allow proxy users to add new sites, to make the entire network accessible through a proxy. The NoNew sites if it is on, turn this feature off anyway.
---
plugins/disabled-Multiuser/MultiuserPlugin.py | 556 +++++++++---------
1 file changed, 278 insertions(+), 278 deletions(-)
diff --git a/plugins/disabled-Multiuser/MultiuserPlugin.py b/plugins/disabled-Multiuser/MultiuserPlugin.py
index 18bdcf205..6a59dd47f 100644
--- a/plugins/disabled-Multiuser/MultiuserPlugin.py
+++ b/plugins/disabled-Multiuser/MultiuserPlugin.py
@@ -1,278 +1,278 @@
-import re
-import sys
-import json
-
-from Config import config
-from Plugin import PluginManager
-from Crypt import CryptEpix
-from . import UserPlugin
-from util.Flag import flag
-from Translate import translate as _
-
-# We can only import plugin host clases after the plugins are loaded
-@PluginManager.afterLoad
-def importPluginnedClasses():
- global UserManager
- from User import UserManager
-
-try:
- local_master_addresses = set(json.load((config.private_dir / 'users.json').open()).keys()) # Users in users.json
-except Exception as err:
- local_master_addresses = set()
-
-
-@PluginManager.registerTo("UiRequest")
-class UiRequestPlugin(object):
- def __init__(self, *args, **kwargs):
- self.user_manager = UserManager.user_manager
- super(UiRequestPlugin, self).__init__(*args, **kwargs)
-
- def parsePath(self, path):
- return super(UiRequestPlugin, self).parsePath(path)
-
- # Create new user and inject user welcome message if necessary
- # Return: Html body also containing the injection
- def actionWrapper(self, path, extra_headers=None):
-
- match = re.match("/(?P[A-Za-z0-9\._-]+)(?P/.*|$)", path)
- if not match:
- return False
-
- inner_path = match.group("inner_path").lstrip("/")
- html_request = "." not in inner_path or inner_path.endswith(".html") # Only inject html to html requests
-
- user_created = False
- if html_request:
- user = self.getCurrentUser() # Get user from cookie
- if not user: # No user found by cookie
- user = self.user_manager.create()
- user_created = True
- else:
- user = None
-
- # Disable new site creation if --multiuser_no_new_sites enabled
- if config.multiuser_no_new_sites:
- path_parts = self.parsePath(path)
- if not self.server.site_manager.get(match.group("address")) and (not user or user.master_address not in local_master_addresses):
- self.sendHeader(404)
- return self.formatError("Not Found", "Adding new sites disabled on this proxy", details=False)
-
- if user_created:
- if not extra_headers:
- extra_headers = {}
- extra_headers['Set-Cookie'] = "master_address=%s;path=/;max-age=2592000;" % user.master_address # = 30 days
-
- loggedin = self.get.get("login") == "done"
-
- back_generator = super(UiRequestPlugin, self).actionWrapper(path, extra_headers) # Get the wrapper frame output
-
- if not back_generator: # Wrapper error or not string returned, injection not possible
- return False
-
- elif loggedin:
- back = next(back_generator)
- inject_html = """
-
-
-