LinkProtection/core/link_replacer.php

213 lines
5.5 KiB
PHP
Executable file

<?php
/*
* This class is used to replace links in a post whenever it is edited, previewed or created.
* It handles manual and automatic link replacement
*/
namespace pedodev\linkprotection\core;
use phpbb\config\config;
use pedodev\linkprotection\core\parser;
use pedodev\linkprotection\core\crypto;
use pedodev\linkprotection\core\automatic_link_helper;
use phpbb\auth\auth;
use phpbb\user;
use phpbb\language\language;
class link_replacer
{
private bool $preview;
private int $link_counter;
public string $error;
public function __construct(
private config $config,
private auth $auth,
private user $user,
private language $language,
private parser $parser,
private crypto $crypto,
private automatic_link_helper $automatic_link_helper,
)
{
$this->preview = false;
$this->link_counter = 0;
$this->error = '';
}
/*
* Must be called once before encrypting links
* Sets the user ID and post ID variables in the crypto object
*/
public function set_crypto_vars(int $user_id, int $post_id): void
{
$this->crypto->set_vars($user_id, $post_id);
}
/*
* Called when a post is created or previewed.
* If previewing, replace links with a link to the preview page
* If not previewing, encrypt and replace links
*/
public function find_protect_links(string $message, bool $preview): string
{
$this->link_counter = $this->parser->count_encrypted_links($message);
$this->preview = $preview;
// Limit is set to 1 above the limit so our error will trigger if they try to protect too many links
$limit = (int)$this->config['pedodev_linkprotection_maxlinks'] + 1;
if ($this->config['pedodev_linkprotection_manualenabled'])
{
$callback = [$this, 'replace_manual_link'];
$message = $this->parser->find_replace_manual_links($message, $callback, $limit);
}
if ($this->config['pedodev_linkprotection_automaticenabled'])
{
$callback = [$this, 'replace_automatic_link'];
$message = $this->parser->find_replace_automatic_links($message, $callback, $limit);
}
return $message;
}
/*
* Called by manual and automatic link replacement
* Replaces an individual link
*/
public function replace_link(array $link_data): string
{
if (!ctype_print($link_data['link']))
{
return $link_data[0];
}
if ($this->link_limit())
{
return $link_data[0];
}
$link_data['title'] = empty($link_data['title']) ? $this->config['pedodev_linkprotection_manualtitle'] : $link_data['title'];
$link_data['encrypted'] = $this->preview ? 'preview' : $this->crypto->encrypt_link($link_data['link']);
if (!isset($link_data['encrypted']))
{
return $link_data[0];
}
return $this->parser->get_encrypted_tag($link_data);
}
/*
* Called when a post is edited
* Replaces all encrypted links with the original, unprotected links
*/
public function find_restore_links(string $message): string
{
$callback = [$this, 'restore_link'];
$limit = (int)$this->config['pedodev_linkprotection_maxlinks'];
return $this->parser->find_restore_encrypted_links($message, $callback, $limit);
}
/*
* Our callback function for find_restore_links. Decrypts and restores an individual link
*/
public function restore_link(array $data): string
{
if ($this->link_limit())
{
return $data[0];
}
$link_data = $this->crypto->decrypt_link($data['encrypted']);
if (!$link_data)
{
return $data[0];
}
$link_data['title'] = $data['title'];
$can_view_original = ($link_data['user_id'] == $this->user->data['user_id']) || $this->auth->acl_get('m_pedodev_linkprotection_vieworiginallinks');
return $can_view_original ? $this->parser->get_original_text($link_data) : $data[0];
}
/*
* Called whenever a link is protected
* Increments the link counter by 1
* If the link limit is reached, outputs an error and returns true
* Otherwise returns false
*/
private function link_limit(): bool
{
$this->link_counter++;
if ($this->link_counter > $this->config['pedodev_linkprotection_maxlinks'])
{
$this->error = $this->language->lang('LINKPROTECTION_TOO_MANY_LINKS', $this->config['pedodev_linkprotection_maxlinks']);
return true;
}
return false;
}
/*
* Callback function for manual link replacement
*/
public function replace_manual_link(array $link_data): string
{
if (strlen($link_data['link']) > (int)$this->config['pedodev_linkprotection_manuallength'])
{
return $link_data[0];
}
if ($this->config['pedodev_linkprotection_strictmanual'] && !filter_var($link_data['link'], FILTER_VALIDATE_URL))
{
return $link_data[0];
}
return $this->replace_link($link_data);
}
/*
* Callback function for automatic link replacement
*/
public function replace_automatic_link(array $link_data): string
{
$link = $link_data[0];
$title = $this->automatic_link_helper->find_substring_match($link);
if (empty($title) || strlen($link) > (int)$this->config['pedodev_linkprotection_automaticlength'])
{
return $link;
}
$link_data = array_merge($link_data, array(
'link' => $link,
'title' => $title,
));
return $this->replace_link($link_data);
}
/*
* Checks whether user has permission to replace links.
* If no permission, checks whether a user is trying to manually protect links
* If trying to protect links without permission, outputs an error message
*/
public function check_permission(string $message = ''): bool
{
$has_permission = $this->auth->acl_get('u_pedodev_linkprotection_canprotectlinks');
if (!$has_permission && $this->parser->find_manual_links($message))
{
$this->error = $this->language->lang('LINKPROTECTION_CANNOT_PROTECT_LINKS');
}
return $has_permission;
}
}