Fixed Broken Shit
@ -1,445 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SteppingHat\EmojiDetector;
|
||||
|
||||
use Exception;
|
||||
use SteppingHat\EmojiDetector\Model\EmojiInfo;
|
||||
|
||||
class EmojiDetector {
|
||||
|
||||
const LONGEST_EMOJI = 8;
|
||||
|
||||
const SKIN_TONES = [
|
||||
'1F3FB' => 'skin-tone-2',
|
||||
'1F3FC' => 'skin-tone-3',
|
||||
'1F3FD' => 'skin-tone-4',
|
||||
'1F3FE' => 'skin-tone-5',
|
||||
'1F3FF' => 'skin-tone-6'
|
||||
];
|
||||
|
||||
private $map;
|
||||
private $regex;
|
||||
private $dataDir;
|
||||
|
||||
/**
|
||||
* EmojiDetector constructor.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->dataDir = __DIR__ . '/../var';
|
||||
$this->loadMap();
|
||||
$this->loadRawEmojis();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return EmojiInfo[]
|
||||
*/
|
||||
public function detect($string) {
|
||||
|
||||
$oldEncoding = mb_internal_encoding();
|
||||
mb_internal_encoding('UTF-8');
|
||||
|
||||
/** @var EmojiInfo[] $emojiInfos */
|
||||
$emojiInfos = [];
|
||||
|
||||
$matches = [];
|
||||
foreach($this->regex as $icon) {
|
||||
$strpos = mb_strpos($string, $icon);
|
||||
if($strpos !== false) {
|
||||
$matches[] = [$icon, $strpos];
|
||||
}
|
||||
}
|
||||
|
||||
$length = 0;
|
||||
|
||||
foreach($matches as $match) {
|
||||
$emojiInfo = new EmojiInfo();
|
||||
|
||||
$emojiInfo->setEmoji($match[0]);
|
||||
$emojiInfo->setOffset(strpos($string, $match[0]));
|
||||
$emojiInfo->setMbOffset(mb_strpos($string, $match[0]));
|
||||
|
||||
// Break apart the hex characters and build the hex string
|
||||
$hexCodes = [];
|
||||
|
||||
for($i = 0; $i < mb_strlen($emojiInfo->getEmoji()); $i++) {
|
||||
$hexCodes[] = strtoupper(dechex($this->unicodeOrd(mb_substr($match[0], $i, 1))));
|
||||
}
|
||||
$emojiInfo->setHexCodes($hexCodes);
|
||||
|
||||
// Denote the emoji name
|
||||
if(array_key_exists($emojiInfo->getHexString(), $this->map)) {
|
||||
$emojiInfo->setName($this->map[$emojiInfo->getHexString()]['name']);
|
||||
$emojiInfo->setShortName($this->map[$emojiInfo->getHexString()]['shortName']);
|
||||
$emojiInfo->setCategory($this->map[$emojiInfo->getHexString()]['category']);
|
||||
}
|
||||
|
||||
|
||||
// Denote the skin tone
|
||||
foreach($hexCodes as $hexCode) {
|
||||
if(array_key_exists($hexCode, self::SKIN_TONES)) {
|
||||
$emojiInfo->setSkinTone(self::SKIN_TONES[$hexCode]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$length += (strlen($emojiInfo->getEmoji()) - 1);
|
||||
|
||||
$emojiInfos[] = $emojiInfo;
|
||||
}
|
||||
|
||||
usort($emojiInfos, function(EmojiInfo $a, EmojiInfo $b) {
|
||||
if($a->getOffset() == $b->getOffset()) {
|
||||
return 0;
|
||||
}
|
||||
return $a->getOffset() < $b->getOffset() ? -1 : 1;
|
||||
});
|
||||
|
||||
/** @var EmojiInfo[] $data */
|
||||
$data = [];
|
||||
foreach($emojiInfos as $emoji) {
|
||||
if(count($data) == 0) {
|
||||
$data[] = $emoji;
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var EmojiInfo $last */
|
||||
$last = end($data);
|
||||
$key = key($data);
|
||||
|
||||
if($last->getOffset() == $emoji->getOffset()) {
|
||||
if($last->getMbLength() < $emoji->getMbLength()) {
|
||||
$data[$key] = $emoji;
|
||||
}
|
||||
} else if($emoji->getOffset() >= strlen($last->getEmoji()) + $last->getOffset()) {
|
||||
$data[] = $emoji;
|
||||
}
|
||||
|
||||
reset($data);
|
||||
}
|
||||
|
||||
mb_internal_encoding($oldEncoding);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return bool
|
||||
*/
|
||||
public function isSingleEmoji($string) {
|
||||
if(mb_strlen($string) > self::LONGEST_EMOJI) return false;
|
||||
|
||||
$emojis = $this->detect($string);
|
||||
if(count($emojis) !== 1) return false;
|
||||
|
||||
$emoji = array_pop($emojis);
|
||||
$string = str_replace($emoji->getEmoji(), '', $string);
|
||||
|
||||
$split = $this->str_split_unicode($string);
|
||||
if(count($split) > 1) return false;
|
||||
else if(count($split) === 1 && $split[0] === '') return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function loadMap() {
|
||||
$mapFile = $this->dataDir . '/map.json';
|
||||
if(!file_exists($mapFile)) {
|
||||
throw new Exception("Could not load Emoji map file");
|
||||
}
|
||||
|
||||
$this->map = json_decode(file_get_contents($mapFile), true);
|
||||
|
||||
}
|
||||
|
||||
private function loadRawEmojis() {
|
||||
$mapFile = $this->dataDir . '/raw.json';
|
||||
if(!file_exists($mapFile)) {
|
||||
throw new Exception("Could not load Emoji raw file");
|
||||
}
|
||||
|
||||
$this->regex = json_decode(file_get_contents($mapFile), true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $hexChar
|
||||
* @return bool|int
|
||||
*/
|
||||
private function unicodeOrd($hexChar) {
|
||||
$ord0 = ord($hexChar[0]);
|
||||
if($ord0 >= 0 && $ord0 <= 127) return $ord0;
|
||||
|
||||
$ord1 = ord($hexChar[1]);
|
||||
if($ord0 >= 192 && $ord0 <= 223) return ($ord0 - 192) * 64 + ($ord1 - 128);
|
||||
|
||||
$ord2 = ord($hexChar[2]);
|
||||
if($ord0 >= 224 && $ord0 <= 239) return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128);
|
||||
|
||||
$ord3 = ord($hexChar[3]);
|
||||
if($ord0 >= 240 && $ord0 <= 247) return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2 - 128) * 64 + ($ord3 - 128);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $str
|
||||
* @param int $l
|
||||
* @return false|string[]
|
||||
*/
|
||||
private function str_split_unicode($str, $l = 0) {
|
||||
if ($l > 0) {
|
||||
$ret = array();
|
||||
$len = mb_strlen($str, "UTF-8");
|
||||
for ($i = 0; $i < $len; $i += $l) {
|
||||
$ret[] = mb_substr($str, $i, $l, "UTF-8");
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SteppingHat\Test\EmojiDetector;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SteppingHat\EmojiDetector\EmojiDetector;
|
||||
|
||||
class EmojiDetectorTest extends TestCase {
|
||||
|
||||
public function testDetectEmoji() {
|
||||
$string = '🍆';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(1, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('🍆', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('eggplant', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('food-vegetable', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Food & Drink', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertSame(1, $emoji->getMbLength(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertNull($emoji->getSkinTone(), "Emoji object contains a skin tone when none should exist");
|
||||
$this->assertSame(0, $emoji->getOffset(), "Emoji is indicating a position other than the start of the string");
|
||||
$this->assertSame(0, $emoji->getMbOffset(), "Emoji is indicating a position other than the start of the string");
|
||||
$this->assertSame(['1F346'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testDetectSimpleEmoji() {
|
||||
$string = '❤️';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(1, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('❤️', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('red heart', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('emotion', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Smileys & Emotion', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertSame(2, $emoji->getMbLength(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertNull($emoji->getSkinTone(), "Emoji object contains a skin tone when none should exist");
|
||||
$this->assertSame(0, $emoji->getOffset(), "Emoji is indicating a position other than the start of the string");
|
||||
$this->assertSame(0, $emoji->getMbOffset(), "Emoji is indicating a position other than the start of the string");
|
||||
$this->assertSame(['2764', 'FE0F'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
public function testDetectEmojiInString() {
|
||||
$string = 'LOL 😂!';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(1, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('😂', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('face with tears of joy', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('face-smiling', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Smileys & Emotion', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertSame(1, $emoji->getMbLength(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertNull($emoji->getSkinTone(), "Emoji object contains a skin tone when none should exist");
|
||||
$this->assertSame(4, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(4, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['1F602'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
public function testDetectMultipleEmoji() {
|
||||
$string = '🌚💩';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(2, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('🌚', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('new moon face', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('sky & weather', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Travel & Places', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertCount(1, $emoji->getHexCodes(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertNull($emoji->getSkinTone(), "Emoji object contains a skin tone when none should exist");
|
||||
$this->assertSame(0, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(0, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['1F31A'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('💩', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('pile of poo', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('face-costume', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Smileys & Emotion', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertSame(1, $emoji->getMbLength(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertNull($emoji->getSkinTone(), "Emoji object contains a skin tone when none should exist");
|
||||
$this->assertSame(4, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(1, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['1F4A9'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
public function testDetectSkinToneEmoji() {
|
||||
$string = '🤦🏻';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(1, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('🤦🏻', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('person facepalming: light skin tone', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('person-gesture', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('People & Body', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertCount(2, $emoji->getHexCodes(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertSame('skin-tone-2', $emoji->getSkinTone(), "Emoji reported a different skin tone than expected");
|
||||
$this->assertSame(0, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(0, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['1F926', '1F3FB'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
public function testDetectComplexString() {
|
||||
$string = 'I ❤️ working on 👨💻';
|
||||
$emojis = (new EmojiDetector())->detect($string);
|
||||
|
||||
$this->assertCount(2, $emojis);
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('❤️', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('red heart', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('emotion', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('Smileys & Emotion', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertCount(2, $emoji->getHexCodes(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertSame(null, $emoji->getSkinTone(), "Emoji reported a different skin tone than expected");
|
||||
$this->assertSame(2, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(2, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['2764', 'FE0F'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
|
||||
$emoji = array_shift($emojis);
|
||||
|
||||
$this->assertSame('👨💻', $emoji->getEmoji(), "Expected emoji does not match actual emoji in string");
|
||||
$this->assertSame('man technologist', $emoji->getName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('person-role', $emoji->getShortName(), "Expected emoji name does not match the actual emoji name");
|
||||
$this->assertSame('People & Body', $emoji->getCategory(), "Expected emoji category does not match the actual emoji category");
|
||||
$this->assertCount(3, $emoji->getHexCodes(), "More emoji characters in the hex string are present than expected");
|
||||
$this->assertSame(null, $emoji->getSkinTone(), "Emoji reported a different skin tone than expected");
|
||||
$this->assertSame(20, $emoji->getOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(16, $emoji->getMbOffset(), "Emoji is indicating a position that is not expected");
|
||||
$this->assertSame(['1F468', '200D', '1F4BB'], $emoji->getHexCodes(), "Invalid hex codes representing the emoji were presented");
|
||||
}
|
||||
|
||||
public function testSingleEmoji() {
|
||||
$detector = new EmojiDetector();
|
||||
|
||||
$string = '🖥';
|
||||
$this->assertTrue($detector->isSingleEmoji($string));
|
||||
|
||||
$string = '😂🎉';
|
||||
$this->assertFalse($detector->isSingleEmoji($string));
|
||||
|
||||
$string = '👨💻'; // This one has a ZWJ character
|
||||
$this->assertTrue($detector->isSingleEmoji($string));
|
||||
|
||||
$string = '👨💻'; // This one does not have a ZWJ character
|
||||
$this->assertFalse($detector->isSingleEmoji($string));
|
||||
}
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SteppingHat\EmojiDetector\Model;
|
||||
|
||||
class EmojiInfo {
|
||||
|
||||
/**
|
||||
* The emoji characters
|
||||
* @var string
|
||||
*/
|
||||
protected $emoji;
|
||||
|
||||
/**
|
||||
* A friendly emoji name
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* An even friendlier emoji name
|
||||
* @var string
|
||||
*/
|
||||
protected $shortName;
|
||||
|
||||
/**
|
||||
* The category of the emoji
|
||||
* @var string
|
||||
*/
|
||||
protected $category;
|
||||
|
||||
/**
|
||||
* The detected skin tone variation (if present)
|
||||
* @var string
|
||||
*/
|
||||
protected $skinTone;
|
||||
|
||||
/**
|
||||
* An array of individual UTF-8 hex characters that make up the emoji
|
||||
* @var array
|
||||
*/
|
||||
protected $hexCodes;
|
||||
|
||||
/**
|
||||
* The offset of the emoji in the parent string if present
|
||||
* @var int
|
||||
*/
|
||||
protected $offset;
|
||||
|
||||
/**
|
||||
* The multibyte offset
|
||||
* @var int
|
||||
*/
|
||||
protected $mbOffset;
|
||||
|
||||
public function __toString() {
|
||||
return $this->getEmoji();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmoji() {
|
||||
return $this->emoji;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $emoji
|
||||
*/
|
||||
public function setEmoji($emoji) {
|
||||
$this->emoji = $emoji;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getShortName() {
|
||||
return $this->shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $shortName
|
||||
*/
|
||||
public function setShortName($shortName) {
|
||||
$this->shortName = $shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory() {
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
*/
|
||||
public function setCategory($category) {
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSkinTone() {
|
||||
return $this->skinTone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $skinTone
|
||||
*/
|
||||
public function setSkinTone($skinTone) {
|
||||
$this->skinTone = $skinTone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHexCodes() {
|
||||
return $this->hexCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $hexCodes
|
||||
*/
|
||||
public function setHexCodes($hexCodes) {
|
||||
$this->hexCodes = $hexCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOffset() {
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*/
|
||||
public function setOffset($offset) {
|
||||
$this->offset = $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMbOffset() {
|
||||
return $this->mbOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $mbOffset
|
||||
*/
|
||||
public function setMbOffset($mbOffset) {
|
||||
$this->mbOffset = $mbOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMbLength() {
|
||||
if($this->hexCodes === null) return false;
|
||||
else return count($this->hexCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHexString() {
|
||||
return implode('-', $this->getHexCodes());
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SteppingHat\EmojiDetector;
|
||||
|
||||
class MapGenerator {
|
||||
|
||||
private static $upstreamUrl = 'https://unpkg.com/emoji.json@13.1.0/emoji.json';
|
||||
|
||||
public static function generateMap() {
|
||||
$dataDir = __DIR__."/../var";
|
||||
if(!is_dir($dataDir)) mkdir($dataDir, 0777, true);
|
||||
|
||||
// Load emoji data from amio/emoji.json
|
||||
$emojiData = json_decode(file_get_contents(self::$upstreamUrl), true);
|
||||
|
||||
$mapData = [];
|
||||
$rawEmojis = [];
|
||||
foreach($emojiData as $emoji) {
|
||||
|
||||
$data = [
|
||||
'name' => strtolower($emoji['name']),
|
||||
'shortName' => self::getShortName($emoji['category']),
|
||||
'category' => self::getCategory($emoji['category'])
|
||||
];
|
||||
|
||||
$mapData[str_replace(' ', '-', $emoji['codes'])] = $data;
|
||||
|
||||
$rawEmojis[] = $emoji['char'];
|
||||
|
||||
if(isset($emoji['variations'])) {
|
||||
foreach($emoji['variations'] as $variation) {
|
||||
$mapData[$variation] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($emoji['skin_variations'])) {
|
||||
foreach($emoji['skin_variations'] as $variation) {
|
||||
$mapData[$variation['unified']] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
file_put_contents($dataDir."/map.json", json_encode($mapData));
|
||||
file_put_contents($dataDir."/raw.json", json_encode($rawEmojis));
|
||||
}
|
||||
|
||||
private static function getShortName($string) {
|
||||
preg_match('#\((.*?)\)#', $string, $matches);
|
||||
return str_replace(['(', ')'], '', array_shift($matches));
|
||||
}
|
||||
|
||||
private static function getCategory($string) {
|
||||
return substr($string, 0, strpos($string, '(') - 1);
|
||||
}
|
||||
|
||||
}
|
175
user/README.md
@ -1,175 +0,0 @@
|
||||
PHP Emoji Detector
|
||||
==================
|
||||
|
||||
[](//packagist.org/packages/steppinghat/emoji-detector) [](//packagist.org/packages/steppinghat/emoji-detector) [](//packagist.org/packages/steppinghat/emoji-detector) [](https://travis-ci.com/SteppingHat/php-emoji-detector)
|
||||
|
||||
Have an input string full of emoji's and you want to know detailed information about each emoji?
|
||||
Want to build an easy way to validate emoji's that come in as input data?
|
||||
|
||||
This :clap: is :clap: the :clap: library :clap: you :clap: want!
|
||||
|
||||
## What does this thing do?
|
||||
|
||||
This library simply parses input strings and returns a list of relevant information about emoji's that are present in the string. It currently supports version 12.1 of the emoji standard.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Install this library using composer
|
||||
|
||||
$ composer require steppinghat/emoji-detector
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### The Model
|
||||
|
||||
For most outputs, the library will provide an `EmojiInfo` object that contains all of the relevant information about an emoji.
|
||||
The object will contain the following information:
|
||||
|
||||
| Property | Description |
|
||||
| ----------- | ----------- |
|
||||
| `emoji` | The emoji chacater itself |
|
||||
| `name` | A user friendly name of the specific emoji |
|
||||
| `shortName` | A shortened name of the emoji |
|
||||
| `category` | A user friendly category name for the emoji |
|
||||
| `skinTone` | The level of skin tone of the emoji (if present) |
|
||||
| `hexCodes` | An array of individual hexadecimal characters that are used to make up the emoji |
|
||||
| `offset` | The position in the string where the emoji starts |
|
||||
| `mbOffset` | The multibyte position in the string where the emoji starts |
|
||||
|
||||
All of these properties are protected, but can be accessed by their appropriate getters and setters. E.g. `getCategory()` or `setSkinTone()`.
|
||||
|
||||
|
||||
### Emoji detection
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require_once('vendor/autoload.php');
|
||||
|
||||
use SteppingHat\EmojiDetector;
|
||||
|
||||
$input = "Hello 👋 world!";
|
||||
$detector = EmojiDetector();
|
||||
$emojis = $detector->detect($input);
|
||||
|
||||
print_r($emojis);
|
||||
```
|
||||
This example is the most common usage of the detector, returning an array of objects that provide detailed information about each emoji found in the input string.
|
||||
|
||||
```
|
||||
Array
|
||||
(
|
||||
[0] => SteppingHat\EmojiDetector\Model\EmojiInfo Object
|
||||
(
|
||||
[emoji:protected] => 👋
|
||||
[name:protected] => waving hand
|
||||
[shortName:protected] => hand-fingers-open
|
||||
[category:protected] => People & Body
|
||||
[skinTone:protected] =>
|
||||
[hexCodes:protected] => Array
|
||||
(
|
||||
[0] => 1F44B
|
||||
)
|
||||
|
||||
[offset:protected] => 6
|
||||
[mbOffset:protected] => 6
|
||||
)
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
The library has full support for complex emoji's that make use of the ZWJ (Zero Width Joiner) character.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require_once('vendor/autoload.php');
|
||||
|
||||
use SteppingHat\EmojiDetector;
|
||||
|
||||
$input = "I ❤️ to 👨💻";
|
||||
$detector = new SteppingHat\EmojiDetector\EmojiDetector();
|
||||
$emojis = $detector->detect($input);
|
||||
|
||||
print_r($emojis);
|
||||
```
|
||||
|
||||
The above will produce the following output:
|
||||
|
||||
```
|
||||
Array
|
||||
(
|
||||
[0] => SteppingHat\EmojiDetector\Model\EmojiInfo Object
|
||||
(
|
||||
[emoji:protected] => ❤️
|
||||
[name:protected] => red heart
|
||||
[shortName:protected] => emotion
|
||||
[category:protected] => Smileys & Emotion
|
||||
[skinTone:protected] =>
|
||||
[hexCodes:protected] => Array
|
||||
(
|
||||
[0] => 2764
|
||||
[1] => FE0F
|
||||
)
|
||||
|
||||
[offset:protected] => 2
|
||||
[mbOffset:protected] => 2
|
||||
)
|
||||
|
||||
[1] => SteppingHat\EmojiDetector\Model\EmojiInfo Object
|
||||
(
|
||||
[emoji:protected] => 👨💻
|
||||
[name:protected] => man technologist
|
||||
[shortName:protected] => person-role
|
||||
[category:protected] => People & Body
|
||||
[skinTone:protected] =>
|
||||
[hexCodes:protected] => Array
|
||||
(
|
||||
[0] => 1F468
|
||||
[1] => 200D
|
||||
[2] => 1F4BB
|
||||
)
|
||||
|
||||
[offset:protected] => 13
|
||||
[mbOffset:protected] => 9
|
||||
)
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
### Testing for single emojis
|
||||
|
||||
Sometimes it is handy to test if an input string is a single emoji on it's own.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require_once('vendor/autoload.php');
|
||||
|
||||
use SteppingHat\EmojiDetector;
|
||||
|
||||
$detector = new SteppingHat\EmojiDetector\EmojiDetector();
|
||||
|
||||
$detector->isSingleEmoji("💩"); // Returns TRUE
|
||||
$detector->isSingleEmoji("Time to dance 🌚"); // Returns FALSE
|
||||
$detector->isSingleEmoji("🍆🍒"); // Returns FALSE
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Included for library development purposes is a small set of test cases to assure that basic library functions work as expected. These tests can be launched by running the following:
|
||||
|
||||
```bash
|
||||
$ php vendor/bin/simple-phpunit
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Made with :heart: by Javan Eskander
|
||||
|
||||
Available for use under the MIT license.
|
||||
|
||||
Emoji data sourced from [amio/emoji.json](https://github.com/amio/emoji.json)
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitb527cf0f410cc8001ea07d072eb1427c::getLoader();
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'SteppingHat\\EmojiDetector\\' => array($vendorDir . '/steppinghat/emoji-detector/src'),
|
||||
);
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitb527cf0f410cc8001ea07d072eb1427c
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitb527cf0f410cc8001ea07d072eb1427c', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitb527cf0f410cc8001ea07d072eb1427c', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitb527cf0f410cc8001ea07d072eb1427c::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitb527cf0f410cc8001ea07d072eb1427c
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'SteppingHat\\EmojiDetector\\' => 26,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'SteppingHat\\EmojiDetector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/steppinghat/emoji-detector/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitb527cf0f410cc8001ea07d072eb1427c::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitb527cf0f410cc8001ea07d072eb1427c::$prefixDirsPsr4;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "steppinghat/emoji-detector",
|
||||
"description": "Detect and validate emoji in an input string",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/steppinghat/emoji-detector",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Javan Eskander",
|
||||
"homepage": "https://javaneskander.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SteppingHat\\EmojiDetector\\": "src/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-mbstring": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^5.0@dev"
|
||||
},
|
||||
"scripts": {
|
||||
"generate-map": "SteppingHat\\EmojiDetector\\MapGenerator::generateMap"
|
||||
}
|
||||
}
|
177
user/composer.lock
generated
@ -1,177 +0,0 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "3789659f661fab9f84bee01c2c0c359e",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "dev-main",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "49dc45a74cbac5fffc6417372a9f5ae1682ca0b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/49dc45a74cbac5fffc6417372a9f5ae1682ca0b4",
|
||||
"reference": "49dc45a74cbac5fffc6417372a9f5ae1682ca0b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.4-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/main"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-25T16:38:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/phpunit-bridge",
|
||||
"version": "5.x-dev",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/phpunit-bridge.git",
|
||||
"reference": "b556f2a53490b04a8645569531c31e54f0abafc5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/b556f2a53490b04a8645569531c31e54f0abafc5",
|
||||
"reference": "b556f2a53490b04a8645569531c31e54f0abafc5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"symfony/deprecation-contracts": "^2.1"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<7.5|9.1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/error-handler": "^4.4|^5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
|
||||
},
|
||||
"default-branch": true,
|
||||
"bin": [
|
||||
"bin/simple-phpunit"
|
||||
],
|
||||
"type": "symfony-bridge",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"name": "phpunit/phpunit",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Bridge\\PhpUnit\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides utilities for PHPUnit, especially user deprecation notices management",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/phpunit-bridge/tree/5.x"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-17T17:02:51+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"symfony/phpunit-bridge": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.0",
|
||||
"ext-mbstring": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
@ -22,7 +22,7 @@ define( 'YOURLS_DB_NAME', '' );
|
||||
|
||||
/** MySQL hostname.
|
||||
** If using a non standard port, specify it like 'hostname:port', e.g. 'localhost:9999' or '127.0.0.1:666' */
|
||||
define( 'YOURLS_DB_HOST', 'localhost' );
|
||||
define( 'YOURLS_DB_HOST', '' );
|
||||
|
||||
/** MySQL tables prefix
|
||||
** YOURLS will create tables using this prefix (eg `yourls_url`, `yourls_options`, ...)
|
||||
@ -68,6 +68,7 @@ $yourls_user_passwords = [
|
||||
// You can have one or more 'login'=>'password' lines
|
||||
];
|
||||
|
||||
|
||||
/** URL shortening method: either 36 or 62
|
||||
** 36: generates all lowercase keywords (ie: 13jkm)
|
||||
** 62: generates mixed case keywords (ie: 13jKm or 13JKm)
|
||||
@ -94,11 +95,9 @@ $yourls_reserved_URL = [
|
||||
** Proxy Server
|
||||
!!!!!!!LIST!!!!!!!
|
||||
|
||||
168.8.209.253:8080
|
||||
47.176.153.17:80
|
||||
103.11.106.105:8181
|
||||
157.245.83.157:80
|
||||
54.200.36.47:80
|
||||
20.111.54.16:8123
|
||||
47.51.51.190:8080
|
||||
*/
|
||||
|
||||
define( 'YOURLS_PROXY', '54.200.36.47:80' );
|
||||
define( 'YOURLS_PROXY', '20.111.54.16:8123' );
|
||||
|
@ -1,46 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "steppinghat/emoji-detector",
|
||||
"version": "1.1.0",
|
||||
"version_normalized": "1.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SteppingHat/php-emoji-detector.git",
|
||||
"reference": "d2301e9553795e1ac2f3f2638438b3808654c57f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SteppingHat/php-emoji-detector/zipball/d2301e9553795e1ac2f3f2638438b3808654c57f",
|
||||
"reference": "d2301e9553795e1ac2f3f2638438b3808654c57f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^5.0@dev"
|
||||
},
|
||||
"time": "2021-03-18T06:03:22+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SteppingHat\\EmojiDetector\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Javan Eskander",
|
||||
"homepage": "https://javaneskander.com"
|
||||
}
|
||||
],
|
||||
"description": "Detect and validate emoji in an input string",
|
||||
"homepage": "https://github.com/steppinghat/emoji-detector"
|
||||
}
|
||||
]
|
@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="EmojiDetector">
|
||||
<directory>./test/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<ini name="xdebug.overload_var_dump" value="0" />
|
||||
</php>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
23
user/plugins/302-instead/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
302-instead
|
||||
===========
|
||||
|
||||
YOURLS plugin to send a 302 (temporary) redirect instead of 301 (permanent) for sites where shortlinks may change. This is a fork of the original 302-instead plugin by BrettR, hosted on GitHub at the following URL:
|
||||
|
||||
https://github.com/EpicPilgrim/302-instead
|
||||
|
||||
The plugin adds a menu option to allow you to select the mode you want to use:
|
||||
- 302 redirects for every URL (some clients may have old 301 redirects cached, you can't do much about that)
|
||||
- 301 redirects for every URL (this is the default YOURLS behaviour)
|
||||
- 302 redirects only for URLs that are not short URLs for the current YOURLS installation
|
||||
|
||||
Requirements
|
||||
|
||||
YOURLS 1.5+
|
||||
|
||||
Installation
|
||||
|
||||
Create a user/plugins/302-instead directory in YOURLS
|
||||
Place the plugin.php file in above directory
|
||||
Activate plugin in YOURLS
|
||||
|
||||
You can also clone the git repository into your plugins directory. This will allow you to update the plugin more easily.
|
99
user/plugins/302-instead/plugin.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Plugin Name: 302 Instead + 301 for YOURLS URLs
|
||||
Plugin URI: https://github.com/timcrockford/302-instead
|
||||
Description: Send a 302 (temporary) redirects that do not redirect to other short URLs and a 301 for YOURLS URLs
|
||||
Version: 1.2
|
||||
Author: BrettR / Tim Crockford
|
||||
Author URI: http://codearoundcorners.com/
|
||||
*/
|
||||
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
yourls_add_filter('redirect_code', 'temp_instead_function');
|
||||
yourls_add_action( 'plugins_loaded', 'temp_instead_admin_page_add' );
|
||||
|
||||
// This function will check the URL and the HTTP status code of the passed
|
||||
// in arguments. If the URL happens to be an existing short URL on the same
|
||||
// YOURLS installation, it does nothing. Otherwise it will send a 302
|
||||
// redirect. Useful when you want to change the short URLs that end users
|
||||
// might be using but you can't change.
|
||||
|
||||
function temp_instead_function($code, $url) {
|
||||
$match = strpos($url, yourls_site_url(false));
|
||||
$mode = intval(yourls_get_option( 'temp_instead_mode', 1 ));
|
||||
|
||||
// We check here if the url contains the YOURLS installation address,
|
||||
// and if it doesn't we'll return a 302 redirect if it isn't getting
|
||||
// one already.
|
||||
if ( $code != 302 && ($mode == 1 || ($match === false && $mode == 3))) {
|
||||
return 302;
|
||||
}
|
||||
|
||||
// We check here if the url contains the YOURLS installation address,
|
||||
// and if it does we'll return a 301 redirect if it isn't getting
|
||||
// one already.
|
||||
if ( $code != 301 && ($mode == 2 || ($match !== false && $mode == 3))) {
|
||||
return 301;
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
// Register our plugin admin page
|
||||
function temp_instead_admin_page_add() {
|
||||
yourls_register_plugin_page( 'temp_instead', 'Redirect Rules', 'temp_instead_admin_page_do' );
|
||||
}
|
||||
|
||||
// Display admin page
|
||||
function temp_instead_admin_page_do() {
|
||||
if( isset( $_POST['temp_instead_mode'] ) ) {
|
||||
yourls_verify_nonce( 'temp_instead' );
|
||||
temp_instead_admin_page_update();
|
||||
}
|
||||
|
||||
$mode = intval(yourls_get_option( 'temp_instead_mode', 1 ));
|
||||
$nonce = yourls_create_nonce( 'temp_instead' );
|
||||
|
||||
// If the option hasn't been added previously, we add the default value of everything using
|
||||
// 302 redirects.
|
||||
echo '<h2>302-Redirect Redirection Rules</h2>';
|
||||
echo '<p>This plugin allows you to configure how the 302-redirect plugin operates.</p>';
|
||||
echo '<form method="post">';
|
||||
echo '<input type="hidden" name="nonce" value="' . $nonce . '" />';
|
||||
|
||||
echo '<label for="temp_instead_mode">Select Redirect Mode:</label>';
|
||||
echo '<select id="temp_instead_mode" name="temp_instead_mode">';
|
||||
|
||||
$opt1 = ( $mode == 1 ? ' selected' : '');
|
||||
$opt2 = ( $mode == 2 ? ' selected' : '');
|
||||
$opt3 = ( $mode == 3 ? ' selected' : '');
|
||||
|
||||
echo '<option value=1' . $opt1 . '>Redirect all using 302 temporary redirect</option>';
|
||||
echo '<option value=2' . $opt2 . '>Redirect all using 301 permanent redirect</option>';
|
||||
echo '<option value=3' . $opt3 . '>Redirect full URLs using 302 and short URLs using 301</option>';
|
||||
|
||||
echo '<p><input type="submit" value="Update Redirect Mode" /></p>';
|
||||
|
||||
echo '</select>';
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
// Update option in database
|
||||
function temp_instead_admin_page_update() {
|
||||
$mode = $_POST['temp_instead_mode'];
|
||||
|
||||
if( $mode ) {
|
||||
$mode = intval($mode);
|
||||
|
||||
if ( yourls_get_option( 'temp_instead_mode' ) !== false ) {
|
||||
echo '<b>Redirect mode was updated successfully.</b>';
|
||||
yourls_update_option( 'temp_instead_mode', $mode );
|
||||
} else {
|
||||
echo '<b>Redirect mode was stored successfully.</b>';
|
||||
yourls_add_option( 'temp_instead_mode', $mode );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
35
user/plugins/BlackListIP/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
YourlsBlacklistIPs
|
||||
|
||||
Plugin for Yourls allowing to blacklist IPs
|
||||
|
||||
This plugin is intended to be used with YOURLS (cf. http://yourls.org)
|
||||
|
||||
It has been tested on YOURLS v1.5 and v1.5.1
|
||||
|
||||
Current version is 1.3, updated on 18/09/2012
|
||||
|
||||
Contact : Ludo at Ludo.Boggio+GitHub@GMail.com
|
||||
|
||||
INSTALL :
|
||||
- In /user/plugins, create a new folder named BlackListIP
|
||||
- In this new directory, copy the plugin.php and ludo_blacklist_ip_Check_IP_Module.php files from this repository
|
||||
- Go to the Plugins administration page and activate the plugin
|
||||
|
||||
You will see in the admin section a new admin page where you can add the IP addresses you want to blacklist.
|
||||
|
||||
Please enter one IP address per line. Other syntax should provide unexpected behaviours.
|
||||
|
||||
v1.0 : initialization
|
||||
v1.1 : Add admin page
|
||||
v1.2 : Add some checks on IP format and some warnings for use
|
||||
v1.3 : Add several possibilities to provide IP ranges :
|
||||
- A.B.C.D-X.Y.Z.T range : all IPs from A.B.C.D to X.Y.Z.T are blacklisted
|
||||
- A.B.C.0 range : all IPs from A.B.C.0 to A.B.C.255 are blacklisted
|
||||
- A.B.0.0 range : all IPs from A.B.0.0 to A.B.255.255 are blacklisted
|
||||
- A.0.0.0 range : all IPs from A.0.0.0 to A.255.255.255 are blacklisted
|
||||
- A.B.C.D/X.Y.Z.T : A.B.C.D is an IP address, X.Y.Z.T is a subnet mask, all IPs addresses corresponding to that IP and mask are blacklisted
|
||||
- A.B.C.D/T, T between 0 TO 32 : CIDR notation.
|
||||
|
||||
For explanations, feel free to check http://en.wikipedia.org/wiki/IP_address .
|
||||
|
||||
Actual roadmap is empty, but I'm open to suggestions. Feel free to contact me.
|
118
user/plugins/BlackListIP/plugin.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: BlackListIP
|
||||
Plugin URI: https://github.com/LudoBoggio/YourlsBlackListIPs
|
||||
Description: Plugin which block blacklisted IPs
|
||||
Version: 1.3
|
||||
Author: Ludo
|
||||
Author URI: http://ludovic.boggio.fr
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
include "ludo_blacklist_ip_Check_IP_Module.php";
|
||||
|
||||
// Hook the custom function into the 'pre_check_ip_flood' event
|
||||
yourls_add_action( 'pre_check_ip_flood', 'ludo_blacklist_ip_root' );
|
||||
|
||||
// Hook the admin page into the 'plugins_loaded' event
|
||||
yourls_add_action( 'plugins_loaded', 'ludo_blacklist_ip_add_page' );
|
||||
|
||||
// Get blacklisted IPs from YOURLS options feature and compare with current IP address
|
||||
function ludo_blacklist_ip_root ( $args ) {
|
||||
$IP = $args[0];
|
||||
$Intervalle_IP = yourls_get_option ('ludo_blacklist_ip_liste');
|
||||
$Intervalle_IP = ( $Intervalle_IP ) ? ( unserialize ( $Intervalle_IP ) ):((array)NULL);
|
||||
|
||||
foreach ( $Intervalle_IP as $value ) {
|
||||
$IPs = explode ( "-" , $value );
|
||||
|
||||
if ( $IP >= $IPs[0] AND $IP <= $IPs[1]) {
|
||||
// yourls_die ( "Your IP has been blacklisted.", "Black list",403);
|
||||
echo "<center>Your IP has been blacklisted.</center>";
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add admin page
|
||||
function ludo_blacklist_ip_add_page () {
|
||||
yourls_register_plugin_page( 'ludo_blacklist_ip', 'Blacklist IPs', 'ludo_blacklist_ip_do_page' );
|
||||
}
|
||||
|
||||
// Display admin page
|
||||
function ludo_blacklist_ip_do_page () {
|
||||
if( isset( $_POST['action'] ) && $_POST['action'] == 'blacklist_ip' ) {
|
||||
ludo_blacklist_ip_process ();
|
||||
} else {
|
||||
ludo_blacklist_ip_form ();
|
||||
}
|
||||
}
|
||||
|
||||
// Display form to administrate blacklisted IPs list
|
||||
function ludo_blacklist_ip_form () {
|
||||
$nonce = yourls_create_nonce( 'blacklist_ip' ) ;
|
||||
$liste_ip = yourls_get_option ('ludo_blacklist_ip_liste','Enter IP addresses here, one entry per line');
|
||||
if ($liste_ip != 'Enter IP addresses here, one entry per line' )
|
||||
$liste_ip_display = implode ( "\r\n" , unserialize ( $liste_ip ) );
|
||||
else
|
||||
$liste_ip_display=$liste_ip;
|
||||
echo <<<HTML
|
||||
<h2>BlackList IPs</h2>
|
||||
<form method="post">
|
||||
|
||||
<input type="hidden" name="action" value="blacklist_ip" />
|
||||
<input type="hidden" name="nonce" value="$nonce" />
|
||||
|
||||
<p>Blacklist following IPs (one range or IP per line, no wildcards allowed) :</p>
|
||||
<p><textarea cols="50" rows="10" name="blacklist_form">$liste_ip_display</textarea></p>
|
||||
|
||||
<p><input type="submit" value="Save" /></p>
|
||||
<p>I suggest to add here IPs that you saw adding bulk URL. It is your own responsibility to check the use of the IPs you block. WARNING : erroneous entries may create unexpected behaviours, please double-check before validation.</p>
|
||||
<p>Examples :
|
||||
<ul>
|
||||
<li>10.0.0.1/24 : blacklist from 10.0.0.0 to 10.0.0.255 (CIDR notation).</li>
|
||||
<li>192.168.1.2/255.255.255.128 : blacklist from 192.168.1.0 to 192.168.0.128.</li>
|
||||
<li>192.168.1.12-192.168.1.59 : blacklist from 192.168.1.12 to 192.168.1.59.</li>
|
||||
<li>192.168.0.0 : blacklist from 192.168.0.0 to 192.168.255.255</li>
|
||||
<li>10.0.0.58 : blacklist only 10.0.0.58 IP address.</li>
|
||||
</ul>
|
||||
</p>
|
||||
</form>
|
||||
HTML;
|
||||
}
|
||||
|
||||
// Update blacklisted IPs list
|
||||
function ludo_blacklist_ip_process () {
|
||||
// Check nonce
|
||||
yourls_verify_nonce( 'blacklist_ip' ) ;
|
||||
|
||||
// Check if the answer is correct.
|
||||
$IP_Form = explode ( "\r\n" , $_POST['blacklist_form'] ) ;
|
||||
|
||||
if (! is_array ($IP_Form) ) {
|
||||
echo "Bad answer, Blacklist not updated";
|
||||
die ();
|
||||
}
|
||||
|
||||
$boucle = 0;
|
||||
|
||||
foreach ($IP_Form as $value) {
|
||||
$Retour = ludo_blacklist_ip_Analyze_IP ( $value ) ;
|
||||
if ( $Retour != "NULL" ) {
|
||||
$IPList[$boucle++] = $Retour ;
|
||||
}
|
||||
}
|
||||
// Update list
|
||||
yourls_update_option ( 'ludo_blacklist_ip_liste', serialize ( $IPList ) );
|
||||
echo "Black list updated. New blacklist is " ;
|
||||
if ( count ( $IPList ) == 0 )
|
||||
echo "empty.";
|
||||
else {
|
||||
echo ":<BR />";
|
||||
foreach ($IPList as $value) echo $value."<BR />";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
22
user/plugins/YOURLS-Forward-Slash-In-Urls-master/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Allow Forward Slashes in Short URLs
|
||||
---------------------------------------------
|
||||
|
||||
- Plugin Name: Allow Forward Slashes in Short URLs
|
||||
- Plugin URI: http://williambargent.co.uk
|
||||
- Description: Allow Forward Slashes in Short URLs
|
||||
- Version: 1.0
|
||||
- Author: William Bargent
|
||||
|
||||
|
||||
This plugin will allow forward slashes `/` in keywords when shortening URLS with YOURLS.
|
||||
|
||||
*NOTE This plugin will not work with URL Forwarding plugins active. Deactivate before activating this plugin.
|
||||
|
||||
###Installation
|
||||
|
||||
1. Download these file as a .zip.
|
||||
2. Extract the three files and copy them into a `New Folder` called `forward-slash-in-urls`.
|
||||
3. Copy this folder.
|
||||
4. Paste this folder in your YOURLs directory under `users/plugins/`.
|
||||
5. Go to your plugin manager and click `Activate`.
|
22
user/plugins/YOURLS-Forward-Slash-In-Urls-master/plugin.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Plugin Name: Allow Forward Slashes in Short URLs
|
||||
Plugin URI: http://williambargent.co.uk
|
||||
Description: Allow Forward Slashes in Short URLs
|
||||
Version: 1.0
|
||||
Author: William Bargent
|
||||
Author URI: http://williambargent.co.uk
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
yourls_add_filter( 'get_shorturl_charset', 'slash_in_charset' );
|
||||
function slash_in_charset( $in ) {
|
||||
return $in.'/';
|
||||
}
|
||||
|
||||
|
||||
//This plugin will not work with URL forwarding plugins active
|
69
user/plugins/YourlsBlacklistDomains-master/README.md
Normal file
@ -0,0 +1,69 @@
|
||||
YourlsBlackListDomains
|
||||
======================
|
||||
|
||||
Plugin for Yourls that disallows blacklisted domains. Further, if YourlsBlacklistIPs is installed it also blacklists the submitter's IP address.
|
||||
|
||||
This plugin is intended to be used with YOURLS (cf. http://yourls.org)
|
||||
|
||||
It has been tested on YOURLS v1.5.1 and YourlsBlacklistIPs v1.3
|
||||
|
||||
Current version is 0.03
|
||||
|
||||
Contact : *apelly[ at ]len[ dot ]io*
|
||||
|
||||
**INSTALL :**
|
||||
|
||||
- In user/plugins, `git clone https://github.com/apelly/YourlsBlacklistDomains.git`
|
||||
- Go to the plugins administration page and activate the plugin.
|
||||
|
||||
**UPDATE :**
|
||||
|
||||
- In user/plugins/YourlsBlacklistDomains, `git pull https://github.com/apelly/YourlsBlacklistDomains.git`
|
||||
|
||||
**USAGE :**
|
||||
|
||||
You will see in the admin section a new admin page where you can manage the blacklist.
|
||||
|
||||
Enter one domain on each line, the blacklisting relies on simple substring matching within the whole URL.
|
||||
|
||||
If you have [YourlsBlacklistIPs](https://github.com/LudoBoggio/YourlsBlacklistIPs) installed and activated then any IP that attempts to shorten a blacklisted URL will automatically be blacklisted by that plugin too.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Thanks to [Panthro](https://github.com/Panthro) for [YourlsWhiteListDomains](https://github.com/Panthro/YourlsWhitelistDomains) which was basically all of the code for this, and for kindly giving permission to puplish under GPL.
|
||||
> You are free to fork whatever you want, that's what code is for!
|
||||
|
||||
Also thanks to [LudoBoggio](https://github.com/LudoBoggio) for the [YourlsBlacklistIPs](https://github.com/LudoBoggio/YourlsBlacklistIPs) plugin which was the base for YourlsWhiteListDomains.
|
||||
>I've written this plugin for the community, to help Yourls users, to help Yourls author, to help to spread this software, to pay my free use of it, and to learn a bit more of programming. I didn't provide any license informations because I never tried to understand them. Therefore, I leave you all rights to use my plugin in any way you want, the fact that it help to bring more Yourls user is just enough from my point of view.
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
v0.03 Fix some crap code (of mine)
|
||||
v0.02 Cosmetic changes
|
||||
v0.01 Initial code
|
||||
|
||||
---
|
||||
|
||||
Notice
|
||||
------
|
||||
|
||||
Neither YourlsWhiteListDomains, nor YourlsBlacklistIPs are distributed with licensing or copyright details but both [Panthro](https://github.com/Panthro) and [LudoBoggio](https://github.com/LudoBoggio) have given explicit permission to use and distribute their code.
|
||||
|
||||
**Copyright© (2012) Aaron Pelly**
|
||||
|
||||
**License**
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
130
user/plugins/YourlsBlacklistDomains-master/plugin.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: YourlsBlackListDomains
|
||||
Plugin URI: https://github.com/apelly/YourlsBlacklistDomains
|
||||
Description: Plugin which disallows blacklisted domains and bans the submitters IP address. GPL v3
|
||||
Version: 0.03
|
||||
Author: apelly
|
||||
Author URI: http://len.io
|
||||
*/
|
||||
/*
|
||||
Copyright(c) (2012) Aaron Pelly
|
||||
|
||||
License:
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
// Hook the custom function into the 'shunt_add_new_link' event
|
||||
yourls_add_filter( 'shunt_add_new_link', 'apelly_blacklist_domain_root' );
|
||||
|
||||
// Hook the admin page into the 'plugins_loaded' event
|
||||
yourls_add_action( 'plugins_loaded', 'apelly_blacklist_domain_add_page' );
|
||||
|
||||
// Get blacklisted domains from YOURLS options feature and compare with current domain address
|
||||
function apelly_blacklist_domain_root ( $bol, $url ) {
|
||||
$return = false;
|
||||
$domain_list = yourls_get_option ('apelly_blacklist_domain_list');
|
||||
if ( $domain_list ) {
|
||||
$domain_list = unserialize ( $domain_list );
|
||||
foreach($domain_list as $blacklisted_domain) {
|
||||
if (strpos($url,$blacklisted_domain)) {
|
||||
// Check if a YourlsBlacklistIPs is installed and active
|
||||
if (yourls_is_active_plugin( YOURLS_PLUGINDIR .'/BlackListIP/plugin.php' )) {
|
||||
$IP = yourls_get_IP();
|
||||
|
||||
// IP blacklisted already?
|
||||
ludo_blacklist_ip_root( array( $IP ) ); // <---- dies if ip is blacklisted
|
||||
|
||||
// fetch the blacklisted IP addresses
|
||||
$IP_List = yourls_get_option ('ludo_blacklist_ip_liste');
|
||||
$IP_List = ( $IP_List ) ? ( unserialize ( $IP_List ) ):((array)NULL);
|
||||
|
||||
// add this IP
|
||||
$Parsed_IP = ludo_blacklist_ip_Analyze_IP ( $IP ) ;
|
||||
if ( $Parsed_IP != "NULL" ) {
|
||||
$IP_List[] = $Parsed_IP ;
|
||||
}
|
||||
|
||||
// Update the blacklist
|
||||
yourls_update_option ( 'ludo_blacklist_ip_liste', serialize ( $IP_List ) );
|
||||
}
|
||||
|
||||
// stop
|
||||
//yourls_die( 'Blacklisted domain', 'Forbidden', 403 );
|
||||
"<center>Blacklisted domain.</center>";
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Add admin page
|
||||
function apelly_blacklist_domain_add_page () {
|
||||
yourls_register_plugin_page( 'apelly_blacklist_domain', 'Blacklist domains', 'apelly_blacklist_domain_do_page' );
|
||||
}
|
||||
|
||||
// Display admin page
|
||||
function apelly_blacklist_domain_do_page () {
|
||||
if( isset( $_POST['action'] ) && $_POST['action'] == 'blacklist_domain' ) {
|
||||
apelly_blacklist_domain_process ();
|
||||
} else {
|
||||
apelly_blacklist_domain_form ();
|
||||
}
|
||||
}
|
||||
|
||||
// Display form to administrate blacklisted domains list
|
||||
function apelly_blacklist_domain_form () {
|
||||
$nonce = yourls_create_nonce( 'blacklist_domain' ) ;
|
||||
$domain_list = yourls_get_option ('apelly_blacklist_domain_list','Enter domain addresses here, one per line');
|
||||
if ($domain_list != 'Enter domain addresses here, one per line' ){
|
||||
$domain_list_display = implode ( "\r\n" , unserialize ( $domain_list ) );
|
||||
}else{
|
||||
$domain_list_display = $domain_list;
|
||||
}
|
||||
echo <<<HTML
|
||||
<h2>BlackList domains</h2>
|
||||
<form method="post">
|
||||
|
||||
<input type="hidden" name="action" value="blacklist_domain" />
|
||||
<input type="hidden" name="nonce" value="$nonce" />
|
||||
|
||||
<p>Blacklist following domains</p>
|
||||
<p><textarea cols="60" rows="15" name="blacklist_form">$domain_list_display</textarea></p>
|
||||
|
||||
<p><input type="submit" value="Save" /></p>
|
||||
</form>
|
||||
HTML;
|
||||
}
|
||||
|
||||
// Update blacklisted domains list
|
||||
function apelly_blacklist_domain_process () {
|
||||
// Check nonce
|
||||
yourls_verify_nonce( 'blacklist_domain' ) ;
|
||||
|
||||
// Update list
|
||||
$blacklist_form = explode ( "\r\n" , $_POST['blacklist_form'] ) ;
|
||||
yourls_update_option ( 'apelly_blacklist_domain_list', serialize($blacklist_form) );
|
||||
echo "Black list updated. New blacklist is " ;
|
||||
if ( count ( $blacklist_form ) == 0 )
|
||||
echo "empty.";
|
||||
else {
|
||||
echo ":<BR />";
|
||||
foreach ($blacklist_form as $value) echo $value."<BR />";
|
||||
}
|
||||
}
|
||||
?>
|
104
user/plugins/antispam/plugin.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Anti spam
|
||||
Plugin URI: http://yourls.org/
|
||||
Description: Absolute anti-spam plugin. Checks URL against major black lists and removes all crap. Might OR MIGHT NOT work for you. Read the readme.
|
||||
Version: 1.0.4
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
|
||||
// Check for spam when someone adds a new link
|
||||
yourls_add_filter( 'shunt_add_new_link', 'ozh_yourls_antispam_check_add' );
|
||||
function ozh_yourls_antispam_check_add( $false, $url ) {
|
||||
// Sanitize URL and make sure there's a protocol
|
||||
$url = yourls_sanitize_url( $url );
|
||||
|
||||
// only check for 'http(s)'
|
||||
if( !in_array( yourls_get_protocol( $url ), array( 'http://', 'https://' ) ) )
|
||||
return $false;
|
||||
|
||||
if ( ozh_yourls_antispam_is_blacklisted( $url ) === yourls_apply_filter( 'ozh_yourls_antispam_malformed', 'malformed' ) ) {
|
||||
return array(
|
||||
'status' => 'fail',
|
||||
'code' => 'error:nourl',
|
||||
'message' => yourls__( 'Missing or malformed URL' ),
|
||||
'errorCode' => '400',
|
||||
);
|
||||
}
|
||||
|
||||
if ( ozh_yourls_antispam_is_blacklisted( $url ) != false ) {
|
||||
return array(
|
||||
'status' => 'fail',
|
||||
'code' => 'error:spam',
|
||||
'message' => 'This domain is blacklisted',
|
||||
'errorCode' => '403',
|
||||
);
|
||||
}
|
||||
|
||||
// All clear, not interrupting the normal flow of events
|
||||
return $false;
|
||||
}
|
||||
|
||||
|
||||
// Has the remote link become compromised lately? Check on redirection
|
||||
yourls_add_action( 'redirect_shorturl', 'ozh_yourls_antispam_check_redirect' );
|
||||
function ozh_yourls_antispam_check_redirect( $url, $keyword = false ) {
|
||||
|
||||
if( is_array( $url ) && $keyword == false ) {
|
||||
$keyword = $url[1];
|
||||
$url = $url[0];
|
||||
}
|
||||
|
||||
// Check when the link was added
|
||||
// If shorturl is fresh (ie probably clicked more often?) check once every 15 times, otherwise once every 5 times
|
||||
// Define fresh = 3 days = 259200 secondes
|
||||
// TODO: when there's a shorturl_meta table, store last check date to allow checking every 2 or 3 days
|
||||
$now = date( 'U' );
|
||||
$then = date( 'U', strtotime( yourls_get_keyword_timestamp( $keyword ) ) );
|
||||
$chances = ( ( $now - $then ) > 259200 ? 15 : 5 );
|
||||
|
||||
if( $chances == mt_rand( 1, $chances ) ) {
|
||||
if( ozh_yourls_antispam_is_blacklisted( $url ) != false ) {
|
||||
// Delete link & die
|
||||
yourls_delete_link_by_keyword( $keyword );
|
||||
yourls_die( 'This domain has been blacklisted. This short URL has been deleted from our record.', 'Domain blacklisted', '403' );
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing, move along
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Is the link spam? true for "yes it's shit", false for "nope, safe"
|
||||
function ozh_yourls_antispam_is_blacklisted( $url ) {
|
||||
$parsed = parse_url( $url );
|
||||
|
||||
if( !isset( $parsed['host'] ) )
|
||||
return yourls_apply_filter( 'ozh_yourls_antispam_malformed', 'malformed' );
|
||||
|
||||
// Remove www. from domain (but not from www.com)
|
||||
$parsed['host'] = preg_replace( '/^www\.(.+\.)/i', '$1', $parsed['host'] );
|
||||
|
||||
// Major blacklists. There's a filter if you want to manipulate this.
|
||||
$blacklists = yourls_apply_filter( 'ozh_yourls_antispam_list',
|
||||
array(
|
||||
'dbl.spamhaus.org',
|
||||
'multi.surbl.org',
|
||||
)
|
||||
);
|
||||
|
||||
// Check against each blacklist, exit if blacklisted
|
||||
foreach( $blacklists as $blacklist ) {
|
||||
$domain = $parsed['host'] . '.' . $blacklist . '.';
|
||||
$record = @dns_get_record( $domain );
|
||||
|
||||
if( $record && count( $record ) > 0 )
|
||||
return yourls_apply_filter( 'ozh_yourls_antispam_blacklisted', true );
|
||||
}
|
||||
|
||||
// All clear, probably not spam
|
||||
return yourls_apply_filter( 'ozh_yourls_antispam_clean', false );
|
||||
}
|
19
user/plugins/antispam/readme.md
Normal file
@ -0,0 +1,19 @@
|
||||
Plugin for YOURLS 1.5+: Antispam
|
||||
|
||||
# What for
|
||||
|
||||
This is a __merciless__ __antispam__ plugin that uses the three major blacklists (<a href="http://spamhaus.org">Spamhaus</a>, <a href="http://uribl.com/">URIBL</a> and <a href="http://surbl.org/">SURBL</a>).
|
||||
|
||||
URL are checked against the blacklist when short urls are created. They are also randomly checked when someone follows a short
|
||||
URL and if the link has been compromised recently, the short URL is deleted.
|
||||
|
||||
# How to
|
||||
|
||||
* In `/user/plugins`, create a new folder named `antispam`
|
||||
* Drop these files in that directory
|
||||
* Go to the Plugins administration page and activate the plugin
|
||||
* Have fun
|
||||
|
||||
# Disclaimer
|
||||
|
||||
Checking against blacklists may or may not work for you, this may depend on the type of spam you are getting and on other factors such as your server IP, your server ISP, the DNS you are using. It may even result in all domains being blacklisted from your server. Try and see.
|
686
user/plugins/authMgrPlus/plugin.php
Normal file
@ -0,0 +1,686 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Auth Manager Plus
|
||||
Plugin URI: https://github.com/joshp23/YOURLS-AuthMgrPlus
|
||||
Description: Role Based Access Controlls with seperated user data for authenticated users
|
||||
Version: 2.3.1
|
||||
Author: Josh Panter, nicwaller, Ian Barber <ian.barber@gmail.com>
|
||||
Author URI: https://unfettered.net
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
/****************** SET UP CONSTANTS ******************/
|
||||
|
||||
class ampRoles {
|
||||
const Administrator = 'Administrator';
|
||||
const Editor = 'Editor';
|
||||
const Contributor = 'Contributor';
|
||||
}
|
||||
|
||||
class ampCap {
|
||||
const ShowAdmin = 'ShowAdmin';
|
||||
const AddURL = 'AddURL';
|
||||
const DeleteURL = 'DeleteURL';
|
||||
const EditURL = 'EditURL';
|
||||
const ShareURL = 'ShareURL';
|
||||
const Traceless = 'Traceless';
|
||||
const ManageAnonURL = 'ManageAnonURL';
|
||||
const ManageUsrsURL = 'ManageUsrsURL';
|
||||
const ManagePlugins = 'ManagePlugins';
|
||||
const API = 'API';
|
||||
const APIu = 'APIu';
|
||||
const ViewStats = 'ViewStats';
|
||||
const ViewAll = 'ViewAll';
|
||||
}
|
||||
|
||||
/********** Add hooks to intercept functionality in CORE **********/
|
||||
|
||||
yourls_add_action( 'load_template_infos', 'amp_intercept_stats' );
|
||||
function amp_intercept_stats() {
|
||||
if ( 'YOURLS_PRIVATE_INFOS' === true ) {
|
||||
amp_require_capability( ampCap::ViewStats );
|
||||
}
|
||||
}
|
||||
|
||||
yourls_add_action( 'api', 'amp_intercept_api' );
|
||||
function amp_intercept_api() {
|
||||
if ( 'YOURLS_PRIVATE_API' === true ) {
|
||||
if ( isset( $_REQUEST['shorturl'] ) || isset( $_REQUEST['stats'] ) ) {
|
||||
amp_require_capability( ampCap::APIu );
|
||||
} else {
|
||||
amp_require_capability( ampCap::API );
|
||||
}
|
||||
}
|
||||
}
|
||||
yourls_add_action( 'auth_successful', function() {
|
||||
if( yourls_is_admin() ) amp_intercept_admin();
|
||||
} );
|
||||
|
||||
/**
|
||||
* YOURLS processes most actions in the admin page. It would be ideal
|
||||
* to add a unique hook for each action, but unfortunately we need to
|
||||
* hook the admin page load itself, and try to figure out what action
|
||||
* is intended.
|
||||
*
|
||||
* TODO: look for these hooks
|
||||
*
|
||||
* At this point, reasonably assume that the current request is for
|
||||
* a rendering of the admin page.
|
||||
*/
|
||||
function amp_intercept_admin() {
|
||||
amp_require_capability( ampCap::ShowAdmin );
|
||||
|
||||
// we use this GET param to send up a feedback notice to user
|
||||
if ( isset( $_GET['access'] ) && $_GET['access']=='denied' ) {
|
||||
yourls_add_notice('Access Denied');
|
||||
}
|
||||
|
||||
// allow manipulation of this list ( be mindfull of extending Auth mp Capability class if needed )
|
||||
$action_capability_map = yourls_apply_filter( 'amp_action_capability_map',
|
||||
array( 'add' => ampCap::AddURL,
|
||||
'delete' => ampCap::DeleteURL,
|
||||
'edit_display' => ampCap::EditURL,
|
||||
'edit_save' => ampCap::EditURL,
|
||||
'activate' => ampCap::ManagePlugins,
|
||||
'deactivate' => ampCap::ManagePlugins,
|
||||
) );
|
||||
|
||||
// Key actions like Add/Edit/Delete are AJAX requests
|
||||
if ( yourls_is_Ajax() ) {
|
||||
|
||||
// Define some boundaries for ownership
|
||||
// Allow some flexability with those boundaries
|
||||
$restricted_actions = yourls_apply_filter( 'amp_restricted_ajax_actions',
|
||||
array( 'edit_display',
|
||||
'edit_save',
|
||||
'delete'
|
||||
) );
|
||||
|
||||
$action_keyword = $_REQUEST['action'];
|
||||
$cap_needed = $action_capability_map[$action_keyword];
|
||||
|
||||
// Check the action against those boundaries
|
||||
if ( in_array( $action_keyword, $restricted_actions) ) {
|
||||
$keyword = $_REQUEST['keyword'];
|
||||
$do = amp_manage_keyword( $keyword, $cap_needed );
|
||||
} else {
|
||||
$do = amp_have_capability( $cap_needed );
|
||||
}
|
||||
|
||||
if ( $do !== true ) {
|
||||
$err = array();
|
||||
$err['status'] = 'fail';
|
||||
$err['code'] = 'error:authorization';
|
||||
$err['message'] = 'Access Denied';
|
||||
$err['errorCode'] = '403';
|
||||
echo json_encode( $err );
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
// Intercept requests for plugin management
|
||||
if( isset( $_SERVER['REQUEST_URI'] ) && preg_match('/\/admin\/plugins\.php.*/', $_SERVER['REQUEST_URI'] ) ) {
|
||||
// Is this a plugin page request?
|
||||
if ( isset( $_REQUEST['page'] ) ) {
|
||||
// Is this an allowed plugin?
|
||||
global $amp_allowed_plugin_pages;
|
||||
if ( amp_have_capability( ampCap::ManagePlugins ) !== true) {
|
||||
$r = $_REQUEST['page'];
|
||||
if(!in_array($r, $amp_allowed_plugin_pages ) ) {
|
||||
yourls_redirect( yourls_admin_url( '?access=denied' ), 302 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Should this user touch plugins?
|
||||
if ( amp_have_capability( ampCap::ManagePlugins ) !== true) {
|
||||
yourls_redirect( yourls_admin_url( '?access=denied' ), 302 );
|
||||
}
|
||||
}
|
||||
|
||||
// intercept requests for global plugin management actions
|
||||
if (isset( $_REQUEST['plugin'] ) ) {
|
||||
$action_keyword = $_REQUEST['action'];
|
||||
$cap_needed = $action_capability_map[$action_keyword];
|
||||
if ( $cap_needed !== NULL && amp_have_capability( $cap_needed ) !== true) {
|
||||
yourls_redirect( yourls_admin_url( '?access=denied' ), 302 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cosmetic filter: removes disallowed buttons from link list per short link
|
||||
*/
|
||||
|
||||
yourls_add_filter( 'table_add_row_action_array', 'amp_ajax_button_check' );
|
||||
function amp_ajax_button_check( $actions, $keyword ) {
|
||||
// define the amp capabilities that map to the buttons
|
||||
$button_cap_map = array('stats' => ampCap::ViewStats,
|
||||
'share' => ampCap::ShareURL,
|
||||
'edit' => ampCap::EditURL,
|
||||
'delete' => ampCap::DeleteURL,
|
||||
);
|
||||
|
||||
$button_cap_map = yourls_apply_filter( 'amp_button_capability_map', $button_cap_map );
|
||||
|
||||
// define restricted buttons
|
||||
$restricted_buttons = array('delete', 'edit');
|
||||
if ( 'YOURLS_PRIVATE_INFOS' === true )
|
||||
$restricted_buttons += ['stats'];
|
||||
|
||||
$restricted_buttons = yourls_apply_filter( 'amp_restricted_buttons', $restricted_buttons );
|
||||
|
||||
// unset any disallowed buttons
|
||||
foreach ( $actions as $action => $vars ) {
|
||||
$cap_needed = $button_cap_map[$action];
|
||||
if ( in_array( $action, $restricted_buttons) )
|
||||
$show = amp_manage_keyword( $keyword, $cap_needed );
|
||||
else
|
||||
$show = amp_have_capability( $cap_needed );
|
||||
if (!$show)
|
||||
unset( $actions[$action] );
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cosmetic filter: removes disallowed plugins from link list
|
||||
*/
|
||||
|
||||
yourls_add_filter( 'admin_sublinks', 'amp_admin_sublinks' );
|
||||
function amp_admin_sublinks( $links ) {
|
||||
|
||||
global $amp_allowed_plugin_pages;
|
||||
|
||||
if( empty( $links['plugins'] ) ) {
|
||||
|
||||
unset($links['plugins']);
|
||||
|
||||
} else {
|
||||
|
||||
if ( amp_have_capability( ampCap::ManagePlugins ) !== true) {
|
||||
foreach( $links['plugins'] as $link => $ar ) {
|
||||
if(!in_array($link, $amp_allowed_plugin_pages) )
|
||||
unset($links['plugins'][$link]);
|
||||
}
|
||||
}
|
||||
sort($links['plugins']);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cosmetic filter: displays currently available roles
|
||||
* by hovering mouse over the username in logout link.
|
||||
*/
|
||||
|
||||
yourls_add_filter( 'logout_link', 'amp_html_append_roles' );
|
||||
function amp_html_append_roles( $original ) {
|
||||
if ( amp_is_valid_user() ) {
|
||||
$listcaps = implode(', ', amp_current_capabilities());
|
||||
return '<div title="'.$listcaps.'">'.$original.'</div>';
|
||||
} else {
|
||||
return $original;
|
||||
}
|
||||
}
|
||||
|
||||
/**************** CAPABILITY TESTING ****************/
|
||||
|
||||
/*
|
||||
* If capability is not permitted in current context, then abort.
|
||||
* This is the most basic way to intercept unauthorized usage.
|
||||
*/
|
||||
// TODO: API responses!
|
||||
function amp_require_capability( $capability ) {
|
||||
if ( !amp_have_capability( $capability ) ) {
|
||||
// If the user can't view admin interface, return a plain error.
|
||||
if ( !amp_have_capability( ampCap::ShowAdmin ) ) {
|
||||
// header("HTTP/1.0 403 Forbidden");
|
||||
die('Require permissions to show admin interface.');
|
||||
}
|
||||
// Otherwise, render errors in admin interface
|
||||
yourls_redirect( yourls_admin_url( '?access=denied' ), 302 );
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
// Heart of system - Can the user do "X"?
|
||||
function amp_have_capability( $capability ) {
|
||||
|
||||
global $amp_anon_capabilities;
|
||||
global $amp_role_capabilities;
|
||||
global $amp_admin_ipranges;
|
||||
global $amp_default_role;
|
||||
|
||||
// Make sure the environment has been setup
|
||||
amp_env_check();
|
||||
|
||||
// Check anon capabilities
|
||||
$return = in_array( $capability, $amp_anon_capabilities );
|
||||
|
||||
// Check user-role based auth
|
||||
if( !$return ) {
|
||||
// Only users have roles
|
||||
if ( !amp_is_valid_user() ) //XXX
|
||||
return false;
|
||||
// List capabilities of particular user role
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : NULL;
|
||||
$user_caps = array();
|
||||
if ( amp_user_is_assigned ( $user ) )
|
||||
foreach ( $amp_role_capabilities as $rolename => $rolecaps )
|
||||
if ( amp_user_has_role( $user, $rolename ) )
|
||||
$user_caps = array_merge( $user_caps, $rolecaps );
|
||||
|
||||
elseif ( isset( $amp_default_role ) && in_array ($amp_default_role, array_keys( $amp_role_capabilities ) ) )
|
||||
$user_caps = $amp_role_capabilities [ $amp_default_role ];
|
||||
|
||||
$user_caps = array_unique( $user_caps );
|
||||
// Is the requested capability in this list?
|
||||
$return = in_array( $capability, $user_caps );
|
||||
}
|
||||
|
||||
// Is user connecting from an admin designated IP?
|
||||
if( !$return ) {
|
||||
// the array of ranges: '127.0.0.0/8' will always be admin
|
||||
foreach ($amp_admin_ipranges as $range) {
|
||||
$return = amp_cidr_match( $_SERVER['REMOTE_ADDR'], $range );
|
||||
if( $return )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Determine if a user has been assigned a role
|
||||
function amp_user_is_assigned ( $username ) {
|
||||
|
||||
global $amp_role_assignment;
|
||||
if ( empty( $amp_role_assignment ) )
|
||||
return false;
|
||||
|
||||
$return = false;
|
||||
|
||||
foreach ( $amp_role_assignment as $role )
|
||||
if ( in_array( $username, $role ) ) {
|
||||
$return = true;
|
||||
break;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Determine whether a specific user has a role.
|
||||
function amp_user_has_role( $username, $rolename ) {
|
||||
|
||||
global $amp_role_assignment;
|
||||
|
||||
// if no role assignments are created, grant everything FIXME: Make 'admin'
|
||||
// so the site still works even if stuff is configured wrong
|
||||
if ( empty( $amp_role_assignment ) )
|
||||
return true;
|
||||
|
||||
// do this the case-insensitive way
|
||||
// the entire array was made lowercase in environment check
|
||||
$username = strtolower($username);
|
||||
$rolename = strtolower($rolename);
|
||||
|
||||
// if the role doesn't exist, give up now.
|
||||
if ( !in_array( $rolename, array_keys( $amp_role_assignment ) ) )
|
||||
return false;
|
||||
|
||||
$users_in_role = $amp_role_assignment[$rolename];
|
||||
return in_array( $username, $users_in_role );
|
||||
}
|
||||
|
||||
/********************* KEYWORD OWNERSHIP ************************/
|
||||
|
||||
// Filter out restricted access to keyword data in...
|
||||
// Admin list
|
||||
yourls_add_filter( 'admin_list_where', 'amp_admin_list_where' );
|
||||
function amp_admin_list_where($where) {
|
||||
|
||||
if ( amp_have_capability( ampCap::ViewAll ) )
|
||||
return $where; // Allow admin/editor users to see the lot.
|
||||
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : NULL;
|
||||
$where['sql'] = $where['sql'] . " AND (`user` = :user OR `user` IS NULL) ";
|
||||
$where['binds']['user'] = $user;
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
// API stats
|
||||
yourls_add_filter( 'api_url_stats', 'amp_api_url_stats' );
|
||||
function amp_api_url_stats( $return, $shorturl ) {
|
||||
|
||||
$keyword = str_replace( YOURLS_SITE . '/' , '', $shorturl ); // accept either 'http://ozh.in/abc' or 'abc'
|
||||
$keyword = yourls_sanitize_string( $keyword );
|
||||
$keyword = addslashes($keyword);
|
||||
|
||||
if( ( !defined('YOURLS_PRIVATE_INFOS') || YOURLS_PRIVATE_INFOS !== false )
|
||||
&& !amp_access_keyword($keyword) )
|
||||
return array('simple' => "URL is owned by another user", 'message' => 'URL is owned by another user', 'errorCode' => 403);
|
||||
|
||||
else
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Info pages
|
||||
yourls_add_action( 'pre_yourls_infos', 'amp_pre_yourls_infos' );
|
||||
function amp_pre_yourls_infos( $keyword ) {
|
||||
|
||||
if( yourls_is_private() && !amp_access_keyword($keyword) ) {
|
||||
|
||||
if ( !amp_is_valid_user() )
|
||||
yourls_redirect( yourls_admin_url( '?access=denied' ), 302 );
|
||||
else
|
||||
yourls_redirect( YOURLS_SITE, 302 );
|
||||
}
|
||||
}
|
||||
|
||||
// DB stats
|
||||
yourls_add_filter( 'get_db_stats', 'amp_get_db_stats' );
|
||||
function amp_get_db_stats( $return, $where ) {
|
||||
|
||||
if ( amp_have_capability( ampCap::ViewAll ) )
|
||||
return $return; // Allow admin/editor users to see the lot.
|
||||
|
||||
// or... filter results
|
||||
global $ydb;
|
||||
$table_url = YOURLS_DB_TABLE_URL;
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : NULL;
|
||||
|
||||
$where['sql'] = $where['sql'] . " AND (`user` = :user OR `user` IS NULL) ";
|
||||
$where['binds']['user'] = $user;
|
||||
|
||||
$sql = "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'];
|
||||
$binds = $where['binds'];
|
||||
|
||||
$totals = $ydb->fetchObject($sql, $binds);
|
||||
|
||||
$return = array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum );
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Fine tune track-me-not
|
||||
yourls_add_action('redirect_shorturl', 'amp_tracking');
|
||||
function amp_tracking( $u, $k = false ) {
|
||||
if( amp_is_valid_user() && ( amp_keyword_owner($k) || amp_have_capability( ampCap::Traceless ) ) ) {
|
||||
// No logging
|
||||
yourls_add_filter( 'shunt_update_clicks', function( ) { return true; } );
|
||||
yourls_add_filter( 'shunt_log_redirect', function( ) { return true; } );
|
||||
}
|
||||
}
|
||||
/********************* HOUSEKEEPING ************************/
|
||||
// Validate environment setup
|
||||
function amp_env_check() {
|
||||
global $amp_anon_capabilities;
|
||||
global $amp_role_capabilities;
|
||||
global $amp_role_assignment;
|
||||
global $amp_admin_ipranges;
|
||||
global $amp_allowed_plugin_pages;
|
||||
|
||||
if ( !isset( $amp_anon_capabilities) ) {
|
||||
$amp_anon_capabilities = array();
|
||||
}
|
||||
|
||||
if ( !isset( $amp_role_capabilities) ) {
|
||||
$amp_role_capabilities = array(
|
||||
ampRoles::Administrator => array(
|
||||
ampCap::ShowAdmin,
|
||||
ampCap::AddURL,
|
||||
ampCap::EditURL,
|
||||
ampCap::DeleteURL,
|
||||
ampCap::ShareURL,
|
||||
ampCap::Traceless,
|
||||
ampCap::ManageAnonURL,
|
||||
ampCap::ManageUsrsURL,
|
||||
ampCap::ManagePlugins,
|
||||
ampCap::API,
|
||||
ampCap::APIu,
|
||||
ampCap::ViewStats,
|
||||
ampCap::ViewAll,
|
||||
),
|
||||
ampRoles::Editor => array(
|
||||
ampCap::ShowAdmin,
|
||||
ampCap::AddURL,
|
||||
ampCap::EditURL,
|
||||
ampCap::DeleteURL,
|
||||
ampCap::ShareURL,
|
||||
ampCap::Traceless,
|
||||
ampCap::ManageAnonURL,
|
||||
ampCap::APIu,
|
||||
ampCap::ViewStats,
|
||||
ampCap::ViewAll,
|
||||
),
|
||||
ampRoles::Contributor => array(
|
||||
ampCap::ShowAdmin,
|
||||
ampCap::AddURL,
|
||||
ampCap::EditURL,
|
||||
ampCap::DeleteURL,
|
||||
ampCap::ShareURL,
|
||||
ampCap::APIu,
|
||||
ampCap::ViewStats,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ( !isset( $amp_role_assignment ) ) {
|
||||
$amp_role_assignment = array();
|
||||
}
|
||||
|
||||
if ( !isset( $amp_admin_ipranges ) ) {
|
||||
$amp_admin_ipranges = array(
|
||||
'127.0.0.0/8',
|
||||
);
|
||||
}
|
||||
|
||||
if ( !isset( $amp_allowed_plugin_pages ) ) {
|
||||
$amp_allowed_plugin_pages = array(
|
||||
);
|
||||
}
|
||||
|
||||
// convert role assignment table to lower case if it hasn't been done already
|
||||
// this makes searches much easier!
|
||||
$amp_role_assignment_lower = array();
|
||||
foreach ( $amp_role_assignment as $key => $value ) {
|
||||
$t_key = strtolower( $key );
|
||||
$t_value = array_map('strtolower', $value);
|
||||
$amp_role_assignment_lower[$t_key] = $t_value;
|
||||
}
|
||||
$amp_role_assignment = $amp_role_assignment_lower;
|
||||
unset($amp_role_assignment_lower);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Activation: add the user column to the URL table if not added
|
||||
yourls_add_action('activated_plugin', 'amp_activated');
|
||||
function amp_activated() {
|
||||
global $ydb;
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$sql = "DESCRIBE `".$table."`";
|
||||
$results = $ydb->fetchObjects($sql);
|
||||
|
||||
$activated = false;
|
||||
foreach($results as $r) {
|
||||
if($r->Field == 'user') {
|
||||
$activated = true;
|
||||
}
|
||||
}
|
||||
if(!$activated) {
|
||||
if ($version) {
|
||||
$sql = "ALTER TABLE `".$table."` ADD `user` VARCHAR(255) NULL";
|
||||
$insert = $ydb->fetchAffected($sql);
|
||||
} else {
|
||||
$ydb->query("ALTER TABLE `".$table."` ADD `user` VARCHAR(255) NULL");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** HELPER FUNCTIONS ********************/
|
||||
|
||||
// List currently available capabilities
|
||||
function amp_current_capabilities() {
|
||||
$current_capabilities = array();
|
||||
$all_capabilities = array(
|
||||
ampCap::ShowAdmin,
|
||||
ampCap::AddURL,
|
||||
ampCap::EditURL,
|
||||
ampCap::DeleteURL,
|
||||
ampCap::ShareURL,
|
||||
ampCap::Traceless,
|
||||
ampCap::ManageAnonURL,
|
||||
ampCap::ManageUsrsURL,
|
||||
ampCap::ManagePlugins,
|
||||
ampCap::API,
|
||||
ampCap::APIu,
|
||||
ampCap::ViewStats,
|
||||
ampCap::ViewAll,
|
||||
);
|
||||
|
||||
foreach ( $all_capabilities as $cap ) {
|
||||
if ( amp_have_capability( $cap ) ) {
|
||||
$current_capabilities[] = $cap;
|
||||
}
|
||||
}
|
||||
// allow manipulation of this list ( be mindfull of extending the ampCap class if needed )
|
||||
$current_capabilities = yourls_apply_filter( 'amp_current_capabilities', $current_capabilities);
|
||||
return $current_capabilities;
|
||||
}
|
||||
|
||||
// Check for IP in a range
|
||||
// from: http://stackoverflow.com/questions/594112/matching-an-ip-to-a-cidr-mask-in-php5
|
||||
function amp_cidr_match($ip, $range) {
|
||||
list ($subnet, $bits) = explode('/', $range);
|
||||
$ip = ip2long($ip);
|
||||
$subnet = ip2long($subnet);
|
||||
$mask = -1 << (32 - $bits);
|
||||
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
|
||||
return ($ip & $mask) == $subnet;
|
||||
}
|
||||
|
||||
// Check user access to a keyword ( can they see it )
|
||||
function amp_access_keyword( $keyword ) {
|
||||
|
||||
$users = array( YOURLS_USER !== false ? YOURLS_USER : NULL , NULL );
|
||||
$owner = amp_keyword_owner( $keyword );
|
||||
if ( amp_have_capability( ampCap::ViewAll ) || in_array( $owner , $users ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check user rights to a keyword ( can manage it )
|
||||
function amp_manage_keyword( $keyword, $capability ) {
|
||||
$return = false; // default is to deny access
|
||||
if ( amp_is_valid_user() ) { // only authenticated users can manaage keywords
|
||||
$owner = amp_keyword_owner($keyword);
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : NULL;
|
||||
if ( amp_have_capability( ampCap::ManageUsrsURL ) // Admin?
|
||||
|| ( $owner === NULL && amp_have_capability( ampCap::ManageAnonURL ) ) // Editor?
|
||||
|| ( $owner === $user && amp_have_capability( $capability ) ) ) // Self Edit?
|
||||
$return = true;
|
||||
}
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
// Check keyword ownership
|
||||
function amp_keyword_owner( $keyword ) {
|
||||
global $ydb;
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$binds = array( 'keyword' => $keyword );
|
||||
$sql = "SELECT * FROM `$table` WHERE `keyword` = :keyword";
|
||||
$result = $ydb->fetchOne($sql, $binds);
|
||||
return $result['user'];
|
||||
}
|
||||
|
||||
// Record user info on keyword creation
|
||||
yourls_add_action( 'insert_link', 'amp_insert_link' );
|
||||
function amp_insert_link($actions) {
|
||||
global $ydb;
|
||||
|
||||
$keyword = $actions[2];
|
||||
$user = defined('YOURLS_USER') ? YOURLS_USER : NULL;
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
|
||||
// Insert $keyword against $username
|
||||
$binds = array( 'user' => $user,
|
||||
'keyword' => $keyword);
|
||||
$sql = "UPDATE `$table` SET `user` = :user WHERE `keyword` = :keyword";
|
||||
$result = $ydb->fetchAffected($sql, $binds);
|
||||
}
|
||||
|
||||
// Quick user validation without triggering hooks
|
||||
function amp_is_valid_user() {
|
||||
|
||||
$valid = defined( 'YOURLS_USER' ) ? true : false;
|
||||
|
||||
if ( !$valid ) {
|
||||
|
||||
if ( yourls_is_API()
|
||||
&& isset( $_REQUEST['timestamp'] ) && !empty($_REQUEST['timestamp'] )
|
||||
&& isset( $_REQUEST['signature'] ) && !empty($_REQUEST['signature'] ) )
|
||||
$valid = yourls_check_signature_timestamp();
|
||||
elseif ( yourls_is_API()
|
||||
&& !isset( $_REQUEST['timestamp'] )
|
||||
&& isset( $_REQUEST['signature'] ) && !empty( $_REQUEST['signature'] ) )
|
||||
$valid = yourls_check_signature();
|
||||
elseif ( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] )
|
||||
&& !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password'] ) )
|
||||
$valid = yourls_check_username_password();
|
||||
elseif ( !yourls_is_API() && isset( $_COOKIE[ yourls_cookie_name() ] ) )
|
||||
$valid = yourls_check_auth_cookie();
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
yourls_add_action( 'html_footer', 'amp_format_table_javascript' );
|
||||
|
||||
function amp_format_table_javascript() {
|
||||
echo <<<JS
|
||||
|
||||
<script>
|
||||
if($("body").hasClass("index")) {
|
||||
document.querySelector("#main_table tfoot th").colSpan = 7;
|
||||
document.querySelector("#nourl_found td").colSpan = 7;
|
||||
}
|
||||
</script>
|
||||
|
||||
JS;
|
||||
}
|
||||
|
||||
function array_insert($array, $position, $insert_array) {
|
||||
$first_array = array_splice($array, 0, $position);
|
||||
$array = array_merge($first_array, $insert_array, $array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
yourls_add_filter('table_head_cells', 'amp_username_table_head');
|
||||
function amp_username_table_head( $cells ) {
|
||||
$user_head = array( 'username' => 'Username' );
|
||||
$cells = array_insert($cells, 5, $user_head);
|
||||
return $cells;
|
||||
}
|
||||
|
||||
yourls_add_filter('table_add_row_cell_array', 'amp_add_user_row');
|
||||
function amp_add_user_row( $cells, $keyword ) {
|
||||
$username = amp_keyword_owner($keyword);
|
||||
$user_cell = array(
|
||||
'username' => array(
|
||||
'template' => '%username%',
|
||||
'username' => $username,
|
||||
)
|
||||
);
|
||||
$cells = array_insert($cells, 5, $user_cell);
|
||||
|
||||
return $cells;
|
||||
}
|
||||
|
||||
?>
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 496 B |
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 243 B |
141
user/plugins/backend/assets/js/theme.js
Normal file
79
user/plugins/backend/plugin.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: DarkSleeky Backend
|
||||
Plugin URI: https://sophia.wtf
|
||||
Description: UI overhaul of the YOURLS backend
|
||||
Version: 2.4.1
|
||||
Author: Sophia Atkinson
|
||||
Author URI: https://sophia.wtf
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
// Plugin location URL
|
||||
$url = yourls_plugin_url( __DIR__ );
|
||||
|
||||
yourls_add_action( 'html_head', 'init' );
|
||||
|
||||
function init()
|
||||
{
|
||||
echo <<<HEAD
|
||||
<style>body {background: unset;}</style>
|
||||
HEAD;
|
||||
}
|
||||
|
||||
// Inject Sleeky files
|
||||
yourls_add_action( 'html_head', 'sleeky_head_scripts' );
|
||||
|
||||
function sleeky_head_scripts() {
|
||||
|
||||
// This is so the user doesn't have to reload page twice in settings screen
|
||||
if (isset( $_POST['theme_choice'] )) {
|
||||
// User has just changed theme
|
||||
if ($_POST['theme_choice'] == "light") {
|
||||
setTheme("light");
|
||||
} else {
|
||||
setTheme("dark");
|
||||
}
|
||||
} else {
|
||||
// User has not just changed theme
|
||||
if (yourls_get_option( 'theme_choice' ) == "light") {
|
||||
setTheme("light");
|
||||
} else {
|
||||
setTheme("dark");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inject Sleeky files
|
||||
|
||||
function setTheme($theme) {
|
||||
$url = yourls_plugin_url( __DIR__ );
|
||||
if ($theme == "light") {
|
||||
echo <<<HEAD
|
||||
<link rel="stylesheet" href="$url/assets/css/light.css">
|
||||
<link rel="stylesheet" href="$url/assets/css/animate.min.css">
|
||||
<script src="$url/assets/js/theme.js"></script>
|
||||
<meta name="sleeky_theme" content="light">
|
||||
HEAD;
|
||||
} else if ($theme == "dark") {
|
||||
echo <<<HEAD
|
||||
<link rel="stylesheet" href="$url/assets/css/dark.css">
|
||||
<link rel="stylesheet" href="$url/assets/css/animate.min.css">
|
||||
<script src="$url/assets/js/theme.js"></script>
|
||||
<meta name="sleeky_theme" content="dark">
|
||||
HEAD;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject information and options into the frontend
|
||||
yourls_add_action( 'html_head', 'addOptions' );
|
||||
|
||||
function addOptions()
|
||||
{
|
||||
$url = yourls_plugin_url( __DIR__ );
|
||||
echo <<<HEAD
|
||||
<meta name="pluginURL" content="$url">
|
||||
HEAD;
|
||||
}
|
28
user/plugins/custom-protocols/plugin.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Custom Protocols
|
||||
Plugin URI: http://yourls.org/
|
||||
Description: Add custom protocol <code>blah://</code> for trusted users, blacklist all but <code>http</code> and <code>https</code> for others
|
||||
Version: 1.0
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
// Hook into 'kses_allowed_protocols' to modify array. See functions-kses.php/yourls_kses_init()
|
||||
yourls_add_filter( 'kses_allowed_protocols', 'customproto_allowed_protocols' );
|
||||
|
||||
// Whitelist or blacklist protocols depending on user context
|
||||
function customproto_allowed_protocols( $protocols ) {
|
||||
|
||||
if( yourls_is_valid_user() && yourls_is_admin() ) {
|
||||
// if user is logged in, or valid cookie exists on the computer, and we're in admin area:
|
||||
// add custom protocol 'blah://' to authorized protocols
|
||||
$protocols[] = 'blah://';
|
||||
} else {
|
||||
// if no known user: remove all protocols except http & https
|
||||
$protocols = array( 'http://', 'https://' );
|
||||
}
|
||||
|
||||
return $protocols;
|
||||
}
|
||||
|
18
user/plugins/custom-protocols/readme.md
Normal file
@ -0,0 +1,18 @@
|
||||
Plugin for YOURLS 1.6+: Custom Protocols
|
||||
|
||||
# What for
|
||||
|
||||
If the user is known, this plugin adds custom protocol `blah://` to authorized protocols.
|
||||
|
||||
If the user is unknown (using a public interface for instance) then this plugin restricts
|
||||
authorized protocols to `http` and `https` only.
|
||||
|
||||
See [Public Shortening](https://github.com/YOURLS/YOURLS/wiki/Public-Shortening).
|
||||
|
||||
# How to
|
||||
|
||||
* In `/user/plugins`, create a new folder named `custom-protocols`
|
||||
* Drop these files in that directory
|
||||
* Go to the Plugins administration page and activate the plugin
|
||||
* Have fun
|
||||
|
37
user/plugins/force-lowercase/plugin.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Force Lowercase
|
||||
Plugin URI: http://yourls.org/
|
||||
Description: Force lowercase so http://sho.rt/ABC == http://sho.rt/abc
|
||||
Version: 1.0
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
|
||||
/*********************************************************************************
|
||||
* DISCLAIMER *
|
||||
* This is stupid. The web is case sensitive and http://bit.ly/BLAH is different *
|
||||
* from http://bit.ly/blah. Deal with it. More about this: see *
|
||||
* http://www.w3.org/TR/WD-html40-970708/htmlweb.html *
|
||||
* *
|
||||
* This said, lots of users are pestering me for that kind of plugin, so there *
|
||||
* it is. Have fun breaking the web! :) *
|
||||
*********************************************************************************/
|
||||
|
||||
// Redirection: http://sho.rt/ABC first converted to http://sho.rt/abc
|
||||
yourls_add_filter( 'get_request', 'ozh_break_the_web_lowercase' );
|
||||
function ozh_break_the_web_lowercase( $keyword ){
|
||||
return strtolower( $keyword );
|
||||
}
|
||||
|
||||
// Short URL creation: custom keyword 'ABC' converted to 'abc'
|
||||
yourls_add_action( 'add_new_link_custom_keyword', 'ozh_break_the_web_add_filter' );
|
||||
function ozh_break_the_web_add_filter() {
|
||||
yourls_add_filter( 'get_shorturl_charset', 'ozh_break_the_web_add_uppercase' );
|
||||
yourls_add_filter( 'custom_keyword', 'ozh_break_the_web_lowercase' );
|
||||
}
|
||||
function ozh_break_the_web_add_uppercase( $charset ) {
|
||||
return $charset . strtoupper( $charset );
|
||||
}
|
||||
|
21
user/plugins/force-lowercase/readme.md
Normal file
@ -0,0 +1,21 @@
|
||||
Plugin for YOURLS 1.5+: Force Lowercase
|
||||
|
||||
# What for
|
||||
|
||||
Force short urls to lowercase so that http://sho.rt/ABC is the same as http://sho.rt/abc
|
||||
|
||||
# How to
|
||||
|
||||
* In `/user/plugins`, create a new folder named `force-lowercase`
|
||||
* Drop these files in that directory
|
||||
* Go to the Plugins administration page and activate the plugin
|
||||
* Have fun
|
||||
|
||||
# Disclaimer: this is stupid
|
||||
|
||||
Disclaimer: this is **stupid**. The web is case sensitive, http://bit.ly/BLAH is different from http://bit.ly/blah. Deal with it.
|
||||
|
||||
More about this: see http://www.w3.org/TR/WD-html40-970708/htmlweb.html and particularly the part that says:
|
||||
>URLs in general are case-sensitive (with the exception of machine names). There may be URLs, or parts of URLs, where case doesn't matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive.
|
||||
|
||||
This said, lots of users are pestering me for that kind of plugin, so there it is. Have fun breaking the web! :)
|
22
user/plugins/full-stop-in-short-urls/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Allow Full Stops in Short URLs
|
||||
---------------------------------------------
|
||||
|
||||
- Plugin Name: Allow Full Stops in Short URLs
|
||||
- Plugin URI: http://sophia.wtf
|
||||
- Description: Allow Full Stops in Short URLs
|
||||
- Version: 1.0
|
||||
- Author: Sophia Atkinson
|
||||
- Author URI: http://sophia.wtf
|
||||
- Plugin Based Off Of William Bargent's Allow Forward Slashes in Short URLs.
|
||||
|
||||
|
||||
This plugin will allow forward slashes `.` in keywords when shortening URLS with YOURLS.
|
||||
|
||||
*NOTE This plugin will not work with URL Forwarding plugins active. Deactivate before activating this plugin.
|
||||
|
||||
###Installation
|
||||
|
||||
1. Download these file as a .zip.
|
||||
2. Extract the files and copy them into `users/plugins/`.
|
||||
3. Go to your plugin manager and click `Activate`.
|
23
user/plugins/full-stop-in-short-urls/plugin.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Plugin Name: Allow Full Stops in Short URLs
|
||||
Plugin URI: http://sophia.wtf
|
||||
Description: Allow Full Stops in Short URLs
|
||||
Version: 1.0
|
||||
Author: Sophia Atkinson
|
||||
Author URI: http://sophia.wtf
|
||||
Plugin Based Off Of William Bargent's Allow Forward Slashes in Short URLs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
yourls_add_filter( 'get_shorturl_charset', 'full_stop_in_charset' );
|
||||
function full_stop_in_charset( $in ) {
|
||||
return $in.'.';
|
||||
}
|
||||
|
||||
|
||||
//This plugin will work with URL forwarding plugins active!
|
112
user/plugins/google-safe-browsing/plugin.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Google Safe Browsing
|
||||
Plugin URI: https://github.com/yourls/google-safe-browsing/
|
||||
Description: Check new links against Google's Safe Browsing service
|
||||
Version: 1.1
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
yourls_add_filter( 'shunt_add_new_link', 'ozh_yourls_gsb_check_add' );
|
||||
|
||||
/**
|
||||
* Check for spam when someone adds a new link
|
||||
*
|
||||
* The filter used here is 'shunt_add_new_link', which passes in false as first argument. See
|
||||
* https://github.com/YOURLS/YOURLS/blob/1.7/includes/functions.php#L192-L194
|
||||
*
|
||||
* @param bool $false bool false is passed in by the filter 'shunt_add_new_link'
|
||||
* @param string $url URL to check, as passed in by the filter
|
||||
* @return mixed false if nothing to do, anything else will interrupt the flow of events
|
||||
*/
|
||||
function ozh_yourls_gsb_check_add( $false, $url ) {
|
||||
|
||||
list( $blacklisted, $desc ) = ozh_yourls_gsb_is_blacklisted( $url );
|
||||
|
||||
// If blacklisted, halt here
|
||||
if ( $blacklisted ) {
|
||||
return array(
|
||||
'status' => 'fail',
|
||||
'code' => 'error:' . $desc,
|
||||
'message' => 'This domain is blacklisted by Google Safe Browsing because of ' . $desc . ' suspicion. <a href="http://code.google.com/apis/safebrowsing/safebrowsing_faq.html#whyAdvisory" target="_blank">Read more</a>.',
|
||||
'errorCode' => '403',
|
||||
);
|
||||
}
|
||||
|
||||
// If not blacklisted but still unsure (error message), we should warn the user
|
||||
if( $desc ) {
|
||||
define( 'OZH_YOURLS_GSB_EXTRA_INFO', $desc );
|
||||
yourls_add_filter( 'add_new_link', 'ozh_yourls_gsb_extra_info' );
|
||||
}
|
||||
|
||||
// All clear, don't interrupt the normal flow of events
|
||||
return $false;
|
||||
}
|
||||
|
||||
yourls_add_action( 'plugins_loaded', 'ozh_yourls_gsb_add_page' );
|
||||
|
||||
/**
|
||||
* Register our plugin admin page
|
||||
*/
|
||||
function ozh_yourls_gsb_add_page() {
|
||||
yourls_register_plugin_page( 'ozh_yourls_gsb', 'Google Safe Browsing', 'ozh_yourls_gsb_admin_page' );
|
||||
|
||||
if( ! yourls_get_option( 'ozh_yourls_gsb' ) ) {
|
||||
ozh_yourls_gsb_please_configure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra information to the notification when a link has been added
|
||||
*
|
||||
* @param array Array passed in by filter 'add_new_link'
|
||||
* @return array
|
||||
*/
|
||||
function ozh_yourls_gsb_extra_info( $return ) {
|
||||
$return['message'] .= '<br/>(' . OZH_YOURLS_GSB_EXTRA_INFO . ')';
|
||||
$return['status'] = 'error';
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is blacklisted by Google Safe Browsing
|
||||
*
|
||||
* @param string $url URL to check
|
||||
* @return array array( (boolean)is_blacklisted, (string)description )
|
||||
*/
|
||||
function ozh_yourls_gsb_is_blacklisted( $url ) {
|
||||
include_once dirname( __FILE__ ) . '/includes/class-gsb.php';
|
||||
|
||||
$api_key = yourls_get_option( 'ozh_yourls_gsb' );
|
||||
if( !$api_key ) {
|
||||
ozh_yourls_gsb_please_configure();
|
||||
return false;
|
||||
}
|
||||
|
||||
$gsb = new ozh_yourls_GSB( $api_key );
|
||||
|
||||
return $gsb->is_blacklisted( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the admin page
|
||||
*
|
||||
*/
|
||||
function ozh_yourls_gsb_admin_page() {
|
||||
include_once dirname( __FILE__ ) . '/includes/admin-page.php';
|
||||
ozh_yourls_gsb_display_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nag user about missing configuration
|
||||
*
|
||||
*/
|
||||
function ozh_yourls_gsb_please_configure() {
|
||||
yourls_add_notice( 'Plugin <strong>Google Safe Browsing</strong> is not configured' );
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
1139
user/plugins/httpBL/plugin.php
Normal file
6
user/plugins/hyphens-in-urls/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
Hyphens in URLs
|
||||
===============
|
||||
This is a core plugin, bundled with YOURLS.
|
||||
Don't modify this plugin. Instead, copy its folder
|
||||
and modify your own copy. This way, your code won't
|
||||
be overwritten when you upgrade YOURLS.
|
19
user/plugins/hyphens-in-urls/plugin.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Allow Hyphens in Short URLs
|
||||
Plugin URI: http://yourls.org/
|
||||
Description: Allow hyphens in short URLs (like <tt>http://sho.rt/hello-world</tt>)
|
||||
Version: 1.0
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
yourls_add_filter( 'get_shorturl_charset', 'ozh_hyphen_in_charset' );
|
||||
function ozh_hyphen_in_charset( $in ) {
|
||||
return $in.'-';
|
||||
}
|
||||
|
||||
|
7
user/plugins/random-shorturls/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
Random ShortURLs
|
||||
================
|
||||
|
||||
This is a core plugin, bundled with YOURLS.
|
||||
Don't modify this plugin. Instead, copy its folder
|
||||
and modify your own copy. This way, your code won't
|
||||
be overwritten when you upgrade YOURLS.
|
93
user/plugins/random-shorturls/plugin.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Random ShortURLs
|
||||
Plugin URI: https://yourls.org/
|
||||
Description: Assign random keywords to shorturls, like bitly (sho.rt/hJudjK)
|
||||
Version: 1.2
|
||||
Author: Ozh
|
||||
Author URI: https://ozh.org/
|
||||
*/
|
||||
|
||||
/* Release History:
|
||||
*
|
||||
* 1.0 Initial release
|
||||
* 1.1 Added: don't increment sequential keyword counter & save one SQL query
|
||||
* Fixed: plugin now complies to character set defined in config.php
|
||||
* 1.2 Adopted as YOURLS core plugin under a new name
|
||||
* Now configured via YOURLS options instead of editing plugin file
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
// Only register things if the old third-party plugin is not present
|
||||
if( function_exists('ozh_random_keyword') ) {
|
||||
yourls_add_notice( "<b>Random ShortURLs</b> plugin cannot function unless <b>Random Keywords</b> is removed first." );
|
||||
} else {
|
||||
// filter registration happens conditionally, to avoid conflicts
|
||||
// settings action is left out here, as it allows checking settings before deleting the old plugin
|
||||
yourls_add_filter( 'random_keyword', 'ozh_random_shorturl' );
|
||||
yourls_add_filter( 'get_next_decimal', 'ozh_random_shorturl_next_decimal' );
|
||||
}
|
||||
|
||||
// Generate a random keyword
|
||||
function ozh_random_shorturl() {
|
||||
$possible = yourls_get_shorturl_charset() ;
|
||||
$str='';
|
||||
while( strlen( $str ) < yourls_get_option( 'random_shorturls_length', 5 ) ) {
|
||||
$str .= substr($possible, rand( 0, strlen( $possible ) - 1 ), 1 );
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
// Don't increment sequential keyword tracker
|
||||
function ozh_random_shorturl_next_decimal( $next ) {
|
||||
return ( $next - 1 );
|
||||
}
|
||||
|
||||
// Plugin settings page etc.
|
||||
yourls_add_action( 'plugins_loaded', 'ozh_random_shorturl_add_settings' );
|
||||
function ozh_random_shorturl_add_settings() {
|
||||
yourls_register_plugin_page( 'random_shorturl_settings', 'Random ShortURLs Settings', 'ozh_random_shorturl_settings_page' );
|
||||
}
|
||||
|
||||
function ozh_random_shorturl_settings_page() {
|
||||
// Check if form was submitted
|
||||
if( isset( $_POST['random_length'] ) ) {
|
||||
// If so, verify nonce
|
||||
yourls_verify_nonce( 'random_shorturl_settings' );
|
||||
// and process submission if nonce is valid
|
||||
ozh_random_shorturl_settings_update();
|
||||
}
|
||||
|
||||
$random_length = yourls_get_option('random_shorturls_length', 5);
|
||||
$nonce = yourls_create_nonce( 'random_shorturl_settings' );
|
||||
|
||||
echo <<<HTML
|
||||
<main>
|
||||
<h2>Random ShortURLs Settings</h2>
|
||||
<form method="post">
|
||||
<input type="hidden" name="nonce" value="$nonce" />
|
||||
<p>
|
||||
<label>Random Keyword Length</label>
|
||||
<input type="number" name="random_length" min="1" max="128" value="$random_length" />
|
||||
</p>
|
||||
<p><input type="submit" value="Save" class="button" /></p>
|
||||
</form>
|
||||
</main>
|
||||
HTML;
|
||||
}
|
||||
|
||||
function ozh_random_shorturl_settings_update() {
|
||||
$random_length = $_POST['random_length'];
|
||||
|
||||
if( $random_length ) {
|
||||
if( is_numeric( $random_length ) ) {
|
||||
yourls_update_option( 'random_shorturls_length', intval( $random_length ) );
|
||||
} else {
|
||||
echo "Error: Length given was not a number.";
|
||||
}
|
||||
} else {
|
||||
echo "Error: No length value given.";
|
||||
}
|
||||
}
|
22
user/plugins/reverseproxy/plugin.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: ReverseProxy
|
||||
Plugin URI: https://github.com/Diftraku/yourls_cloudflare/
|
||||
Description: Fixes incoming IPs to use the client IP from reverse proxies
|
||||
Version: 2.0
|
||||
Author: Diftraku
|
||||
*/
|
||||
|
||||
// Block direct access to the plugin
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
// Add a filter to get_IP for the real IP instead of the reverse proxy
|
||||
yourls_add_filter( 'get_IP', 'reverseproxy_get_ip');
|
||||
function reverseproxy_get_ip( $ip ) {
|
||||
if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
|
||||
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
|
||||
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
return yourls_sanitize_ip( $ip );
|
||||
}
|
3
user/plugins/telepathics-yourls-emojis/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
/vendor/
|
||||
/build/
|
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Javan Eskander
|
||||
Copyright (c) 2021 telepathics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
34
user/plugins/telepathics-yourls-emojis/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# YOURLS Emojis
|
||||
A YOURLS plugin that allows emojis in the custom short URLs.
|
||||
## Description
|
||||
Emojis in domain names and URLs are becoming increasingly popular, so I thought it would be fun to be able to include them in your YOURLS short codes.
|
||||
|
||||
[](https://github.com/YOURLS/awesome-yourls/)
|
||||
[](https://github.com/sponsors/telepathics)
|
||||
[](https://github.com/telepathics/yourls-emojis/actions/workflows/php.yml)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Unzip the [latest release](https://github.com/telepathics/yourls-emojis/releases) and move it into your YOURLS `/user/plugins` folder
|
||||
2. Visit your plugins page (e.g. https://sho.rt/admin/plugins.php)
|
||||
3. Activate the "Emojis" plugin by telepathics
|
||||
4. Have fun!
|
||||
|
||||
### Upgrading
|
||||
1. Delete (or replace) the old folder
|
||||
2. Follow aforementioned installation instructions
|
||||
|
||||
## Contributing
|
||||
|
||||
Feature suggestion? Bug to report?
|
||||
|
||||
__Before opening any issue, please search for existing [issues](https://github.com/telepathics/yourls-emojis/issues) (open and closed) and read the [Contributing Guidelines](https://github.com/telepathics/yourls-emojis/blob/main/.github/CONTRIBUTING.md).__
|
||||
|
||||
Also visit the living [to do](https://github.com/telepathics/yourls-emojis/projects/1) kanban board and [discussions](https://github.com/telepathics/yourls-emojis/discussions) page.
|
||||
|
||||
## License
|
||||
Released under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
|
||||
See also:
|
||||
[YOURLS](https://github.com/YOURLS/YOURLS) ♡
|
||||
[SteppingHat/php-emoji-detector](https://github.com/SteppingHat/php-emoji-detector) ♡ [unicode.org](https://unicode.org/Public/emoji/13.1/emoji-test.txt)
|
10
user/plugins/telepathics-yourls-emojis/composer.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "telepathics/yourls-emojis",
|
||||
"description": "A YOURLS plugin that allows emojis in the custom short URLs",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/telepathics/yourls-emojis",
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"steppinghat/emoji-detector": "^1.1"
|
||||
}
|
||||
}
|
63
user/plugins/telepathics-yourls-emojis/composer.lock
generated
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d8ae5b9fddab58bd68089f83a8564e1e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "steppinghat/emoji-detector",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SteppingHat/php-emoji-detector.git",
|
||||
"reference": "d2301e9553795e1ac2f3f2638438b3808654c57f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SteppingHat/php-emoji-detector/zipball/d2301e9553795e1ac2f3f2638438b3808654c57f",
|
||||
"reference": "d2301e9553795e1ac2f3f2638438b3808654c57f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^5.0@dev"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SteppingHat\\EmojiDetector\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Javan Eskander",
|
||||
"homepage": "https://javaneskander.com"
|
||||
}
|
||||
],
|
||||
"description": "Detect and validate emoji in an input string",
|
||||
"homepage": "https://github.com/steppinghat/emoji-detector",
|
||||
"time": "2021-03-18T06:03:22+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
50
user/plugins/telepathics-yourls-emojis/plugin.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Emojis
|
||||
Description: Create an emoji-only short link, like http://sho.rt/✨ or http://sho.rt/😎🆒🔗
|
||||
Version: 1.0
|
||||
Author: telepathics
|
||||
Author URI: https://telepathics.xyz
|
||||
*/
|
||||
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
require_once(__DIR__ . '/vendor/autoload.php');
|
||||
use SteppingHat\EmojiDetector;
|
||||
|
||||
/*
|
||||
* Accept detected emojis
|
||||
*/
|
||||
yourls_add_filter( 'get_shorturl_charset', 'path_emojis_in_charset');
|
||||
function path_emojis_in_charset($in) {
|
||||
return $in . file_get_contents(__DIR__ . '/util/emojis.txt');
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts URLs that are ONLY emojis
|
||||
*/
|
||||
yourls_add_filter( 'sanitize_url', 'path_emojis_sanitize_url' );
|
||||
function path_emojis_sanitize_url($unsafe_url) {
|
||||
$clean_url = '';
|
||||
$detector = new SteppingHat\EmojiDetector\EmojiDetector();
|
||||
$detect_emoji = $detector->detect(urldecode($unsafe_url));
|
||||
|
||||
if( sizeof($detect_emoji) > 0 ) {
|
||||
foreach ($detect_emoji as $emoji) {
|
||||
$clean_url .= $emoji->getEmoji();
|
||||
}
|
||||
return $clean_url;
|
||||
}
|
||||
return $unsafe_url;
|
||||
}
|
||||
|
||||
/*
|
||||
* filter wrong spacing whoopsies
|
||||
* see @link https://github.com/YOURLS/YOURLS/issues/1303
|
||||
*/
|
||||
yourls_add_filter( 'sanitize_url', 'fix_long_url' );
|
||||
function fix_long_url( $url, $unsafe_url ) {
|
||||
$search = array ( '%2520', '%2521', '%2522', '%2523', '%2524', '%2525', '%2526', '%2527', '%2528', '%2529', '%252A', '%252B', '%252C', '%252D', '%252E', '%252F', '%253D', '%253F', '%255C', '%255F' );
|
||||
$replace = array ( '%20', '%21', '%22', '%23', '%24', '%25', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%2D', '%2E', '%2F', '%3D', '%3F', '%5C', '%5F' );
|
||||
$url = str_ireplace ( $search, $replace ,$url );
|
||||
return yourls_apply_filter( 'after_fix_long_url', $url, $unsafe_url );
|
||||
}
|
16
user/plugins/useragent/plugin.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Change User Agent
|
||||
Plugin URI: http://yourls.org/
|
||||
Description: Identify YOURLS as a vanilla mozilla
|
||||
Version: 1.0
|
||||
Author: Ozh
|
||||
Author URI: http://ozh.org/
|
||||
*/
|
||||
|
||||
yourls_add_filter( 'http_user_agent', 'misterfu_useragent' );
|
||||
|
||||
// My own UA
|
||||
function misterfu_useragent() {
|
||||
return "CodsworthCrawler (SOP.wtf)";
|
||||
}
|
21
user/plugins/yourls-password-protection/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Matthew Ghost
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
24
user/plugins/yourls-password-protection/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
YOURLS Password Protection
|
||||
====================
|
||||
|
||||
Plugin for [YOURLS](http://yourls.org) `1.7.3`.
|
||||
|
||||
Description
|
||||
-----------
|
||||
The *Password Protection* Plugin will give you the ability to password protect any Short URL you want (*Passwords are set individually*)! The plugin will promt the user for a password before redircting them!
|
||||
|
||||
Installation
|
||||
------------
|
||||
1. In `/user/plugins`, create a new folder named `password-protection`.
|
||||
2. Drop these files in that directory.
|
||||
3. Go to the Plugins administration page ( *eg* `http://sho.rt/admin/plugins.php` ) and activate the plugin.
|
||||
3. Configure the plugin ( *eg* `http://sho.rt/admin/plugins.php?page=matthew_pwp` )!
|
||||
4. Have fun!
|
||||
|
||||
Example
|
||||
-------
|
||||

|
||||
|
||||
License
|
||||
-------
|
||||
[Here](LICENSE)
|
339
user/plugins/yourls-password-protection/plugin.php
Normal file
@ -0,0 +1,339 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: YOURLSs Password Protection
|
||||
Plugin URI: https://matc.io/yourls-password
|
||||
Description: This plugin enables the feature of password protecting your short URLs!
|
||||
Version: 1.4
|
||||
Author: Matthew
|
||||
Author URI: https://matc.io
|
||||
*/
|
||||
|
||||
// No direct call
|
||||
if( !defined( 'YOURLS_ABSPATH' ) ) die();
|
||||
|
||||
// Hook our custom function into the 'pre_redirect' event
|
||||
yourls_add_action( 'pre_redirect', 'warning_redirection' );
|
||||
|
||||
// Custom function that will be triggered when the event occurs
|
||||
function warning_redirection( $args ) {
|
||||
$matthew_pwprotection_array = json_decode(yourls_get_option('matthew_pwprotection'), true);
|
||||
if ($matthew_pwprotection_array === false) {
|
||||
yourls_add_option('matthew_pwprotection', 'null');
|
||||
$matthew_pwprotection_array = json_decode(yourls_get_option('matthew_pwprotection'), true);
|
||||
if ($matthew_pwprotection_array === false) {
|
||||
die("Unable to properly enable password protection due to an apparent problem with the database.");
|
||||
}
|
||||
}
|
||||
|
||||
$matthew_pwprotection_fullurl = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
|
||||
$matthew_pwprotection_urlpath = parse_url( $matthew_pwprotection_fullurl, PHP_URL_PATH );
|
||||
$matthew_pwprotection_pathFragments = explode( '/', $matthew_pwprotection_urlpath );
|
||||
$matthew_pwprotection_short = end( $matthew_pwprotection_pathFragments );
|
||||
|
||||
if( array_key_exists( $matthew_pwprotection_short, (array)$matthew_pwprotection_array ) ){
|
||||
// Check if password is submited, and if it matches the DB
|
||||
if( isset( $_POST[ 'password' ] ) && password_verify( $_POST[ 'password' ], $matthew_pwprotection_array[ $matthew_pwprotection_short ]) ){
|
||||
$url = $args[ 0 ];
|
||||
|
||||
// Redirect client
|
||||
header("Location: $url");
|
||||
|
||||
die();
|
||||
} else {
|
||||
$error = ( isset( $_POST[ 'password' ] ) ? "<script>alertify.error(\"Incorrect Password, try again\")</script>" : "");
|
||||
$matthew_ppu = yourls__( "Password Protected URL", "matthew_pwp" ); // Translate Password Title
|
||||
$matthew_ph = yourls__( "Password" , "matthew_pwp" ); // Translate the word Password
|
||||
$matthew_sm = yourls__( "Please enter the password below to continue.", "matthew_pwp" ); // Translate the main message
|
||||
$matthew_submit = yourls__( "Send!" , "matthew_pwp" ); // Translate the Submit button
|
||||
// Displays main "Insert Password" area
|
||||
echo <<<PWP
|
||||
<html>
|
||||
<head>
|
||||
<title>Redirection Notice</title>
|
||||
<style>
|
||||
@import url(https://weloveiconfonts.com/api/?family=fontawesome);
|
||||
@import url(https://meyerweb.com/eric/tools/css/reset/reset.css);
|
||||
[class*="fontawesome-"]:before {
|
||||
font-family: 'FontAwesome', sans-serif;
|
||||
}
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
*:before, *:after {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #2c3338;
|
||||
color: #606468;
|
||||
font: 87.5%/1.5em 'Open Sans', sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #eee;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
input {
|
||||
border: none;
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5em;
|
||||
padding: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
*zoom: 1;
|
||||
}
|
||||
.clearfix:before, .clearfix:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.container {
|
||||
left: 50%;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
#login {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
#login form span {
|
||||
background-color: #363b41;
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
color: #606468;
|
||||
display: block;
|
||||
float: left;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#login form input {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#login form input[type="text"], input[type="password"] {
|
||||
background-color: #3b4148;
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
color: #606468;
|
||||
margin-bottom: 1em;
|
||||
padding: 0 16px;
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
#login form input[type="submit"] {
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
background-color: #ea4c88;
|
||||
color: #eee;
|
||||
font-weight: bold;
|
||||
margin-bottom: 2em;
|
||||
text-transform: uppercase;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
#login form input[type="submit"]:hover {
|
||||
background-color: #d44179;
|
||||
}
|
||||
|
||||
#login > p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#login > p span {
|
||||
padding-left: 5px;
|
||||
}
|
||||
</style>
|
||||
<!-- JavaScript -->
|
||||
<script src="//cdn.jsdelivr.net/npm/alertifyjs@1.11.4/build/alertify.min.js"></script>
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/alertifyjs@1.11.4/build/css/alertify.min.css"/>
|
||||
<!-- Default theme -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/alertifyjs@1.11.4/build/css/themes/default.min.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="login">
|
||||
<form method="post">
|
||||
<fieldset class="clearfix">
|
||||
<p><span class="fontawesome-lock"></span><input type="password" name="password" value="Password" onBlur="if(this.value == '') this.value = 'Password'" onFocus="if(this.value == 'Password') this.value = ''" required></p>
|
||||
<p><input type="submit" value="$matthew_submit"></p>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
$error
|
||||
</body>
|
||||
</html>
|
||||
PWP;
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register plugin page in admin page
|
||||
yourls_add_action( 'plugins_loaded', 'matthew_pwprotection_display_panel' );
|
||||
function matthew_pwprotection_display_panel() {
|
||||
yourls_register_plugin_page( 'matthew_pwp', 'Password Protection', 'matthew_pwprotection_display_page' );
|
||||
}
|
||||
|
||||
// Function which will draw the admin page
|
||||
function matthew_pwprotection_display_page() {
|
||||
if( isset( $_POST[ 'checked' ] ) && isset( $_POST[ 'password' ] ) || isset( $_POST[ 'unchecked' ] ) ) {
|
||||
matthew_pwprotection_process_new();
|
||||
matthew_pwprotection_process_display();
|
||||
} else {
|
||||
if(yourls_get_option('matthew_pwprotection') !== false){
|
||||
yourls_add_option( 'matthew_pwprotection', 'null' );
|
||||
}
|
||||
matthew_pwprotection_process_display();
|
||||
}
|
||||
}
|
||||
|
||||
// Set/Delete password from DB
|
||||
function matthew_pwprotection_process_new() {
|
||||
// Verify nonce token.
|
||||
yourls_verify_nonce( "matthew_pwprotection_update" );
|
||||
|
||||
$matthew_pwprotection_array = json_decode(yourls_get_option('matthew_pwprotection'), true);
|
||||
|
||||
foreach( $_POST[ 'password' ] as $url => $url_password) {
|
||||
if($url_password != "DONOTCHANGE_8fggwrFrRXvqndzw") {
|
||||
$_POST[ 'password' ][ $url ] = password_hash($url_password, PASSWORD_BCRYPT);
|
||||
} else {
|
||||
$_POST[ 'password' ][ $url ] = $matthew_pwprotection_array[ $url ];
|
||||
}
|
||||
}
|
||||
|
||||
// Update database
|
||||
yourls_update_option( 'matthew_pwprotection', json_encode( $_POST[ 'password' ] ) );
|
||||
|
||||
echo "<p style='color: green'>Success!</p>";
|
||||
}
|
||||
|
||||
// Display Form
|
||||
function matthew_pwprotection_process_display() {
|
||||
$ydb = yourls_get_db();
|
||||
|
||||
$table = YOURLS_DB_TABLE_URL;
|
||||
$sql = "SELECT * FROM `$table` WHERE 1=1";
|
||||
$query = $ydb->fetchAll( $sql );
|
||||
|
||||
$matthew_su = yourls__( "Short URL" , "matthew_pwp" ); // Translate "Short URL"
|
||||
$matthew_ou = yourls__( "Original URL", "matthew_pwp" ); // Translate "Original URL"
|
||||
$matthew_pw = yourls__( "Password" , "matthew_pwp" ); // Translate "Password"
|
||||
|
||||
// Protect action with nonce
|
||||
$matthew_pwprotection_noncefield = yourls_nonce_field( "matthew_pwprotection_update" );
|
||||
|
||||
echo <<<TB
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tr:nth-child(even){background-color: #313131}
|
||||
tr:nth-child(odd){background-color: #232323}
|
||||
</style>
|
||||
<div style="overflow-x:auto;">
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<th>$matthew_su</th>
|
||||
<th>$matthew_ou</th>
|
||||
<th>$matthew_pw</th>
|
||||
</tr>
|
||||
TB;
|
||||
|
||||
foreach( $query as $link ) { // Displays all shorturls in the YOURLS DB
|
||||
$short = $link["keyword"];
|
||||
$url = $link["url"];
|
||||
$matthew_pwprotection_array = json_decode(yourls_get_option('matthew_pwprotection'), true); // Get array of currently active Password Protected URLs
|
||||
if( strlen( $url ) > 51 ) { // If URL is too long, shorten it with '...'
|
||||
$sURL = substr( $url, 0, 30 ). "...";
|
||||
} else {
|
||||
$sURL = $url;
|
||||
}
|
||||
if( array_key_exists( $short, (array)$matthew_pwprotection_array ) ){ // Check if URL is currently password protected or not
|
||||
$text = yourls__( "Enable?" );
|
||||
$password = "DONOTCHANGE_8fggwrFrRXvqndzw";
|
||||
$checked = " checked";
|
||||
$unchecked = '';
|
||||
$style = '';
|
||||
$disabled = '';
|
||||
} else {
|
||||
$text = yourls__( "Enable?" );
|
||||
$password = '';
|
||||
$checked = '';
|
||||
$unchecked = ' disabled';
|
||||
$style = 'display: none';
|
||||
$disabled = ' disabled';
|
||||
}
|
||||
|
||||
echo <<<TABLE
|
||||
<tr>
|
||||
<td>$short</td>
|
||||
<td><span title="$url">$sURL</span></td>
|
||||
<td>
|
||||
<input type="checkbox" name="checked[{$short}]" class="matthew_pwprotection_checkbox" value="enable" data-input="$short"$checked> $text
|
||||
<input type="hidden" name="unchecked[{$short}]" id="{$short}_hidden" value="true"$unchecked>
|
||||
<input id="$short" type="password" name="password[$short]" style="$style" value="$password" placeholder="Password..."$disabled ><br>
|
||||
</td>
|
||||
</tr>
|
||||
TABLE;
|
||||
}
|
||||
echo <<<END
|
||||
</table>
|
||||
$matthew_pwprotection_noncefield
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
$( ".matthew_pwprotection_checkbox" ).click(function() {
|
||||
var dataAttr = "#" + this.dataset.input;
|
||||
$( dataAttr ).toggle();
|
||||
if( $( dataAttr ).attr( 'disabled' ) ) {
|
||||
$( dataAttr ).removeAttr( 'disabled' );
|
||||
|
||||
$( dataAttr + "_hidden" ).attr( 'disabled' );
|
||||
$( dataAttr + "_hidden" ).prop('disabled', true);
|
||||
} else {
|
||||
$( dataAttr ).attr( 'disabled' );
|
||||
$( dataAttr ).prop('disabled', true);
|
||||
|
||||
$( dataAttr + "_hidden" ).removeAttr( 'disabled' );
|
||||
}
|
||||
});
|
||||
</script>
|
||||
END;
|
||||
}
|
||||
?>
|
21
user/plugins/yourls-preview-url-with-qrcode-master/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Denny Dai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
19
user/plugins/yourls-preview-url-with-qrcode-master/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
Preview URL with QR Code
|
||||
====================
|
||||
|
||||
Plugin for [YOURLS](http://yourls.org) `1.5+`.
|
||||
|
||||
Description
|
||||
-----------
|
||||
Add the character '~' to a short URL to display a preview screen & QR code before redirection
|
||||
|
||||
Installation
|
||||
------------
|
||||
1. In `/user/plugins`, create a new folder named `preview-url-with-qrcode`.
|
||||
2. Drop these files in that directory.
|
||||
3. Go to the Plugins administration page ( *eg* `http://sho.rt/admin/plugins.php` ) and activate the plugin.
|
||||
4. Have fun!
|
||||
|
||||
License
|
||||
-------
|
||||
MIT License
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Preview URL with QR Code
|
||||
Plugin URI: https://github.com/dennydai
|
||||
Description: Preview URLs before you're redirected there
|
||||
Version: 1.0
|
||||
Author: Denny Dai
|
||||
Author URI: https://dennydai.github.io
|
||||
*/
|
||||
|
||||
// EDIT THIS
|
||||
|
||||
// Character to add to a short URL to trigger the preview interruption
|
||||
define( 'DD_PREVIEW_CHAR', '~' );
|
||||
|
||||
// DO NO EDIT FURTHER
|
||||
|
||||
// Handle failed loader request and check if there's a ~
|
||||
yourls_add_action( 'loader_failed', 'dd_preview_loader_failed' );
|
||||
function dd_preview_loader_failed( $args ) {
|
||||
$request = $args[0];
|
||||
$pattern = yourls_make_regexp_pattern( yourls_get_shorturl_charset() );
|
||||
if( preg_match( "@^([$pattern]+)".DD_PREVIEW_CHAR."$@", $request, $matches ) ) {
|
||||
$keyword = isset( $matches[1] ) ? $matches[1] : '';
|
||||
$keyword = yourls_sanitize_keyword( $keyword );
|
||||
dd_preview_show( $keyword );
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
// Show the preview screen for a short URL
|
||||
function dd_preview_show( $keyword ) {
|
||||
require_once( YOURLS_INC.'/functions-html.php' );
|
||||
|
||||
yourls_html_head( 'preview', 'Short URL preview' );
|
||||
yourls_html_logo();
|
||||
|
||||
$title = yourls_get_keyword_title( $keyword );
|
||||
$url = yourls_get_keyword_longurl( $keyword );
|
||||
$base = YOURLS_SITE;
|
||||
$char = DD_PREVIEW_CHAR;
|
||||
$qrcode = 'https://api.qrserver.com/v1/create-qr-code/?size=256x256&format=svg&bgcolor=1D1D1D&color=fff&charset-source=UTF-8&ecc=H&data='.YOURLS_SITE.'/'.$keyword;
|
||||
|
||||
echo <<<HTML
|
||||
<h2>Link Preview</h2>
|
||||
<p>You requested the short URL <strong><a href="$base/$keyword">$base/$keyword</a></strong></p>
|
||||
<p>This short URL points to:</p>
|
||||
<ul>
|
||||
<li>Long URL: <strong><a href="$base/$keyword">$url</a></strong></li>
|
||||
<li>Page title: <strong>$title</strong></li>
|
||||
<li>QR Code: <br><img src="$qrcode"></li>
|
||||
</ul>
|
||||
<p>If you still want to visit this link, please <strong><a href="$base/$keyword">click here</a></strong>.</p>
|
||||
|
||||
<p>Thank you for using the SOP link shortener.</p>
|
||||
HTML;
|
||||
|
||||
yourls_html_footer();
|
||||
}
|
@ -1,45 +1,45 @@
|
||||
h1. YOURLS Pseudonymize Plugin
|
||||
|
||||
This plugin "pseudonymizes" the IP addresses so that it is in line with the German privacy laws.
|
||||
|
||||
This effectively means, that the last segment of an IP address is changed into a 0 ("zero"), thus removed.
|
||||
|
||||
*IPv4 and IPv6 addresses are supported.*
|
||||
|
||||
*NOTE*: Requires PHP >= 5.2.0 due to "filter_var":http://php.net/manual/en/function.filter-var.php usage.
|
||||
|
||||
h2. Download
|
||||
|
||||
Latest version: <a href="https://raw.github.com/ubicoo/yourls-pseudonymize/master/plugin.php">plugin.php</a>
|
||||
|
||||
h2. Install
|
||||
|
||||
Copy plugin.php to *YOURLS_HOME*/user/plugins/yourls-pseudonymize folder and activate via the admin menu.
|
||||
|
||||
h2. Support
|
||||
|
||||
<a href="http://blog.yourls.org/forums/topic/yourls-pseudonymize-plugin/">Discuss about this plugin</a> in the YOURLS forum.
|
||||
|
||||
<a href="https://github.com/ubicoo/yourls-pseudonymize/issues/new">File an issue</a> right here on the GitHub project home.
|
||||
|
||||
h2. MIT License
|
||||
|
||||
Copyright (c) 2010 Ubicoo - http://www.ubicoo.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
h1. YOURLS Pseudonymize Plugin
|
||||
|
||||
This plugin "pseudonymizes" the IP addresses so that it is in line with the German privacy laws.
|
||||
|
||||
This effectively means, that the last segment of an IP address is changed into a 0 ("zero"), thus removed.
|
||||
|
||||
*IPv4 and IPv6 addresses are supported.*
|
||||
|
||||
*NOTE*: Requires PHP >= 5.2.0 due to "filter_var":http://php.net/manual/en/function.filter-var.php usage.
|
||||
|
||||
h2. Download
|
||||
|
||||
Latest version: <a href="https://raw.github.com/ubicoo/yourls-pseudonymize/master/plugin.php">plugin.php</a>
|
||||
|
||||
h2. Install
|
||||
|
||||
Copy plugin.php to *YOURLS_HOME*/user/plugins/yourls-pseudonymize folder and activate via the admin menu.
|
||||
|
||||
h2. Support
|
||||
|
||||
<a href="http://blog.yourls.org/forums/topic/yourls-pseudonymize-plugin/">Discuss about this plugin</a> in the YOURLS forum.
|
||||
|
||||
<a href="https://github.com/ubicoo/yourls-pseudonymize/issues/new">File an issue</a> right here on the GitHub project home.
|
||||
|
||||
h2. MIT License
|
||||
|
||||
Copyright (c) 2010 Ubicoo - http://www.ubicoo.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
34
user/plugins/yourls-pseudonymize-master/plugin.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Pseudonymize Plugin
|
||||
Plugin URI: http://github.com/ubicoo/yourls-pseudonymize
|
||||
Description: Pseudonymize IP addresses (remove last segment). Supports IPv4 and IPv6.
|
||||
Version: 1.1
|
||||
Author: Ubicoo
|
||||
Author URI: http://www.ubicoo.com
|
||||
*/
|
||||
|
||||
|
||||
yourls_add_filter( 'get_IP', 'ubicoo_pseudonymize_IP' );
|
||||
|
||||
function ubicoo_pseudonymize_IP( $ip ) {
|
||||
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$segments = explode(":", $ip);
|
||||
$segments[count($segments)-1] = 0;
|
||||
|
||||
$pseudo_IP = implode(":", $segments);
|
||||
|
||||
# FIXME: also handle IPv4 addresses at the end of IPv6, like ::ffff:127.0.0.1
|
||||
|
||||
} elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$segments = explode(".", $ip);
|
||||
$segments[3] = 0;
|
||||
|
||||
$pseudo_IP = implode(".", $segments);
|
||||
} else {
|
||||
$pseudo_IP = $ip;
|
||||
}
|
||||
|
||||
return $pseudo_IP;
|
||||
}
|
||||
|
141
user/theme.js
@ -1,141 +0,0 @@
|
||||
console.log("SOP Proprietary v1.9");
|
||||
|
||||
$( document ).ready(function() {
|
||||
// Get the theme URL
|
||||
var url;
|
||||
if ($('meta[name=pluginURL]').attr("content")) {
|
||||
url = $('meta[name=pluginURL]').attr("content");
|
||||
} else {
|
||||
// If for some reason we can't find the URL attribute
|
||||
url = "/user/plugins/backend";
|
||||
}
|
||||
|
||||
// Detect theme
|
||||
var theme;
|
||||
if ($('meta[name=sleeky_theme]').attr("content") == 'light') {
|
||||
theme = "light";
|
||||
} else if ($('meta[name=sleeky_theme]').attr("content") == 'dark') {
|
||||
theme = "dark";
|
||||
}
|
||||
|
||||
console.log("Theme is", theme)
|
||||
|
||||
// Update favicon
|
||||
$('link[rel="shortcut icon"]').attr('href', url + "/images/favicon.ico");
|
||||
|
||||
// Update meta viewport
|
||||
$('head').append('<meta name="viewport" content="width=device-width, initial-scale=1.0">');
|
||||
|
||||
// Detect pages
|
||||
if ($("body").hasClass("login")) {
|
||||
// Login page
|
||||
console.log("Login page");
|
||||
|
||||
if (theme == "light") {
|
||||
$("#login").prepend(`<img class="login-logo" src="${url}/assets/img/logo_black.png">`);
|
||||
} else if (theme == "dark") {
|
||||
$("#login").prepend(`<img class="login-logo" src="${url}/assets/img/logo_white.png">`);
|
||||
}
|
||||
|
||||
|
||||
} else if ($("body").hasClass("index")) {
|
||||
// Index page
|
||||
console.log("Index page");
|
||||
|
||||
handleNav()
|
||||
|
||||
// Hide YOURLS new URL section
|
||||
$("#new_url").hide();
|
||||
|
||||
// Grab the nonce id
|
||||
var nonce = $("#nonce-add").val();
|
||||
|
||||
// Remove the YOURLS new URL Section
|
||||
$("#new_url").remove();
|
||||
|
||||
// Create the sleeky new URL section from the template
|
||||
$("nav").append($('<div>').load(`${url}/assets/html/form.html`, function () {
|
||||
$("#nonce-add").val(nonce);
|
||||
}));
|
||||
} else if ($("body").hasClass("tools")) {
|
||||
// Tools page
|
||||
console.log("Tools page");
|
||||
|
||||
handleNav()
|
||||
|
||||
} else if ($("body").hasClass("plugins")) {
|
||||
// Plugins page
|
||||
console.log("Plugins page");
|
||||
|
||||
handleNav()
|
||||
|
||||
} else if ($("body").hasClass("plugin_page_sleeky_settings")) {
|
||||
// Tools page
|
||||
console.log("Sleeky Settings Page");
|
||||
|
||||
handleNav()
|
||||
|
||||
$("#ui_selector").val($("#ui_selector").attr("value"));
|
||||
|
||||
} else if ($("body").hasClass("infos")) {
|
||||
// Information page
|
||||
console.log("Information page");
|
||||
|
||||
handleNav()
|
||||
|
||||
$("#historical_clicks li").each(function (index) {
|
||||
if (index % 2 != 0) {
|
||||
$("#historical_clicks li").eq(index).css("background", "");
|
||||
}
|
||||
})
|
||||
|
||||
// Update tab headers
|
||||
var titles = ['Statistics', 'Location', 'Sources']
|
||||
for (let i = 0; i < 3; i++) {
|
||||
$($('#headers > li')[i]).find('h2').text(titles[i]);
|
||||
}
|
||||
} else {
|
||||
console.warn("Unknown page");
|
||||
|
||||
handleNav();
|
||||
}
|
||||
|
||||
function handleNav() {
|
||||
// Add logo
|
||||
$("#wrap").prepend(`<img class="logo" src="${url}/assets/img/logo_white.png">`);
|
||||
|
||||
// Add mobile nav hamburger
|
||||
$("#wrap").prepend(`<div class="nav-open" id="navOpen"><i class="material-icons">menu</i></div>`);
|
||||
|
||||
// Add frontend link
|
||||
$('#admin_menu').append('<li class="admin_menu_toplevel frontend_link"><a href="/"><i class="material-icons">arrow_back</i> Frontend Interface</a></li>');
|
||||
|
||||
// admin_menu
|
||||
$('#navOpen').on('click', function() {
|
||||
$('#admin_menu').slideToggle();
|
||||
})
|
||||
|
||||
$(window).resize(function () {
|
||||
if ($(window).width() > 899) {
|
||||
$('#admin_menu').show();
|
||||
} else {
|
||||
$('#admin_menu').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update P elements
|
||||
$("p").each(function (index) {
|
||||
if (/Display/.test($(this).text()) || /Overall/.test($(this).text())) {
|
||||
// Move info on index page to the bottom
|
||||
$("main").append("<p>" + $(this).html() + "</p>");
|
||||
$(this).remove();
|
||||
} else if (/Powered by/.test($(this).text())) {
|
||||
// Update footer
|
||||
var content = $(this).html();
|
||||
var i = 77
|
||||
var updated_content = 'Running <a href="https://sop.wtf/YOURLS">YOURLS</a> &' + ' <a href="https://sop.wtf/darksleeky">Dark Sleeky</a>' + content.slice(i-1)
|
||||
$(this).html(updated_content);
|
||||
}
|
||||
});
|
||||
});
|