Fixed Broken Shit

This commit is contained in:
2022-10-30 14:32:20 -07:00
parent 7335796263
commit 4dabf5a6bf
635 changed files with 74885 additions and 17688 deletions

View File

@ -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;
}

View 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);
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -1,175 +0,0 @@
PHP Emoji Detector
==================
[![Latest Stable Version](https://poser.pugx.org/steppinghat/emoji-detector/v)](//packagist.org/packages/steppinghat/emoji-detector) [![Total Downloads](https://poser.pugx.org/steppinghat/emoji-detector/downloads)](//packagist.org/packages/steppinghat/emoji-detector) [![License](https://poser.pugx.org/steppinghat/emoji-detector/license)](//packagist.org/packages/steppinghat/emoji-detector) [![Build Status](https://travis-ci.com/SteppingHat/php-emoji-detector.svg?branch=master)](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)

View File

@ -1,7 +0,0 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitb527cf0f410cc8001ea07d072eb1427c::getLoader();

View File

@ -1,9 +0,0 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -1,9 +0,0 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -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'),
);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
View File

@ -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"
}

View File

@ -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' );

View File

@ -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"
}
]

File diff suppressed because one or more lines are too long

View File

@ -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>

View 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.

View 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 );
}
}
}
?>

View 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.

View 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 />";
}
}

View 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`.

View 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

View 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&copy; (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/>.

View 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 />";
}
}
?>

View 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 );
}

View 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.

View 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;
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

View File

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

View File

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 243 B

File diff suppressed because one or more lines are too long

View 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;
}

View 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;
}

View 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

View 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 );
}

View 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! :)

View 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`.

View 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!

View 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' );
}

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because it is too large Load Diff

View 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.

View 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.'-';
}

View 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.

View 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.";
}
}

View 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 );
}

View File

@ -0,0 +1,3 @@
.DS_Store
/vendor/
/build/

View File

@ -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

View 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.
[![Mentioned in Awesome YOURLS](https://awesome.re/mentioned-badge.svg)](https://github.com/YOURLS/awesome-yourls/)
[![Github Sponsors](https://img.shields.io/badge/sponsors-4-green.svg)](https://github.com/sponsors/telepathics)
[![PHP Composer](https://github.com/telepathics/yourls-emojis/actions/workflows/php.yml/badge.svg?branch=main)](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)

View 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"
}
}

View 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"
}

View 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 );
}

View 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)";
}

View 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.

View 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
-------
![Password Manager Example](https://mateoc.net/b_plugin/yourls_PasswordProtection/yourlsPasswordManager-1.1.gif "Password Manager Example")
License
-------
[Here](LICENSE)

View 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;
}
?>

View 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.

View 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

View File

@ -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();
}

View File

@ -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.

View 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;
}

File diff suppressed because one or more lines are too long

View File

@ -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);
}
});
});